• 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/render/cpdf_renderstatus.h"
8 
9 #include <stdint.h>
10 
11 #include <algorithm>
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_pageimagecache.h"
33 #include "core/fpdfapi/page/cpdf_pageobject.h"
34 #include "core/fpdfapi/page/cpdf_pathobject.h"
35 #include "core/fpdfapi/page/cpdf_shadingobject.h"
36 #include "core/fpdfapi/page/cpdf_shadingpattern.h"
37 #include "core/fpdfapi/page/cpdf_textobject.h"
38 #include "core/fpdfapi/page/cpdf_tilingpattern.h"
39 #include "core/fpdfapi/page/cpdf_transferfunc.h"
40 #include "core/fpdfapi/parser/cpdf_array.h"
41 #include "core/fpdfapi/parser/cpdf_document.h"
42 #include "core/fpdfapi/parser/cpdf_stream.h"
43 #include "core/fpdfapi/parser/fpdf_parser_utility.h"
44 #include "core/fpdfapi/render/charposlist.h"
45 #include "core/fpdfapi/render/cpdf_docrenderdata.h"
46 #include "core/fpdfapi/render/cpdf_imagerenderer.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_rendertiling.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/check.h"
55 #include "core/fxcrt/compiler_specific.h"
56 #include "core/fxcrt/containers/contains.h"
57 #include "core/fxcrt/data_vector.h"
58 #include "core/fxcrt/fx_2d_size.h"
59 #include "core/fxcrt/fx_safe_types.h"
60 #include "core/fxcrt/fx_system.h"
61 #include "core/fxcrt/notreached.h"
62 #include "core/fxcrt/span.h"
63 #include "core/fxcrt/stl_util.h"
64 #include "core/fxcrt/unowned_ptr.h"
65 #include "core/fxge/agg/cfx_agg_imagerenderer.h"
66 #include "core/fxge/cfx_defaultrenderdevice.h"
67 #include "core/fxge/cfx_fillrenderoptions.h"
68 #include "core/fxge/cfx_glyphbitmap.h"
69 #include "core/fxge/cfx_path.h"
70 #include "core/fxge/dib/cfx_dibitmap.h"
71 #include "core/fxge/fx_font.h"
72 #include "core/fxge/renderdevicedriver_iface.h"
73 #include "core/fxge/text_char_pos.h"
74 #include "core/fxge/text_glyph_pos.h"
75 
76 #if BUILDFLAG(IS_WIN)
77 #include "core/fpdfapi/render/cpdf_scaledrenderbuffer.h"
78 #endif
79 
80 namespace {
81 
82 constexpr int kRenderMaxRecursionDepth = 64;
83 int g_CurrentRecursionDepth = 0;
84 
GetFillOptionsForDrawPathWithBlend(const CPDF_RenderOptions::Options & options,const CPDF_PathObject * path_obj,CFX_FillRenderOptions::FillType fill_type,bool is_stroke,bool is_type3_char)85 CFX_FillRenderOptions GetFillOptionsForDrawPathWithBlend(
86     const CPDF_RenderOptions::Options& options,
87     const CPDF_PathObject* path_obj,
88     CFX_FillRenderOptions::FillType fill_type,
89     bool is_stroke,
90     bool is_type3_char) {
91   CFX_FillRenderOptions fill_options(fill_type);
92   if (fill_type != CFX_FillRenderOptions::FillType::kNoFill &&
93       options.bRectAA) {
94     fill_options.rect_aa = true;
95   }
96   if (options.bNoPathSmooth) {
97     fill_options.aliased_path = true;
98   }
99   if (path_obj->general_state().GetStrokeAdjust()) {
100     fill_options.adjust_stroke = true;
101   }
102   if (is_stroke) {
103     fill_options.stroke = true;
104   }
105   if (is_type3_char) {
106     fill_options.text_mode = true;
107   }
108 
109   return fill_options;
110 }
111 
GetFillOptionsForDrawTextPath(const CPDF_RenderOptions::Options & options,const CPDF_TextObject * text_obj,bool is_stroke,bool is_fill)112 CFX_FillRenderOptions GetFillOptionsForDrawTextPath(
113     const CPDF_RenderOptions::Options& options,
114     const CPDF_TextObject* text_obj,
115     bool is_stroke,
116     bool is_fill) {
117   CFX_FillRenderOptions fill_options;
118   if (is_stroke && is_fill) {
119     fill_options.stroke = true;
120     fill_options.stroke_text_mode = true;
121   }
122   if (text_obj->general_state().GetStrokeAdjust()) {
123     fill_options.adjust_stroke = true;
124   }
125   if (options.bNoTextSmooth) {
126     fill_options.aliased_path = true;
127   }
128 
129   return fill_options;
130 }
131 
GetFormatForLuminosity(bool is_luminosity)132 FXDIB_Format GetFormatForLuminosity(bool is_luminosity) {
133   if (!is_luminosity)
134     return FXDIB_Format::k8bppMask;
135 #if BUILDFLAG(IS_APPLE)
136   return FXDIB_Format::kBgrx;
137 #else
138   if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
139     return FXDIB_Format::kBgrx;
140   }
141   return FXDIB_Format::kBgr;
142 #endif
143 }
144 
IsAvailableMatrix(const CFX_Matrix & matrix)145 bool IsAvailableMatrix(const CFX_Matrix& matrix) {
146   if (matrix.a == 0 || matrix.d == 0)
147     return matrix.b != 0 && matrix.c != 0;
148 
149   if (matrix.b == 0 || matrix.c == 0)
150     return matrix.a != 0 && matrix.d != 0;
151 
152   return true;
153 }
154 
MissingFillColor(const CPDF_ColorState * pColorState)155 bool MissingFillColor(const CPDF_ColorState* pColorState) {
156   return !pColorState->HasRef() || pColorState->GetFillColor()->IsNull();
157 }
158 
MissingStrokeColor(const CPDF_ColorState * pColorState)159 bool MissingStrokeColor(const CPDF_ColorState* pColorState) {
160   return !pColorState->HasRef() || pColorState->GetStrokeColor()->IsNull();
161 }
162 
Type3CharMissingFillColor(const CPDF_Type3Char * pChar,const CPDF_ColorState * pColorState)163 bool Type3CharMissingFillColor(const CPDF_Type3Char* pChar,
164                                const CPDF_ColorState* pColorState) {
165   return pChar && (!pChar->colored() || MissingFillColor(pColorState));
166 }
167 
Type3CharMissingStrokeColor(const CPDF_Type3Char * pChar,const CPDF_ColorState * pColorState)168 bool Type3CharMissingStrokeColor(const CPDF_Type3Char* pChar,
169                                  const CPDF_ColorState* pColorState) {
170   return pChar && (!pChar->colored() || MissingStrokeColor(pColorState));
171 }
172 
173 }  // namespace
174 
CPDF_RenderStatus(CPDF_RenderContext * pContext,CFX_RenderDevice * pDevice)175 CPDF_RenderStatus::CPDF_RenderStatus(CPDF_RenderContext* pContext,
176                                      CFX_RenderDevice* pDevice)
177     : m_pContext(pContext), m_pDevice(pDevice) {}
178 
179 CPDF_RenderStatus::~CPDF_RenderStatus() = default;
180 
Initialize(const CPDF_RenderStatus * pParentStatus,const CPDF_GraphicStates * pInitialStates)181 void CPDF_RenderStatus::Initialize(const CPDF_RenderStatus* pParentStatus,
182                                    const CPDF_GraphicStates* pInitialStates) {
183 #if BUILDFLAG(IS_WIN)
184   m_bPrint = m_pDevice->GetDeviceType() == DeviceType::kPrinter;
185 #endif
186   m_pPageResource.Reset(m_pContext->GetPageResources());
187   if (pInitialStates && !m_pType3Char) {
188     m_InitialStates = *pInitialStates;
189     if (pParentStatus) {
190       if (!m_InitialStates.color_state().HasFillColor()) {
191         m_InitialStates.mutable_color_state().SetFillColorRef(
192             pParentStatus->m_InitialStates.color_state().GetFillColorRef());
193         *m_InitialStates.mutable_color_state().GetMutableFillColor() =
194             *pParentStatus->m_InitialStates.color_state().GetFillColor();
195       }
196       if (!m_InitialStates.color_state().HasStrokeColor()) {
197         m_InitialStates.mutable_color_state().SetStrokeColorRef(
198             pParentStatus->m_InitialStates.color_state().GetFillColorRef());
199         *m_InitialStates.mutable_color_state().GetMutableStrokeColor() =
200             *pParentStatus->m_InitialStates.color_state().GetStrokeColor();
201       }
202     }
203   } else {
204     m_InitialStates.SetDefaultStates();
205   }
206 }
207 
RenderObjectList(const CPDF_PageObjectHolder * pObjectHolder,const CFX_Matrix & mtObj2Device)208 void CPDF_RenderStatus::RenderObjectList(
209     const CPDF_PageObjectHolder* pObjectHolder,
210     const CFX_Matrix& mtObj2Device) {
211   CFX_FloatRect clip_rect = mtObj2Device.GetInverse().TransformRect(
212       CFX_FloatRect(m_pDevice->GetClipBox()));
213   for (const auto& pCurObj : *pObjectHolder) {
214     if (pCurObj.get() == m_pStopObj) {
215       m_bStopped = true;
216       return;
217     }
218     if (!pCurObj || !pCurObj->IsActive()) {
219       continue;
220     }
221 
222     if (pCurObj->GetRect().left > clip_rect.right ||
223         pCurObj->GetRect().right < clip_rect.left ||
224         pCurObj->GetRect().bottom > clip_rect.top ||
225         pCurObj->GetRect().top < clip_rect.bottom) {
226       continue;
227     }
228     RenderSingleObject(pCurObj.get(), mtObj2Device);
229     if (m_bStopped)
230       return;
231   }
232 }
233 
RenderSingleObject(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)234 void CPDF_RenderStatus::RenderSingleObject(CPDF_PageObject* pObj,
235                                            const CFX_Matrix& mtObj2Device) {
236   AutoRestorer<int> restorer(&g_CurrentRecursionDepth);
237   if (++g_CurrentRecursionDepth > kRenderMaxRecursionDepth) {
238     return;
239   }
240   m_pCurObj = pObj;
241   if (!m_Options.CheckPageObjectVisible(pObj)) {
242     return;
243   }
244   ProcessClipPath(pObj->clip_path(), mtObj2Device);
245   if (ProcessTransparency(pObj, mtObj2Device)) {
246     return;
247   }
248   ProcessObjectNoClip(pObj, mtObj2Device);
249 }
250 
ContinueSingleObject(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device,PauseIndicatorIface * pPause)251 bool CPDF_RenderStatus::ContinueSingleObject(CPDF_PageObject* pObj,
252                                              const CFX_Matrix& mtObj2Device,
253                                              PauseIndicatorIface* pPause) {
254   if (m_pImageRenderer) {
255     if (m_pImageRenderer->Continue(pPause))
256       return true;
257 
258     if (!m_pImageRenderer->GetResult())
259       DrawObjWithBackground(pObj, mtObj2Device);
260     m_pImageRenderer.reset();
261     return false;
262   }
263 
264   m_pCurObj = pObj;
265   if (!m_Options.CheckPageObjectVisible(pObj))
266     return false;
267 
268   ProcessClipPath(pObj->clip_path(), mtObj2Device);
269   if (ProcessTransparency(pObj, mtObj2Device))
270     return false;
271 
272   if (!pObj->IsImage()) {
273     ProcessObjectNoClip(pObj, mtObj2Device);
274     return false;
275   }
276 
277   m_pImageRenderer = std::make_unique<CPDF_ImageRenderer>(this);
278   if (!m_pImageRenderer->Start(pObj->AsImage(), mtObj2Device,
279                                /*bStdCS=*/false)) {
280     if (!m_pImageRenderer->GetResult())
281       DrawObjWithBackground(pObj, mtObj2Device);
282     m_pImageRenderer.reset();
283     return false;
284   }
285   return ContinueSingleObject(pObj, mtObj2Device, pPause);
286 }
287 
GetObjectClippedRect(const CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device) const288 FX_RECT CPDF_RenderStatus::GetObjectClippedRect(
289     const CPDF_PageObject* pObj,
290     const CFX_Matrix& mtObj2Device) const {
291   FX_RECT rect = pObj->GetTransformedBBox(mtObj2Device);
292   rect.Intersect(m_pDevice->GetClipBox());
293   return rect;
294 }
295 
ProcessObjectNoClip(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)296 void CPDF_RenderStatus::ProcessObjectNoClip(CPDF_PageObject* pObj,
297                                             const CFX_Matrix& mtObj2Device) {
298   bool bRet = false;
299   switch (pObj->GetType()) {
300     case CPDF_PageObject::Type::kText:
301       bRet = ProcessText(pObj->AsText(), mtObj2Device, nullptr);
302       break;
303     case CPDF_PageObject::Type::kPath:
304       bRet = ProcessPath(pObj->AsPath(), mtObj2Device);
305       break;
306     case CPDF_PageObject::Type::kImage:
307       bRet = ProcessImage(pObj->AsImage(), mtObj2Device);
308       break;
309     case CPDF_PageObject::Type::kShading:
310       ProcessShading(pObj->AsShading(), mtObj2Device);
311       return;
312     case CPDF_PageObject::Type::kForm:
313       bRet = ProcessForm(pObj->AsForm(), mtObj2Device);
314       break;
315   }
316   if (!bRet)
317     DrawObjWithBackground(pObj, mtObj2Device);
318 }
319 
DrawObjWithBlend(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)320 bool CPDF_RenderStatus::DrawObjWithBlend(CPDF_PageObject* pObj,
321                                          const CFX_Matrix& mtObj2Device) {
322   switch (pObj->GetType()) {
323     case CPDF_PageObject::Type::kPath:
324       return ProcessPath(pObj->AsPath(), mtObj2Device);
325     case CPDF_PageObject::Type::kImage:
326       return ProcessImage(pObj->AsImage(), mtObj2Device);
327     case CPDF_PageObject::Type::kForm:
328       return ProcessForm(pObj->AsForm(), mtObj2Device);
329     case CPDF_PageObject::Type::kText:
330     case CPDF_PageObject::Type::kShading:
331       return false;
332   }
333 }
334 
DrawObjWithBackground(CPDF_PageObject * pObj,const CFX_Matrix & mtObj2Device)335 void CPDF_RenderStatus::DrawObjWithBackground(CPDF_PageObject* pObj,
336                                               const CFX_Matrix& mtObj2Device) {
337   FX_RECT rect = GetObjectClippedRect(pObj, mtObj2Device);
338   if (rect.IsEmpty())
339     return;
340 
341   const bool needs_buffer =
342       !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_GET_BITS);
343   if (!needs_buffer) {
344     DrawObjWithBackgroundToDevice(pObj, mtObj2Device, m_pDevice, CFX_Matrix());
345     return;
346   }
347 
348 #if BUILDFLAG(IS_WIN)
349   CPDF_ScaledRenderBuffer buffer(m_pDevice, rect);
350   int res = (pObj->IsImage() && IsPrint()) ? 0 : 300;
351   if (!buffer.Initialize(m_pContext, pObj, m_Options, res)) {
352     return;
353   }
354 
355   DrawObjWithBackgroundToDevice(pObj, mtObj2Device, buffer.GetDevice(),
356                                 buffer.GetMatrix());
357   buffer.OutputToDevice();
358 #else
359   NOTREACHED_NORETURN();
360 #endif
361 }
362 
DrawObjWithBackgroundToDevice(CPDF_PageObject * obj,const CFX_Matrix & object_to_device,CFX_RenderDevice * device,const CFX_Matrix & device_matrix)363 void CPDF_RenderStatus::DrawObjWithBackgroundToDevice(
364     CPDF_PageObject* obj,
365     const CFX_Matrix& object_to_device,
366     CFX_RenderDevice* device,
367     const CFX_Matrix& device_matrix) {
368   RetainPtr<const CPDF_Dictionary> pFormResource;
369   const CPDF_FormObject* pFormObj = obj->AsForm();
370   if (pFormObj) {
371     pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
372   }
373 
374   CPDF_RenderStatus status(m_pContext, device);
375   status.SetOptions(m_Options);
376   status.SetDeviceMatrix(device_matrix);
377   status.SetTransparency(m_Transparency);
378   status.SetDropObjects(m_bDropObjects);
379   status.SetFormResource(std::move(pFormResource));
380   status.SetInGroup(m_bInGroup);
381   status.Initialize(nullptr, nullptr);
382   status.RenderSingleObject(obj, object_to_device * device_matrix);
383 }
384 
ProcessForm(const CPDF_FormObject * pFormObj,const CFX_Matrix & mtObj2Device)385 bool CPDF_RenderStatus::ProcessForm(const CPDF_FormObject* pFormObj,
386                                     const CFX_Matrix& mtObj2Device) {
387   RetainPtr<const CPDF_Dictionary> pOC =
388       pFormObj->form()->GetDict()->GetDictFor("OC");
389   if (pOC && !m_Options.CheckOCGDictVisible(pOC.Get()))
390     return true;
391 
392   CFX_Matrix matrix = pFormObj->form_matrix() * mtObj2Device;
393   RetainPtr<const CPDF_Dictionary> pResources =
394       pFormObj->form()->GetDict()->GetDictFor("Resources");
395   CPDF_RenderStatus status(m_pContext, m_pDevice);
396   status.SetOptions(m_Options);
397   status.SetStopObject(m_pStopObj);
398   status.SetTransparency(m_Transparency);
399   status.SetDropObjects(m_bDropObjects);
400   status.SetFormResource(std::move(pResources));
401   status.SetInGroup(m_bInGroup);
402   status.Initialize(this, &pFormObj->graphic_states());
403   {
404     CFX_RenderDevice::StateRestorer restorer(m_pDevice);
405     status.RenderObjectList(pFormObj->form(), matrix);
406     m_bStopped = status.m_bStopped;
407   }
408   return true;
409 }
410 
ProcessPath(CPDF_PathObject * path_obj,const CFX_Matrix & mtObj2Device)411 bool CPDF_RenderStatus::ProcessPath(CPDF_PathObject* path_obj,
412                                     const CFX_Matrix& mtObj2Device) {
413   CFX_FillRenderOptions::FillType fill_type = path_obj->filltype();
414   bool stroke = path_obj->stroke();
415   ProcessPathPattern(path_obj, mtObj2Device, &fill_type, &stroke);
416   if (fill_type == CFX_FillRenderOptions::FillType::kNoFill && !stroke)
417     return true;
418 
419   // If the option to convert fill paths to stroke is enabled for forced color,
420   // set |fill_type| to FillType::kNoFill and |stroke| to true.
421   CPDF_RenderOptions::Options& options = m_Options.GetOptions();
422   if (m_Options.ColorModeIs(CPDF_RenderOptions::Type::kForcedColor) &&
423       options.bConvertFillToStroke &&
424       fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
425     stroke = true;
426     fill_type = CFX_FillRenderOptions::FillType::kNoFill;
427   }
428 
429   uint32_t fill_argb = fill_type != CFX_FillRenderOptions::FillType::kNoFill
430                            ? GetFillArgb(path_obj)
431                            : 0;
432   uint32_t stroke_argb = stroke ? GetStrokeArgb(path_obj) : 0;
433   CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device;
434   if (!IsAvailableMatrix(path_matrix)) {
435     return true;
436   }
437 
438   return m_pDevice->DrawPath(
439       *path_obj->path().GetObject(), &path_matrix,
440       path_obj->graph_state().GetObject(), fill_argb, stroke_argb,
441       GetFillOptionsForDrawPathWithBlend(options, path_obj, fill_type, stroke,
442                                          m_pType3Char));
443 }
444 
GetTransferFunc(RetainPtr<const CPDF_Object> pObj) const445 RetainPtr<CPDF_TransferFunc> CPDF_RenderStatus::GetTransferFunc(
446     RetainPtr<const CPDF_Object> pObj) const {
447   DCHECK(pObj);
448   auto* pDocCache = CPDF_DocRenderData::FromDocument(m_pContext->GetDocument());
449   return pDocCache ? pDocCache->GetTransferFunc(std::move(pObj)) : nullptr;
450 }
451 
GetFillArgb(CPDF_PageObject * pObj) const452 FX_ARGB CPDF_RenderStatus::GetFillArgb(CPDF_PageObject* pObj) const {
453   if (Type3CharMissingFillColor(m_pType3Char, &pObj->color_state())) {
454     return m_T3FillColor;
455   }
456 
457   return GetFillArgbForType3(pObj);
458 }
459 
GetFillArgbForType3(CPDF_PageObject * pObj) const460 FX_ARGB CPDF_RenderStatus::GetFillArgbForType3(CPDF_PageObject* pObj) const {
461   const CPDF_ColorState* pColorState = &pObj->color_state();
462   if (MissingFillColor(pColorState))
463     pColorState = &m_InitialStates.color_state();
464 
465   FX_COLORREF colorref = pColorState->GetFillColorRef();
466   if (colorref == 0xFFFFFFFF)
467     return 0;
468 
469   int32_t alpha =
470       static_cast<int32_t>((pObj->general_state().GetFillAlpha() * 255));
471   RetainPtr<const CPDF_Object> pTR = pObj->general_state().GetTR();
472   if (pTR) {
473     if (!pObj->general_state().GetTransferFunc()) {
474       pObj->mutable_general_state().SetTransferFunc(
475           GetTransferFunc(std::move(pTR)));
476     }
477     if (pObj->general_state().GetTransferFunc()) {
478       colorref =
479           pObj->general_state().GetTransferFunc()->TranslateColor(colorref);
480     }
481   }
482   return m_Options.TranslateObjectFillColor(
483       AlphaAndColorRefToArgb(alpha, colorref), pObj->GetType());
484 }
485 
GetStrokeArgb(CPDF_PageObject * pObj) const486 FX_ARGB CPDF_RenderStatus::GetStrokeArgb(CPDF_PageObject* pObj) const {
487   const CPDF_ColorState* pColorState = &pObj->color_state();
488   if (Type3CharMissingStrokeColor(m_pType3Char, pColorState))
489     return m_T3FillColor;
490 
491   if (MissingStrokeColor(pColorState))
492     pColorState = &m_InitialStates.color_state();
493 
494   FX_COLORREF colorref = pColorState->GetStrokeColorRef();
495   if (colorref == 0xFFFFFFFF)
496     return 0;
497 
498   int32_t alpha = static_cast<int32_t>(pObj->general_state().GetStrokeAlpha() *
499                                        255);  // not rounded.
500   RetainPtr<const CPDF_Object> pTR = pObj->general_state().GetTR();
501   if (pTR) {
502     if (!pObj->general_state().GetTransferFunc()) {
503       pObj->mutable_general_state().SetTransferFunc(
504           GetTransferFunc(std::move(pTR)));
505     }
506     if (pObj->general_state().GetTransferFunc()) {
507       colorref =
508           pObj->general_state().GetTransferFunc()->TranslateColor(colorref);
509     }
510   }
511   return m_Options.TranslateObjectStrokeColor(
512       AlphaAndColorRefToArgb(alpha, colorref), pObj->GetType());
513 }
514 
ProcessClipPath(const CPDF_ClipPath & ClipPath,const CFX_Matrix & mtObj2Device)515 void CPDF_RenderStatus::ProcessClipPath(const CPDF_ClipPath& ClipPath,
516                                         const CFX_Matrix& mtObj2Device) {
517   if (!ClipPath.HasRef()) {
518     if (m_LastClipPath.HasRef()) {
519       m_pDevice->RestoreState(true);
520       m_LastClipPath.SetNull();
521     }
522     return;
523   }
524   if (m_LastClipPath == ClipPath)
525     return;
526 
527   m_LastClipPath = ClipPath;
528   m_pDevice->RestoreState(true);
529   for (size_t i = 0; i < ClipPath.GetPathCount(); ++i) {
530     const CFX_Path* pPath = ClipPath.GetPath(i).GetObject();
531     if (!pPath)
532       continue;
533 
534     if (pPath->GetPoints().empty()) {
535       CFX_Path empty_path;
536       empty_path.AppendRect(-1, -1, 0, 0);
537       m_pDevice->SetClip_PathFill(empty_path, nullptr,
538                                   CFX_FillRenderOptions::WindingOptions());
539     } else {
540       m_pDevice->SetClip_PathFill(
541           *pPath, &mtObj2Device,
542           CFX_FillRenderOptions(ClipPath.GetClipType(i)));
543     }
544   }
545 
546   if (ClipPath.GetTextCount() == 0)
547     return;
548 
549   if (!IsPrint() &&
550       !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP)) {
551     return;
552   }
553 
554   std::unique_ptr<CFX_Path> pTextClippingPath;
555   for (size_t i = 0; i < ClipPath.GetTextCount(); ++i) {
556     CPDF_TextObject* pText = ClipPath.GetText(i);
557     if (pText) {
558       if (!pTextClippingPath)
559         pTextClippingPath = std::make_unique<CFX_Path>();
560       ProcessText(pText, mtObj2Device, pTextClippingPath.get());
561       continue;
562     }
563 
564     if (!pTextClippingPath)
565       continue;
566 
567     CFX_FillRenderOptions fill_options(CFX_FillRenderOptions::WindingOptions());
568     if (m_Options.GetOptions().bNoTextSmooth) {
569       fill_options.aliased_path = true;
570     }
571     m_pDevice->SetClip_PathFill(*pTextClippingPath, nullptr, fill_options);
572     pTextClippingPath.reset();
573   }
574 }
575 
ClipPattern(const CPDF_PageObject * page_obj,const CFX_Matrix & mtObj2Device,bool stroke)576 bool CPDF_RenderStatus::ClipPattern(const CPDF_PageObject* page_obj,
577                                     const CFX_Matrix& mtObj2Device,
578                                     bool stroke) {
579   if (page_obj->IsPath())
580     return SelectClipPath(page_obj->AsPath(), mtObj2Device, stroke);
581   if (page_obj->IsImage()) {
582     m_pDevice->SetClip_Rect(page_obj->GetTransformedBBox(mtObj2Device));
583     return true;
584   }
585   return false;
586 }
587 
SelectClipPath(const CPDF_PathObject * path_obj,const CFX_Matrix & mtObj2Device,bool stroke)588 bool CPDF_RenderStatus::SelectClipPath(const CPDF_PathObject* path_obj,
589                                        const CFX_Matrix& mtObj2Device,
590                                        bool stroke) {
591   CFX_Matrix path_matrix = path_obj->matrix() * mtObj2Device;
592   if (stroke) {
593     return m_pDevice->SetClip_PathStroke(*path_obj->path().GetObject(),
594                                          &path_matrix,
595                                          path_obj->graph_state().GetObject());
596   }
597   CFX_FillRenderOptions fill_options(path_obj->filltype());
598   if (m_Options.GetOptions().bNoPathSmooth) {
599     fill_options.aliased_path = true;
600   }
601   return m_pDevice->SetClip_PathFill(*path_obj->path().GetObject(),
602                                      &path_matrix, fill_options);
603 }
604 
ProcessTransparency(CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device)605 bool CPDF_RenderStatus::ProcessTransparency(CPDF_PageObject* pPageObj,
606                                             const CFX_Matrix& mtObj2Device) {
607   const BlendMode blend_type = pPageObj->general_state().GetBlendType();
608   RetainPtr<CPDF_Dictionary> pSMaskDict =
609       pPageObj->mutable_general_state().GetMutableSoftMask();
610   if (pSMaskDict) {
611     if (pPageObj->IsImage() &&
612         pPageObj->AsImage()->GetImage()->GetDict()->KeyExist("SMask")) {
613       pSMaskDict = nullptr;
614     }
615   }
616   RetainPtr<const CPDF_Dictionary> pFormResource;
617   float group_alpha = 1.0f;
618   float initial_alpha = 1.0f;
619   CPDF_Transparency transparency = m_Transparency;
620   bool bGroupTransparent = false;
621   const CPDF_FormObject* pFormObj = pPageObj->AsForm();
622   if (pFormObj) {
623     group_alpha = pFormObj->general_state().GetFillAlpha();
624     transparency = pFormObj->form()->GetTransparency();
625     bGroupTransparent = transparency.IsIsolated();
626     pFormResource = pFormObj->form()->GetDict()->GetDictFor("Resources");
627     initial_alpha = m_InitialStates.general_state().GetFillAlpha();
628   }
629   bool bTextClip =
630       !IsPrint() && pPageObj->clip_path().HasRef() &&
631       pPageObj->clip_path().GetTextCount() > 0 &&
632       !(m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_SOFT_CLIP);
633   if (!pSMaskDict && group_alpha == 1.0f && blend_type == BlendMode::kNormal &&
634       !bTextClip && !bGroupTransparent && initial_alpha == 1.0f) {
635     return false;
636   }
637 #if BUILDFLAG(IS_WIN)
638   if (IsPrint()) {
639     DrawObjWithBackground(pPageObj, mtObj2Device);
640     return true;
641   }
642 #endif
643   FX_RECT rect = pPageObj->GetTransformedBBox(mtObj2Device);
644   rect.Intersect(m_pDevice->GetClipBox());
645   if (rect.IsEmpty())
646     return true;
647 
648   const int width = rect.Width();
649   const int height = rect.Height();
650   CFX_DefaultRenderDevice bitmap_device;
651   RetainPtr<CFX_DIBitmap> backdrop;
652   if (!transparency.IsIsolated() &&
653       (m_pDevice->GetRenderCaps() & FXRC_GET_BITS)) {
654     backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
655     if (!m_pDevice->CreateCompatibleBitmap(backdrop, width, height))
656       return true;
657     m_pDevice->GetDIBits(backdrop, rect.left, rect.top);
658   }
659   if (!bitmap_device.CreateWithBackdrop(
660           width, height, GetCompatibleArgbFormat(), std::move(backdrop))) {
661     return true;
662   }
663 
664   CFX_Matrix new_matrix = mtObj2Device;
665   new_matrix.Translate(-rect.left, -rect.top);
666 
667   RetainPtr<CFX_DIBitmap> text_mask_bitmap;
668   if (bTextClip) {
669     text_mask_bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
670     if (!text_mask_bitmap->Create(width, height, FXDIB_Format::k8bppMask)) {
671       return true;
672     }
673 
674     CFX_DefaultRenderDevice text_device;
675     text_device.Attach(text_mask_bitmap);
676     for (size_t i = 0; i < pPageObj->clip_path().GetTextCount(); ++i) {
677       CPDF_TextObject* textobj = pPageObj->clip_path().GetText(i);
678       if (!textobj)
679         break;
680 
681       // TODO(thestig): Should we check the return value here?
682       CPDF_TextRenderer::DrawTextPath(
683           &text_device, textobj->GetCharCodes(), textobj->GetCharPositions(),
684           textobj->text_state().GetFont().Get(),
685           textobj->text_state().GetFontSize(), textobj->GetTextMatrix(),
686           &new_matrix, textobj->graph_state().GetObject(), 0xffffffff, 0,
687           nullptr, CFX_FillRenderOptions());
688     }
689   }
690   CPDF_RenderStatus bitmap_render(m_pContext, &bitmap_device);
691   bitmap_render.SetOptions(m_Options);
692   bitmap_render.SetStopObject(m_pStopObj);
693   bitmap_render.SetStdCS(true);
694   bitmap_render.SetDropObjects(m_bDropObjects);
695   bitmap_render.SetFormResource(std::move(pFormResource));
696   bitmap_render.SetInGroup(transparency.IsGroup());
697   bitmap_render.Initialize(nullptr, nullptr);
698   bitmap_render.ProcessObjectNoClip(pPageObj, new_matrix);
699   m_bStopped = bitmap_render.m_bStopped;
700   if (pSMaskDict) {
701     CFX_Matrix smask_matrix =
702         *pPageObj->general_state().GetSMaskMatrix() * mtObj2Device;
703     RetainPtr<CFX_DIBitmap> smask_bitmap =
704         LoadSMask(pSMaskDict.Get(), rect, smask_matrix);
705     if (smask_bitmap) {
706       bitmap_device.MultiplyAlphaMask(std::move(smask_bitmap));
707     }
708   }
709   if (text_mask_bitmap) {
710     bitmap_device.MultiplyAlphaMask(std::move(text_mask_bitmap));
711   }
712   if (transparency.IsGroup()) {
713     bitmap_device.MultiplyAlpha(group_alpha);
714   }
715   if (initial_alpha != 1.0f && !m_bInGroup) {
716     bitmap_device.MultiplyAlpha(initial_alpha);
717   }
718   transparency = m_Transparency;
719   if (pPageObj->IsForm()) {
720     transparency.SetGroup();
721   }
722   CompositeDIBitmap(bitmap_device.GetBitmap(), rect.left, rect.top,
723                     /*mask_argb=*/0, /*alpha=*/1.0f, blend_type, transparency);
724   return true;
725 }
726 
GetClippedBBox(const FX_RECT & rect) const727 FX_RECT CPDF_RenderStatus::GetClippedBBox(const FX_RECT& rect) const {
728   FX_RECT bbox = rect;
729   bbox.Intersect(m_pDevice->GetClipBox());
730   return bbox;
731 }
732 
GetBackdrop(const CPDF_PageObject * pObj,const FX_RECT & bbox,bool bBackAlphaRequired)733 RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::GetBackdrop(
734     const CPDF_PageObject* pObj,
735     const FX_RECT& bbox,
736     bool bBackAlphaRequired) {
737   int width = bbox.Width();
738   int height = bbox.Height();
739   auto backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
740   if (bBackAlphaRequired && !m_bDropObjects) {
741     // TODO(crbug.com/42271020): Consider adding support for
742     // `FXDIB_Format::kBgraPremul`
743     if (!backdrop->Create(width, height, FXDIB_Format::kBgra)) {
744       return nullptr;
745     }
746   } else {
747     if (!m_pDevice->CreateCompatibleBitmap(backdrop, width, height)) {
748       return nullptr;
749     }
750   }
751 
752   const int cap_to_check =
753       backdrop->IsAlphaFormat() ? FXRC_ALPHA_OUTPUT : FXRC_GET_BITS;
754   if (m_pDevice->GetRenderCaps() & cap_to_check) {
755     m_pDevice->GetDIBits(backdrop, bbox.left, bbox.top);
756     return backdrop;
757   }
758   CFX_Matrix FinalMatrix = m_DeviceMatrix;
759   FinalMatrix.Translate(-bbox.left, -bbox.top);
760   if (!backdrop->IsAlphaFormat()) {
761     backdrop->Clear(0xffffffff);
762   }
763 
764   CFX_DefaultRenderDevice device;
765   device.Attach(backdrop);
766   m_pContext->Render(&device, pObj, &m_Options, &FinalMatrix);
767   return backdrop;
768 }
769 
CloneObjStates(const CPDF_GraphicStates * pSrcStates,bool stroke)770 std::unique_ptr<CPDF_GraphicStates> CPDF_RenderStatus::CloneObjStates(
771     const CPDF_GraphicStates* pSrcStates,
772     bool stroke) {
773   if (!pSrcStates)
774     return nullptr;
775 
776   auto pStates = std::make_unique<CPDF_GraphicStates>(*pSrcStates);
777   const CPDF_Color* pObjColor = stroke
778                                     ? pSrcStates->color_state().GetStrokeColor()
779                                     : pSrcStates->color_state().GetFillColor();
780   if (!pObjColor->IsNull()) {
781     pStates->mutable_color_state().SetFillColorRef(
782         stroke ? pSrcStates->color_state().GetStrokeColorRef()
783                : pSrcStates->color_state().GetFillColorRef());
784     pStates->mutable_color_state().SetStrokeColorRef(
785         pStates->color_state().GetFillColorRef());
786   }
787   return pStates;
788 }
789 
ProcessText(CPDF_TextObject * textobj,const CFX_Matrix & mtObj2Device,CFX_Path * clipping_path)790 bool CPDF_RenderStatus::ProcessText(CPDF_TextObject* textobj,
791                                     const CFX_Matrix& mtObj2Device,
792                                     CFX_Path* clipping_path) {
793   if (textobj->GetCharCodes().empty())
794     return true;
795 
796   const TextRenderingMode text_render_mode =
797       textobj->text_state().GetTextMode();
798   if (text_render_mode == TextRenderingMode::MODE_INVISIBLE)
799     return true;
800 
801   RetainPtr<CPDF_Font> pFont = textobj->text_state().GetFont();
802   if (pFont->IsType3Font())
803     return ProcessType3Text(textobj, mtObj2Device);
804 
805   bool is_fill = false;
806   bool is_stroke = false;
807   bool is_clip = false;
808   if (clipping_path) {
809     is_clip = true;
810   } else {
811     switch (text_render_mode) {
812       case TextRenderingMode::MODE_FILL:
813       case TextRenderingMode::MODE_FILL_CLIP:
814         is_fill = true;
815         break;
816       case TextRenderingMode::MODE_STROKE:
817       case TextRenderingMode::MODE_STROKE_CLIP:
818         if (pFont->HasFace())
819           is_stroke = true;
820         else
821           is_fill = true;
822         break;
823       case TextRenderingMode::MODE_FILL_STROKE:
824       case TextRenderingMode::MODE_FILL_STROKE_CLIP:
825         is_fill = true;
826         if (pFont->HasFace())
827           is_stroke = true;
828         break;
829       case TextRenderingMode::MODE_INVISIBLE:
830         // Already handled above, but the compiler is not smart enough to
831         // realize it.
832         NOTREACHED_NORETURN();
833       case TextRenderingMode::MODE_CLIP:
834         return true;
835       case TextRenderingMode::MODE_UNKNOWN:
836         NOTREACHED_NORETURN();
837     }
838   }
839   FX_ARGB stroke_argb = 0;
840   FX_ARGB fill_argb = 0;
841   bool bPattern = false;
842   if (is_stroke) {
843     if (textobj->color_state().GetStrokeColor()->IsPattern()) {
844       bPattern = true;
845     } else {
846       stroke_argb = GetStrokeArgb(textobj);
847     }
848   }
849   if (is_fill) {
850     if (textobj->color_state().GetFillColor()->IsPattern()) {
851       bPattern = true;
852     } else {
853       fill_argb = GetFillArgb(textobj);
854     }
855   }
856   CFX_Matrix text_matrix = textobj->GetTextMatrix();
857   if (!IsAvailableMatrix(text_matrix))
858     return true;
859 
860   float font_size = textobj->text_state().GetFontSize();
861   if (bPattern) {
862     DrawTextPathWithPattern(textobj, mtObj2Device, pFont.Get(), font_size,
863                             text_matrix, is_fill, is_stroke);
864     return true;
865   }
866   if (is_clip || is_stroke) {
867     const CFX_Matrix* pDeviceMatrix = &mtObj2Device;
868     CFX_Matrix device_matrix;
869     if (is_stroke) {
870       pdfium::span<const float> pCTM = textobj->text_state().GetCTM();
871       if (pCTM[0] != 1.0f || pCTM[3] != 1.0f) {
872         CFX_Matrix ctm(pCTM[0], pCTM[1], pCTM[2], pCTM[3], 0, 0);
873         text_matrix *= ctm.GetInverse();
874         device_matrix = ctm * mtObj2Device;
875         pDeviceMatrix = &device_matrix;
876       }
877     }
878     return CPDF_TextRenderer::DrawTextPath(
879         m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
880         pFont.Get(), font_size, text_matrix, pDeviceMatrix,
881         textobj->graph_state().GetObject(), fill_argb, stroke_argb,
882         clipping_path,
883         GetFillOptionsForDrawTextPath(m_Options.GetOptions(), textobj,
884                                       is_stroke, is_fill));
885   }
886   text_matrix.Concat(mtObj2Device);
887   return CPDF_TextRenderer::DrawNormalText(
888       m_pDevice, textobj->GetCharCodes(), textobj->GetCharPositions(),
889       pFont.Get(), font_size, text_matrix, fill_argb, m_Options);
890 }
891 
892 // TODO(npm): Font fallback for type 3 fonts? (Completely separate code!!)
ProcessType3Text(CPDF_TextObject * textobj,const CFX_Matrix & mtObj2Device)893 bool CPDF_RenderStatus::ProcessType3Text(CPDF_TextObject* textobj,
894                                          const CFX_Matrix& mtObj2Device) {
895   CPDF_Type3Font* pType3Font = textobj->text_state().GetFont()->AsType3Font();
896   if (pdfium::Contains(m_Type3FontCache, pType3Font))
897     return true;
898 
899   FX_ARGB fill_argb = GetFillArgbForType3(textobj);
900   int fill_alpha = FXARGB_A(fill_argb);
901 #if BUILDFLAG(IS_WIN)
902   if (IsPrint() && fill_alpha < 255) {
903     return false;
904   }
905 #endif
906 
907   CFX_Matrix text_matrix = textobj->GetTextMatrix();
908   CFX_Matrix char_matrix = pType3Font->GetFontMatrix();
909   float font_size = textobj->text_state().GetFontSize();
910   char_matrix.Scale(font_size, font_size);
911 
912   // Must come before |glyphs|, because |glyphs| points into |refTypeCache|.
913   std::set<RetainPtr<CPDF_Type3Cache>> refTypeCache;
914   std::vector<TextGlyphPos> glyphs;
915   if (!IsPrint()) {
916     glyphs.resize(textobj->GetCharCodes().size());
917   }
918 
919   for (size_t iChar = 0; iChar < textobj->GetCharCodes().size(); ++iChar) {
920     uint32_t charcode = textobj->GetCharCodes()[iChar];
921     if (charcode == static_cast<uint32_t>(-1))
922       continue;
923 
924     CPDF_Type3Char* pType3Char = pType3Font->LoadChar(charcode);
925     if (!pType3Char)
926       continue;
927 
928     CFX_Matrix matrix = char_matrix;
929     matrix.e += iChar > 0 ? textobj->GetCharPositions()[iChar - 1] : 0;
930     matrix.Concat(text_matrix);
931     matrix.Concat(mtObj2Device);
932     if (!pType3Char->LoadBitmapFromSoleImageOfForm()) {
933       if (!glyphs.empty()) {
934         for (size_t i = 0; i < iChar; ++i) {
935           const TextGlyphPos& glyph = glyphs[i];
936           if (!glyph.m_pGlyph)
937             continue;
938 
939           std::optional<CFX_Point> point = glyph.GetOrigin({0, 0});
940           if (!point.has_value())
941             continue;
942 
943           m_pDevice->SetBitMask(glyph.m_pGlyph->GetBitmap(), point->x, point->y,
944                                 fill_argb);
945         }
946         glyphs.clear();
947       }
948 
949       std::unique_ptr<CPDF_GraphicStates> pStates =
950           CloneObjStates(&textobj->graphic_states(), false);
951       CPDF_RenderOptions options = m_Options;
952       options.GetOptions().bForceHalftone = true;
953       options.GetOptions().bRectAA = true;
954 
955       const auto* pForm = static_cast<const CPDF_Form*>(pType3Char->form());
956       RetainPtr<const CPDF_Dictionary> pFormResource =
957           pForm->GetDict()->GetDictFor("Resources");
958 
959       if (fill_alpha == 255) {
960         CPDF_RenderStatus status(m_pContext, m_pDevice);
961         status.SetOptions(options);
962         status.SetTransparency(pForm->GetTransparency());
963         status.SetType3Char(pType3Char);
964         status.SetFillColor(fill_argb);
965         status.SetDropObjects(m_bDropObjects);
966         status.SetFormResource(std::move(pFormResource));
967         status.Initialize(this, pStates.get());
968         status.m_Type3FontCache = m_Type3FontCache;
969         status.m_Type3FontCache.emplace_back(pType3Font);
970 
971         CFX_RenderDevice::StateRestorer restorer(m_pDevice);
972         status.RenderObjectList(pForm, matrix);
973       } else {
974         FX_RECT rect =
975             matrix.TransformRect(pForm->CalcBoundingBox()).GetOuterRect();
976         if (!rect.Valid())
977           continue;
978 
979         CFX_DefaultRenderDevice bitmap_device;
980         // TODO(crbug.com/42271020): Consider adding support for
981         // `FXDIB_Format::kBgraPremul`
982         if (!bitmap_device.Create(rect.Width(), rect.Height(),
983                                   FXDIB_Format::kBgra)) {
984           return true;
985         }
986         CPDF_RenderStatus status(m_pContext, &bitmap_device);
987         status.SetOptions(options);
988         status.SetTransparency(pForm->GetTransparency());
989         status.SetType3Char(pType3Char);
990         status.SetFillColor(fill_argb);
991         status.SetDropObjects(m_bDropObjects);
992         status.SetFormResource(std::move(pFormResource));
993         status.Initialize(this, pStates.get());
994         status.m_Type3FontCache = m_Type3FontCache;
995         status.m_Type3FontCache.emplace_back(pType3Font);
996         matrix.Translate(-rect.left, -rect.top);
997         status.RenderObjectList(pForm, matrix);
998         m_pDevice->SetDIBits(bitmap_device.GetBitmap(), rect.left, rect.top);
999       }
1000     } else if (pType3Char->GetBitmap()) {
1001 #if BUILDFLAG(IS_WIN)
1002       if (IsPrint()) {
1003         CFX_Matrix image_matrix = pType3Char->matrix() * matrix;
1004         CPDF_ImageRenderer renderer(this);
1005         if (renderer.Start(pType3Char->GetBitmap(), fill_argb, image_matrix,
1006                            FXDIB_ResampleOptions(), false)) {
1007           renderer.Continue(nullptr);
1008         }
1009         if (!renderer.GetResult()) {
1010           return false;
1011         }
1012         continue;
1013       }
1014 #endif
1015 
1016       CPDF_Document* pDoc = pType3Font->GetDocument();
1017       RetainPtr<CPDF_Type3Cache> pCache =
1018           CPDF_DocRenderData::FromDocument(pDoc)->GetCachedType3(pType3Font);
1019 
1020       const CFX_GlyphBitmap* pBitmap = pCache->LoadGlyph(charcode, matrix);
1021       if (!pBitmap) {
1022         continue;
1023       }
1024 
1025       refTypeCache.insert(std::move(pCache));
1026 
1027       CFX_Point origin(FXSYS_roundf(matrix.e), FXSYS_roundf(matrix.f));
1028       if (glyphs.empty()) {
1029         FX_SAFE_INT32 left = origin.x;
1030         left += pBitmap->left();
1031         if (!left.IsValid()) {
1032           continue;
1033         }
1034 
1035         FX_SAFE_INT32 top = origin.y;
1036         top -= pBitmap->top();
1037         if (!top.IsValid()) {
1038           continue;
1039         }
1040 
1041         m_pDevice->SetBitMask(pBitmap->GetBitmap(), left.ValueOrDie(),
1042                               top.ValueOrDie(), fill_argb);
1043       } else {
1044         glyphs[iChar].m_pGlyph = pBitmap;
1045         glyphs[iChar].m_Origin = origin;
1046       }
1047     }
1048   }
1049 
1050   if (glyphs.empty())
1051     return true;
1052 
1053   FX_RECT rect = GetGlyphsBBox(glyphs, 0);
1054   auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
1055   if (!bitmap->Create(rect.Width(), rect.Height(), FXDIB_Format::k8bppMask)) {
1056     return true;
1057   }
1058 
1059   for (const TextGlyphPos& glyph : glyphs) {
1060     if (!glyph.m_pGlyph || !glyph.m_pGlyph->GetBitmap()->IsMaskFormat())
1061       continue;
1062 
1063     std::optional<CFX_Point> point = glyph.GetOrigin({rect.left, rect.top});
1064     if (!point.has_value())
1065       continue;
1066 
1067     bitmap->CompositeMask(
1068         point->x, point->y, glyph.m_pGlyph->GetBitmap()->GetWidth(),
1069         glyph.m_pGlyph->GetBitmap()->GetHeight(), glyph.m_pGlyph->GetBitmap(),
1070         fill_argb, 0, 0, BlendMode::kNormal, nullptr, false);
1071   }
1072   m_pDevice->SetBitMask(std::move(bitmap), rect.left, rect.top, fill_argb);
1073   return true;
1074 }
1075 
DrawTextPathWithPattern(const CPDF_TextObject * textobj,const CFX_Matrix & mtObj2Device,CPDF_Font * pFont,float font_size,const CFX_Matrix & mtTextMatrix,bool fill,bool stroke)1076 void CPDF_RenderStatus::DrawTextPathWithPattern(const CPDF_TextObject* textobj,
1077                                                 const CFX_Matrix& mtObj2Device,
1078                                                 CPDF_Font* pFont,
1079                                                 float font_size,
1080                                                 const CFX_Matrix& mtTextMatrix,
1081                                                 bool fill,
1082                                                 bool stroke) {
1083   if (!stroke) {
1084     std::vector<std::unique_ptr<CPDF_TextObject>> pCopy;
1085     pCopy.push_back(textobj->Clone());
1086 
1087     CPDF_PathObject path;
1088     path.set_filltype(CFX_FillRenderOptions::FillType::kWinding);
1089     path.mutable_clip_path().CopyClipPath(m_LastClipPath);
1090     path.mutable_clip_path().AppendTexts(&pCopy);
1091     path.mutable_color_state() = textobj->color_state();
1092     path.mutable_general_state() = textobj->general_state();
1093     path.path().AppendFloatRect(textobj->GetRect());
1094     path.SetRect(textobj->GetRect());
1095 
1096     AutoRestorer<UnownedPtr<const CPDF_PageObject>> restorer2(&m_pCurObj);
1097     RenderSingleObject(&path, mtObj2Device);
1098     return;
1099   }
1100 
1101   std::vector<TextCharPos> char_pos_list = GetCharPosList(
1102       textobj->GetCharCodes(), textobj->GetCharPositions(), pFont, font_size);
1103   for (const TextCharPos& charpos : char_pos_list) {
1104     auto* font = charpos.m_FallbackFontPosition == -1
1105                      ? pFont->GetFont()
1106                      : pFont->GetFontFallback(charpos.m_FallbackFontPosition);
1107     const CFX_Path* pPath =
1108         font->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth);
1109     if (!pPath)
1110       continue;
1111 
1112     CPDF_PathObject path;
1113     path.mutable_graph_state() = textobj->graph_state();
1114     path.mutable_color_state() = textobj->color_state();
1115 
1116     CFX_Matrix matrix = charpos.GetEffectiveMatrix(CFX_Matrix(
1117         font_size, 0, 0, font_size, charpos.m_Origin.x, charpos.m_Origin.y));
1118     matrix.Concat(mtTextMatrix);
1119     path.set_stroke(stroke);
1120     path.set_filltype(fill ? CFX_FillRenderOptions::FillType::kWinding
1121                            : CFX_FillRenderOptions::FillType::kNoFill);
1122     path.path().Append(*pPath, &matrix);
1123     path.SetPathMatrix(CFX_Matrix());
1124     ProcessPath(&path, mtObj2Device);
1125   }
1126 }
1127 
DrawShadingPattern(CPDF_ShadingPattern * pattern,const CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device,bool stroke)1128 void CPDF_RenderStatus::DrawShadingPattern(CPDF_ShadingPattern* pattern,
1129                                            const CPDF_PageObject* pPageObj,
1130                                            const CFX_Matrix& mtObj2Device,
1131                                            bool stroke) {
1132   if (!pattern->Load())
1133     return;
1134 
1135   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1136   if (!ClipPattern(pPageObj, mtObj2Device, stroke))
1137     return;
1138 
1139   FX_RECT rect = GetObjectClippedRect(pPageObj, mtObj2Device);
1140   if (rect.IsEmpty())
1141     return;
1142 
1143   CFX_Matrix matrix = pattern->pattern_to_form() * mtObj2Device;
1144   int alpha =
1145       FXSYS_roundf(255 * (stroke ? pPageObj->general_state().GetStrokeAlpha()
1146                                  : pPageObj->general_state().GetFillAlpha()));
1147   CPDF_RenderShading::Draw(m_pDevice, m_pContext, m_pCurObj, pattern, matrix,
1148                            rect, alpha, m_Options);
1149 }
1150 
ProcessShading(const CPDF_ShadingObject * pShadingObj,const CFX_Matrix & mtObj2Device)1151 void CPDF_RenderStatus::ProcessShading(const CPDF_ShadingObject* pShadingObj,
1152                                        const CFX_Matrix& mtObj2Device) {
1153   FX_RECT rect = GetObjectClippedRect(pShadingObj, mtObj2Device);
1154   if (rect.IsEmpty())
1155     return;
1156 
1157   CFX_Matrix matrix = pShadingObj->matrix() * mtObj2Device;
1158   CPDF_RenderShading::Draw(
1159       m_pDevice, m_pContext, m_pCurObj, pShadingObj->pattern(), matrix, rect,
1160       FXSYS_roundf(255 * pShadingObj->general_state().GetFillAlpha()),
1161       m_Options);
1162 }
1163 
DrawTilingPattern(CPDF_TilingPattern * pattern,CPDF_PageObject * pPageObj,const CFX_Matrix & mtObj2Device,bool stroke)1164 void CPDF_RenderStatus::DrawTilingPattern(CPDF_TilingPattern* pattern,
1165                                           CPDF_PageObject* pPageObj,
1166                                           const CFX_Matrix& mtObj2Device,
1167                                           bool stroke) {
1168   const std::unique_ptr<CPDF_Form> pPatternForm = pattern->Load(pPageObj);
1169   if (!pPatternForm)
1170     return;
1171 
1172   CFX_RenderDevice::StateRestorer restorer(m_pDevice);
1173   if (!ClipPattern(pPageObj, mtObj2Device, stroke))
1174     return;
1175 
1176   FX_RECT clip_box = m_pDevice->GetClipBox();
1177   if (clip_box.IsEmpty())
1178     return;
1179 
1180   RetainPtr<CFX_DIBitmap> screen =
1181       CPDF_RenderTiling::Draw(this, pPageObj, pattern, pPatternForm.get(),
1182                               mtObj2Device, clip_box, stroke);
1183   if (!screen) {
1184     return;
1185   }
1186 
1187   constexpr FX_ARGB kMask = 0;
1188   CompositeDIBitmap(std::move(screen), clip_box.left, clip_box.top, kMask,
1189                     /*alpha=*/1.0f, BlendMode::kNormal, CPDF_Transparency());
1190 }
1191 
DrawPathWithPattern(CPDF_PathObject * path_obj,const CFX_Matrix & mtObj2Device,const CPDF_Color * pColor,bool stroke)1192 void CPDF_RenderStatus::DrawPathWithPattern(CPDF_PathObject* path_obj,
1193                                             const CFX_Matrix& mtObj2Device,
1194                                             const CPDF_Color* pColor,
1195                                             bool stroke) {
1196   RetainPtr<CPDF_Pattern> pattern = pColor->GetPattern();
1197   if (!pattern)
1198     return;
1199 
1200   if (CPDF_TilingPattern* pTilingPattern = pattern->AsTilingPattern())
1201     DrawTilingPattern(pTilingPattern, path_obj, mtObj2Device, stroke);
1202   else if (CPDF_ShadingPattern* pShadingPattern = pattern->AsShadingPattern())
1203     DrawShadingPattern(pShadingPattern, path_obj, mtObj2Device, stroke);
1204 }
1205 
ProcessPathPattern(CPDF_PathObject * path_obj,const CFX_Matrix & mtObj2Device,CFX_FillRenderOptions::FillType * fill_type,bool * stroke)1206 void CPDF_RenderStatus::ProcessPathPattern(
1207     CPDF_PathObject* path_obj,
1208     const CFX_Matrix& mtObj2Device,
1209     CFX_FillRenderOptions::FillType* fill_type,
1210     bool* stroke) {
1211   DCHECK(fill_type);
1212   DCHECK(stroke);
1213 
1214   if (*fill_type != CFX_FillRenderOptions::FillType::kNoFill) {
1215     const CPDF_Color& FillColor = *path_obj->color_state().GetFillColor();
1216     if (FillColor.IsPattern()) {
1217       DrawPathWithPattern(path_obj, mtObj2Device, &FillColor, false);
1218       *fill_type = CFX_FillRenderOptions::FillType::kNoFill;
1219     }
1220   }
1221   if (*stroke) {
1222     const CPDF_Color& StrokeColor = *path_obj->color_state().GetStrokeColor();
1223     if (StrokeColor.IsPattern()) {
1224       DrawPathWithPattern(path_obj, mtObj2Device, &StrokeColor, true);
1225       *stroke = false;
1226     }
1227   }
1228 }
1229 
ProcessImage(CPDF_ImageObject * pImageObj,const CFX_Matrix & mtObj2Device)1230 bool CPDF_RenderStatus::ProcessImage(CPDF_ImageObject* pImageObj,
1231                                      const CFX_Matrix& mtObj2Device) {
1232   CPDF_ImageRenderer render(this);
1233   if (render.Start(pImageObj, mtObj2Device, m_bStdCS)) {
1234     render.Continue(nullptr);
1235   }
1236   return render.GetResult();
1237 }
1238 
CompositeDIBitmap(RetainPtr<CFX_DIBitmap> bitmap,int left,int top,FX_ARGB mask_argb,float alpha,BlendMode blend_mode,const CPDF_Transparency & transparency)1239 void CPDF_RenderStatus::CompositeDIBitmap(
1240     RetainPtr<CFX_DIBitmap> bitmap,
1241     int left,
1242     int top,
1243     FX_ARGB mask_argb,
1244     float alpha,
1245     BlendMode blend_mode,
1246     const CPDF_Transparency& transparency) {
1247   CHECK(bitmap);
1248 
1249   if (blend_mode == BlendMode::kNormal) {
1250     if (bitmap->IsMaskFormat()) {
1251 #if BUILDFLAG(IS_WIN)
1252       FX_ARGB fill_argb = m_Options.TranslateColor(mask_argb);
1253       if (alpha != 1.0f) {
1254         auto& bgra = reinterpret_cast<FX_BGRA_STRUCT<uint8_t>&>(fill_argb);
1255         bgra.alpha *= FXSYS_roundf(alpha * 255) / 255;
1256       }
1257       if (m_pDevice->SetBitMask(bitmap, left, top, fill_argb)) {
1258         return;
1259       }
1260 #else
1261       NOTREACHED_NORETURN();
1262 #endif
1263     } else {
1264       if (alpha != 1.0f) {
1265         if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1266           CFX_Matrix matrix = CFX_RenderDevice::GetFlipMatrix(
1267               bitmap->GetWidth(), bitmap->GetHeight(), left, top);
1268           m_pDevice->StartDIBits(std::move(bitmap), alpha, /*argb=*/0, matrix,
1269                                  FXDIB_ResampleOptions());
1270           return;
1271         }
1272         bitmap->MultiplyAlpha(alpha);
1273       }
1274       if (m_pDevice->SetDIBits(bitmap, left, top)) {
1275         return;
1276       }
1277     }
1278   }
1279   bool bIsolated = transparency.IsIsolated();
1280   bool bBackAlphaRequired =
1281       blend_mode != BlendMode::kNormal && bIsolated && !m_bDropObjects;
1282   bool bGetBackGround =
1283       ((m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT)) ||
1284       (!(m_pDevice->GetRenderCaps() & FXRC_ALPHA_OUTPUT) &&
1285        (m_pDevice->GetRenderCaps() & FXRC_GET_BITS) && !bBackAlphaRequired);
1286   if (bGetBackGround) {
1287     if (bIsolated || !transparency.IsGroup()) {
1288       if (!bitmap->IsMaskFormat()) {
1289         m_pDevice->SetDIBitsWithBlend(std::move(bitmap), left, top, blend_mode);
1290       }
1291       return;
1292     }
1293 
1294     FX_RECT rect(left, top, left + bitmap->GetWidth(),
1295                  top + bitmap->GetHeight());
1296     rect.Intersect(m_pDevice->GetClipBox());
1297     RetainPtr<CFX_DIBitmap> pClone;
1298     if (m_pDevice->GetBackDrop() && m_pDevice->GetBitmap()) {
1299       pClone = m_pDevice->GetBackDrop()->ClipTo(rect);
1300       if (!pClone)
1301         return;
1302 
1303       pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1304                               m_pDevice->GetBitmap(), rect.left, rect.top,
1305                               BlendMode::kNormal, nullptr, false);
1306       left = std::min(left, 0);
1307       top = std::min(top, 0);
1308       if (bitmap->IsMaskFormat()) {
1309 #if BUILDFLAG(IS_WIN)
1310         pClone->CompositeMask(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1311                               bitmap, mask_argb, left, top, blend_mode, nullptr,
1312                               false);
1313 #else
1314         NOTREACHED_NORETURN();
1315 #endif
1316       } else {
1317         pClone->CompositeBitmap(0, 0, pClone->GetWidth(), pClone->GetHeight(),
1318                                 bitmap, left, top, blend_mode, nullptr, false);
1319       }
1320     } else {
1321       pClone = bitmap;
1322     }
1323     if (m_pDevice->GetBackDrop()) {
1324       m_pDevice->SetDIBits(pClone, rect.left, rect.top);
1325     } else {
1326       if (!bitmap->IsMaskFormat()) {
1327         m_pDevice->SetDIBitsWithBlend(std::move(bitmap), rect.left, rect.top,
1328                                       blend_mode);
1329       }
1330     }
1331     return;
1332   }
1333 
1334   FX_RECT bbox = GetClippedBBox(
1335       FX_RECT(left, top, left + bitmap->GetWidth(), top + bitmap->GetHeight()));
1336   RetainPtr<CFX_DIBitmap> backdrop = GetBackdrop(
1337       m_pCurObj, bbox, blend_mode != BlendMode::kNormal && bIsolated);
1338   if (!backdrop) {
1339     return;
1340   }
1341 
1342   const int width = bitmap->GetWidth();
1343   const int height = bitmap->GetHeight();
1344   if (bitmap->IsMaskFormat()) {
1345 #if BUILDFLAG(IS_WIN)
1346     backdrop->CompositeMask(left - bbox.left, top - bbox.top, width, height,
1347                             std::move(bitmap), mask_argb, 0, 0, blend_mode,
1348                             nullptr, false);
1349 #else
1350     NOTREACHED_NORETURN();
1351 #endif
1352   } else {
1353     backdrop->CompositeBitmap(left - bbox.left, top - bbox.top, width, height,
1354                               std::move(bitmap), 0, 0, blend_mode, nullptr,
1355                               false);
1356   }
1357 
1358   auto new_backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
1359   CHECK(new_backdrop->Create(backdrop->GetWidth(), backdrop->GetHeight(),
1360                              FXDIB_Format::kBgrx));
1361   new_backdrop->Clear(0xffffffff);
1362   new_backdrop->CompositeBitmap(0, 0, new_backdrop->GetWidth(),
1363                                 new_backdrop->GetHeight(), std::move(backdrop),
1364                                 0, 0, BlendMode::kNormal, nullptr, false);
1365   m_pDevice->SetDIBits(std::move(new_backdrop), bbox.left, bbox.top);
1366 }
1367 
LoadSMask(CPDF_Dictionary * smask_dict,const FX_RECT & clip_rect,const CFX_Matrix & smask_matrix)1368 RetainPtr<CFX_DIBitmap> CPDF_RenderStatus::LoadSMask(
1369     CPDF_Dictionary* smask_dict,
1370     const FX_RECT& clip_rect,
1371     const CFX_Matrix& smask_matrix) {
1372   RetainPtr<CPDF_Stream> pGroup =
1373       smask_dict->GetMutableStreamFor(pdfium::transparency::kG);
1374   if (!pGroup)
1375     return nullptr;
1376 
1377   std::unique_ptr<CPDF_Function> pFunc;
1378   RetainPtr<const CPDF_Object> pFuncObj =
1379       smask_dict->GetDirectObjectFor(pdfium::transparency::kTR);
1380   if (pFuncObj && (pFuncObj->IsDictionary() || pFuncObj->IsStream()))
1381     pFunc = CPDF_Function::Load(std::move(pFuncObj));
1382 
1383   CFX_Matrix matrix = smask_matrix;
1384   matrix.Translate(-clip_rect.left, -clip_rect.top);
1385 
1386   CPDF_Form form(m_pContext->GetDocument(),
1387                  m_pContext->GetMutablePageResources(), pGroup);
1388   form.ParseContent();
1389 
1390   CFX_DefaultRenderDevice bitmap_device;
1391   bool bLuminosity =
1392       smask_dict->GetByteStringFor(pdfium::transparency::kSoftMaskSubType) !=
1393       pdfium::transparency::kAlpha;
1394   const int width = clip_rect.Width();
1395   const int height = clip_rect.Height();
1396   const FXDIB_Format format = GetFormatForLuminosity(bLuminosity);
1397   if (!bitmap_device.Create(width, height, format)) {
1398     return nullptr;
1399   }
1400 
1401   CPDF_ColorSpace::Family nCSFamily = CPDF_ColorSpace::Family::kUnknown;
1402   const FX_ARGB background_color =
1403       bLuminosity
1404           ? GetBackgroundColor(smask_dict, pGroup->GetDict().Get(), &nCSFamily)
1405           : 0;
1406   bitmap_device.Clear(background_color);
1407 
1408   RetainPtr<const CPDF_Dictionary> pFormResource =
1409       form.GetDict()->GetDictFor("Resources");
1410   CPDF_RenderOptions options;
1411   options.SetColorMode(bLuminosity ? CPDF_RenderOptions::kNormal
1412                                    : CPDF_RenderOptions::kAlpha);
1413   CPDF_RenderStatus status(m_pContext, &bitmap_device);
1414   status.SetOptions(options);
1415   status.SetGroupFamily(nCSFamily);
1416   status.SetLoadMask(bLuminosity);
1417   status.SetStdCS(true);
1418   status.SetFormResource(std::move(pFormResource));
1419   status.SetDropObjects(m_bDropObjects);
1420   status.Initialize(nullptr, nullptr);
1421   status.RenderObjectList(&form, matrix);
1422 
1423   auto result_mask = pdfium::MakeRetain<CFX_DIBitmap>();
1424   if (!result_mask->Create(width, height, FXDIB_Format::k8bppMask)) {
1425     return nullptr;
1426   }
1427 
1428   pdfium::span<uint8_t> dest_buf = result_mask->GetWritableBuffer();
1429   RetainPtr<const CFX_DIBitmap> bitmap = bitmap_device.GetBitmap();
1430   pdfium::span<const uint8_t> src_buf = bitmap->GetBuffer();
1431   const int dest_pitch = result_mask->GetPitch();
1432   const int src_pitch = bitmap->GetPitch();
1433   DataVector<uint8_t> transfers(256);
1434   if (pFunc) {
1435     std::vector<float> results(pFunc->OutputCount());
1436     for (size_t i = 0; i < transfers.size(); ++i) {
1437       float input = i / 255.0f;
1438       pFunc->Call(pdfium::span_from_ref(input), results);
1439       transfers[i] = FXSYS_roundf(results[0] * 255);
1440     }
1441   } else {
1442     // Fill |transfers| with 0, 1, ... N.
1443     std::iota(transfers.begin(), transfers.end(), 0);
1444   }
1445   if (bLuminosity) {
1446     const int bytes_per_pixel = bitmap->GetBPP() / 8;
1447     for (int row = 0; row < height; row++) {
1448       const size_t dest_offset = Fx2DSizeOrDie(row, dest_pitch);
1449       const size_t src_offset = Fx2DSizeOrDie(row, src_pitch);
1450       uint8_t* dest_pos = dest_buf.subspan(dest_offset).data();
1451       const uint8_t* src_pos = src_buf.subspan(src_offset).data();
1452       for (int col = 0; col < width; col++) {
1453         UNSAFE_TODO({
1454           *dest_pos++ = transfers[FXRGB2GRAY(src_pos[2], src_pos[1], *src_pos)];
1455           src_pos += bytes_per_pixel;
1456         });
1457       }
1458     }
1459   } else if (pFunc) {
1460     int size = dest_pitch * height;
1461     for (int i = 0; i < size; i++) {
1462       dest_buf[i] = transfers[src_buf[i]];
1463     }
1464   } else {
1465     fxcrt::Copy(src_buf.first(dest_pitch * height), dest_buf);
1466   }
1467   return result_mask;
1468 }
1469 
GetBackgroundColor(const CPDF_Dictionary * pSMaskDict,const CPDF_Dictionary * pGroupDict,CPDF_ColorSpace::Family * pCSFamily)1470 FX_ARGB CPDF_RenderStatus::GetBackgroundColor(
1471     const CPDF_Dictionary* pSMaskDict,
1472     const CPDF_Dictionary* pGroupDict,
1473     CPDF_ColorSpace::Family* pCSFamily) {
1474   static constexpr FX_ARGB kDefaultColor = ArgbEncode(255, 0, 0, 0);
1475   RetainPtr<const CPDF_Array> pBC =
1476       pSMaskDict->GetArrayFor(pdfium::transparency::kBC);
1477   if (!pBC)
1478     return kDefaultColor;
1479 
1480   RetainPtr<const CPDF_Object> pCSObj;
1481   RetainPtr<const CPDF_Dictionary> pGroup =
1482       pGroupDict ? pGroupDict->GetDictFor("Group") : nullptr;
1483   if (pGroup)
1484     pCSObj = pGroup->GetDirectObjectFor(pdfium::transparency::kCS);
1485   RetainPtr<CPDF_ColorSpace> pCS =
1486       CPDF_DocPageData::FromDocument(m_pContext->GetDocument())
1487           ->GetColorSpace(pCSObj.Get(), nullptr);
1488   if (!pCS)
1489     return kDefaultColor;
1490 
1491   CPDF_ColorSpace::Family family = pCS->GetFamily();
1492   if (family == CPDF_ColorSpace::Family::kLab || pCS->IsSpecial() ||
1493       (family == CPDF_ColorSpace::Family::kICCBased && !pCS->IsNormal())) {
1494     return kDefaultColor;
1495   }
1496 
1497   // Store Color Space Family to use in CPDF_RenderStatus::Initialize().
1498   *pCSFamily = family;
1499 
1500   uint32_t comps = std::max(8u, pCS->ComponentCount());
1501   size_t count = std::min<size_t>(8, pBC->size());
1502   std::vector<float> floats = ReadArrayElementsToVector(pBC.Get(), count);
1503   floats.resize(comps);
1504 
1505   auto rgb = pCS->GetRGBOrZerosOnError(floats);
1506   return ArgbEncode(255, static_cast<int>(rgb.red * 255),
1507                     static_cast<int>(rgb.green * 255),
1508                     static_cast<int>(rgb.blue * 255));
1509 }
1510 
GetCompatibleArgbFormat() const1511 FXDIB_Format CPDF_RenderStatus::GetCompatibleArgbFormat() const {
1512 #if defined(PDF_USE_SKIA)
1513   if (m_pDevice->GetDeviceCaps(FXDC_RENDER_CAPS) & FXRC_PREMULTIPLIED_ALPHA) {
1514     return FXDIB_Format::kBgraPremul;
1515   }
1516 #endif
1517   return FXDIB_Format::kBgra;
1518 }
1519