Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
cgdi_device_driver.cpp
Go to the documentation of this file.
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
31namespace {
32
33constexpr int FillTypeToGdiFillType(CFX_FillRenderOptions::FillType fill_type) {
34 return static_cast<int>(fill_type);
35}
36
37static_assert(FillTypeToGdiFillType(
38 CFX_FillRenderOptions::FillType::kEvenOdd) == ALTERNATE,
39 "CFX_FillRenderOptions::FillType::kEvenOdd value mismatch");
40
41static_assert(
42 FillTypeToGdiFillType(CFX_FillRenderOptions::FillType::kWinding) == WINDING,
43 "CFX_FillRenderOptions::FillType::kWinding value mismatch");
44
45HPEN 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
107HBRUSH CreateBrush(uint32_t argb) {
108 return CreateSolidBrush(ArgbToColorRef(argb));
109}
110
111void 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
156ByteString GetBitmapInfo(const RetainPtr<const 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(PDF_USE_SKIA)
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
208struct rect_base {
209 float x1;
210 float y1;
211 float x2;
212 float y2;
213};
214
215unsigned 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(PDF_USE_SKIA)
310
311unsigned 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(PDF_USE_SKIA)
320 if (CFX_DefaultRenderDevice::UseSkiaRenderer()) {
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
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 }
355 } else {
357 }
358}
359
361
365
366int CGdiDeviceDriver::GetDeviceCaps(int caps_id) const {
367 switch (caps_id) {
368 case FXDC_PIXEL_WIDTH:
369 return m_Width;
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_NORETURN();
378 }
379}
380
382 SaveDC(m_hDC);
383}
384
385void CGdiDeviceDriver::RestoreState(bool bKeepSaved) {
386 RestoreDC(m_hDC, -1);
387 if (bKeepSaved)
388 SaveDC(m_hDC);
389}
390
392 const FX_RECT& src_rect,
393 int left,
394 int top) {
396 RetainPtr<const CFX_DIBitmap> flipped_source =
397 source->FlipImage(/*bXFlip=*/false, /*bYFlip=*/true);
398 if (!flipped_source) {
399 return false;
400 }
401
402 CHECK(!flipped_source->GetBuffer().empty());
403 ByteString info = GetBitmapInfo(flipped_source);
404 ((BITMAPINFOHEADER*)info.c_str())->biHeight *= -1;
405 FX_RECT dst_rect(0, 0, src_rect.Width(), src_rect.Height());
406 dst_rect.Intersect(0, 0, flipped_source->GetWidth(),
407 flipped_source->GetHeight());
408 int dst_width = dst_rect.Width();
409 int dst_height = dst_rect.Height();
410 ::StretchDIBits(m_hDC, left, top, dst_width, dst_height, 0, 0, dst_width,
411 dst_height, flipped_source->GetBuffer().data(),
412 (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS, SRCCOPY);
413 return true;
414 }
415
416 RetainPtr<const CFX_DIBitmap> realized_source = source->RealizeIfNeeded();
417 if (!realized_source) {
418 return false;
419 }
420 ByteString info = GetBitmapInfo(realized_source);
421 ::SetDIBitsToDevice(
422 m_hDC, left, top, src_rect.Width(), src_rect.Height(), src_rect.left,
423 realized_source->GetHeight() - src_rect.bottom, 0,
424 realized_source->GetHeight(), realized_source->GetBuffer().data(),
425 (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS);
426 return true;
427}
428
430 int dest_left,
431 int dest_top,
432 int dest_width,
433 int dest_height,
434 const FXDIB_ResampleOptions& options) {
435 if (!source || dest_width == 0 || dest_height == 0) {
436 return false;
437 }
438
439 if ((int64_t)abs(dest_width) * abs(dest_height) <
440 (int64_t)source->GetWidth() * source->GetHeight() * 4 ||
441 options.bInterpolateBilinear) {
442 SetStretchBltMode(m_hDC, HALFTONE);
443 } else {
444 SetStretchBltMode(m_hDC, COLORONCOLOR);
445 }
446
447 RetainPtr<const CFX_DIBitmap> realized_source;
449 ((int64_t)source->GetWidth() * source->GetHeight() >
450 (int64_t)abs(dest_width) * abs(dest_height))) {
451 realized_source = source->StretchTo(dest_width, dest_height,
452 FXDIB_ResampleOptions(), nullptr);
453 } else {
454 realized_source = source->RealizeIfNeeded();
455 }
456 if (!realized_source) {
457 return false;
458 }
459
460 CHECK(!realized_source->GetBuffer().empty());
461 ByteString info = GetBitmapInfo(realized_source);
462 ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
463 realized_source->GetWidth(), realized_source->GetHeight(),
464 realized_source->GetBuffer().data(),
465 (BITMAPINFO*)info.c_str(), DIB_RGB_COLORS, SRCCOPY);
466 return true;
467}
468
470 int dest_left,
471 int dest_top,
472 int dest_width,
473 int dest_height,
474 uint32_t bitmap_color) {
475 if (!source || dest_width == 0 || dest_height == 0) {
476 return false;
477 }
478
479 RetainPtr<const CFX_DIBitmap> realized_source = source->RealizeIfNeeded();
480 if (!realized_source) {
481 return false;
482 }
483
484 int width = realized_source->GetWidth();
485 int height = realized_source->GetHeight();
486 struct {
487 BITMAPINFOHEADER bmiHeader;
488 uint32_t bmiColors[2];
489 } bmi;
490 memset(&bmi.bmiHeader, 0, sizeof(BITMAPINFOHEADER));
491 bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
492 bmi.bmiHeader.biBitCount = 1;
493 bmi.bmiHeader.biCompression = BI_RGB;
494 bmi.bmiHeader.biHeight = -height;
495 bmi.bmiHeader.biPlanes = 1;
496 bmi.bmiHeader.biWidth = width;
497 if (m_nBitsPerPixel != 1) {
498 SetStretchBltMode(m_hDC, HALFTONE);
499 }
500 bmi.bmiColors[0] = 0xffffff;
501 bmi.bmiColors[1] = 0;
502
503 HBRUSH hPattern = CreateBrush(bitmap_color);
504 HBRUSH hOld = (HBRUSH)SelectObject(m_hDC, hPattern);
505
506 // In PDF, when image mask is 1, use device bitmap; when mask is 0, use brush
507 // bitmap.
508 // A complete list of the boolen operations is as follows:
509
510 /* P(bitmap_color) S(ImageMask) D(DeviceBitmap) Result
511 * 0 0 0 0
512 * 0 0 1 0
513 * 0 1 0 0
514 * 0 1 1 1
515 * 1 0 0 1
516 * 1 0 1 1
517 * 1 1 0 0
518 * 1 1 1 1
519 */
520 // The boolen codes is B8. Based on
521 // http://msdn.microsoft.com/en-us/library/aa932106.aspx, the ROP3 code is
522 // 0xB8074A
523
524 ::StretchDIBits(m_hDC, dest_left, dest_top, dest_width, dest_height, 0, 0,
525 width, height, realized_source->GetBuffer().data(),
526 (BITMAPINFO*)&bmi, DIB_RGB_COLORS, 0xB8074A);
527
528 SelectObject(m_hDC, hOld);
529 DeleteObject(hPattern);
530
531 return true;
532}
533
535 return !!(::GetClipBox(m_hDC, (RECT*)pRect));
536}
537
538bool CGdiDeviceDriver::MultiplyAlpha(float alpha) {
539 // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices
540 // anyway.
541 NOTREACHED_NORETURN();
542}
543
545 const RetainPtr<const CFX_DIBBase>& mask) {
546 // Not implemented. All callers are using `CFX_DIBitmap`-backed raster devices
547 // anyway.
548 NOTREACHED_NORETURN();
549}
550
551void CGdiDeviceDriver::DrawLine(float x1, float y1, float x2, float y2) {
552 if (!m_bMetafileDCType) { // EMF drawing is not bound to the DC.
553 int startOutOfBoundsFlag = (x1 < 0) | ((x1 > m_Width) << 1) |
554 ((y1 < 0) << 2) | ((y1 > m_Height) << 3);
555 int endOutOfBoundsFlag = (x2 < 0) | ((x2 > m_Width) << 1) |
556 ((y2 < 0) << 2) | ((y2 > m_Height) << 3);
557 if (startOutOfBoundsFlag & endOutOfBoundsFlag)
558 return;
559
560 if (startOutOfBoundsFlag || endOutOfBoundsFlag) {
561 float x[2];
562 float y[2];
563 unsigned np = LineClip(m_Width, m_Height, x1, y1, x2, y2, x, y);
564 if (np == 0)
565 return;
566
567 if (np == 1) {
568 x2 = x[0];
569 y2 = y[0];
570 } else {
571 DCHECK_EQ(np, 2);
572 x1 = x[0];
573 y1 = y[0];
574 x2 = x[1];
575 y2 = y[1];
576 }
577 }
578 }
579
580 MoveToEx(m_hDC, FXSYS_roundf(x1), FXSYS_roundf(y1), nullptr);
581 LineTo(m_hDC, FXSYS_roundf(x2), FXSYS_roundf(y2));
582}
583
585 const CFX_Matrix* pMatrix,
586 const CFX_GraphStateData* pGraphState,
587 uint32_t fill_color,
588 uint32_t stroke_color,
589 const CFX_FillRenderOptions& fill_options,
590 BlendMode blend_type) {
591 if (blend_type != BlendMode::kNormal)
592 return false;
593
594 auto* pPlatform =
595 static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
596 if (!(pGraphState || stroke_color == 0) &&
597 !pPlatform->m_GdiplusExt.IsAvailable()) {
598 CFX_FloatRect bbox_f = path.GetBoundingBox();
599 if (pMatrix)
600 bbox_f = pMatrix->TransformRect(bbox_f);
601
602 FX_RECT bbox = bbox_f.GetInnerRect();
603 if (bbox.Width() <= 0) {
604 return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
605 CFX_PointF(bbox.left, bbox.bottom + 1),
606 fill_color, BlendMode::kNormal);
607 }
608 if (bbox.Height() <= 0) {
609 return DrawCosmeticLine(CFX_PointF(bbox.left, bbox.top),
610 CFX_PointF(bbox.right + 1, bbox.top), fill_color,
612 }
613 }
614 int fill_alpha = FXARGB_A(fill_color);
615 int stroke_alpha = FXARGB_A(stroke_color);
616 bool bDrawAlpha = (fill_alpha > 0 && fill_alpha < 255) ||
617 (stroke_alpha > 0 && stroke_alpha < 255 && pGraphState);
618 if (!pPlatform->m_GdiplusExt.IsAvailable() && bDrawAlpha)
619 return false;
620
621 if (pPlatform->m_GdiplusExt.IsAvailable()) {
622 if (bDrawAlpha ||
623 ((m_DeviceType != DeviceType::kPrinter && !fill_options.full_cover) ||
624 (pGraphState && !pGraphState->m_DashArray.empty()))) {
625 if (!((!pMatrix || !pMatrix->WillScale()) && pGraphState &&
626 pGraphState->m_LineWidth == 1.0f && path.IsRect())) {
627 if (pPlatform->m_GdiplusExt.DrawPath(m_hDC, path, pMatrix, pGraphState,
628 fill_color, stroke_color,
629 fill_options)) {
630 return true;
631 }
632 }
633 }
634 }
635 const bool fill =
637 HPEN hPen = nullptr;
638 HBRUSH hBrush = nullptr;
639 if (pGraphState && stroke_alpha) {
640 SetMiterLimit(m_hDC, pGraphState->m_MiterLimit, nullptr);
641 hPen = CreateExtPen(pGraphState, pMatrix, stroke_color);
642 hPen = (HPEN)SelectObject(m_hDC, hPen);
643 }
644 if (fill && fill_alpha) {
645 SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type));
646 hBrush = CreateBrush(fill_color);
647 hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
648 }
649 if (path.GetPoints().size() == 2 && pGraphState &&
650 !pGraphState->m_DashArray.empty()) {
651 CFX_PointF pos1 = path.GetPoint(0);
652 CFX_PointF pos2 = path.GetPoint(1);
653 if (pMatrix) {
654 pos1 = pMatrix->Transform(pos1);
655 pos2 = pMatrix->Transform(pos2);
656 }
657 DrawLine(pos1.x, pos1.y, pos2.x, pos2.y);
658 } else {
659 SetPathToDC(m_hDC, path, pMatrix);
660 if (pGraphState && stroke_alpha) {
661 if (fill && fill_alpha) {
662 if (fill_options.text_mode) {
663 StrokeAndFillPath(m_hDC);
664 } else {
665 FillPath(m_hDC);
666 SetPathToDC(m_hDC, path, pMatrix);
667 StrokePath(m_hDC);
668 }
669 } else {
670 StrokePath(m_hDC);
671 }
672 } else if (fill && fill_alpha) {
673 FillPath(m_hDC);
674 }
675 }
676 if (hPen) {
677 hPen = (HPEN)SelectObject(m_hDC, hPen);
678 DeleteObject(hPen);
679 }
680 if (hBrush) {
681 hBrush = (HBRUSH)SelectObject(m_hDC, hBrush);
682 DeleteObject(hBrush);
683 }
684 return true;
685}
686
688 uint32_t fill_color,
689 BlendMode blend_type) {
690 if (blend_type != BlendMode::kNormal)
691 return false;
692
693 int alpha;
694 FX_COLORREF colorref;
695 std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(fill_color);
696 if (alpha == 0)
697 return true;
698
699 if (alpha < 255)
700 return false;
701
702 HBRUSH hBrush = CreateSolidBrush(colorref);
703 const RECT* pRect = reinterpret_cast<const RECT*>(&rect);
704 ::FillRect(m_hDC, pRect, hBrush);
705 DeleteObject(hBrush);
706 return true;
707}
708
710 m_BaseClipBox = rect;
711}
712
714 const CFX_Path& path,
715 const CFX_Matrix* pMatrix,
716 const CFX_FillRenderOptions& fill_options) {
717 absl::optional<CFX_FloatRect> maybe_rectf = path.GetRect(pMatrix);
718 if (maybe_rectf.has_value()) {
719 FX_RECT rect = maybe_rectf.value().GetOuterRect();
720 // Can easily apply base clip to protect against wildly large rectangular
721 // clips. crbug.com/1019026
722 if (m_BaseClipBox.has_value())
723 rect.Intersect(m_BaseClipBox.value());
724 return IntersectClipRect(m_hDC, rect.left, rect.top, rect.right,
725 rect.bottom) != ERROR;
726 }
727 SetPathToDC(m_hDC, path, pMatrix);
728 SetPolyFillMode(m_hDC, FillTypeToGdiFillType(fill_options.fill_type));
729 SelectClipPath(m_hDC, RGN_AND);
730 return true;
731}
732
734 const CFX_Path& path,
735 const CFX_Matrix* pMatrix,
736 const CFX_GraphStateData* pGraphState) {
737 HPEN hPen = CreateExtPen(pGraphState, pMatrix, 0xff000000);
738 hPen = (HPEN)SelectObject(m_hDC, hPen);
739 SetPathToDC(m_hDC, path, pMatrix);
740 WidenPath(m_hDC);
741 SetPolyFillMode(m_hDC, WINDING);
742 bool ret = !!SelectClipPath(m_hDC, RGN_AND);
743 hPen = (HPEN)SelectObject(m_hDC, hPen);
744 DeleteObject(hPen);
745 return ret;
746}
747
748bool CGdiDeviceDriver::DrawCosmeticLine(const CFX_PointF& ptMoveTo,
749 const CFX_PointF& ptLineTo,
750 uint32_t color,
751 BlendMode blend_type) {
752 if (blend_type != BlendMode::kNormal)
753 return false;
754
755 int alpha;
756 FX_COLORREF colorref;
757 std::tie(alpha, colorref) = ArgbToAlphaAndColorRef(color);
758 if (alpha == 0)
759 return true;
760
761 HPEN hPen = CreatePen(PS_SOLID, 1, colorref);
762 hPen = (HPEN)SelectObject(m_hDC, hPen);
763 MoveToEx(m_hDC, FXSYS_roundf(ptMoveTo.x), FXSYS_roundf(ptMoveTo.y), nullptr);
764 LineTo(m_hDC, FXSYS_roundf(ptLineTo.x), FXSYS_roundf(ptLineTo.y));
765 hPen = (HPEN)SelectObject(m_hDC, hPen);
766 DeleteObject(hPen);
767 return true;
768}
FX_RECT GetInnerRect() const
CFX_FloatRect & operator=(const CFX_FloatRect &that)=default
PlatformIface * GetPlatform() const
static CFX_GEModule * Get()
CFX_FloatRect TransformRect(const CFX_FloatRect &rect) const
float TransformDistance(float distance) const
CFX_PointF Transform(const CFX_PointF &point) const
bool WillScale() const
CFX_FloatRect GetBoundingBox() const
Definition cfx_path.cpp:322
bool IsRect() const
Definition cfx_path.cpp:397
bool MultiplyAlphaMask(const RetainPtr< const CFX_DIBBase > &mask) override
void SetBaseClip(const FX_RECT &rect) override
const DeviceType m_DeviceType
bool SetClip_PathStroke(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_GraphStateData *pGraphState) override
void DrawLine(float x1, float y1, float x2, float y2)
~CGdiDeviceDriver() override
bool SetClip_PathFill(const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_FillRenderOptions &fill_options) override
bool 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, BlendMode blend_type) override
bool GDI_SetDIBits(const RetainPtr< const CFX_DIBBase > &source, const FX_RECT &src_rect, int left, int top)
bool GetClipBox(FX_RECT *pRect) override
bool GDI_StretchDIBits(RetainPtr< const CFX_DIBBase > source, int dest_left, int dest_top, int dest_width, int dest_height, const FXDIB_ResampleOptions &options)
bool DrawCosmeticLine(const CFX_PointF &ptMoveTo, const CFX_PointF &ptLineTo, uint32_t color, BlendMode blend_type) override
int GetDeviceCaps(int caps_id) const override
bool MultiplyAlpha(float alpha) override
CGdiDeviceDriver(HDC hDC, DeviceType device_type)
void RestoreState(bool bKeepSaved) override
DeviceType GetDeviceType() const override
void SaveState() override
bool FillRectWithBlend(const FX_RECT &rect, uint32_t fill_color, BlendMode blend_type) override
bool GDI_StretchBitMask(RetainPtr< const CFX_DIBBase > source, int dest_left, int dest_top, int dest_width, int dest_height, uint32_t bitmap_color)
BlendMode
Definition fx_dib.h:49
#define FXARGB_A(argb)
Definition fx_dib.h:124
constexpr FX_ARGB ArgbEncode(uint32_t a, uint32_t r, uint32_t g, uint32_t b)
Definition fx_dib.h:118
FX_COLORREF ArgbToColorRef(FX_ARGB argb)
Definition fx_dib.cpp:53
#define CHECK(cvref)
#define FXDC_BITS_PIXEL
#define FXDC_RENDER_CAPS
#define FXDC_PIXEL_WIDTH
#define FXRC_BIT_MASK
#define FXDC_PIXEL_HEIGHT
#define FXRC_GET_BITS
bool bInterpolateBilinear
Definition fx_dib.h:41
int Height() const
int32_t bottom
int32_t right
int Width() const
int32_t top
int32_t left
constexpr FX_RECT(int l, int t, int r, int b)