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