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_plus_ext.cpp
Go to the documentation of this file.
1// Copyright 2014 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_plus_ext.h"
8
9#include <windows.h>
10
11#include <objidl.h>
12
13#include <algorithm>
14#include <array>
15#include <sstream>
16#include <utility>
17#include <vector>
18
19#include "core/fxcrt/check_op.h"
20#include "core/fxcrt/fx_memcpy_wrappers.h"
21#include "core/fxcrt/fx_memory.h"
22#include "core/fxcrt/fx_string.h"
23#include "core/fxcrt/fx_string_wrappers.h"
24#include "core/fxcrt/fx_system.h"
25#include "core/fxcrt/numerics/safe_conversions.h"
26#include "core/fxcrt/span.h"
27#include "core/fxge/cfx_fillrenderoptions.h"
28#include "core/fxge/cfx_gemodule.h"
29#include "core/fxge/cfx_graphstatedata.h"
30#include "core/fxge/cfx_path.h"
31#include "core/fxge/dib/cfx_dibbase.h"
32#include "core/fxge/dib/cfx_dibitmap.h"
33#include "core/fxge/win32/cwin32_platform.h"
34
35// Has to come before gdiplus.h
36namespace Gdiplus {
37using std::max;
38using std::min;
39} // namespace Gdiplus
40
41#include <gdiplus.h> // NOLINT
42
43namespace {
44
45enum {
46 FuncId_GdipCreatePath2,
47 FuncId_GdipSetPenDashArray,
48 FuncId_GdipSetPenLineJoin,
49 FuncId_GdipCreateFromHDC,
50 FuncId_GdipSetPageUnit,
51 FuncId_GdipSetSmoothingMode,
52 FuncId_GdipCreateSolidFill,
53 FuncId_GdipFillPath,
54 FuncId_GdipDeleteBrush,
55 FuncId_GdipCreatePen1,
56 FuncId_GdipSetPenMiterLimit,
57 FuncId_GdipDrawPath,
58 FuncId_GdipDeletePen,
59 FuncId_GdipDeletePath,
60 FuncId_GdipDeleteGraphics,
61 FuncId_GdipDisposeImage,
62 FuncId_GdipCreateBitmapFromScan0,
63 FuncId_GdipSetInterpolationMode,
64 FuncId_GdipDrawImagePointsI,
65 FuncId_GdiplusStartup,
66 FuncId_GdipDrawLineI,
67 FuncId_GdipCreatePath,
68 FuncId_GdipSetPathFillMode,
69 FuncId_GdipSetClipRegion,
70 FuncId_GdipWidenPath,
71 FuncId_GdipAddPathLine,
72 FuncId_GdipAddPathRectangle,
73 FuncId_GdipDeleteRegion,
74 FuncId_GdipSetPenLineCap197819,
75 FuncId_GdipSetPenDashOffset,
76 FuncId_GdipCreateMatrix2,
77 FuncId_GdipDeleteMatrix,
78 FuncId_GdipSetWorldTransform,
79 FuncId_GdipSetPixelOffsetMode,
80};
81
82LPCSTR g_GdipFuncNames[] = {
83 "GdipCreatePath2",
84 "GdipSetPenDashArray",
85 "GdipSetPenLineJoin",
86 "GdipCreateFromHDC",
87 "GdipSetPageUnit",
88 "GdipSetSmoothingMode",
89 "GdipCreateSolidFill",
90 "GdipFillPath",
91 "GdipDeleteBrush",
92 "GdipCreatePen1",
93 "GdipSetPenMiterLimit",
94 "GdipDrawPath",
95 "GdipDeletePen",
96 "GdipDeletePath",
97 "GdipDeleteGraphics",
98 "GdipDisposeImage",
99 "GdipCreateBitmapFromScan0",
100 "GdipSetInterpolationMode",
101 "GdipDrawImagePointsI",
102 "GdiplusStartup",
103 "GdipDrawLineI",
104 "GdipCreatePath",
105 "GdipSetPathFillMode",
106 "GdipSetClipRegion",
107 "GdipWidenPath",
108 "GdipAddPathLine",
109 "GdipAddPathRectangle",
110 "GdipDeleteRegion",
111 "GdipSetPenLineCap197819",
112 "GdipSetPenDashOffset",
113 "GdipCreateMatrix2",
114 "GdipDeleteMatrix",
115 "GdipSetWorldTransform",
116 "GdipSetPixelOffsetMode",
117};
118static_assert(std::size(g_GdipFuncNames) ==
119 static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
120 "g_GdipFuncNames has wrong size");
121
122using FuncType_GdipCreatePath2 =
123 decltype(&Gdiplus::DllExports::GdipCreatePath2);
124using FuncType_GdipSetPenDashArray =
125 decltype(&Gdiplus::DllExports::GdipSetPenDashArray);
126using FuncType_GdipSetPenLineJoin =
127 decltype(&Gdiplus::DllExports::GdipSetPenLineJoin);
128using FuncType_GdipCreateFromHDC =
129 decltype(&Gdiplus::DllExports::GdipCreateFromHDC);
130using FuncType_GdipSetPageUnit =
131 decltype(&Gdiplus::DllExports::GdipSetPageUnit);
132using FuncType_GdipSetSmoothingMode =
133 decltype(&Gdiplus::DllExports::GdipSetSmoothingMode);
134using FuncType_GdipCreateSolidFill =
135 decltype(&Gdiplus::DllExports::GdipCreateSolidFill);
136using FuncType_GdipFillPath = decltype(&Gdiplus::DllExports::GdipFillPath);
137using FuncType_GdipDeleteBrush =
138 decltype(&Gdiplus::DllExports::GdipDeleteBrush);
139using FuncType_GdipCreatePen1 = decltype(&Gdiplus::DllExports::GdipCreatePen1);
140using FuncType_GdipSetPenMiterLimit =
141 decltype(&Gdiplus::DllExports::GdipSetPenMiterLimit);
142using FuncType_GdipDrawPath = decltype(&Gdiplus::DllExports::GdipDrawPath);
143using FuncType_GdipDeletePen = decltype(&Gdiplus::DllExports::GdipDeletePen);
144using FuncType_GdipDeletePath = decltype(&Gdiplus::DllExports::GdipDeletePath);
145using FuncType_GdipDeleteGraphics =
146 decltype(&Gdiplus::DllExports::GdipDeleteGraphics);
147using FuncType_GdipDisposeImage =
148 decltype(&Gdiplus::DllExports::GdipDisposeImage);
149using FuncType_GdipCreateBitmapFromScan0 =
150 decltype(&Gdiplus::DllExports::GdipCreateBitmapFromScan0);
151using FuncType_GdipSetInterpolationMode =
152 decltype(&Gdiplus::DllExports::GdipSetInterpolationMode);
153using FuncType_GdipDrawImagePointsI =
154 decltype(&Gdiplus::DllExports::GdipDrawImagePointsI);
155using FuncType_GdiplusStartup = decltype(&Gdiplus::GdiplusStartup);
156using FuncType_GdipDrawLineI = decltype(&Gdiplus::DllExports::GdipDrawLineI);
157using FuncType_GdipCreatePath = decltype(&Gdiplus::DllExports::GdipCreatePath);
158using FuncType_GdipSetPathFillMode =
159 decltype(&Gdiplus::DllExports::GdipSetPathFillMode);
160using FuncType_GdipSetClipRegion =
161 decltype(&Gdiplus::DllExports::GdipSetClipRegion);
162using FuncType_GdipWidenPath = decltype(&Gdiplus::DllExports::GdipWidenPath);
163using FuncType_GdipAddPathLine =
164 decltype(&Gdiplus::DllExports::GdipAddPathLine);
165using FuncType_GdipAddPathRectangle =
166 decltype(&Gdiplus::DllExports::GdipAddPathRectangle);
167using FuncType_GdipDeleteRegion =
168 decltype(&Gdiplus::DllExports::GdipDeleteRegion);
169using FuncType_GdipSetPenLineCap197819 =
170 decltype(&Gdiplus::DllExports::GdipSetPenLineCap197819);
171using FuncType_GdipSetPenDashOffset =
172 decltype(&Gdiplus::DllExports::GdipSetPenDashOffset);
173using FuncType_GdipCreateMatrix2 =
174 decltype(&Gdiplus::DllExports::GdipCreateMatrix2);
175using FuncType_GdipDeleteMatrix =
176 decltype(&Gdiplus::DllExports::GdipDeleteMatrix);
177using FuncType_GdipSetWorldTransform =
178 decltype(&Gdiplus::DllExports::GdipSetWorldTransform);
179using FuncType_GdipSetPixelOffsetMode =
180 decltype(&Gdiplus::DllExports::GdipSetPixelOffsetMode);
181
182#define CALLFUNC(gdi_plus_ext, funcname, ...)
183 reinterpret_cast<FuncType_##funcname>(
184 (gdi_plus_ext).functions()[FuncId_##funcname])(__VA_ARGS__)
185
186Gdiplus::GpFillMode FillType2Gdip(CFX_FillRenderOptions::FillType fill_type) {
187 return fill_type == CFX_FillRenderOptions::FillType::kEvenOdd
188 ? Gdiplus::FillModeAlternate
189 : Gdiplus::FillModeWinding;
190}
191
192const CGdiplusExt& GetGdiplusExt() {
193 auto* pData =
194 static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
195 return pData->m_GdiplusExt;
196}
197
198Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) {
199 Gdiplus::GpSolidFill* solidBrush = nullptr;
200 CALLFUNC(GetGdiplusExt(), GdipCreateSolidFill,
201 static_cast<Gdiplus::ARGB>(argb), &solidBrush);
202 return solidBrush;
203}
204
205void OutputImage(Gdiplus::GpGraphics* graphics,
206 RetainPtr<const CFX_DIBBase> source,
207 int dest_left,
208 int dest_top,
209 int dest_width,
210 int dest_height) {
211 CHECK_EQ(FXDIB_Format::kBgra, source->GetFormat());
212 const CGdiplusExt& gdi_plus_ext = GetGdiplusExt();
213
214 RetainPtr<const CFX_DIBitmap> realized_source = source->RealizeIfNeeded();
215 if (!realized_source) {
216 return;
217 }
218
219 // `GdipCreateBitmapFromScan0()` requires a `BYTE*` scanline buffer, but in
220 // this case, the bitmap only gets read by `GdipDrawImagePointsI()`, then
221 // disposed of, so it's safe to cast away `const` here.
222 uint8_t* scan0 = const_cast<uint8_t*>(realized_source->GetBuffer().data());
223 Gdiplus::GpBitmap* bitmap = nullptr;
224 CALLFUNC(gdi_plus_ext, GdipCreateBitmapFromScan0, realized_source->GetWidth(),
225 realized_source->GetHeight(), realized_source->GetPitch(),
226 PixelFormat32bppARGB, scan0, &bitmap);
227 if (dest_height < 0) {
228 dest_height--;
229 }
230 if (dest_width < 0) {
231 dest_width--;
232 }
233 const Gdiplus::Point destination_points[] = {
234 Gdiplus::Point(dest_left, dest_top),
235 Gdiplus::Point(dest_left + dest_width, dest_top),
236 Gdiplus::Point(dest_left, dest_top + dest_height)};
237 CALLFUNC(gdi_plus_ext, GdipDrawImagePointsI, graphics, bitmap,
238 destination_points, std::size(destination_points));
239 CALLFUNC(gdi_plus_ext, GdipDisposeImage, bitmap);
240}
241
242Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState,
243 const CFX_Matrix* pMatrix,
244 DWORD argb,
245 bool bTextMode) {
246 const CGdiplusExt& gdi_plus_ext = GetGdiplusExt();
247 float width = pGraphState->m_LineWidth;
248 if (!bTextMode) {
249 float unit = pMatrix
250 ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
251 : 1.0f;
252 width = std::max(width, unit);
253 }
254 Gdiplus::GpPen* pPen = nullptr;
255 CALLFUNC(gdi_plus_ext, GdipCreatePen1, static_cast<Gdiplus::ARGB>(argb),
256 width, Gdiplus::UnitWorld, &pPen);
257 Gdiplus::LineCap lineCap = Gdiplus::LineCapFlat;
258 Gdiplus::DashCap dashCap = Gdiplus::DashCapFlat;
259 bool bDashExtend = false;
260 switch (pGraphState->m_LineCap) {
261 case CFX_GraphStateData::LineCap::kButt:
262 lineCap = Gdiplus::LineCapFlat;
263 break;
264 case CFX_GraphStateData::LineCap::kRound:
265 lineCap = Gdiplus::LineCapRound;
266 dashCap = Gdiplus::DashCapRound;
267 bDashExtend = true;
268 break;
269 case CFX_GraphStateData::LineCap::kSquare:
270 lineCap = Gdiplus::LineCapSquare;
271 bDashExtend = true;
272 break;
273 }
274 CALLFUNC(gdi_plus_ext, GdipSetPenLineCap197819, pPen, lineCap, lineCap,
275 dashCap);
276 Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped;
277 switch (pGraphState->m_LineJoin) {
278 case CFX_GraphStateData::LineJoin::kMiter:
279 lineJoin = Gdiplus::LineJoinMiterClipped;
280 break;
281 case CFX_GraphStateData::LineJoin::kRound:
282 lineJoin = Gdiplus::LineJoinRound;
283 break;
284 case CFX_GraphStateData::LineJoin::kBevel:
285 lineJoin = Gdiplus::LineJoinBevel;
286 break;
287 }
288 CALLFUNC(gdi_plus_ext, GdipSetPenLineJoin, pPen, lineJoin);
289 if (!pGraphState->m_DashArray.empty()) {
290 float* pDashArray =
291 FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
292 int nCount = 0;
293 float on_leftover = 0;
294 float off_leftover = 0;
295 for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) {
296 float on_phase = pGraphState->m_DashArray[i];
297 float off_phase;
298 if (i + 1 < pGraphState->m_DashArray.size()) {
299 off_phase = pGraphState->m_DashArray[i + 1];
300 } else {
301 off_phase = on_phase;
302 }
303 on_phase /= width;
304 off_phase /= width;
305 if (on_phase + off_phase <= 0.00002f) {
306 on_phase = 0.1f;
307 off_phase = 0.1f;
308 }
309 if (bDashExtend) {
310 if (off_phase < 1)
311 off_phase = 0;
312 else
313 --off_phase;
314 ++on_phase;
315 }
316 if (on_phase == 0 || off_phase == 0) {
317 if (nCount == 0) {
318 on_leftover += on_phase;
319 off_leftover += off_phase;
320 } else {
322 pDashArray[nCount - 2] += on_phase;
323 pDashArray[nCount - 1] += off_phase;
324 });
325 }
326 } else {
328 pDashArray[nCount++] = on_phase + on_leftover;
329 on_leftover = 0;
330 pDashArray[nCount++] = off_phase + off_leftover;
331 off_leftover = 0;
332 });
333 }
334 }
335 CALLFUNC(gdi_plus_ext, GdipSetPenDashArray, pPen, pDashArray, nCount);
336 float phase = pGraphState->m_DashPhase;
337 if (bDashExtend) {
338 if (phase < 0.5f)
339 phase = 0;
340 else
341 phase -= 0.5f;
342 }
343 CALLFUNC(gdi_plus_ext, GdipSetPenDashOffset, pPen, phase);
344 FX_Free(pDashArray);
345 pDashArray = nullptr;
346 }
347 CALLFUNC(gdi_plus_ext, GdipSetPenMiterLimit, pPen, pGraphState->m_MiterLimit);
348 return pPen;
349}
350
351std::optional<std::pair<size_t, size_t>> IsSmallTriangle(
352 pdfium::span<const Gdiplus::PointF> points,
353 const CFX_Matrix* pMatrix) {
354 static constexpr std::array<std::pair<const size_t, const size_t>, 3> kPairs =
355 {{{1, 2}, {0, 2}, {0, 1}}};
356 for (size_t i = 0; i < std::size(kPairs); ++i) {
357 size_t pair1 = kPairs[i].first;
358 size_t pair2 = kPairs[i].second;
359 CFX_PointF p1(points[pair1].X, points[pair1].Y);
360 CFX_PointF p2(points[pair2].X, points[pair2].Y);
361 if (pMatrix) {
362 p1 = pMatrix->Transform(p1);
363 p2 = pMatrix->Transform(p2);
364 }
365 CFX_PointF diff = p1 - p2;
366 float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
367 if (distance_square < 2.25f)
368 return std::make_pair(i, pair1);
369 }
370 return std::nullopt;
371}
372
373class GpStream final : public IStream {
374 public:
375 GpStream() = default;
376 ~GpStream() = default;
377
378 // IUnknown
379 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
380 void** ppvObject) override {
381 if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
382 iid == __uuidof(ISequentialStream)) {
383 *ppvObject = static_cast<IStream*>(this);
384 AddRef();
385 return S_OK;
386 }
387 return E_NOINTERFACE;
388 }
389 ULONG STDMETHODCALLTYPE AddRef() override {
390 return (ULONG)InterlockedIncrement(&m_RefCount);
391 }
392 ULONG STDMETHODCALLTYPE Release() override {
393 ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
394 if (res == 0) {
395 delete this;
396 }
397 return res;
398 }
399
400 // ISequentialStream
401 HRESULT STDMETHODCALLTYPE Read(void* output,
402 ULONG cb,
403 ULONG* pcbRead) override {
404 if (pcbRead)
405 *pcbRead = 0;
406
407 if (m_ReadPos >= m_InterStream.tellp())
408 return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
409
410 size_t bytes_left = pdfium::checked_cast<size_t>(
411 std::streamoff(m_InterStream.tellp()) - m_ReadPos);
412 size_t bytes_out = std::min(pdfium::checked_cast<size_t>(cb), bytes_left);
413 UNSAFE_TODO(FXSYS_memcpy(output, m_InterStream.str().c_str() + m_ReadPos,
414 bytes_out));
415 m_ReadPos += bytes_out;
416 if (pcbRead)
417 *pcbRead = (ULONG)bytes_out;
418
419 return S_OK;
420 }
421 HRESULT STDMETHODCALLTYPE Write(const void* input,
422 ULONG cb,
423 ULONG* pcbWritten) override {
424 if (cb <= 0) {
425 if (pcbWritten)
426 *pcbWritten = 0;
427 return S_OK;
428 }
429 m_InterStream.write(reinterpret_cast<const char*>(input), cb);
430 if (pcbWritten)
431 *pcbWritten = cb;
432 return S_OK;
433 }
434
435 // IStream
436 HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
437 return E_NOTIMPL;
438 }
439 HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
440 ULARGE_INTEGER,
441 ULARGE_INTEGER*,
442 ULARGE_INTEGER*) override {
443 return E_NOTIMPL;
444 }
445 HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
446 HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
447 HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
448 ULARGE_INTEGER,
449 DWORD) override {
450 return E_NOTIMPL;
451 }
452 HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
453 ULARGE_INTEGER,
454 DWORD) override {
455 return E_NOTIMPL;
456 }
457 HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
458 return E_NOTIMPL;
459 }
460 HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
461 DWORD dwOrigin,
462 ULARGE_INTEGER* lpNewFilePointer) override {
463 std::streamoff start;
464 std::streamoff new_read_position;
465 switch (dwOrigin) {
466 case STREAM_SEEK_SET:
467 start = 0;
468 break;
469 case STREAM_SEEK_CUR:
470 start = m_ReadPos;
471 break;
472 case STREAM_SEEK_END:
473 if (m_InterStream.tellp() < 0)
474 return STG_E_SEEKERROR;
475 start = m_InterStream.tellp();
476 break;
477 default:
478 return STG_E_INVALIDFUNCTION;
479 }
480 new_read_position = start + liDistanceToMove.QuadPart;
481 if (new_read_position > m_InterStream.tellp())
482 return STG_E_SEEKERROR;
483
484 m_ReadPos = new_read_position;
485 if (lpNewFilePointer)
486 lpNewFilePointer->QuadPart = m_ReadPos;
487
488 return S_OK;
489 }
490 HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
491 DWORD grfStatFlag) override {
492 if (!pStatstg)
493 return STG_E_INVALIDFUNCTION;
494
495 ZeroMemory(pStatstg, sizeof(STATSTG));
496
497 if (m_InterStream.tellp() < 0)
498 return STG_E_SEEKERROR;
499
500 pStatstg->cbSize.QuadPart = m_InterStream.tellp();
501 return S_OK;
502 }
503
504 private:
505 LONG m_RefCount = 1;
506 std::streamoff m_ReadPos = 0;
507 fxcrt::ostringstream m_InterStream;
508};
509
510} // namespace
511
512CGdiplusExt::CGdiplusExt() = default;
513
515 FreeLibrary(gdiplus_module_);
516}
517
519 char buf[MAX_PATH];
520 GetSystemDirectoryA(buf, MAX_PATH);
521 ByteString dllpath = buf;
522 dllpath += "\\GDIPLUS.DLL";
523 gdiplus_module_ = LoadLibraryA(dllpath.c_str());
524 if (!gdiplus_module_) {
525 return;
526 }
527
528 gdiplus_functions_.resize(std::size(g_GdipFuncNames));
530 for (size_t i = 0; i < std::size(g_GdipFuncNames); ++i) {
531 gdiplus_functions_[i] =
532 GetProcAddress(gdiplus_module_, g_GdipFuncNames[i]);
533 if (!gdiplus_functions_[i]) {
534 FreeLibrary(gdiplus_module_);
535 gdiplus_module_ = nullptr;
536 return;
537 }
538 }
539 });
540 ULONG_PTR gdiplus_token;
541 Gdiplus::GdiplusStartupInput gdiplus_startup_input;
542 ((FuncType_GdiplusStartup)gdiplus_functions_[FuncId_GdiplusStartup])(
543 &gdiplus_token, &gdiplus_startup_input, nullptr);
544}
545
547 RetainPtr<const CFX_DIBBase> source,
548 int dest_left,
549 int dest_top,
550 int dest_width,
551 int dest_height) {
552 CHECK(source->IsAlphaFormat());
553 Gdiplus::GpGraphics* pGraphics;
554 const CGdiplusExt& gdi_plus_ext = GetGdiplusExt();
555 CALLFUNC(gdi_plus_ext, GdipCreateFromHDC, hDC, &pGraphics);
556 CALLFUNC(gdi_plus_ext, GdipSetPageUnit, pGraphics, Gdiplus::UnitPixel);
557 if (source->GetWidth() > abs(dest_width) / 2 ||
558 source->GetHeight() > abs(dest_height) / 2) {
559 CALLFUNC(gdi_plus_ext, GdipSetInterpolationMode, pGraphics,
560 Gdiplus::InterpolationModeHighQuality);
561 } else {
562 CALLFUNC(gdi_plus_ext, GdipSetInterpolationMode, pGraphics,
563 Gdiplus::InterpolationModeBilinear);
564 }
565 OutputImage(pGraphics, std::move(source), dest_left, dest_top, dest_width,
566 dest_height);
567 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
568 return true;
569}
570
571bool CGdiplusExt::DrawPath(HDC hDC,
572 const CFX_Path& path,
573 const CFX_Matrix* pObject2Device,
574 const CFX_GraphStateData* pGraphState,
575 uint32_t fill_argb,
576 uint32_t stroke_argb,
577 const CFX_FillRenderOptions& fill_options) {
578 pdfium::span<const CFX_Path::Point> points = path.GetPoints();
579 if (points.empty())
580 return true;
581
582 Gdiplus::GpGraphics* pGraphics = nullptr;
583 const CGdiplusExt& gdi_plus_ext = GetGdiplusExt();
584 CALLFUNC(gdi_plus_ext, GdipCreateFromHDC, hDC, &pGraphics);
585 CALLFUNC(gdi_plus_ext, GdipSetPageUnit, pGraphics, Gdiplus::UnitPixel);
586 CALLFUNC(gdi_plus_ext, GdipSetPixelOffsetMode, pGraphics,
587 Gdiplus::PixelOffsetModeHalf);
588 Gdiplus::GpMatrix* pMatrix = nullptr;
589 if (pObject2Device) {
590 CALLFUNC(gdi_plus_ext, GdipCreateMatrix2, pObject2Device->a,
591 pObject2Device->b, pObject2Device->c, pObject2Device->d,
592 pObject2Device->e, pObject2Device->f, &pMatrix);
593 CALLFUNC(gdi_plus_ext, GdipSetWorldTransform, pGraphics, pMatrix);
594 }
595 std::vector<Gdiplus::PointF> gp_points(points.size());
596 std::vector<BYTE> gp_types(points.size());
597 int nSubPathes = 0;
598 bool bSubClose = false;
599 bool bSmooth = false;
600 size_t pos_subclose = 0;
601 size_t startpoint = 0;
602 for (size_t i = 0; i < points.size(); ++i) {
603 gp_points[i].X = points[i].m_Point.x;
604 gp_points[i].Y = points[i].m_Point.y;
605
606 CFX_PointF pos = points[i].m_Point;
607 if (pObject2Device)
608 pos = pObject2Device->Transform(pos);
609
610 if (pos.x > 50000.0f)
611 gp_points[i].X = 50000.0f;
612 if (pos.x < -50000.0f)
613 gp_points[i].X = -50000.0f;
614 if (pos.y > 50000.0f)
615 gp_points[i].Y = 50000.0f;
616 if (pos.y < -50000.0f)
617 gp_points[i].Y = -50000.0f;
618
619 CFX_Path::Point::Type point_type = points[i].m_Type;
620 if (point_type == CFX_Path::Point::Type::kMove) {
621 gp_types[i] = Gdiplus::PathPointTypeStart;
622 nSubPathes++;
623 bSubClose = false;
624 startpoint = i;
625 } else if (point_type == CFX_Path::Point::Type::kLine) {
626 gp_types[i] = Gdiplus::PathPointTypeLine;
627 if (points[i - 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove) &&
628 (i + 1 == points.size() ||
629 points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove)) &&
630 gp_points[i].Y == gp_points[i - 1].Y &&
631 gp_points[i].X == gp_points[i - 1].X) {
632 gp_points[i].X += 0.01f;
633 continue;
634 }
635 if (!bSmooth && gp_points[i].X != gp_points[i - 1].X &&
636 gp_points[i].Y != gp_points[i - 1].Y) {
637 bSmooth = true;
638 }
639 } else if (point_type == CFX_Path::Point::Type::kBezier) {
640 gp_types[i] = Gdiplus::PathPointTypeBezier;
641 bSmooth = true;
642 }
643 if (points[i].m_CloseFigure) {
644 if (bSubClose)
645 gp_types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
646 else
647 bSubClose = true;
648 pos_subclose = i;
649 gp_types[i] |= Gdiplus::PathPointTypeCloseSubpath;
650 if (!bSmooth && gp_points[i].X != gp_points[startpoint].X &&
651 gp_points[i].Y != gp_points[startpoint].Y) {
652 bSmooth = true;
653 }
654 }
655 }
656 const bool fill =
658 if (fill_options.aliased_path) {
659 bSmooth = false;
660 CALLFUNC(gdi_plus_ext, GdipSetSmoothingMode, pGraphics,
661 Gdiplus::SmoothingModeNone);
662 } else if (!fill_options.full_cover) {
663 if (!bSmooth && fill)
664 bSmooth = true;
665
666 if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
667 CALLFUNC(gdi_plus_ext, GdipSetSmoothingMode, pGraphics,
668 Gdiplus::SmoothingModeAntiAlias);
669 }
670 }
671 if (points.size() == 4 && !pGraphState) {
672 auto indices = IsSmallTriangle(gp_points, pObject2Device);
673 if (indices.has_value()) {
674 auto [v1, v2] = indices.value();
675 Gdiplus::GpPen* pPen = nullptr;
676 CALLFUNC(gdi_plus_ext, GdipCreatePen1, fill_argb, 1.0f,
677 Gdiplus::UnitPixel, &pPen);
678 CALLFUNC(gdi_plus_ext, GdipDrawLineI, pGraphics, pPen,
679 FXSYS_roundf(gp_points[v1].X), FXSYS_roundf(gp_points[v1].Y),
680 FXSYS_roundf(gp_points[v2].X), FXSYS_roundf(gp_points[v2].Y));
681 CALLFUNC(gdi_plus_ext, GdipDeletePen, pPen);
682 return true;
683 }
684 }
685 Gdiplus::GpPath* pGpPath = nullptr;
686 const Gdiplus::GpFillMode gp_fill_mode =
687 FillType2Gdip(fill_options.fill_type);
688 CALLFUNC(gdi_plus_ext, GdipCreatePath2, gp_points.data(), gp_types.data(),
689 pdfium::checked_cast<int>(points.size()), gp_fill_mode, &pGpPath);
690 if (!pGpPath) {
691 if (pMatrix) {
692 CALLFUNC(gdi_plus_ext, GdipDeleteMatrix, pMatrix);
693 }
694
695 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
696 return false;
697 }
698 if (fill) {
699 Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb);
700 CALLFUNC(gdi_plus_ext, GdipSetPathFillMode, pGpPath, gp_fill_mode);
701 CALLFUNC(gdi_plus_ext, GdipFillPath, pGraphics, pBrush, pGpPath);
702 CALLFUNC(gdi_plus_ext, GdipDeleteBrush, pBrush);
703 }
704 if (pGraphState && stroke_argb) {
705 Gdiplus::GpPen* pPen =
706 GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb,
707 fill_options.stroke_text_mode);
708 if (nSubPathes == 1) {
709 CALLFUNC(gdi_plus_ext, GdipDrawPath, pGraphics, pPen, pGpPath);
710 } else {
711 size_t iStart = 0;
712 for (size_t i = 0; i < points.size(); ++i) {
713 if (i + 1 == points.size() ||
714 gp_types[i + 1] == Gdiplus::PathPointTypeStart) {
715 Gdiplus::GpPath* pSubPath;
716 CALLFUNC(gdi_plus_ext, GdipCreatePath2, &gp_points[iStart],
717 &gp_types[iStart], pdfium::checked_cast<int>(i - iStart + 1),
718 gp_fill_mode, &pSubPath);
719 iStart = i + 1;
720 CALLFUNC(gdi_plus_ext, GdipDrawPath, pGraphics, pPen, pSubPath);
721 CALLFUNC(gdi_plus_ext, GdipDeletePath, pSubPath);
722 }
723 }
724 }
725 CALLFUNC(gdi_plus_ext, GdipDeletePen, pPen);
726 }
727 if (pMatrix) {
728 CALLFUNC(gdi_plus_ext, GdipDeleteMatrix, pMatrix);
729 }
730 CALLFUNC(gdi_plus_ext, GdipDeletePath, pGpPath);
731 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
732 return true;
733}
fxcrt::ByteString ByteString
Definition bytestring.h:180
#define CALLFUNC(gdi_plus_ext, funcname,...)
#define CHECK_EQ(x, y)
Definition check_op.h:10
PlatformIface * GetPlatform() const
static CFX_GEModule * Get()
CFX_PointF Transform(const CFX_PointF &point) const
float GetXUnit() const
float GetYUnit() const
bool DrawPath(HDC hDC, const CFX_Path &path, const CFX_Matrix *pObject2Device, const CFX_GraphStateData *pGraphState, uint32_t fill_argb, uint32_t stroke_argb, const CFX_FillRenderOptions &fill_options)
bool StretchDIBits(HDC hDC, RetainPtr< const CFX_DIBBase > source, int dest_left, int dest_top, int dest_width, int dest_height)
ByteString & operator+=(const char *str)
#define UNSAFE_TODO(...)
CFX_PTemplate< float > CFX_PointF
FXDIB_Format
Definition fx_dib.h:21
#define CHECK(cvref)