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