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