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
36 namespace Gdiplus {
37 using std::max;
38 using std::min;
39 } // namespace Gdiplus
40
41 #include <gdiplus.h> // NOLINT
42
43 namespace {
44
45 enum {
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
82 LPCSTR 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 };
118 static_assert(std::size(g_GdipFuncNames) ==
119 static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
120 "g_GdipFuncNames has wrong size");
121
122 using FuncType_GdipCreatePath2 =
123 decltype(&Gdiplus::DllExports::GdipCreatePath2);
124 using FuncType_GdipSetPenDashArray =
125 decltype(&Gdiplus::DllExports::GdipSetPenDashArray);
126 using FuncType_GdipSetPenLineJoin =
127 decltype(&Gdiplus::DllExports::GdipSetPenLineJoin);
128 using FuncType_GdipCreateFromHDC =
129 decltype(&Gdiplus::DllExports::GdipCreateFromHDC);
130 using FuncType_GdipSetPageUnit =
131 decltype(&Gdiplus::DllExports::GdipSetPageUnit);
132 using FuncType_GdipSetSmoothingMode =
133 decltype(&Gdiplus::DllExports::GdipSetSmoothingMode);
134 using FuncType_GdipCreateSolidFill =
135 decltype(&Gdiplus::DllExports::GdipCreateSolidFill);
136 using FuncType_GdipFillPath = decltype(&Gdiplus::DllExports::GdipFillPath);
137 using FuncType_GdipDeleteBrush =
138 decltype(&Gdiplus::DllExports::GdipDeleteBrush);
139 using FuncType_GdipCreatePen1 = decltype(&Gdiplus::DllExports::GdipCreatePen1);
140 using FuncType_GdipSetPenMiterLimit =
141 decltype(&Gdiplus::DllExports::GdipSetPenMiterLimit);
142 using FuncType_GdipDrawPath = decltype(&Gdiplus::DllExports::GdipDrawPath);
143 using FuncType_GdipDeletePen = decltype(&Gdiplus::DllExports::GdipDeletePen);
144 using FuncType_GdipDeletePath = decltype(&Gdiplus::DllExports::GdipDeletePath);
145 using FuncType_GdipDeleteGraphics =
146 decltype(&Gdiplus::DllExports::GdipDeleteGraphics);
147 using FuncType_GdipDisposeImage =
148 decltype(&Gdiplus::DllExports::GdipDisposeImage);
149 using FuncType_GdipCreateBitmapFromScan0 =
150 decltype(&Gdiplus::DllExports::GdipCreateBitmapFromScan0);
151 using FuncType_GdipSetInterpolationMode =
152 decltype(&Gdiplus::DllExports::GdipSetInterpolationMode);
153 using FuncType_GdipDrawImagePointsI =
154 decltype(&Gdiplus::DllExports::GdipDrawImagePointsI);
155 using FuncType_GdiplusStartup = decltype(&Gdiplus::GdiplusStartup);
156 using FuncType_GdipDrawLineI = decltype(&Gdiplus::DllExports::GdipDrawLineI);
157 using FuncType_GdipCreatePath = decltype(&Gdiplus::DllExports::GdipCreatePath);
158 using FuncType_GdipSetPathFillMode =
159 decltype(&Gdiplus::DllExports::GdipSetPathFillMode);
160 using FuncType_GdipSetClipRegion =
161 decltype(&Gdiplus::DllExports::GdipSetClipRegion);
162 using FuncType_GdipWidenPath = decltype(&Gdiplus::DllExports::GdipWidenPath);
163 using FuncType_GdipAddPathLine =
164 decltype(&Gdiplus::DllExports::GdipAddPathLine);
165 using FuncType_GdipAddPathRectangle =
166 decltype(&Gdiplus::DllExports::GdipAddPathRectangle);
167 using FuncType_GdipDeleteRegion =
168 decltype(&Gdiplus::DllExports::GdipDeleteRegion);
169 using FuncType_GdipSetPenLineCap197819 =
170 decltype(&Gdiplus::DllExports::GdipSetPenLineCap197819);
171 using FuncType_GdipSetPenDashOffset =
172 decltype(&Gdiplus::DllExports::GdipSetPenDashOffset);
173 using FuncType_GdipCreateMatrix2 =
174 decltype(&Gdiplus::DllExports::GdipCreateMatrix2);
175 using FuncType_GdipDeleteMatrix =
176 decltype(&Gdiplus::DllExports::GdipDeleteMatrix);
177 using FuncType_GdipSetWorldTransform =
178 decltype(&Gdiplus::DllExports::GdipSetWorldTransform);
179 using 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
FillType2Gdip(CFX_FillRenderOptions::FillType fill_type)186 Gdiplus::GpFillMode FillType2Gdip(CFX_FillRenderOptions::FillType fill_type) {
187 return fill_type == CFX_FillRenderOptions::FillType::kEvenOdd
188 ? Gdiplus::FillModeAlternate
189 : Gdiplus::FillModeWinding;
190 }
191
GetGdiplusExt()192 const CGdiplusExt& GetGdiplusExt() {
193 auto* pData =
194 static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
195 return pData->m_GdiplusExt;
196 }
197
GdipCreateBrushImpl(DWORD argb)198 Gdiplus::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
OutputImage(Gdiplus::GpGraphics * graphics,RetainPtr<const CFX_DIBBase> source,int dest_left,int dest_top,int dest_width,int dest_height)205 void 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
GdipCreatePenImpl(const CFX_GraphStateData * pGraphState,const CFX_Matrix * pMatrix,DWORD argb,bool bTextMode)242 Gdiplus::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->line_width();
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->line_cap()) {
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->line_join()) {
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 const std::vector<float>& dash_array = pGraphState->dash_array();
290 if (!dash_array.empty()) {
291 float* gdi_dash_array =
292 FX_Alloc(float, FxAlignToBoundary<2>(dash_array.size()));
293 int nCount = 0;
294 float on_leftover = 0;
295 float off_leftover = 0;
296 for (size_t i = 0; i < dash_array.size(); i += 2) {
297 float on_phase = dash_array[i];
298 float off_phase =
299 i + 1 < dash_array.size() ? dash_array[i + 1] : on_phase;
300 on_phase /= width;
301 off_phase /= width;
302 if (on_phase + off_phase <= 0.00002f) {
303 on_phase = 0.1f;
304 off_phase = 0.1f;
305 }
306 if (bDashExtend) {
307 if (off_phase < 1)
308 off_phase = 0;
309 else
310 --off_phase;
311 ++on_phase;
312 }
313 if (on_phase == 0 || off_phase == 0) {
314 if (nCount == 0) {
315 on_leftover += on_phase;
316 off_leftover += off_phase;
317 } else {
318 UNSAFE_TODO({
319 gdi_dash_array[nCount - 2] += on_phase;
320 gdi_dash_array[nCount - 1] += off_phase;
321 });
322 }
323 } else {
324 UNSAFE_TODO({
325 gdi_dash_array[nCount++] = on_phase + on_leftover;
326 on_leftover = 0;
327 gdi_dash_array[nCount++] = off_phase + off_leftover;
328 off_leftover = 0;
329 });
330 }
331 }
332 CALLFUNC(gdi_plus_ext, GdipSetPenDashArray, pPen, gdi_dash_array, nCount);
333 float phase = pGraphState->dash_phase();
334 if (bDashExtend) {
335 if (phase < 0.5f) {
336 phase = 0;
337 } else {
338 phase -= 0.5f;
339 }
340 }
341 CALLFUNC(gdi_plus_ext, GdipSetPenDashOffset, pPen, phase);
342 FX_Free(gdi_dash_array);
343 gdi_dash_array = nullptr;
344 }
345 CALLFUNC(gdi_plus_ext, GdipSetPenMiterLimit, pPen,
346 pGraphState->miter_limit());
347 return pPen;
348 }
349
IsSmallTriangle(pdfium::span<const Gdiplus::PointF> points,const CFX_Matrix * pMatrix)350 std::optional<std::pair<size_t, size_t>> IsSmallTriangle(
351 pdfium::span<const Gdiplus::PointF> points,
352 const CFX_Matrix* pMatrix) {
353 static constexpr std::array<std::pair<const size_t, const size_t>, 3> kPairs =
354 {{{1, 2}, {0, 2}, {0, 1}}};
355 for (size_t i = 0; i < std::size(kPairs); ++i) {
356 size_t pair1 = kPairs[i].first;
357 size_t pair2 = kPairs[i].second;
358 CFX_PointF p1(points[pair1].X, points[pair1].Y);
359 CFX_PointF p2(points[pair2].X, points[pair2].Y);
360 if (pMatrix) {
361 p1 = pMatrix->Transform(p1);
362 p2 = pMatrix->Transform(p2);
363 }
364 CFX_PointF diff = p1 - p2;
365 float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
366 if (distance_square < 2.25f)
367 return std::make_pair(i, pair1);
368 }
369 return std::nullopt;
370 }
371
372 class GpStream final : public IStream {
373 public:
374 GpStream() = default;
375 ~GpStream() = default;
376
377 // IUnknown
QueryInterface(REFIID iid,void ** ppvObject)378 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
379 void** ppvObject) override {
380 if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
381 iid == __uuidof(ISequentialStream)) {
382 *ppvObject = static_cast<IStream*>(this);
383 AddRef();
384 return S_OK;
385 }
386 return E_NOINTERFACE;
387 }
AddRef()388 ULONG STDMETHODCALLTYPE AddRef() override {
389 return (ULONG)InterlockedIncrement(&m_RefCount);
390 }
Release()391 ULONG STDMETHODCALLTYPE Release() override {
392 ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
393 if (res == 0) {
394 delete this;
395 }
396 return res;
397 }
398
399 // ISequentialStream
Read(void * output,ULONG cb,ULONG * pcbRead)400 HRESULT STDMETHODCALLTYPE Read(void* output,
401 ULONG cb,
402 ULONG* pcbRead) override {
403 if (pcbRead)
404 *pcbRead = 0;
405
406 if (m_ReadPos >= m_InterStream.tellp())
407 return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
408
409 size_t bytes_left = pdfium::checked_cast<size_t>(
410 std::streamoff(m_InterStream.tellp()) - m_ReadPos);
411 size_t bytes_out = std::min(pdfium::checked_cast<size_t>(cb), bytes_left);
412 UNSAFE_TODO(FXSYS_memcpy(output, m_InterStream.str().c_str() + m_ReadPos,
413 bytes_out));
414 m_ReadPos += bytes_out;
415 if (pcbRead)
416 *pcbRead = (ULONG)bytes_out;
417
418 return S_OK;
419 }
Write(const void * input,ULONG cb,ULONG * pcbWritten)420 HRESULT STDMETHODCALLTYPE Write(const void* input,
421 ULONG cb,
422 ULONG* pcbWritten) override {
423 if (cb <= 0) {
424 if (pcbWritten)
425 *pcbWritten = 0;
426 return S_OK;
427 }
428 m_InterStream.write(reinterpret_cast<const char*>(input), cb);
429 if (pcbWritten)
430 *pcbWritten = cb;
431 return S_OK;
432 }
433
434 // IStream
SetSize(ULARGE_INTEGER)435 HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
436 return E_NOTIMPL;
437 }
CopyTo(IStream *,ULARGE_INTEGER,ULARGE_INTEGER *,ULARGE_INTEGER *)438 HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
439 ULARGE_INTEGER,
440 ULARGE_INTEGER*,
441 ULARGE_INTEGER*) override {
442 return E_NOTIMPL;
443 }
Commit(DWORD)444 HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
Revert()445 HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
LockRegion(ULARGE_INTEGER,ULARGE_INTEGER,DWORD)446 HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
447 ULARGE_INTEGER,
448 DWORD) override {
449 return E_NOTIMPL;
450 }
UnlockRegion(ULARGE_INTEGER,ULARGE_INTEGER,DWORD)451 HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
452 ULARGE_INTEGER,
453 DWORD) override {
454 return E_NOTIMPL;
455 }
Clone(IStream ** stream)456 HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
457 return E_NOTIMPL;
458 }
Seek(LARGE_INTEGER liDistanceToMove,DWORD dwOrigin,ULARGE_INTEGER * lpNewFilePointer)459 HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
460 DWORD dwOrigin,
461 ULARGE_INTEGER* lpNewFilePointer) override {
462 std::streamoff start;
463 std::streamoff new_read_position;
464 switch (dwOrigin) {
465 case STREAM_SEEK_SET:
466 start = 0;
467 break;
468 case STREAM_SEEK_CUR:
469 start = m_ReadPos;
470 break;
471 case STREAM_SEEK_END:
472 if (m_InterStream.tellp() < 0)
473 return STG_E_SEEKERROR;
474 start = m_InterStream.tellp();
475 break;
476 default:
477 return STG_E_INVALIDFUNCTION;
478 }
479 new_read_position = start + liDistanceToMove.QuadPart;
480 if (new_read_position > m_InterStream.tellp())
481 return STG_E_SEEKERROR;
482
483 m_ReadPos = new_read_position;
484 if (lpNewFilePointer)
485 lpNewFilePointer->QuadPart = m_ReadPos;
486
487 return S_OK;
488 }
Stat(STATSTG * pStatstg,DWORD grfStatFlag)489 HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
490 DWORD grfStatFlag) override {
491 if (!pStatstg)
492 return STG_E_INVALIDFUNCTION;
493
494 ZeroMemory(pStatstg, sizeof(STATSTG));
495
496 if (m_InterStream.tellp() < 0)
497 return STG_E_SEEKERROR;
498
499 pStatstg->cbSize.QuadPart = m_InterStream.tellp();
500 return S_OK;
501 }
502
503 private:
504 LONG m_RefCount = 1;
505 std::streamoff m_ReadPos = 0;
506 fxcrt::ostringstream m_InterStream;
507 };
508
509 } // namespace
510
511 CGdiplusExt::CGdiplusExt() = default;
512
~CGdiplusExt()513 CGdiplusExt::~CGdiplusExt() {
514 FreeLibrary(gdiplus_module_);
515 }
516
Load()517 void CGdiplusExt::Load() {
518 char buf[MAX_PATH];
519 GetSystemDirectoryA(buf, MAX_PATH);
520 ByteString dllpath = buf;
521 dllpath += "\\GDIPLUS.DLL";
522 gdiplus_module_ = LoadLibraryA(dllpath.c_str());
523 if (!gdiplus_module_) {
524 return;
525 }
526
527 gdiplus_functions_.resize(std::size(g_GdipFuncNames));
528 UNSAFE_TODO({
529 for (size_t i = 0; i < std::size(g_GdipFuncNames); ++i) {
530 gdiplus_functions_[i] =
531 GetProcAddress(gdiplus_module_, g_GdipFuncNames[i]);
532 if (!gdiplus_functions_[i]) {
533 FreeLibrary(gdiplus_module_);
534 gdiplus_module_ = nullptr;
535 return;
536 }
537 }
538 });
539 ULONG_PTR gdiplus_token;
540 Gdiplus::GdiplusStartupInput gdiplus_startup_input;
541 ((FuncType_GdiplusStartup)gdiplus_functions_[FuncId_GdiplusStartup])(
542 &gdiplus_token, &gdiplus_startup_input, nullptr);
543 }
544
StretchDIBits(HDC hDC,RetainPtr<const CFX_DIBBase> source,int dest_left,int dest_top,int dest_width,int dest_height)545 bool CGdiplusExt::StretchDIBits(HDC hDC,
546 RetainPtr<const CFX_DIBBase> source,
547 int dest_left,
548 int dest_top,
549 int dest_width,
550 int dest_height) {
551 CHECK(source->IsAlphaFormat());
552 Gdiplus::GpGraphics* pGraphics;
553 const CGdiplusExt& gdi_plus_ext = GetGdiplusExt();
554 CALLFUNC(gdi_plus_ext, GdipCreateFromHDC, hDC, &pGraphics);
555 CALLFUNC(gdi_plus_ext, GdipSetPageUnit, pGraphics, Gdiplus::UnitPixel);
556 if (source->GetWidth() > abs(dest_width) / 2 ||
557 source->GetHeight() > abs(dest_height) / 2) {
558 CALLFUNC(gdi_plus_ext, GdipSetInterpolationMode, pGraphics,
559 Gdiplus::InterpolationModeHighQuality);
560 } else {
561 CALLFUNC(gdi_plus_ext, GdipSetInterpolationMode, pGraphics,
562 Gdiplus::InterpolationModeBilinear);
563 }
564 OutputImage(pGraphics, std::move(source), dest_left, dest_top, dest_width,
565 dest_height);
566 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
567 return true;
568 }
569
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)570 bool CGdiplusExt::DrawPath(HDC hDC,
571 const CFX_Path& path,
572 const CFX_Matrix* pObject2Device,
573 const CFX_GraphStateData* pGraphState,
574 uint32_t fill_argb,
575 uint32_t stroke_argb,
576 const CFX_FillRenderOptions& fill_options) {
577 pdfium::span<const CFX_Path::Point> points = path.GetPoints();
578 if (points.empty())
579 return true;
580
581 Gdiplus::GpGraphics* pGraphics = nullptr;
582 const CGdiplusExt& gdi_plus_ext = GetGdiplusExt();
583 CALLFUNC(gdi_plus_ext, GdipCreateFromHDC, hDC, &pGraphics);
584 CALLFUNC(gdi_plus_ext, GdipSetPageUnit, pGraphics, Gdiplus::UnitPixel);
585 CALLFUNC(gdi_plus_ext, GdipSetPixelOffsetMode, pGraphics,
586 Gdiplus::PixelOffsetModeHalf);
587 Gdiplus::GpMatrix* pMatrix = nullptr;
588 if (pObject2Device) {
589 CALLFUNC(gdi_plus_ext, GdipCreateMatrix2, pObject2Device->a,
590 pObject2Device->b, pObject2Device->c, pObject2Device->d,
591 pObject2Device->e, pObject2Device->f, &pMatrix);
592 CALLFUNC(gdi_plus_ext, GdipSetWorldTransform, pGraphics, pMatrix);
593 }
594 std::vector<Gdiplus::PointF> gp_points(points.size());
595 std::vector<BYTE> gp_types(points.size());
596 int nSubPathes = 0;
597 bool bSubClose = false;
598 bool bSmooth = false;
599 size_t pos_subclose = 0;
600 size_t startpoint = 0;
601 for (size_t i = 0; i < points.size(); ++i) {
602 gp_points[i].X = points[i].m_Point.x;
603 gp_points[i].Y = points[i].m_Point.y;
604
605 CFX_PointF pos = points[i].m_Point;
606 if (pObject2Device)
607 pos = pObject2Device->Transform(pos);
608
609 if (pos.x > 50000.0f)
610 gp_points[i].X = 50000.0f;
611 if (pos.x < -50000.0f)
612 gp_points[i].X = -50000.0f;
613 if (pos.y > 50000.0f)
614 gp_points[i].Y = 50000.0f;
615 if (pos.y < -50000.0f)
616 gp_points[i].Y = -50000.0f;
617
618 CFX_Path::Point::Type point_type = points[i].m_Type;
619 if (point_type == CFX_Path::Point::Type::kMove) {
620 gp_types[i] = Gdiplus::PathPointTypeStart;
621 nSubPathes++;
622 bSubClose = false;
623 startpoint = i;
624 } else if (point_type == CFX_Path::Point::Type::kLine) {
625 gp_types[i] = Gdiplus::PathPointTypeLine;
626 if (points[i - 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove) &&
627 (i + 1 == points.size() ||
628 points[i + 1].IsTypeAndOpen(CFX_Path::Point::Type::kMove)) &&
629 gp_points[i].Y == gp_points[i - 1].Y &&
630 gp_points[i].X == gp_points[i - 1].X) {
631 gp_points[i].X += 0.01f;
632 continue;
633 }
634 if (!bSmooth && gp_points[i].X != gp_points[i - 1].X &&
635 gp_points[i].Y != gp_points[i - 1].Y) {
636 bSmooth = true;
637 }
638 } else if (point_type == CFX_Path::Point::Type::kBezier) {
639 gp_types[i] = Gdiplus::PathPointTypeBezier;
640 bSmooth = true;
641 }
642 if (points[i].m_CloseFigure) {
643 if (bSubClose)
644 gp_types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
645 else
646 bSubClose = true;
647 pos_subclose = i;
648 gp_types[i] |= Gdiplus::PathPointTypeCloseSubpath;
649 if (!bSmooth && gp_points[i].X != gp_points[startpoint].X &&
650 gp_points[i].Y != gp_points[startpoint].Y) {
651 bSmooth = true;
652 }
653 }
654 }
655 const bool fill =
656 fill_options.fill_type != CFX_FillRenderOptions::FillType::kNoFill;
657 if (fill_options.aliased_path) {
658 bSmooth = false;
659 CALLFUNC(gdi_plus_ext, GdipSetSmoothingMode, pGraphics,
660 Gdiplus::SmoothingModeNone);
661 } else if (!fill_options.full_cover) {
662 if (!bSmooth && fill)
663 bSmooth = true;
664
665 if (bSmooth || (pGraphState && pGraphState->line_width() > 2)) {
666 CALLFUNC(gdi_plus_ext, GdipSetSmoothingMode, pGraphics,
667 Gdiplus::SmoothingModeAntiAlias);
668 }
669 }
670 if (points.size() == 4 && !pGraphState) {
671 auto indices = IsSmallTriangle(gp_points, pObject2Device);
672 if (indices.has_value()) {
673 auto [v1, v2] = indices.value();
674 Gdiplus::GpPen* pPen = nullptr;
675 CALLFUNC(gdi_plus_ext, GdipCreatePen1, fill_argb, 1.0f,
676 Gdiplus::UnitPixel, &pPen);
677 CALLFUNC(gdi_plus_ext, GdipDrawLineI, pGraphics, pPen,
678 FXSYS_roundf(gp_points[v1].X), FXSYS_roundf(gp_points[v1].Y),
679 FXSYS_roundf(gp_points[v2].X), FXSYS_roundf(gp_points[v2].Y));
680 CALLFUNC(gdi_plus_ext, GdipDeletePen, pPen);
681 return true;
682 }
683 }
684 Gdiplus::GpPath* pGpPath = nullptr;
685 const Gdiplus::GpFillMode gp_fill_mode =
686 FillType2Gdip(fill_options.fill_type);
687 CALLFUNC(gdi_plus_ext, GdipCreatePath2, gp_points.data(), gp_types.data(),
688 pdfium::checked_cast<int>(points.size()), gp_fill_mode, &pGpPath);
689 if (!pGpPath) {
690 if (pMatrix) {
691 CALLFUNC(gdi_plus_ext, GdipDeleteMatrix, pMatrix);
692 }
693
694 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
695 return false;
696 }
697 if (fill) {
698 Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb);
699 CALLFUNC(gdi_plus_ext, GdipSetPathFillMode, pGpPath, gp_fill_mode);
700 CALLFUNC(gdi_plus_ext, GdipFillPath, pGraphics, pBrush, pGpPath);
701 CALLFUNC(gdi_plus_ext, GdipDeleteBrush, pBrush);
702 }
703 if (pGraphState && stroke_argb) {
704 Gdiplus::GpPen* pPen =
705 GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb,
706 fill_options.stroke_text_mode);
707 if (nSubPathes == 1) {
708 CALLFUNC(gdi_plus_ext, GdipDrawPath, pGraphics, pPen, pGpPath);
709 } else {
710 size_t iStart = 0;
711 for (size_t i = 0; i < points.size(); ++i) {
712 if (i + 1 == points.size() ||
713 gp_types[i + 1] == Gdiplus::PathPointTypeStart) {
714 Gdiplus::GpPath* pSubPath;
715 CALLFUNC(gdi_plus_ext, GdipCreatePath2, &gp_points[iStart],
716 &gp_types[iStart], pdfium::checked_cast<int>(i - iStart + 1),
717 gp_fill_mode, &pSubPath);
718 iStart = i + 1;
719 CALLFUNC(gdi_plus_ext, GdipDrawPath, pGraphics, pPen, pSubPath);
720 CALLFUNC(gdi_plus_ext, GdipDeletePath, pSubPath);
721 }
722 }
723 }
724 CALLFUNC(gdi_plus_ext, GdipDeletePen, pPen);
725 }
726 if (pMatrix) {
727 CALLFUNC(gdi_plus_ext, GdipDeleteMatrix, pMatrix);
728 }
729 CALLFUNC(gdi_plus_ext, GdipDeletePath, pGpPath);
730 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
731 return true;
732 }
733