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