xref: /aosp_15_r20/external/pdfium/core/fxge/win32/cgdi_device_driver.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2020 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/win32/cgdi_device_driver.h"
8 
9 #include <math.h>
10 #include <windows.h>
11 
12 #include <algorithm>
13 #include <vector>
14 
15 #include "core/fxcrt/fx_string.h"
16 #include "core/fxge/agg/fx_agg_driver.h"
17 #include "core/fxge/cfx_defaultrenderdevice.h"
18 #include "core/fxge/cfx_fillrenderoptions.h"
19 #include "core/fxge/cfx_graphstatedata.h"
20 #include "core/fxge/cfx_path.h"
21 #include "core/fxge/dib/cfx_dibbase.h"
22 #include "core/fxge/dib/cfx_dibitmap.h"
23 #include "core/fxge/render_defines.h"
24 #include "core/fxge/win32/cwin32_platform.h"
25 #include "third_party/agg23/agg_clip_liang_barsky.h"
26 #include "third_party/base/check.h"
27 #include "third_party/base/check_op.h"
28 #include "third_party/base/notreached.h"
29 #include "third_party/base/numerics/safe_conversions.h"
30 
31 namespace {
32 
FillTypeToGdiFillType(CFX_FillRenderOptions::FillType fill_type)33 constexpr int FillTypeToGdiFillType(CFX_FillRenderOptions::FillType fill_type) {
34   return static_cast<int>(fill_type);
35 }
36 
37 static_assert(FillTypeToGdiFillType(
38                   CFX_FillRenderOptions::FillType::kEvenOdd) == ALTERNATE,
39               "CFX_FillRenderOptions::FillType::kEvenOdd value mismatch");
40 
41 static_assert(
42     FillTypeToGdiFillType(CFX_FillRenderOptions::FillType::kWinding) == WINDING,
43     "CFX_FillRenderOptions::FillType::kWinding value mismatch");
44 
CreateExtPen(const CFX_GraphStateData * pGraphState,const CFX_Matrix * pMatrix,uint32_t argb)45 HPEN CreateExtPen(const CFX_GraphStateData* pGraphState,
46                   const CFX_Matrix* pMatrix,
47                   uint32_t argb) {
48   DCHECK(pGraphState);
49 
50   float scale = 1.0f;
51   if (pMatrix) {
52     scale = fabs(pMatrix->a) > fabs(pMatrix->b) ? fabs(pMatrix->a)
53                                                 : fabs(pMatrix->b);
54   }
55   float width = std::max(scale * pGraphState->m_LineWidth, 1.0f);
56 
57   uint32_t PenStyle = PS_GEOMETRIC;
58   if (!pGraphState->m_DashArray.empty())
59     PenStyle |= PS_USERSTYLE;
60   else
61     PenStyle |= PS_SOLID;
62 
63   switch (pGraphState->m_LineCap) {
64     case CFX_GraphStateData::LineCap::kButt:
65       PenStyle |= PS_ENDCAP_FLAT;
66       break;
67     case CFX_GraphStateData::LineCap::kRound:
68       PenStyle |= PS_ENDCAP_ROUND;
69       break;
70     case CFX_GraphStateData::LineCap::kSquare:
71       PenStyle |= PS_ENDCAP_SQUARE;
72       break;
73   }
74   switch (pGraphState->m_LineJoin) {
75     case CFX_GraphStateData::LineJoin::kMiter:
76       PenStyle |= PS_JOIN_MITER;
77       break;
78     case CFX_GraphStateData::LineJoin::kRound:
79       PenStyle |= PS_JOIN_ROUND;
80       break;
81     case CFX_GraphStateData::LineJoin::kBevel:
82       PenStyle |= PS_JOIN_BEVEL;
83       break;
84   }
85 
86   FX_COLORREF colorref = ArgbToColorRef(argb);
87   LOGBRUSH lb;
88   lb.lbColor = colorref;
89   lb.lbStyle = BS_SOLID;
90   lb.lbHatch = 0;
91   std::vector<uint32_t> dashes;
92   if (!pGraphState->m_DashArray.empty()) {
93     dashes.resize(pGraphState->m_DashArray.size());
94     for (size_t i = 0; i < pGraphState->m_DashArray.size(); i++) {
95       dashes[i] = FXSYS_roundf(
96           pMatrix ? pMatrix->TransformDistance(pGraphState->m_DashArray[i])
97                   : pGraphState->m_DashArray[i]);
98       dashes[i] = std::max(dashes[i], 1U);
99     }
100   }
101   return ExtCreatePen(
102       PenStyle, (DWORD)ceil(width), &lb,
103       pdfium::base::checked_cast<DWORD>(pGraphState->m_DashArray.size()),
104       reinterpret_cast<const DWORD*>(dashes.data()));
105 }
106 
CreateBrush(uint32_t argb)107 HBRUSH CreateBrush(uint32_t argb) {
108   return CreateSolidBrush(ArgbToColorRef(argb));
109 }
110 
SetPathToDC(HDC hDC,const CFX_Path & path,const CFX_Matrix * pMatrix)111 void SetPathToDC(HDC hDC, const CFX_Path& path, const CFX_Matrix* pMatrix) {
112   BeginPath(hDC);
113 
114   pdfium::span<const CFX_Path::Point> points = path.GetPoints();
115   for (size_t i = 0; i < points.size(); ++i) {
116     CFX_PointF pos = points[i].m_Point;
117     if (pMatrix)
118       pos = pMatrix->Transform(pos);
119 
120     CFX_Point screen(FXSYS_roundf(pos.x), FXSYS_roundf(pos.y));
121     CFX_Path::Point::Type point_type = points[i].m_Type;
122     if (point_type == CFX_Path::Point::Type::kMove) {
123       MoveToEx(hDC, screen.x, screen.y, nullptr);
124     } else if (point_type == CFX_Path::Point::Type::kLine) {
125       if (points[i].m_Point == points[i - 1].m_Point)
126         screen.x++;
127 
128       LineTo(hDC, screen.x, screen.y);
129     } else if (point_type == CFX_Path::Point::Type::kBezier) {
130       POINT lppt[3];
131       lppt[0].x = screen.x;
132       lppt[0].y = screen.y;
133 
134       pos = points[i + 1].m_Point;
135       if (pMatrix)
136         pos = pMatrix->Transform(pos);
137 
138       lppt[1].x = FXSYS_roundf(pos.x);
139       lppt[1].y = FXSYS_roundf(pos.y);
140 
141       pos = points[i + 2].m_Point;
142       if (pMatrix)
143         pos = pMatrix->Transform(pos);
144 
145       lppt[2].x = FXSYS_roundf(pos.x);
146       lppt[2].y = FXSYS_roundf(pos.y);
147       PolyBezierTo(hDC, lppt, 3);
148       i += 2;
149     }
150     if (points[i].m_CloseFigure)
151       CloseFigure(hDC);
152   }
153   EndPath(hDC);
154 }
155 
GetBitmapInfo(const RetainPtr<CFX_DIBBase> & source)156 ByteString GetBitmapInfo(const RetainPtr<CFX_DIBBase>& source) {
157   int len = sizeof(BITMAPINFOHEADER);
158   if (source->GetBPP() == 1 || source->GetBPP() == 8) {
159     len += sizeof(DWORD) * (int)(1 << source->GetBPP());
160   }
161 
162   ByteString result;
163   {
164     // Span's lifetime must end before ReleaseBuffer() below.
165     pdfium::span<char> cspan = result.GetBuffer(len);
166     BITMAPINFOHEADER* pbmih = reinterpret_cast<BITMAPINFOHEADER*>(cspan.data());
167     memset(pbmih, 0, sizeof(BITMAPINFOHEADER));
168     pbmih->biSize = sizeof(BITMAPINFOHEADER);
169     pbmih->biBitCount = source->GetBPP();
170     pbmih->biCompression = BI_RGB;
171     pbmih->biHeight = -(int)source->GetHeight();
172     pbmih->biPlanes = 1;
173     pbmih->biWidth = source->GetWidth();
174     if (source->GetBPP() == 8) {
175       uint32_t* palette = (uint32_t*)(pbmih + 1);
176       if (source->HasPalette()) {
177         pdfium::span<const uint32_t> palette_span = source->GetPaletteSpan();
178         for (int i = 0; i < 256; i++) {
179           palette[i] = palette_span[i];
180         }
181       } else {
182         for (int i = 0; i < 256; i++) {
183           palette[i] = ArgbEncode(0, i, i, i);
184         }
185       }
186     }
187     if (source->GetBPP() == 1) {
188       uint32_t* palette = (uint32_t*)(pbmih + 1);
189       if (source->HasPalette()) {
190         pdfium::span<const uint32_t> palette_span = source->GetPaletteSpan();
191         palette[0] = palette_span[0];
192         palette[1] = palette_span[1];
193       } else {
194         palette[0] = 0;
195         palette[1] = 0xffffff;
196       }
197     }
198   }
199   result.ReleaseBuffer(len);
200   return result;
201 }
202 
203 #if defined(_SKIA_SUPPORT_)
204 // TODO(caryclark)  This antigrain function is duplicated here to permit
205 // removing the last remaining dependency. Eventually, this will be elminiated
206 // altogether and replace by Skia code.
207 
208 struct rect_base {
209   float x1;
210   float y1;
211   float x2;
212   float y2;
213 };
214 
clip_liang_barsky(float x1,float y1,float x2,float y2,const rect_base & clip_box,float * x,float * y)215 unsigned clip_liang_barsky(float x1,
216                            float y1,
217                            float x2,
218                            float y2,
219                            const rect_base& clip_box,
220                            float* x,
221                            float* y) {
222   const float nearzero = 1e-30f;
223   float deltax = x2 - x1;
224   float deltay = y2 - y1;
225   unsigned np = 0;
226   if (deltax == 0)
227     deltax = (x1 > clip_box.x1) ? -nearzero : nearzero;
228   float xin;
229   float xout;
230   if (deltax > 0) {
231     xin = clip_box.x1;
232     xout = clip_box.x2;
233   } else {
234     xin = clip_box.x2;
235     xout = clip_box.x1;
236   }
237   float tinx = (xin - x1) / deltax;
238   if (deltay == 0)
239     deltay = (y1 > clip_box.y1) ? -nearzero : nearzero;
240   float yin;
241   float yout;
242   if (deltay > 0) {
243     yin = clip_box.y1;
244     yout = clip_box.y2;
245   } else {
246     yin = clip_box.y2;
247     yout = clip_box.y1;
248   }
249   float tiny = (yin - y1) / deltay;
250   float tin1;
251   float tin2;
252   if (tinx < tiny) {
253     tin1 = tinx;
254     tin2 = tiny;
255   } else {
256     tin1 = tiny;
257     tin2 = tinx;
258   }
259   if (tin1 <= 1.0f) {
260     if (0 < tin1) {
261       *x++ = xin;
262       *y++ = yin;
263       ++np;
264     }
265     if (tin2 <= 1.0f) {
266       float toutx = (xout - x1) / deltax;
267       float touty = (yout - y1) / deltay;
268       float tout1 = (toutx < touty) ? toutx : touty;
269       if (tin2 > 0 || tout1 > 0) {
270         if (tin2 <= tout1) {
271           if (tin2 > 0) {
272             if (tinx > tiny) {
273               *x++ = xin;
274               *y++ = y1 + (deltay * tinx);
275             } else {
276               *x++ = x1 + (deltax * tiny);
277               *y++ = yin;
278             }
279             ++np;
280           }
281           if (tout1 < 1.0f) {
282             if (toutx < touty) {
283               *x++ = xout;
284               *y++ = y1 + (deltay * toutx);
285             } else {
286               *x++ = x1 + (deltax * touty);
287               *y++ = yout;
288             }
289           } else {
290             *x++ = x2;
291             *y++ = y2;
292           }
293           ++np;
294         } else {
295           if (tinx > tiny) {
296             *x++ = xin;
297             *y++ = yout;
298           } else {
299             *x++ = xout;
300             *y++ = yin;
301           }
302           ++np;
303         }
304       }
305     }
306   }
307   return np;
308 }
309 #endif  //  defined(_SKIA_SUPPORT_)
310 
LineClip(float w,float h,float x1,float y1,float x2,float y2,float * x,float * y)311 unsigned LineClip(float w,
312                   float h,
313                   float x1,
314                   float y1,
315                   float x2,
316                   float y2,
317                   float* x,
318                   float* y) {
319 #if defined(_SKIA_SUPPORT_)
320   if (CFX_DefaultRenderDevice::SkiaIsDefaultRenderer()) {
321     // TODO(caryclark) temporary replacement of antigrain in line function to
322     // permit removing antigrain altogether
323     rect_base rect = {0.0f, 0.0f, w, h};
324     return clip_liang_barsky(x1, y1, x2, y2, rect, x, y);
325   }
326 #endif
327   pdfium::agg::rect_base<float> rect(0.0f, 0.0f, w, h);
328   return pdfium::agg::clip_liang_barsky<float>(x1, y1, x2, y2, rect, x, y);
329 }
330 
331 }  // namespace
332 
CGdiDeviceDriver(HDC hDC,DeviceType device_type)333 CGdiDeviceDriver::CGdiDeviceDriver(HDC hDC, DeviceType device_type)
334     : m_hDC(hDC), m_DeviceType(device_type) {
335   SetStretchBltMode(m_hDC, HALFTONE);
336   DWORD obj_type = GetObjectType(m_hDC);
337   m_bMetafileDCType = obj_type == OBJ_ENHMETADC || obj_type == OBJ_ENHMETAFILE;
338   if (obj_type == OBJ_MEMDC) {
339     HBITMAP hBitmap = CreateBitmap(1, 1, 1, 1, nullptr);
340     hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap);
341     BITMAP bitmap;
342     GetObject(hBitmap, sizeof bitmap, &bitmap);
343     m_nBitsPerPixel = bitmap.bmBitsPixel;
344     m_Width = bitmap.bmWidth;
345     m_Height = abs(bitmap.bmHeight);
346     hBitmap = (HBITMAP)SelectObject(m_hDC, hBitmap);
347     DeleteObject(hBitmap);
348   } else {
349     m_nBitsPerPixel = ::GetDeviceCaps(m_hDC, BITSPIXEL);
350     m_Width = ::GetDeviceCaps(m_hDC, HORZRES);
351     m_Height = ::GetDeviceCaps(m_hDC, VERTRES);
352   }
353   if (m_DeviceType != DeviceType::kDisplay) {
354     m_RenderCaps = FXRC_BIT_MASK;
355   } else {
356     m_RenderCaps = FXRC_GET_BITS | FXRC_BIT_MASK;
357   }
358 }
359 
360 CGdiDeviceDriver::~CGdiDeviceDriver() = default;
361 
GetDeviceType() const362 DeviceType CGdiDeviceDriver::GetDeviceType() const {
363   return m_DeviceType;
364 }
365 
GetDeviceCaps(int caps_id) const366 int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const {
367   switch (caps_id) {
368     case FXDC_PIXEL_WIDTH:
369       return m_Width;
370     case FXDC_PIXEL_HEIGHT:
371       return m_Height;
372     case FXDC_BITS_PIXEL:
373       return m_nBitsPerPixel;
374     case FXDC_RENDER_CAPS:
375       return m_RenderCaps;
376     default:
377       NOTREACHED();
378       return 0;
379   }
380 }
381 
SaveState()382 void CGdiDeviceDriver::SaveState() {
383   SaveDC(m_hDC);
384 }
385 
RestoreState(bool bKeepSaved)386 void CGdiDeviceDriver::RestoreState(bool bKeepSaved) {
387   RestoreDC(m_hDC, -1);
388   if (bKeepSaved)
389     SaveDC(m_hDC);
390 }
391 
GDI_SetDIBits(const RetainPtr<CFX_DIBBase> & source,const FX_RECT & src_rect,int left,int top)392 bool CGdiDeviceDriver::GDI_SetDIBits(const RetainPtr<CFX_DIBBase>& source,
393                                      const FX_RECT& src_rect,
394                                      int left,
395                                      int top) {
396   if (m_DeviceType == DeviceType::kPrinter) {
397     RetainPtr<CFX_DIBBase> flipped_source = source->FlipImage(false, true);
398     if (!flipped_source) {
399       return false;
400     }
401 
402     ByteString info = GetBitmapInfo(flipped_source);
403     ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1;
404     FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height());
405     dst_rect.Intersect(0, 0, flipped_source->GetWidth(),
406                        flipped_source->GetHeight());
407     int dst_width = dst_rect.Width();
408     int dst_height = dst_rect.Height();
409     ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width,
410                     dst_height, flipped_source->GetBuffer().data(),
411                     (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS, SRCCOPY);
412     return true;
413   }
414 
415   ByteString info = GetBitmapInfo(source);
416   ::SetDIBitsToDevice(m_hDC, left, top, src_rect.Width(), src_rect.Height(),
417                       src_rect.left, source->GetHeight() - src_rect.bottom, 0,
418                       source->GetHeight(), source->GetBuffer().data(),
419                       (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
420   return true;
421 }
422 
GDI_StretchDIBits(const RetainPtr<CFX_DIBBase> & source,int dest_left,int dest_top,int dest_width,int dest_height,const FXDIB_ResampleOptions & options)423 bool CGdiDeviceDriver::GDI_StretchDIBits(const RetainPtr<CFX_DIBBase>& source,
424                                          int dest_left,
425                                          int dest_top,
426                                          int dest_width,
427                                          int dest_height,
428                                          const FXDIB_ResampleOptions& options) {
429   if (!source || dest_width == 0 || dest_height == 0) {
430     return false;
431   }
432 
433   ByteString info = GetBitmapInfo(source);
434   if ((int64_t)abs(dest_width) * abs(dest_height) <
435           (int64_t)source->GetWidth() * source->GetHeight() * 4 ||
436       options.bInterpolateBilinear) {
437     SetStretchBltMode(m_hDC, HALFTONE);
438   } else {
439     SetStretchBltMode(m_hDC, COLORONCOLOR);
440   }
441   RetainPtr<CFX_DIBBase> stretch_source = source;
442   if (m_DeviceType == DeviceType::kPrinter &&
443       ((int64_t)source->GetWidth() * source->GetHeight() >
444        (int64_t)abs(dest_width) * abs(dest_height))) {
445     stretch_source = source->StretchTo(dest_width, dest_height,
446                                        FXDIB_ResampleOptions(), nullptr);
447     if (!stretch_source) {
448       return false;
449     }
450     info = GetBitmapInfo(stretch_source);
451   }
452   ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
453                   stretch_source->GetWidth(), stretch_source->GetHeight(),
454                   stretch_source->GetBuffer().data(), (BITMAPINFO*)info.c_str(),
455                   DIB_RGB_COLORS, SRCCOPY);
456   return true;
457 }
458 
GDI_StretchBitMask(const RetainPtr<CFX_DIBBase> & source,int dest_left,int dest_top,int dest_width,int dest_height,uint32_t bitmap_color)459 bool CGdiDeviceDriver::GDI_StretchBitMask(const RetainPtr<CFX_DIBBase>& source,
460                                           int dest_left,
461                                           int dest_top,
462                                           int dest_width,
463                                           int dest_height,
464                                           uint32_t bitmap_color) {
465   if (!source || dest_width == 0 || dest_height == 0) {
466     return false;
467   }
468 
469   int width = source->GetWidth();
470   int height = source->GetHeight();
471   struct {
472     BITMAPINFOHEADER bmiHeader;
473     uint32_t bmiColors[2];
474   } bmi;
475   memset(&bmi.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
476   bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
477   bmi.bmiHeader.biBitCount = 1;
478   bmi.bmiHeader.biCompression = BI_RGB;
479   bmi.bmiHeader.biHeight = -height;
480   bmi.bmiHeader.biPlanes = 1;
481   bmi.bmiHeader.biWidth = width;
482   if (m_nBitsPerPixel != 1) {
483     SetStretchBltMode(m_hDC, HALFTONE);
484   }
485   bmi.bmiColors[0] = 0xffffff;
486   bmi.bmiColors[1] = 0;
487 
488   HBRUSH hPattern = CreateBrush(bitmap_color);
489   HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern);
490 
491   // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush
492   // bitmap.
493   // A complete list of the boolen operations is as follows:
494 
495   /* P(bitmap_color)    S(ImageMask)    D(DeviceBitmap)    Result
496    *        0                 0                0              0
497    *        0                 0                1              0
498    *        0                 1                0              0
499    *        0                 1                1              1
500    *        1                 0                0              1
501    *        1                 0                1              1
502    *        1                 1                0              0
503    *        1                 1                1              1
504    */
505   // The boolen codes is B8. Based on
506   // http://msdn.microsoft.com/en-us/library/aa932106.aspx, the ROP3 code is
507   // 0xB8074A
508 
509   ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
510                   width, height, source->GetBuffer().data(), (BITMAPINFO*)&bmi,
511                   DIB_RGB_COLORS, 0xB8074A);
512 
513   SelectObject(m_hDC, hOld);
514   DeleteObject(hPattern);
515 
516   return true;
517 }
518 
GetClipBox(FX_RECT * pRect)519 bool CGdiDeviceDriver::GetClipBox(FX_RECT* pRect) {
520   return !!(::GetClipBox(m_hDC, (RECT*)pRect));
521 }
522 
MultiplyAlpha(float alpha)523 bool CGdiDeviceDriver::MultiplyAlpha(float alpha) {
524   // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices
525   // anyway.
526   NOTREACHED();
527   return false;
528 }
529 
MultiplyAlpha(const RetainPtr<CFX_DIBBase> & mask)530 bool CGdiDeviceDriver::MultiplyAlpha(const RetainPtr<CFX_DIBBase>& mask) {
531   // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices
532   // anyway.
533   NOTREACHED();
534   return false;
535 }
536 
DrawLine(float x1,float y1,float x2,float y2)537 void CGdiDeviceDriver::DrawLine(float x1, float y1, float x2, float y2) {
538   if (!m_bMetafileDCType) {  // EMF drawing is not bound to the DC.
539     int startOutOfBoundsFlag = (x1 < 0) | ((x1 > m_Width) << 1) |
540                                ((y1 < 0) << 2) | ((y1 > m_Height) << 3);
541     int endOutOfBoundsFlag = (x2 < 0) | ((x2 > m_Width) << 1) |
542                              ((y2 < 0) << 2) | ((y2 > m_Height) << 3);
543     if (startOutOfBoundsFlag & endOutOfBoundsFlag)
544       return;
545 
546     if (startOutOfBoundsFlag || endOutOfBoundsFlag) {
547       float x[2];
548       float y[2];
549       unsigned np = LineClip(m_Width, m_Height, x1, y1, x2, y2, x, y);
550       if (np == 0)
551         return;
552 
553       if (np == 1) {
554         x2 = x[0];
555         y2 = y[0];
556       } else {
557         DCHECK_EQ(np, 2);
558         x1 = x[0];
559         y1 = y[0];
560         x2 = x[1];
561         y2 = y[1];
562       }
563     }
564   }
565 
566   MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr);
567   LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2));
568 }
569 
DrawPath(const CFX_Path & path,const CFX_Matrix * pMatrix,const CFX_GraphStateData * pGraphState,uint32_t fill_color,uint32_t stroke_color,const CFX_FillRenderOptions & fill_options,BlendMode blend_type)570 bool CGdiDeviceDriver::DrawPath(const CFX_Path& path,
571                                 const CFX_Matrix* pMatrix,
572                                 const CFX_GraphStateData* pGraphState,
573                                 uint32_t fill_color,
574                                 uint32_t stroke_color,
575                                 const CFX_FillRenderOptions& fill_options,
576                                 BlendMode blend_type) {
577   if (blend_type != BlendMode::kNormal)
578     return false;
579 
580   auto* pPlatform =
581       static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
582   if (!(pGraphState || stroke_color == 0) &&
583       !pPlatform->m_GdiplusExt.IsAvailable()) {
584     CFX_FloatRect bbox_f = path.GetBoundingBox();
585     if (pMatrix)
586       bbox_f = pMatrix->TransformRect(bbox_f);
587 
588     FX_RECT bbox = bbox_f.GetInnerRect();
589     if (bbox.Width() <= 0) {
590       return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
591                               CFX_PointF(bbox.left, bbox.bottom + 1),
592                               fill_color, BlendMode::kNormal);
593     }
594     if (bbox.Height() <= 0) {
595       return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
596                               CFX_PointF(bbox.right + 1, bbox.top), fill_color,
597                               BlendMode::kNormal);
598     }
599   }
600   int fill_alpha = FXARGB_A(fill_color);
601   int stroke_alpha = FXARGB_A(stroke_color);
602   bool bDrawAlpha = (fill_alpha > 0 && fill_alpha < 255) ||
603                     (stroke_alpha > 0 && stroke_alpha < 255 && pGraphState);
604   if (!pPlatform->m_GdiplusExt.IsAvailable() && bDrawAlpha)
605     return false;
606 
607   if (pPlatform->m_GdiplusExt.IsAvailable()) {
608     if (bDrawAlpha ||
609         ((m_DeviceType != DeviceType::kPrinter && !fill_options.full_cover) ||
610          (pGraphState && !pGraphState->m_DashArray.empty()))) {
611       if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState &&
612             pGraphState->m_LineWidth == 1.0f && path.IsRect())) {
613         if (pPlatform->m_GdiplusExt.DrawPath(m_hDC, path, pMatrix, pGraphState,
614                                              fill_color, stroke_color,
615                                              fill_options)) {
616           return true;
617         }
618       }
619     }
620   }
621   const bool fill =
622       fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill;
623   HPEN hPen = nullptr;
624   HBRUSH hBrush = nullptr;
625   if (pGraphState && stroke_alpha) {
626     SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr);
627     hPen = CreateExtPen(pGraphState, pMatrix, stroke_color);
628     hPen = (HPEN)SelectObject(m_hDC, hPen);
629   }
630   if (fill && fill_alpha) {
631     SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type));
632     hBrush = CreateBrush(fill_color);
633     hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
634   }
635   if (path.GetPoints().size() == 2 && pGraphState &&
636       !pGraphState->m_DashArray.empty()) {
637     CFX_PointF pos1 = path.GetPoint(0);
638     CFX_PointF pos2 = path.GetPoint(1);
639     if (pMatrix) {
640       pos1 = pMatrix->Transform(pos1);
641       pos2 = pMatrix->Transform(pos2);
642     }
643     DrawLine(pos1.x, pos1.y, pos2.x, pos2.y);
644   } else {
645     SetPathToDC(m_hDC, path, pMatrix);
646     if (pGraphState && stroke_alpha) {
647       if (fill && fill_alpha) {
648         if (fill_options.text_mode) {
649           StrokeAndFillPath(m_hDC);
650         } else {
651           FillPath(m_hDC);
652           SetPathToDC(m_hDC, path, pMatrix);
653           StrokePath(m_hDC);
654         }
655       } else {
656         StrokePath(m_hDC);
657       }
658     } else if (fill && fill_alpha) {
659       FillPath(m_hDC);
660     }
661   }
662   if (hPen) {
663     hPen = (HPEN)SelectObject(m_hDC, hPen);
664     DeleteObject(hPen);
665   }
666   if (hBrush) {
667     hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
668     DeleteObject(hBrush);
669   }
670   return true;
671 }
672 
FillRectWithBlend(const FX_RECT & rect,uint32_t fill_color,BlendMode blend_type)673 bool CGdiDeviceDriver::FillRectWithBlend(const FX_RECT& rect,
674                                          uint32_t fill_color,
675                                          BlendMode blend_type) {
676   if (blend_type != BlendMode::kNormal)
677     return false;
678 
679   int alpha;
680   FX_COLORREF colorref;
681   std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color);
682   if (alpha == 0)
683     return true;
684 
685   if (alpha < 255)
686     return false;
687 
688   HBRUSH hBrush = CreateSolidBrush(colorref);
689   const RECT* pRect = reinterpret_cast<const RECT*>(&rect);
690   ::FillRect(m_hDC, pRect, hBrush);
691   DeleteObject(hBrush);
692   return true;
693 }
694 
SetBaseClip(const FX_RECT & rect)695 void CGdiDeviceDriver::SetBaseClip(const FX_RECT& rect) {
696   m_BaseClipBox = rect;
697 }
698 
SetClip_PathFill(const CFX_Path & path,const CFX_Matrix * pMatrix,const CFX_FillRenderOptions & fill_options)699 bool CGdiDeviceDriver::SetClip_PathFill(
700     const CFX_Path& path,
701     const CFX_Matrix* pMatrix,
702     const CFX_FillRenderOptions& fill_options) {
703   absl::optional<CFX_FloatRect> maybe_rectf = path.GetRect(pMatrix);
704   if (maybe_rectf.has_value()) {
705     FX_RECT rect = maybe_rectf.value().GetOuterRect();
706     // Can easily apply base clip to protect against wildly large rectangular
707     // clips. crbug.com/1019026
708     if (m_BaseClipBox.has_value())
709       rect.Intersect(m_BaseClipBox.value());
710     return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right,
711                              rect.bottom) != ERROR;
712   }
713   SetPathToDC(m_hDC, path, pMatrix);
714   SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type));
715   SelectClipPath(m_hDC, RGN_AND);
716   return true;
717 }
718 
SetClip_PathStroke(const CFX_Path & path,const CFX_Matrix * pMatrix,const CFX_GraphStateData * pGraphState)719 bool CGdiDeviceDriver::SetClip_PathStroke(
720     const CFX_Path& path,
721     const CFX_Matrix* pMatrix,
722     const CFX_GraphStateData* pGraphState) {
723   HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000);
724   hPen = (HPEN)SelectObject(m_hDC, hPen);
725   SetPathToDC(m_hDC, path, pMatrix);
726   WidenPath(m_hDC);
727   SetPolyFillMode(m_hDC, WINDING);
728   bool ret = !!SelectClipPath(m_hDC, RGN_AND);
729   hPen = (HPEN)SelectObject(m_hDC, hPen);
730   DeleteObject(hPen);
731   return ret;
732 }
733 
DrawCosmeticLine(const CFX_PointF & ptMoveTo,const CFX_PointF & ptLineTo,uint32_t color,BlendMode blend_type)734 bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
735                                         const CFX_PointF& ptLineTo,
736                                         uint32_t color,
737                                         BlendMode blend_type) {
738   if (blend_type != BlendMode::kNormal)
739     return false;
740 
741   int alpha;
742   FX_COLORREF colorref;
743   std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color);
744   if (alpha == 0)
745     return true;
746 
747   HPEN hPen = CreatePen(PS_SOLID, 1, colorref);
748   hPen = (HPEN)SelectObject(m_hDC, hPen);
749   MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr);
750   LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y));
751   hPen = (HPEN)SelectObject(m_hDC, hPen);
752   DeleteObject(hPen);
753   return true;
754 }
755