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