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