Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cfx_renderdevice.cpp
Go to the documentation of this file.
1// Copyright 2016 The PDFium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "core/fxge/cfx_renderdevice.h"
8
9#include <math.h>
10
11#include <algorithm>
12#include <memory>
13#include <utility>
14
15#include "build/build_config.h"
16#include "core/fxcrt/check.h"
17#include "core/fxcrt/check_op.h"
18#include "core/fxcrt/compiler_specific.h"
19#include "core/fxcrt/fx_safe_types.h"
20#include "core/fxcrt/span.h"
21#include "core/fxge/cfx_color.h"
22#include "core/fxge/cfx_defaultrenderdevice.h"
23#include "core/fxge/cfx_fillrenderoptions.h"
24#include "core/fxge/cfx_font.h"
25#include "core/fxge/cfx_fontmgr.h"
26#include "core/fxge/cfx_gemodule.h"
27#include "core/fxge/cfx_glyphbitmap.h"
28#include "core/fxge/cfx_glyphcache.h"
29#include "core/fxge/cfx_graphstatedata.h"
30#include "core/fxge/cfx_path.h"
31#include "core/fxge/cfx_textrenderoptions.h"
32#include "core/fxge/dib/cfx_dibitmap.h"
33#include "core/fxge/fx_font.h"
34#include "core/fxge/renderdevicedriver_iface.h"
35#include "core/fxge/text_char_pos.h"
36#include "core/fxge/text_glyph_pos.h"
37
38#if defined(PDF_USE_SKIA)
39#include "third_party/skia/include/core/SkTypes.h" // nogncheck
40#endif
41
42namespace {
43
44void AdjustGlyphSpace(std::vector<TextGlyphPos>* pGlyphAndPos) {
45 DCHECK_GT(pGlyphAndPos->size(), 1u);
46 std::vector<TextGlyphPos>& glyphs = *pGlyphAndPos;
47 bool bVertical = glyphs.back().m_Origin.x == glyphs.front().m_Origin.x;
48 if (!bVertical && (glyphs.back().m_Origin.y != glyphs.front().m_Origin.y))
49 return;
50
51 for (size_t i = glyphs.size() - 1; i > 1; --i) {
52 const TextGlyphPos& next = glyphs[i];
53 int next_origin = bVertical ? next.m_Origin.y : next.m_Origin.x;
54 float next_origin_f =
55 bVertical ? next.m_fDeviceOrigin.y : next.m_fDeviceOrigin.x;
56
57 TextGlyphPos& current = glyphs[i - 1];
58 int& current_origin = bVertical ? current.m_Origin.y : current.m_Origin.x;
59 float current_origin_f =
60 bVertical ? current.m_fDeviceOrigin.y : current.m_fDeviceOrigin.x;
61
62 FX_SAFE_INT32 safe_space = next_origin;
63 safe_space -= current_origin;
64 if (!safe_space.IsValid())
65 continue;
66
67 int space = safe_space.ValueOrDie();
68 float space_f = next_origin_f - current_origin_f;
69 float error = fabs(space_f) - fabs(static_cast<float>(space));
70 if (error <= 0.5f)
71 continue;
72
73 FX_SAFE_INT32 safe_origin = current_origin;
74 safe_origin += space > 0 ? -1 : 1;
75 if (!safe_origin.IsValid())
76 continue;
77
78 current_origin = safe_origin.ValueOrDie();
79 }
80}
81
82constexpr std::array<const uint8_t, 256> kTextGammaAdjust = {{
83 0, 2, 3, 4, 6, 7, 8, 10, 11, 12, 13, 15, 16, 17, 18,
84 19, 21, 22, 23, 24, 25, 26, 27, 29, 30, 31, 32, 33, 34, 35,
85 36, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 51, 52,
86 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
87 68, 69, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83,
88 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98,
89 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
90 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128,
91 129, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
92 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 156,
93 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171,
94 172, 173, 174, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185,
95 186, 187, 188, 189, 190, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199,
96 200, 201, 202, 203, 204, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213,
97 214, 215, 216, 217, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227,
98 228, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 239, 240,
99 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 250, 251, 252, 253, 254,
100 255,
101}};
102
103int TextGammaAdjust(int value) {
104 return kTextGammaAdjust[value];
105}
106
107int CalcAlpha(int src, int alpha) {
108 return src * alpha / 255;
109}
110
111void MergeGammaAdjust(uint8_t src, int channel, int alpha, uint8_t* dest) {
112 *dest =
113 FXDIB_ALPHA_MERGE(*dest, channel, CalcAlpha(TextGammaAdjust(src), alpha));
114}
115
116void MergeGammaAdjustRgb(const uint8_t* src,
117 const FX_BGRA_STRUCT<uint8_t>& bgra,
118 uint8_t* dest) {
120 MergeGammaAdjust(src[2], bgra.blue, bgra.alpha, &dest[0]);
121 MergeGammaAdjust(src[1], bgra.green, bgra.alpha, &dest[1]);
122 MergeGammaAdjust(src[0], bgra.red, bgra.alpha, &dest[2]);
123 });
124}
125
126int AverageRgb(const uint8_t* src) {
127 return UNSAFE_TODO((src[0] + src[1] + src[2]) / 3);
128}
129
130uint8_t CalculateDestAlpha(uint8_t back_alpha, int src_alpha) {
131 return back_alpha + src_alpha - back_alpha * src_alpha / 255;
132}
133
134void ApplyAlpha(uint8_t* dest, const FX_BGRA_STRUCT<uint8_t>& bgra, int alpha) {
136 dest[0] = FXDIB_ALPHA_MERGE(dest[0], bgra.blue, alpha);
137 dest[1] = FXDIB_ALPHA_MERGE(dest[1], bgra.green, alpha);
138 dest[2] = FXDIB_ALPHA_MERGE(dest[2], bgra.red, alpha);
139 });
140}
141
142void ApplyDestAlpha(uint8_t back_alpha,
143 int src_alpha,
144 const FX_BGRA_STRUCT<uint8_t>& bgra,
145 uint8_t* dest) {
146 uint8_t dest_alpha = CalculateDestAlpha(back_alpha, src_alpha);
147 ApplyAlpha(dest, bgra, src_alpha * 255 / dest_alpha);
148 UNSAFE_TODO(dest[3] = dest_alpha);
149}
150
151void NormalizeArgb(int src_value,
152 const FX_BGRA_STRUCT<uint8_t>& bgra,
153 uint8_t* dest,
154 int src_alpha) {
156 uint8_t back_alpha = dest[3];
157 if (back_alpha == 0) {
158 FXARGB_SetDIB(dest,
159 ArgbEncode(src_alpha, bgra.red, bgra.green, bgra.blue));
160 } else if (src_alpha != 0) {
161 ApplyDestAlpha(back_alpha, src_alpha, bgra, dest);
162 }
163 });
164}
165
166void NormalizeDest(bool has_alpha,
167 int src_value,
168 const FX_BGRA_STRUCT<uint8_t>& bgra,
169 uint8_t* dest) {
170 if (has_alpha) {
171 NormalizeArgb(src_value, bgra, dest,
172 CalcAlpha(TextGammaAdjust(src_value), bgra.alpha));
173 return;
174 }
175 int src_alpha = CalcAlpha(TextGammaAdjust(src_value), bgra.alpha);
176 if (src_alpha == 0)
177 return;
178
179 ApplyAlpha(dest, bgra, src_alpha);
180}
181
182void NormalizeSrc(bool has_alpha,
183 int src_value,
184 const FX_BGRA_STRUCT<uint8_t>& bgra,
185 uint8_t* dest) {
186 if (!has_alpha) {
187 ApplyAlpha(dest, bgra, CalcAlpha(TextGammaAdjust(src_value), bgra.alpha));
188 return;
189 }
190 int src_alpha = CalcAlpha(TextGammaAdjust(src_value), bgra.alpha);
191 if (src_alpha != 0)
192 NormalizeArgb(src_value, bgra, dest, src_alpha);
193}
194
195void NextPixel(const uint8_t** src_scan, uint8_t** dst_scan, int bpp) {
197 *src_scan += 3;
198 *dst_scan += bpp;
199 });
200}
201
202void SetAlpha(bool has_alpha, uint8_t* alpha) {
203 if (has_alpha) {
204 UNSAFE_TODO(alpha[3] = 255);
205 }
206}
207
208void DrawNormalTextHelper(const RetainPtr<CFX_DIBitmap>& bitmap,
209 const RetainPtr<CFX_DIBitmap>& pGlyph,
210 int nrows,
211 int left,
212 int top,
213 int start_col,
214 int end_col,
215 bool normalize,
216 int x_subpixel,
217 const FX_BGRA_STRUCT<uint8_t>& bgra) {
218 // TODO(crbug.com/42271020): Add support for `FXDIB_Format::kBgraPremul`.
219 CHECK(!bitmap->IsPremultiplied());
220 const bool has_alpha = bitmap->IsAlphaFormat();
221 const int bytes_per_pixel = has_alpha ? 4 : bitmap->GetBPP() / 8;
222 for (int row = 0; row < nrows; ++row) {
223 int dest_row = row + top;
224 if (dest_row < 0 || dest_row >= bitmap->GetHeight())
225 continue;
226
227 const uint8_t* src_scan =
228 pGlyph->GetScanline(row).subspan((start_col - left) * 3).data();
229 uint8_t* dest_scan = bitmap->GetWritableScanline(dest_row)
230 .subspan(start_col * bytes_per_pixel)
231 .data();
232 if (x_subpixel == 0) {
233 for (int col = start_col; col < end_col; ++col) {
234 if (normalize) {
235 int src_value = AverageRgb(&src_scan[0]);
236 NormalizeDest(has_alpha, src_value, bgra, dest_scan);
237 } else {
238 MergeGammaAdjustRgb(&src_scan[0], bgra, &dest_scan[0]);
239 SetAlpha(has_alpha, dest_scan);
240 }
241 NextPixel(&src_scan, &dest_scan, bytes_per_pixel);
242 }
243 continue;
244 }
246 if (x_subpixel == 1) {
247 if (normalize) {
248 int src_value = start_col > left ? AverageRgb(&src_scan[-1])
249 : (src_scan[0] + src_scan[1]) / 3;
250 NormalizeSrc(has_alpha, src_value, bgra, dest_scan);
251 } else {
252 if (start_col > left) {
253 MergeGammaAdjust(src_scan[-1], bgra.red, bgra.alpha, &dest_scan[2]);
254 }
255 MergeGammaAdjust(src_scan[0], bgra.green, bgra.alpha, &dest_scan[1]);
256 MergeGammaAdjust(src_scan[1], bgra.blue, bgra.alpha, &dest_scan[0]);
257 SetAlpha(has_alpha, dest_scan);
258 }
259 NextPixel(&src_scan, &dest_scan, bytes_per_pixel);
260 for (int col = start_col + 1; col < end_col; ++col) {
261 if (normalize) {
262 int src_value = AverageRgb(&src_scan[-1]);
263 NormalizeDest(has_alpha, src_value, bgra, dest_scan);
264 } else {
265 MergeGammaAdjustRgb(&src_scan[-1], bgra, &dest_scan[0]);
266 SetAlpha(has_alpha, dest_scan);
267 }
268 NextPixel(&src_scan, &dest_scan, bytes_per_pixel);
269 }
270 continue;
271 }
272 if (normalize) {
273 int src_value =
274 start_col > left ? AverageRgb(&src_scan[-2]) : src_scan[0] / 3;
275 NormalizeSrc(has_alpha, src_value, bgra, dest_scan);
276 } else {
277 if (start_col > left) {
278 MergeGammaAdjust(src_scan[-2], bgra.red, bgra.alpha, &dest_scan[2]);
279 MergeGammaAdjust(src_scan[-1], bgra.green, bgra.alpha, &dest_scan[1]);
280 }
281 MergeGammaAdjust(src_scan[0], bgra.blue, bgra.alpha, &dest_scan[0]);
282 SetAlpha(has_alpha, dest_scan);
283 }
284 NextPixel(&src_scan, &dest_scan, bytes_per_pixel);
285 for (int col = start_col + 1; col < end_col; ++col) {
286 if (normalize) {
287 int src_value = AverageRgb(&src_scan[-2]);
288 NormalizeDest(has_alpha, src_value, bgra, dest_scan);
289 } else {
290 MergeGammaAdjustRgb(&src_scan[-2], bgra, &dest_scan[0]);
291 SetAlpha(has_alpha, dest_scan);
292 }
293 NextPixel(&src_scan, &dest_scan, bytes_per_pixel);
294 }
295 });
296 }
297}
298
299bool ShouldDrawDeviceText(const CFX_Font* pFont,
300 const CFX_TextRenderOptions& options) {
301#if BUILDFLAG(IS_APPLE)
302 if (options.font_is_cid)
303 return false;
304
305 const ByteString bsPsName = pFont->GetPsName();
306 if (bsPsName.Contains("+ZJHL"))
307 return false;
308
309 if (bsPsName == "CNAAJI+cmex10")
310 return false;
311#endif
312 return true;
313}
314
315// Returns true if the path is a 3-point path that draws A->B->A and forms a
316// zero area, or a 2-point path which draws A->B.
317bool CheckSimpleLinePath(pdfium::span<const CFX_Path::Point> points,
318 const CFX_Matrix* matrix,
319 bool adjust,
320 CFX_Path* new_path,
321 bool* thin,
322 bool* set_identity) {
323 if (points.size() != 2 && points.size() != 3)
324 return false;
325
326 if (points[0].m_Type != CFX_Path::Point::Type::kMove ||
327 points[1].m_Type != CFX_Path::Point::Type::kLine ||
328 (points.size() == 3 &&
329 (points[2].m_Type != CFX_Path::Point::Type::kLine ||
330 points[0].m_Point != points[2].m_Point))) {
331 return false;
332 }
333
334 // A special case that all points are identical, zero area is formed and no
335 // thin line needs to be drawn.
336 if (points[0].m_Point == points[1].m_Point)
337 return true;
338
339 for (size_t i = 0; i < 2; i++) {
340 CFX_PointF point = points[i].m_Point;
341 if (adjust) {
342 if (matrix)
343 point = matrix->Transform(point);
344
345 point = CFX_PointF(static_cast<int>(point.x) + 0.5f,
346 static_cast<int>(point.y) + 0.5f);
347 }
348 new_path->AppendPoint(point, points[i].m_Type);
349 }
350 if (adjust && matrix)
351 *set_identity = true;
352
353 *thin = true;
354 return true;
355}
356
357// Returns true if `points` is palindromic and forms zero area. Otherwise,
358// returns false.
359bool CheckPalindromicPath(pdfium::span<const CFX_Path::Point> points,
360 CFX_Path* new_path,
361 bool* thin) {
362 if (points.size() <= 3 || !(points.size() % 2))
363 return false;
364
365 const size_t mid = points.size() / 2;
366 CFX_Path temp_path;
367 for (size_t i = 0; i < mid; i++) {
368 const CFX_Path::Point& left = points[mid - i - 1];
369 const CFX_Path::Point& right = points[mid + i + 1];
370 bool zero_area = left.m_Point == right.m_Point &&
373 if (!zero_area)
374 return false;
375
376 temp_path.AppendPoint(points[mid - i].m_Point,
379 }
380
381 new_path->Append(temp_path, nullptr);
382 *thin = true;
383 return true;
384}
385
386bool IsFoldingVerticalLine(const CFX_PointF& a,
387 const CFX_PointF& b,
388 const CFX_PointF& c) {
389 return a.x == b.x && b.x == c.x && (b.y - a.y) * (b.y - c.y) > 0;
390}
391
392bool IsFoldingHorizontalLine(const CFX_PointF& a,
393 const CFX_PointF& b,
394 const CFX_PointF& c) {
395 return a.y == b.y && b.y == c.y && (b.x - a.x) * (b.x - c.x) > 0;
396}
397
398bool IsFoldingDiagonalLine(const CFX_PointF& a,
399 const CFX_PointF& b,
400 const CFX_PointF& c) {
401 return a.x != b.x && c.x != b.x && a.y != b.y && c.y != b.y &&
402 (a.y - b.y) * (c.x - b.x) == (c.y - b.y) * (a.x - b.x);
403}
404
405bool GetZeroAreaPath(pdfium::span<const CFX_Path::Point> points,
406 const CFX_Matrix* matrix,
407 bool adjust,
408 CFX_Path* new_path,
409 bool* thin,
410 bool* set_identity) {
411 *set_identity = false;
412
413 if (points.size() < 2)
414 return false;
415
416 if (CheckSimpleLinePath(points, matrix, adjust, new_path, thin,
417 set_identity)) {
418 return true;
419 }
420
421 if (CheckPalindromicPath(points, new_path, thin))
422 return true;
423
424 for (size_t i = 0; i < points.size(); i++) {
425 CFX_Path::Point::Type point_type = points[i].m_Type;
426 if (point_type == CFX_Path::Point::Type::kMove) {
427 DCHECK_EQ(0u, i);
428 continue;
429 }
430
431 if (point_type == CFX_Path::Point::Type::kBezier) {
432 i += 2;
433 DCHECK_LT(i, points.size());
434 continue;
435 }
436
438 size_t next_index = (i + 1) % (points.size());
439 const CFX_Path::Point& next = points[next_index];
441 continue;
442
443 const CFX_Path::Point& prev = points[i - 1];
444 const CFX_Path::Point& cur = points[i];
445 if (IsFoldingVerticalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
446 bool use_prev = fabs(cur.m_Point.y - prev.m_Point.y) <
447 fabs(cur.m_Point.y - next.m_Point.y);
448 const CFX_Path::Point& start = use_prev ? prev : cur;
449 const CFX_Path::Point& end = use_prev ? cur : next;
452 continue;
453 }
454
455 if (IsFoldingHorizontalLine(prev.m_Point, cur.m_Point, next.m_Point) ||
456 IsFoldingDiagonalLine(prev.m_Point, cur.m_Point, next.m_Point)) {
457 bool use_prev = fabs(cur.m_Point.x - prev.m_Point.x) <
458 fabs(cur.m_Point.x - next.m_Point.x);
459 const CFX_Path::Point& start = use_prev ? prev : cur;
460 const CFX_Path::Point& end = use_prev ? cur : next;
463 continue;
464 }
465 }
466
467 size_t new_path_size = new_path->GetPoints().size();
468 if (points.size() > 3 && new_path_size > 0)
469 *thin = true;
470 return new_path_size != 0;
471}
472
473FXDIB_Format GetCreateCompatibleBitmapFormat(int render_caps,
474 bool use_argb_premul) {
475 if (render_caps & FXRC_BYTEMASK_OUTPUT) {
477 }
478#if defined(PDF_USE_SKIA)
479 if (use_argb_premul && (render_caps & FXRC_PREMULTIPLIED_ALPHA)) {
480 return FXDIB_Format::kBgraPremul;
481 }
482#endif
483 if (render_caps & FXRC_ALPHA_OUTPUT) {
484 return FXDIB_Format::kBgra;
485 }
487}
488
489} // namespace
490
492
496
497// static
499 float height,
500 float left,
501 float top) {
502 return CFX_Matrix(width, 0, 0, -height, left, top + height);
503}
504
506 std::unique_ptr<RenderDeviceDriverIface> pDriver) {
507 DCHECK(pDriver);
508 DCHECK(!m_pDeviceDriver);
509 m_pDeviceDriver = std::move(pDriver);
510 InitDeviceInfo();
511}
512
513void CFX_RenderDevice::InitDeviceInfo() {
514 m_Width = m_pDeviceDriver->GetDeviceCaps(FXDC_PIXEL_WIDTH);
515 m_Height = m_pDeviceDriver->GetDeviceCaps(FXDC_PIXEL_HEIGHT);
516 m_bpp = m_pDeviceDriver->GetDeviceCaps(FXDC_BITS_PIXEL);
517 m_RenderCaps = m_pDeviceDriver->GetDeviceCaps(FXDC_RENDER_CAPS);
518 m_DeviceType = m_pDeviceDriver->GetDeviceType();
519 m_ClipBox = m_pDeviceDriver->GetClipBox();
520}
521
523 m_pDeviceDriver->SaveState();
524}
525
526void CFX_RenderDevice::RestoreState(bool bKeepSaved) {
527 if (m_pDeviceDriver) {
528 m_pDeviceDriver->RestoreState(bKeepSaved);
529 UpdateClipBox();
530 }
531}
532
533int CFX_RenderDevice::GetDeviceCaps(int caps_id) const {
534 return m_pDeviceDriver->GetDeviceCaps(caps_id);
535}
536
538 return m_pBitmap;
539}
540
541RetainPtr<const CFX_DIBitmap> CFX_RenderDevice::GetBitmap() const {
542 return m_pBitmap;
543}
544
545void CFX_RenderDevice::SetBitmap(RetainPtr<CFX_DIBitmap> bitmap) {
546 m_pBitmap = std::move(bitmap);
547}
548
550 const RetainPtr<CFX_DIBitmap>& pDIB,
551 int width,
552 int height) const {
553 return pDIB->Create(
554 width, height,
555 GetCreateCompatibleBitmapFormat(m_RenderCaps, /*use_argb_premul=*/true));
556}
557
559 m_pDeviceDriver->SetBaseClip(rect);
560}
561
563 const CFX_Path& path,
564 const CFX_Matrix* pObject2Device,
565 const CFX_FillRenderOptions& fill_options) {
566 if (!m_pDeviceDriver->SetClip_PathFill(path, pObject2Device, fill_options))
567 return false;
568
569 UpdateClipBox();
570 return true;
571}
572
574 const CFX_Path& path,
575 const CFX_Matrix* pObject2Device,
576 const CFX_GraphStateData* pGraphState) {
577 if (!m_pDeviceDriver->SetClip_PathStroke(path, pObject2Device, pGraphState))
578 return false;
579
580 UpdateClipBox();
581 return true;
582}
583
585 CFX_Path path;
586 path.AppendRect(rect.left, rect.bottom, rect.right, rect.top);
587 if (!SetClip_PathFill(path, nullptr,
589 return false;
590 }
591
592 UpdateClipBox();
593 return true;
594}
595
596void CFX_RenderDevice::UpdateClipBox() {
597 m_ClipBox = m_pDeviceDriver->GetClipBox();
598}
599
601 const CFX_Matrix* pObject2Device,
602 const CFX_GraphStateData* pGraphState,
603 uint32_t fill_color,
604 uint32_t stroke_color,
605 const CFX_FillRenderOptions& fill_options) {
606 const bool fill =
608 uint8_t fill_alpha = fill ? FXARGB_A(fill_color) : 0;
609 uint8_t stroke_alpha = pGraphState ? FXARGB_A(stroke_color) : 0;
610 pdfium::span<const CFX_Path::Point> points = path.GetPoints();
611 if (stroke_alpha == 0 && points.size() == 2) {
612 CFX_PointF pos1 = points[0].m_Point;
613 CFX_PointF pos2 = points[1].m_Point;
614 if (pObject2Device) {
615 pos1 = pObject2Device->Transform(pos1);
616 pos2 = pObject2Device->Transform(pos2);
617 }
618 DrawCosmeticLine(pos1, pos2, fill_color, fill_options);
619 return true;
620 }
621
622 if (stroke_alpha == 0 && !fill_options.rect_aa) {
623 std::optional<CFX_FloatRect> maybe_rect_f = path.GetRect(pObject2Device);
624 if (maybe_rect_f.has_value()) {
625 const CFX_FloatRect& rect_f = maybe_rect_f.value();
626 FX_RECT rect_i = rect_f.GetOuterRect();
627
628 // Depending on the top/bottom, left/right values of the rect it's
629 // possible to overflow the Width() and Height() calculations. Check that
630 // the rect will have valid dimension before continuing.
631 if (!rect_i.Valid()) {
632 return false;
633 }
634
635 int width = static_cast<int>(ceil(rect_f.right - rect_f.left));
636 if (width < 1) {
637 width = 1;
638 if (rect_i.left == rect_i.right) {
639 if (!pdfium::CheckAdd(rect_i.right, 1).AssignIfValid(&rect_i.right)) {
640 return false;
641 }
642 }
643 }
644 int height = static_cast<int>(ceil(rect_f.top - rect_f.bottom));
645 if (height < 1) {
646 height = 1;
647 if (rect_i.bottom == rect_i.top) {
648 if (!pdfium::CheckAdd(rect_i.bottom, 1)
649 .AssignIfValid(&rect_i.bottom)) {
650 return false;
651 }
652 }
653 }
654 if (rect_i.Width() >= width + 1) {
655 if (rect_f.left - static_cast<float>(rect_i.left) >
656 static_cast<float>(rect_i.right) - rect_f.right) {
657 if (!pdfium::CheckAdd(rect_i.left, 1).AssignIfValid(&rect_i.left)) {
658 return false;
659 }
660 } else {
661 if (!pdfium::CheckSub(rect_i.right, 1).AssignIfValid(&rect_i.right)) {
662 return false;
663 }
664 }
665 }
666 if (rect_i.Height() >= height + 1) {
667 if (rect_f.top - static_cast<float>(rect_i.top) >
668 static_cast<float>(rect_i.bottom) - rect_f.bottom) {
669 if (!pdfium::CheckAdd(rect_i.top, 1).AssignIfValid(&rect_i.top)) {
670 return false;
671 }
672 } else {
673 if (!pdfium::CheckSub(rect_i.bottom, 1)
674 .AssignIfValid(&rect_i.bottom)) {
675 return false;
676 }
677 }
678 }
679 if (FillRect(rect_i, fill_color)) {
680 return true;
681 }
682 }
683 }
684
685 if (fill && stroke_alpha == 0 && !fill_options.stroke &&
686 !fill_options.text_mode) {
687 bool adjust = !!m_pDeviceDriver->GetDriverType();
688 std::vector<CFX_Path::Point> sub_path;
689 for (size_t i = 0; i < points.size(); i++) {
690 CFX_Path::Point::Type point_type = points[i].m_Type;
691 if (point_type == CFX_Path::Point::Type::kMove) {
692 // Process the existing sub path.
693 DrawZeroAreaPath(sub_path, pObject2Device, adjust,
694 fill_options.aliased_path, fill_color, fill_alpha);
695 sub_path.clear();
696
697 // Start forming the next sub path.
698 sub_path.push_back(points[i]);
699 continue;
700 }
701
702 if (point_type == CFX_Path::Point::Type::kBezier) {
703 sub_path.push_back(points[i]);
704 sub_path.push_back(points[i + 1]);
705 sub_path.push_back(points[i + 2]);
706 i += 2;
707 continue;
708 }
709
711 sub_path.push_back(points[i]);
712 }
713 // Process the last sub paths.
714 DrawZeroAreaPath(sub_path, pObject2Device, adjust,
715 fill_options.aliased_path, fill_color, fill_alpha);
716 }
717
718 if (fill && fill_alpha && stroke_alpha < 0xff && fill_options.stroke) {
719#if defined(PDF_USE_SKIA)
720 if (m_RenderCaps & FXRC_FILLSTROKE_PATH) {
721 const bool using_skia = CFX_DefaultRenderDevice::UseSkiaRenderer();
722 if (using_skia) {
723 m_pDeviceDriver->SetGroupKnockout(true);
724 }
725 bool draw_fillstroke_path_result =
726 m_pDeviceDriver->DrawPath(path, pObject2Device, pGraphState,
727 fill_color, stroke_color, fill_options);
728
729 if (using_skia) {
730 // Restore the group knockout status for `m_pDeviceDriver` after
731 // finishing painting a fill-and-stroke path.
732 m_pDeviceDriver->SetGroupKnockout(false);
733 }
734 return draw_fillstroke_path_result;
735 }
736#endif // defined(PDF_USE_SKIA)
737 return DrawFillStrokePath(path, pObject2Device, pGraphState, fill_color,
738 stroke_color, fill_options);
739 }
740 return m_pDeviceDriver->DrawPath(path, pObject2Device, pGraphState,
741 fill_color, stroke_color, fill_options);
742}
743
744// This can be removed once PDFium entirely relies on Skia
745bool CFX_RenderDevice::DrawFillStrokePath(
746 const CFX_Path& path,
747 const CFX_Matrix* pObject2Device,
748 const CFX_GraphStateData* pGraphState,
749 uint32_t fill_color,
750 uint32_t stroke_color,
751 const CFX_FillRenderOptions& fill_options) {
752 if (!(m_RenderCaps & FXRC_GET_BITS))
753 return false;
754 CFX_FloatRect bbox;
755 if (pGraphState) {
757 pGraphState->m_MiterLimit);
758 } else {
759 bbox = path.GetBoundingBox();
760 }
761 if (pObject2Device)
762 bbox = pObject2Device->TransformRect(bbox);
763
764 FX_RECT rect = bbox.GetOuterRect();
765 if (!rect.Valid())
766 return false;
767
768 auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
769 auto backdrop = pdfium::MakeRetain<CFX_DIBitmap>();
771 return false;
772
773 if (bitmap->IsAlphaFormat()) {
774 backdrop->Copy(bitmap);
775 } else {
776 if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top))
777 return false;
778 backdrop->Copy(bitmap);
779 }
780 CFX_DefaultRenderDevice bitmap_device;
781 bitmap_device.AttachWithBackdropAndGroupKnockout(bitmap, std::move(backdrop),
782 /*bGroupKnockout=*/true);
783
784 CFX_Matrix matrix;
785 if (pObject2Device)
786 matrix = *pObject2Device;
787 matrix.Translate(-rect.left, -rect.top);
788 if (!bitmap_device.GetDeviceDriver()->DrawPath(
789 path, &matrix, pGraphState, fill_color, stroke_color, fill_options)) {
790 return false;
791 }
792 FX_RECT src_rect(0, 0, rect.Width(), rect.Height());
793 return m_pDeviceDriver->SetDIBits(std::move(bitmap), /*color=*/0, src_rect,
794 rect.left, rect.top, BlendMode::kNormal);
795}
796
797bool CFX_RenderDevice::FillRect(const FX_RECT& rect, uint32_t fill_color) {
798 if (m_pDeviceDriver->FillRect(rect, fill_color)) {
799 return true;
800 }
801
802 if (!(m_RenderCaps & FXRC_GET_BITS)) {
803 return false;
804 }
805
806 auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
807 if (!CreateCompatibleBitmap(bitmap, rect.Width(), rect.Height())) {
808 return false;
809 }
810
811 if (!m_pDeviceDriver->GetDIBits(bitmap, rect.left, rect.top)) {
812 return false;
813 }
814
815 if (!bitmap->CompositeRect(0, 0, rect.Width(), rect.Height(), fill_color)) {
816 return false;
817 }
818
819 FX_RECT src_rect(0, 0, rect.Width(), rect.Height());
820 m_pDeviceDriver->SetDIBits(std::move(bitmap), /*color=*/0, src_rect,
821 rect.left, rect.top, BlendMode::kNormal);
822 return true;
823}
824
825bool CFX_RenderDevice::DrawCosmeticLine(
826 const CFX_PointF& ptMoveTo,
827 const CFX_PointF& ptLineTo,
828 uint32_t color,
829 const CFX_FillRenderOptions& fill_options) {
830 if ((color >= 0xff000000) &&
831 m_pDeviceDriver->DrawCosmeticLine(ptMoveTo, ptLineTo, color)) {
832 return true;
833 }
834 CFX_GraphStateData graph_state;
835 CFX_Path path;
838 return m_pDeviceDriver->DrawPath(path, nullptr, &graph_state, 0, color,
839 fill_options);
840}
841
842void CFX_RenderDevice::DrawZeroAreaPath(
843 const std::vector<CFX_Path::Point>& path,
844 const CFX_Matrix* matrix,
845 bool adjust,
846 bool aliased_path,
847 uint32_t fill_color,
848 uint8_t fill_alpha) {
849 if (path.empty())
850 return;
851
852 CFX_Path new_path;
853 bool thin = false;
854 bool set_identity = false;
855
856 if (!GetZeroAreaPath(path, matrix, adjust, &new_path, &thin, &set_identity))
857 return;
858
859 CFX_GraphStateData graph_state;
860 graph_state.m_LineWidth = 0.0f;
861
862 uint32_t stroke_color = fill_color;
863 if (thin)
864 stroke_color = (((fill_alpha >> 2) << 24) | (stroke_color & 0x00ffffff));
865
866 const CFX_Matrix* new_matrix = nullptr;
867 if (matrix && !matrix->IsIdentity() && !set_identity)
868 new_matrix = matrix;
869
870 CFX_FillRenderOptions path_options;
871 path_options.zero_area = true;
872 path_options.aliased_path = aliased_path;
873
874 m_pDeviceDriver->DrawPath(new_path, new_matrix, &graph_state, 0, stroke_color,
875 path_options);
876}
877
878bool CFX_RenderDevice::GetDIBits(RetainPtr<CFX_DIBitmap> bitmap,
879 int left,
880 int top) const {
881 return (m_RenderCaps & FXRC_GET_BITS) &&
882 m_pDeviceDriver->GetDIBits(std::move(bitmap), left, top);
883}
884
886 int left,
887 int top) {
888 return SetDIBitsWithBlend(std::move(bitmap), left, top, BlendMode::kNormal);
889}
890
891RetainPtr<const CFX_DIBitmap> CFX_RenderDevice::GetBackDrop() const {
892 return m_pDeviceDriver->GetBackDrop();
893}
894
896 int left,
897 int top,
898 BlendMode blend_mode) {
899 DCHECK(!bitmap->IsMaskFormat());
900 FX_RECT dest_rect(left, top, left + bitmap->GetWidth(),
901 top + bitmap->GetHeight());
902 dest_rect.Intersect(m_ClipBox);
903 if (dest_rect.IsEmpty())
904 return true;
905
906 FX_RECT src_rect(dest_rect.left - left, dest_rect.top - top,
907 dest_rect.left - left + dest_rect.Width(),
908 dest_rect.top - top + dest_rect.Height());
909 if ((blend_mode == BlendMode::kNormal || (m_RenderCaps & FXRC_BLEND_MODE)) &&
910 (!bitmap->IsAlphaFormat() || (m_RenderCaps & FXRC_ALPHA_IMAGE))) {
911 return m_pDeviceDriver->SetDIBits(std::move(bitmap), /*color=*/0, src_rect,
912 dest_rect.left, dest_rect.top,
913 blend_mode);
914 }
915 if (!(m_RenderCaps & FXRC_GET_BITS))
916 return false;
917
918 int bg_pixel_width = dest_rect.Width();
919 int bg_pixel_height = dest_rect.Height();
920 auto background = pdfium::MakeRetain<CFX_DIBitmap>();
921 if (!background->Create(bg_pixel_width, bg_pixel_height,
923 return false;
924 }
925 if (!m_pDeviceDriver->GetDIBits(background, dest_rect.left, dest_rect.top))
926 return false;
927
928 if (!background->CompositeBitmap(0, 0, bg_pixel_width, bg_pixel_height,
929 std::move(bitmap), src_rect.left,
930 src_rect.top, blend_mode, nullptr, false)) {
931 return false;
932 }
933 FX_RECT rect(0, 0, bg_pixel_width, bg_pixel_height);
934 return m_pDeviceDriver->SetDIBits(std::move(background), /*color=*/0, rect,
935 dest_rect.left, dest_rect.top,
936 BlendMode::kNormal);
937}
938
940 int left,
941 int top,
942 int dest_width,
943 int dest_height) {
945 std::move(bitmap), left, top, dest_width, dest_height,
947}
948
950 RetainPtr<const CFX_DIBBase> bitmap,
951 int left,
952 int top,
953 int dest_width,
954 int dest_height,
955 const FXDIB_ResampleOptions& options,
956 BlendMode blend_mode) {
957 FX_RECT dest_rect(left, top, left + dest_width, top + dest_height);
958 FX_RECT clip_box = m_ClipBox;
959 clip_box.Intersect(dest_rect);
960 return clip_box.IsEmpty() || m_pDeviceDriver->StretchDIBits(
961 std::move(bitmap), 0, left, top, dest_width,
962 dest_height, &clip_box, options, blend_mode);
963}
964
966 int left,
967 int top,
968 uint32_t argb) {
969 FX_RECT src_rect(0, 0, bitmap->GetWidth(), bitmap->GetHeight());
970 return m_pDeviceDriver->SetDIBits(std::move(bitmap), argb, src_rect, left,
971 top, BlendMode::kNormal);
972}
973
975 int left,
976 int top,
977 int dest_width,
978 int dest_height,
979 uint32_t color) {
980 return StretchBitMaskWithFlags(std::move(bitmap), left, top, dest_width,
981 dest_height, color, FXDIB_ResampleOptions());
982}
983
985 RetainPtr<CFX_DIBBase> bitmap,
986 int left,
987 int top,
988 int dest_width,
989 int dest_height,
990 uint32_t argb,
991 const FXDIB_ResampleOptions& options) {
992 FX_RECT dest_rect(left, top, left + dest_width, top + dest_height);
993 FX_RECT clip_box = m_ClipBox;
994 clip_box.Intersect(dest_rect);
995 return m_pDeviceDriver->StretchDIBits(std::move(bitmap), argb, left, top,
996 dest_width, dest_height, &clip_box,
997 options, BlendMode::kNormal);
998}
999
1001 RetainPtr<const CFX_DIBBase> bitmap,
1002 float alpha,
1003 uint32_t argb,
1004 const CFX_Matrix& matrix,
1005 const FXDIB_ResampleOptions& options) {
1006 return StartDIBitsWithBlend(std::move(bitmap), alpha, argb, matrix, options,
1008}
1009
1011 RetainPtr<const CFX_DIBBase> bitmap,
1012 float alpha,
1013 uint32_t argb,
1014 const CFX_Matrix& matrix,
1015 const FXDIB_ResampleOptions& options,
1016 BlendMode blend_mode) {
1017 return m_pDeviceDriver->StartDIBits(std::move(bitmap), alpha, argb, matrix,
1018 options, blend_mode);
1019}
1020
1022 PauseIndicatorIface* pPause) {
1023 return m_pDeviceDriver->ContinueDIBits(handle, pPause);
1024}
1025
1026#if defined(PDF_USE_SKIA)
1027bool CFX_RenderDevice::DrawShading(const CPDF_ShadingPattern& pattern,
1028 const CFX_Matrix& matrix,
1029 const FX_RECT& clip_rect,
1030 int alpha) {
1031 return m_pDeviceDriver->DrawShading(pattern, matrix, clip_rect, alpha);
1032}
1033
1034bool CFX_RenderDevice::SetBitsWithMask(RetainPtr<const CFX_DIBBase> bitmap,
1035 RetainPtr<const CFX_DIBBase> mask,
1036 int left,
1037 int top,
1038 float alpha,
1039 BlendMode blend_type) {
1040 return m_pDeviceDriver->SetBitsWithMask(std::move(bitmap), std::move(mask),
1041 left, top, alpha, blend_type);
1042}
1043
1044void CFX_RenderDevice::SyncInternalBitmaps() {
1045 m_pDeviceDriver->SyncInternalBitmaps();
1046}
1047#endif // defined(PDF_USE_SKIA)
1048
1049bool CFX_RenderDevice::DrawNormalText(pdfium::span<const TextCharPos> pCharPos,
1050 CFX_Font* pFont,
1051 float font_size,
1052 const CFX_Matrix& mtText2Device,
1053 uint32_t fill_color,
1054 const CFX_TextRenderOptions& options) {
1055 // `anti_alias` and `normalize` don't affect Skia rendering.
1056 int anti_alias = FT_RENDER_MODE_MONO;
1057 bool normalize = false;
1058 const bool is_text_smooth = options.IsSmooth();
1059 // |text_options| has the potential to affect all derived classes of
1060 // RenderDeviceDriverIface. But now it only affects Skia rendering.
1061 CFX_TextRenderOptions text_options(options);
1062 if (is_text_smooth) {
1063 if (GetDeviceType() == DeviceType::kDisplay && m_bpp > 1) {
1065 // Some Freetype implementations (like the one packaged with Fedora) do
1066 // not support hinting due to patents 6219025, 6239783, 6307566,
1067 // 6225973, 6243070, 6393145, 6421054, 6282327, and 6624828; the latest
1068 // one expires 10/7/19. This makes LCD anti-aliasing very ugly, so we
1069 // instead fall back on NORMAL anti-aliasing.
1070 anti_alias = FT_RENDER_MODE_NORMAL;
1071 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
1072 // Since |anti_alias| doesn't affect Skia rendering, and Skia only
1073 // follows strictly to the options provided by |text_options|, we need
1074 // to update |text_options| so that Skia falls back on normal
1075 // anti-aliasing as well.
1077 }
1078 } else if ((m_RenderCaps & FXRC_ALPHA_OUTPUT)) {
1079 // Whether Skia uses LCD optimization should strictly follow the
1080 // rendering options provided by |text_options|. No change needs to be
1081 // done for |text_options| here.
1082 anti_alias = FT_RENDER_MODE_LCD;
1083 normalize = true;
1084 } else if (m_bpp < 16) {
1085 // This case doesn't apply to Skia since Skia always have |m_bpp| = 32.
1086 anti_alias = FT_RENDER_MODE_NORMAL;
1087 } else {
1088 // Whether Skia uses LCD optimization should strictly follow the
1089 // rendering options provided by |text_options|. No change needs to be
1090 // done for |text_options| here.
1091 anti_alias = FT_RENDER_MODE_LCD;
1092 normalize = !pFont->GetFaceRec() ||
1094 }
1095 }
1096 }
1097
1098#if BUILDFLAG(IS_WIN)
1099 const bool is_printer = GetDeviceType() == DeviceType::kPrinter;
1100 bool try_native_text = true;
1101#else
1102 constexpr bool is_printer = false;
1103 constexpr bool try_native_text = true;
1104#endif
1105
1106#if BUILDFLAG(IS_WIN)
1107 if (GetDeviceType() == DeviceType::kPrinter) {
1108 if (ShouldDrawDeviceText(pFont, options) &&
1109 m_pDeviceDriver->DrawDeviceText(pCharPos, pFont, mtText2Device,
1110 font_size, fill_color, text_options)) {
1111 return true;
1112 }
1113 if (FXARGB_A(fill_color) < 255) {
1114 return false;
1115 }
1116
1117 try_native_text = false;
1118 }
1119#endif
1120
1121 if (try_native_text && options.native_text) {
1122 if (ShouldDrawDeviceText(pFont, options) &&
1123 m_pDeviceDriver->DrawDeviceText(pCharPos, pFont, mtText2Device,
1124 font_size, fill_color, text_options)) {
1125 return true;
1126 }
1127 }
1128
1129 CFX_Matrix char2device = mtText2Device;
1130 CFX_Matrix text2Device = mtText2Device;
1131 char2device.Scale(font_size, -font_size);
1132 if (fabs(char2device.a) + fabs(char2device.b) > 50 * 1.0f || is_printer) {
1133 if (pFont->GetFaceRec()) {
1134 CFX_FillRenderOptions path_options;
1135 path_options.aliased_path = !is_text_smooth;
1136 return DrawTextPath(pCharPos, pFont, font_size, mtText2Device, nullptr,
1137 nullptr, fill_color, 0, nullptr, path_options);
1138 }
1139 }
1140 std::vector<TextGlyphPos> glyphs(pCharPos.size());
1141 for (size_t i = 0; i < glyphs.size(); ++i) {
1142 TextGlyphPos& glyph = glyphs[i];
1143 const TextCharPos& charpos = pCharPos[i];
1144
1145 glyph.m_fDeviceOrigin = text2Device.Transform(charpos.m_Origin);
1146 if (anti_alias < FT_RENDER_MODE_LCD)
1147 glyph.m_Origin.x = FXSYS_roundf(glyph.m_fDeviceOrigin.x);
1148 else
1149 glyph.m_Origin.x = static_cast<int>(floor(glyph.m_fDeviceOrigin.x));
1150 glyph.m_Origin.y = FXSYS_roundf(glyph.m_fDeviceOrigin.y);
1151
1152 CFX_Matrix matrix = charpos.GetEffectiveMatrix(char2device);
1153 glyph.m_pGlyph = pFont->LoadGlyphBitmap(
1154 charpos.m_GlyphIndex, charpos.m_bFontStyle, matrix,
1155 charpos.m_FontCharWidth, anti_alias, &text_options);
1156 }
1157 if (anti_alias < FT_RENDER_MODE_LCD && glyphs.size() > 1)
1158 AdjustGlyphSpace(&glyphs);
1159
1160 FX_RECT bmp_rect = GetGlyphsBBox(glyphs, anti_alias);
1161 bmp_rect.Intersect(m_ClipBox);
1162 if (bmp_rect.IsEmpty())
1163 return true;
1164
1165 int pixel_width = bmp_rect.Width();
1166 int pixel_height = bmp_rect.Height();
1167 int pixel_left = bmp_rect.left;
1168 int pixel_top = bmp_rect.top;
1169 if (anti_alias == FT_RENDER_MODE_MONO) {
1170 auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
1171 if (!bitmap->Create(pixel_width, pixel_height, FXDIB_Format::k1bppMask))
1172 return false;
1173 for (const TextGlyphPos& glyph : glyphs) {
1174 if (!glyph.m_pGlyph)
1175 continue;
1176
1177 std::optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
1178 if (!point.has_value())
1179 continue;
1180
1181 const RetainPtr<CFX_DIBitmap>& pGlyph = glyph.m_pGlyph->GetBitmap();
1182 bitmap->CompositeOneBPPMask(point.value().x, point.value().y,
1183 pGlyph->GetWidth(), pGlyph->GetHeight(),
1184 pGlyph, 0, 0);
1185 }
1186 return SetBitMask(std::move(bitmap), bmp_rect.left, bmp_rect.top,
1187 fill_color);
1188 }
1189 auto bitmap = pdfium::MakeRetain<CFX_DIBitmap>();
1190 if (m_bpp == 8) {
1191 if (!bitmap->Create(pixel_width, pixel_height, FXDIB_Format::k8bppMask))
1192 return false;
1193 } else {
1194 // TODO(crbug.com/42271020): Switch to CreateCompatibleBitmap() once
1195 // DrawNormalTextHelper() supports `FXDIB_Format::kBgraPremul`.
1196 if (!bitmap->Create(pixel_width, pixel_height,
1197 GetCreateCompatibleBitmapFormat(
1198 m_RenderCaps, /*use_argb_premul=*/false))) {
1199 return false;
1200 }
1201 }
1202 if (!bitmap->IsAlphaFormat() && !bitmap->IsMaskFormat()) {
1203 bitmap->Clear(0xFFFFFFFF);
1204 if (!GetDIBits(bitmap, bmp_rect.left, bmp_rect.top))
1205 return false;
1206 }
1207 int dest_width = pixel_width;
1208 FX_BGRA_STRUCT<uint8_t> bgra;
1209 if (anti_alias == FT_RENDER_MODE_LCD) {
1210 bgra = ArgbToBGRAStruct(fill_color);
1211 }
1212
1213 for (const TextGlyphPos& glyph : glyphs) {
1214 if (!glyph.m_pGlyph)
1215 continue;
1216
1217 std::optional<CFX_Point> point = glyph.GetOrigin({pixel_left, pixel_top});
1218 if (!point.has_value())
1219 continue;
1220
1221 const RetainPtr<CFX_DIBitmap>& pGlyph = glyph.m_pGlyph->GetBitmap();
1222 int ncols = pGlyph->GetWidth();
1223 int nrows = pGlyph->GetHeight();
1224 if (anti_alias == FT_RENDER_MODE_NORMAL) {
1225 if (!bitmap->CompositeMask(point.value().x, point.value().y, ncols, nrows,
1226 pGlyph, fill_color, 0, 0, BlendMode::kNormal,
1227 nullptr, false)) {
1228 return false;
1229 }
1230 continue;
1231 }
1232 ncols /= 3;
1233 int x_subpixel = static_cast<int>(glyph.m_fDeviceOrigin.x * 3) % 3;
1234 int start_col = std::max(point->x, 0);
1235 FX_SAFE_INT32 end_col_safe = point->x;
1236 end_col_safe += ncols;
1237 if (!end_col_safe.IsValid())
1238 continue;
1239
1240 int end_col = std::min<int>(end_col_safe.ValueOrDie(), dest_width);
1241 if (start_col >= end_col)
1242 continue;
1243
1244 DrawNormalTextHelper(bitmap, pGlyph, nrows, point->x, point->y, start_col,
1245 end_col, normalize, x_subpixel, bgra);
1246 }
1247
1248 if (bitmap->IsMaskFormat()) {
1249 SetBitMask(std::move(bitmap), bmp_rect.left, bmp_rect.top, fill_color);
1250 } else {
1251 SetDIBits(std::move(bitmap), bmp_rect.left, bmp_rect.top);
1252 }
1253 return true;
1254}
1255
1256bool CFX_RenderDevice::DrawTextPath(pdfium::span<const TextCharPos> pCharPos,
1257 CFX_Font* pFont,
1258 float font_size,
1259 const CFX_Matrix& mtText2User,
1260 const CFX_Matrix* pUser2Device,
1261 const CFX_GraphStateData* pGraphState,
1262 uint32_t fill_color,
1263 FX_ARGB stroke_color,
1264 CFX_Path* pClippingPath,
1265 const CFX_FillRenderOptions& fill_options) {
1266 for (const auto& charpos : pCharPos) {
1267 const CFX_Path* pPath =
1268 pFont->LoadGlyphPath(charpos.m_GlyphIndex, charpos.m_FontCharWidth);
1269 if (!pPath)
1270 continue;
1271
1272 CFX_Matrix matrix(font_size, 0, 0, font_size, charpos.m_Origin.x,
1273 charpos.m_Origin.y);
1274 matrix = charpos.GetEffectiveMatrix(matrix);
1275 matrix.Concat(mtText2User);
1276
1277 CFX_Path transformed_path(*pPath);
1278 transformed_path.Transform(matrix);
1279 if (fill_color || stroke_color) {
1280 CFX_FillRenderOptions options(fill_options);
1281 if (fill_color) {
1282 options.fill_type = CFX_FillRenderOptions::FillType::kWinding;
1283 }
1284 options.text_mode = true;
1285 if (!DrawPath(transformed_path, pUser2Device, pGraphState, fill_color,
1286 stroke_color, options)) {
1287 return false;
1288 }
1289 }
1290 if (pClippingPath) {
1291 pClippingPath->Append(transformed_path, pUser2Device);
1292 }
1293 }
1294 return true;
1295}
1296
1297void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device,
1298 const CFX_FloatRect& rect,
1299 const FX_COLORREF& color) {
1300 CFX_Path path;
1301 path.AppendFloatRect(rect);
1302 DrawPath(path, pUser2Device, nullptr, color, 0,
1304}
1305
1306void CFX_RenderDevice::DrawFillArea(const CFX_Matrix& mtUser2Device,
1307 const std::vector<CFX_PointF>& points,
1308 const FX_COLORREF& color) {
1309 DCHECK(!points.empty());
1310 CFX_Path path;
1312 for (size_t i = 1; i < points.size(); ++i)
1314
1315 DrawPath(path, &mtUser2Device, nullptr, color, 0,
1317}
1318
1319void CFX_RenderDevice::DrawStrokeRect(const CFX_Matrix& mtUser2Device,
1320 const CFX_FloatRect& rect,
1321 const FX_COLORREF& color,
1322 float fWidth) {
1324 gsd.m_LineWidth = fWidth;
1325
1326 CFX_Path path;
1327 path.AppendFloatRect(rect);
1328 DrawPath(path, &mtUser2Device, &gsd, 0, color,
1330}
1331
1333 const CFX_PointF& ptMoveTo,
1334 const CFX_PointF& ptLineTo,
1335 const FX_COLORREF& color,
1336 float fWidth) {
1337 CFX_Path path;
1340
1342 gsd.m_LineWidth = fWidth;
1343
1344 DrawPath(path, pUser2Device, &gsd, 0, color,
1346}
1347
1348void CFX_RenderDevice::DrawFillRect(const CFX_Matrix* pUser2Device,
1349 const CFX_FloatRect& rect,
1350 const CFX_Color& color,
1351 int32_t nTransparency) {
1352 DrawFillRect(pUser2Device, rect, color.ToFXColor(nTransparency));
1353}
1354
1355void CFX_RenderDevice::DrawShadow(const CFX_Matrix& mtUser2Device,
1356 const CFX_FloatRect& rect,
1357 int32_t nTransparency,
1358 int32_t nStartGray,
1359 int32_t nEndGray) {
1360 constexpr float kBorder = 0.5f;
1361 constexpr float kSegmentWidth = 1.0f;
1362 constexpr float kLineWidth = 1.5f;
1363
1364 float fStepGray = (nEndGray - nStartGray) / rect.Height();
1365 CFX_PointF start(rect.left, 0);
1366 CFX_PointF end(rect.right, 0);
1367
1368 for (float fy = rect.bottom + kBorder; fy <= rect.top - kBorder;
1369 fy += kSegmentWidth) {
1370 start.y = fy;
1371 end.y = fy;
1372 int nGray = nStartGray + static_cast<int>(fStepGray * (fy - rect.bottom));
1373 FX_ARGB color = ArgbEncode(nTransparency, nGray, nGray, nGray);
1374 DrawStrokeLine(&mtUser2Device, start, end, color, kLineWidth);
1375 }
1376}
1377
1378void CFX_RenderDevice::DrawBorder(const CFX_Matrix* pUser2Device,
1379 const CFX_FloatRect& rect,
1380 float fWidth,
1381 const CFX_Color& color,
1382 const CFX_Color& crLeftTop,
1383 const CFX_Color& crRightBottom,
1384 BorderStyle nStyle,
1385 int32_t nTransparency) {
1386 if (fWidth <= 0.0f)
1387 return;
1388
1389 const float fLeft = rect.left;
1390 const float fRight = rect.right;
1391 const float fTop = rect.top;
1392 const float fBottom = rect.bottom;
1393 const float fHalfWidth = fWidth / 2.0f;
1394
1395 switch (nStyle) {
1396 case BorderStyle::kSolid: {
1397 CFX_Path path;
1398 path.AppendRect(fLeft, fBottom, fRight, fTop);
1399 path.AppendRect(fLeft + fWidth, fBottom + fWidth, fRight - fWidth,
1400 fTop - fWidth);
1401 DrawPath(path, pUser2Device, nullptr, color.ToFXColor(nTransparency), 0,
1403 break;
1404 }
1405 case BorderStyle::kDash: {
1407 gsd.m_DashArray = {3.0f, 3.0f};
1408 gsd.m_DashPhase = 0;
1409 gsd.m_LineWidth = fWidth;
1410
1411 CFX_Path path;
1412 path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
1414 path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth),
1416 path.AppendPoint(CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
1418 path.AppendPoint(CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth),
1420 path.AppendPoint(CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
1422 DrawPath(path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
1424 break;
1425 }
1427 case BorderStyle::kInset: {
1429 gsd.m_LineWidth = fHalfWidth;
1430
1431 CFX_Path path_left_top;
1432 path_left_top.AppendPoint(
1433 CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
1435 path_left_top.AppendPoint(
1436 CFX_PointF(fLeft + fHalfWidth, fTop - fHalfWidth),
1438 path_left_top.AppendPoint(
1439 CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
1441 path_left_top.AppendPoint(CFX_PointF(fRight - fWidth, fTop - fWidth),
1443 path_left_top.AppendPoint(CFX_PointF(fLeft + fWidth, fTop - fWidth),
1445 path_left_top.AppendPoint(CFX_PointF(fLeft + fWidth, fBottom + fWidth),
1447 path_left_top.AppendPoint(
1448 CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
1450 DrawPath(path_left_top, pUser2Device, &gsd,
1451 crLeftTop.ToFXColor(nTransparency), 0,
1453
1454 CFX_Path path_right_bottom;
1455 path_right_bottom.AppendPoint(
1456 CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
1458 path_right_bottom.AppendPoint(
1459 CFX_PointF(fRight - fHalfWidth, fBottom + fHalfWidth),
1461 path_right_bottom.AppendPoint(
1462 CFX_PointF(fLeft + fHalfWidth, fBottom + fHalfWidth),
1464 path_right_bottom.AppendPoint(
1465 CFX_PointF(fLeft + fWidth, fBottom + fWidth),
1467 path_right_bottom.AppendPoint(
1468 CFX_PointF(fRight - fWidth, fBottom + fWidth),
1470 path_right_bottom.AppendPoint(CFX_PointF(fRight - fWidth, fTop - fWidth),
1472 path_right_bottom.AppendPoint(
1473 CFX_PointF(fRight - fHalfWidth, fTop - fHalfWidth),
1475 DrawPath(path_right_bottom, pUser2Device, &gsd,
1476 crRightBottom.ToFXColor(nTransparency), 0,
1478
1479 CFX_Path path;
1480 path.AppendRect(fLeft, fBottom, fRight, fTop);
1481 path.AppendRect(fLeft + fHalfWidth, fBottom + fHalfWidth,
1482 fRight - fHalfWidth, fTop - fHalfWidth);
1483 DrawPath(path, pUser2Device, &gsd, color.ToFXColor(nTransparency), 0,
1485 break;
1486 }
1489 gsd.m_LineWidth = fWidth;
1490
1491 CFX_Path path;
1492 path.AppendPoint(CFX_PointF(fLeft, fBottom + fHalfWidth),
1494 path.AppendPoint(CFX_PointF(fRight, fBottom + fHalfWidth),
1496 DrawPath(path, pUser2Device, &gsd, 0, color.ToFXColor(nTransparency),
1498 break;
1499 }
1500 }
1501}
1502
1504 return m_pDeviceDriver->MultiplyAlpha(alpha);
1505}
1506
1507bool CFX_RenderDevice::MultiplyAlphaMask(RetainPtr<const CFX_DIBitmap> mask) {
1508 return m_pDeviceDriver->MultiplyAlphaMask(std::move(mask));
1509}
1510
1512 : m_pDevice(pDevice) {
1513 m_pDevice->SaveState();
1514}
1515
1517 m_pDevice->RestoreState(false);
1518}
BorderStyle
#define DCHECK
Definition check.h:33
#define DCHECK_GT(x, y)
Definition check_op.h:20
#define DCHECK_EQ(x, y)
Definition check_op.h:17
#define DCHECK_LT(x, y)
Definition check_op.h:19
static constexpr FXDIB_Format kPlatformRGBFormat
Definition cfx_dibbase.h:42
bool AttachWithBackdropAndGroupKnockout(RetainPtr< CFX_DIBitmap > pBitmap, RetainPtr< CFX_DIBitmap > pBackdropBitmap, bool bGroupKnockout)
CFX_FloatRect & operator=(const CFX_FloatRect &that)=default
float Height() const
FX_RECT GetOuterRect() const
bool FTLibrarySupportsHinting() const
Definition cfx_fontmgr.h:75
const CFX_GlyphBitmap * LoadGlyphBitmap(uint32_t glyph_index, bool bFontStyle, const CFX_Matrix &matrix, int dest_width, int anti_alias, CFX_TextRenderOptions *text_options) const
Definition cfx_font.cpp:458
FXFT_FaceRec * GetFaceRec() const
Definition cfx_font.h:76
static CFX_GEModule * Get()
CFX_FontMgr * GetFontMgr() const
CFX_Matrix & operator=(const CFX_Matrix &other)=default
CFX_FloatRect TransformRect(const CFX_FloatRect &rect) const
CFX_PointF Transform(const CFX_PointF &point) const
void Translate(int32_t x, int32_t y)
constexpr CFX_Matrix(float a1, float b1, float c1, float d1, float e1, float f1)
bool IsIdentity() const
void Scale(float sx, float sy)
CFX_FloatRect GetBoundingBox() const
Definition cfx_path.cpp:323
void AppendRect(float left, float bottom, float right, float top)
Definition cfx_path.cpp:310
void Append(const CFX_Path &src, const CFX_Matrix *matrix)
Definition cfx_path.cpp:276
CFX_FloatRect GetBoundingBoxForStrokePath(float line_width, float miter_limit) const
Definition cfx_path.cpp:333
void AppendFloatRect(const CFX_FloatRect &rect)
Definition cfx_path.cpp:306
void AppendPoint(const CFX_PointF &point, Point::Type type)
Definition cfx_path.cpp:290
StateRestorer(CFX_RenderDevice *pDevice)
void DrawBorder(const CFX_Matrix *pUser2Device, const CFX_FloatRect &rect, float fWidth, const CFX_Color &color, const CFX_Color &crLeftTop, const CFX_Color &crRightBottom, BorderStyle nStyle, int32_t nTransparency)
bool StretchBitMask(RetainPtr< CFX_DIBBase > bitmap, int left, int top, int dest_width, int dest_height, uint32_t color)
bool MultiplyAlpha(float alpha)
bool DrawTextPath(pdfium::span< const TextCharPos > pCharPos, CFX_Font *pFont, float font_size, const CFX_Matrix &mtText2User, const CFX_Matrix *pUser2Device, const CFX_GraphStateData *pGraphState, uint32_t fill_color, uint32_t stroke_color, CFX_Path *pClippingPath, const CFX_FillRenderOptions &fill_options)
bool SetClip_Rect(const FX_RECT &pRect)
bool SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_FillRenderOptions &fill_options)
bool StretchDIBits(RetainPtr< const CFX_DIBBase > bitmap, int left, int top, int dest_width, int dest_height)
bool SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_GraphStateData *pGraphState)
RenderDeviceDriverIface * GetDeviceDriver() const
RetainPtr< const CFX_DIBitmap > GetBackDrop() const
void SetBaseClip(const FX_RECT &rect)
bool FillRect(const FX_RECT &rect, uint32_t color)
bool StretchDIBitsWithFlagsAndBlend(RetainPtr< const CFX_DIBBase > bitmap, int left, int top, int dest_width, int dest_height, const FXDIB_ResampleOptions &options, BlendMode blend_mode)
bool MultiplyAlphaMask(RetainPtr< const CFX_DIBitmap > mask)
void DrawFillRect(const CFX_Matrix *pUser2Device, const CFX_FloatRect &rect, const FX_COLORREF &color)
RetainPtr< const CFX_DIBitmap > GetBitmap() const
void SetDeviceDriver(std::unique_ptr< RenderDeviceDriverIface > pDriver)
bool SetDIBits(RetainPtr< const CFX_DIBBase > bitmap, int left, int top)
static CFX_Matrix GetFlipMatrix(float width, float height, float left, float top)
void DrawFillArea(const CFX_Matrix &mtUser2Device, const std::vector< CFX_PointF > &points, const FX_COLORREF &color)
bool SetDIBitsWithBlend(RetainPtr< const CFX_DIBBase > bitmap, int left, int top, BlendMode blend_mode)
bool SetBitMask(RetainPtr< const CFX_DIBBase > bitmap, int left, int top, uint32_t argb)
bool StretchBitMaskWithFlags(RetainPtr< CFX_DIBBase > bitmap, int left, int top, int dest_width, int dest_height, uint32_t argb, const FXDIB_ResampleOptions &options)
bool DrawPath(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_GraphStateData *pGraphState, uint32_t fill_color, uint32_t stroke_color, const CFX_FillRenderOptions &fill_options)
void SetBitmap(RetainPtr< CFX_DIBitmap > bitmap)
void DrawShadow(const CFX_Matrix &mtUser2Device, const CFX_FloatRect &rect, int32_t nTransparency, int32_t nStartGray, int32_t nEndGray)
RenderDeviceDriverIface::StartResult StartDIBits(RetainPtr< const CFX_DIBBase > bitmap, float alpha, uint32_t argb, const CFX_Matrix &matrix, const FXDIB_ResampleOptions &options)
void DrawStrokeLine(const CFX_Matrix *pUser2Device, const CFX_PointF &ptMoveTo, const CFX_PointF &ptLineTo, const FX_COLORREF &color, float fWidth)
bool DrawNormalText(pdfium::span< const TextCharPos > pCharPos, CFX_Font *pFont, float font_size, const CFX_Matrix &mtText2Device, uint32_t fill_color, const CFX_TextRenderOptions &options)
RenderDeviceDriverIface::StartResult StartDIBitsWithBlend(RetainPtr< const CFX_DIBBase > bitmap, float alpha, uint32_t argb, const CFX_Matrix &matrix, const FXDIB_ResampleOptions &options, BlendMode blend_mode)
void DrawStrokeRect(const CFX_Matrix &mtUser2Device, const CFX_FloatRect &rect, const FX_COLORREF &color, float fWidth)
RetainPtr< CFX_DIBitmap > GetBitmap()
int GetDeviceCaps(int id) const
void RestoreState(bool bKeepSaved)
bool CreateCompatibleBitmap(const RetainPtr< CFX_DIBitmap > &pDIB, int width, int height) const
DeviceType GetDeviceType() const
bool ContinueDIBits(CFX_AggImageRenderer *handle, PauseIndicatorIface *pPause)
void DrawFillRect(const CFX_Matrix *pUser2Device, const CFX_FloatRect &rect, const CFX_Color &color, int32_t nTransparency)
bool GetDIBits(RetainPtr< CFX_DIBitmap > bitmap, int left, int top) const
uint32_t m_GlyphIndex
CFX_Matrix GetEffectiveMatrix(const CFX_Matrix &matrix) const
#define UNSAFE_TODO(...)
CFX_PTemplate< float > CFX_PointF
uint32_t FX_ARGB
Definition fx_dib.h:36
BlendMode
Definition fx_dib.h:119
@ kNormal
Definition fx_dib.h:120
#define FXARGB_A(argb)
Definition fx_dib.h:196
uint32_t FX_COLORREF
Definition fx_dib.h:42
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
Definition fx_dib.h:188
FXDIB_Format
Definition fx_dib.h:21
#define FXDIB_ALPHA_MERGE(backdrop, source, source_alpha)
Definition fx_dib.h:204
pdfium::CheckedNumeric< int32_t > FX_SAFE_INT32
#define CHECK(cvref)
#define FXDC_BITS_PIXEL
#define FXDC_RENDER_CAPS
#define FXDC_PIXEL_WIDTH
#define FXRC_ALPHA_OUTPUT
#define FXDC_PIXEL_HEIGHT
#define FXRC_BLEND_MODE
#define FXRC_ALPHA_IMAGE
#define FXRC_GET_BITS
#define FXRC_BYTEMASK_OUTPUT
FX_COLORREF ToFXColor(int32_t nTransparency) const
static constexpr CFX_FillRenderOptions EvenOddOptions()
static constexpr CFX_FillRenderOptions WindingOptions()
constexpr CFX_TextRenderOptions(const CFX_TextRenderOptions &other)=default
int Height() const
int32_t bottom
bool Valid() const
int32_t right
int Width() const
int32_t top
int32_t left
void Intersect(const FX_RECT &src)
bool IsEmpty() const
constexpr FX_RECT(int l, int t, int r, int b)