7#include "core/fxge/win32/cgdi_plus_ext.h"
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"
46 FuncId_GdipCreatePath2,
47 FuncId_GdipSetPenDashArray,
48 FuncId_GdipSetPenLineJoin,
49 FuncId_GdipCreateFromHDC,
50 FuncId_GdipSetPageUnit,
51 FuncId_GdipSetSmoothingMode,
52 FuncId_GdipCreateSolidFill,
54 FuncId_GdipDeleteBrush,
55 FuncId_GdipCreatePen1,
56 FuncId_GdipSetPenMiterLimit,
59 FuncId_GdipDeletePath,
60 FuncId_GdipDeleteGraphics,
61 FuncId_GdipDisposeImage,
62 FuncId_GdipCreateBitmapFromScan0,
63 FuncId_GdipSetInterpolationMode,
64 FuncId_GdipDrawImagePointsI,
65 FuncId_GdiplusStartup,
67 FuncId_GdipCreatePath,
68 FuncId_GdipSetPathFillMode,
69 FuncId_GdipSetClipRegion,
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,
82LPCSTR g_GdipFuncNames[] = {
84 "GdipSetPenDashArray",
88 "GdipSetSmoothingMode",
89 "GdipCreateSolidFill",
93 "GdipSetPenMiterLimit",
99 "GdipCreateBitmapFromScan0",
100 "GdipSetInterpolationMode",
101 "GdipDrawImagePointsI",
105 "GdipSetPathFillMode",
109 "GdipAddPathRectangle",
111 "GdipSetPenLineCap197819",
112 "GdipSetPenDashOffset",
115 "GdipSetWorldTransform",
116 "GdipSetPixelOffsetMode",
118static_assert(
std::size(g_GdipFuncNames) ==
119 static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
120 "g_GdipFuncNames has wrong size");
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);
182#define CALLFUNC(gdi_plus_ext, funcname, ...)
183 reinterpret_cast<FuncType_##funcname>(
184 (gdi_plus_ext).functions()[FuncId_##funcname])(__VA_ARGS__)
187 return fill_type == CFX_FillRenderOptions::FillType::kEvenOdd
188 ? Gdiplus::FillModeAlternate
189 : Gdiplus::FillModeWinding;
195 return pData->m_GdiplusExt;
198Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) {
199 Gdiplus::GpSolidFill* solidBrush =
nullptr;
200 CALLFUNC(GetGdiplusExt(), GdipCreateSolidFill,
201 static_cast<Gdiplus::ARGB>(argb), &solidBrush);
205void OutputImage(Gdiplus::GpGraphics* graphics,
214 RetainPtr<
const CFX_DIBitmap> realized_source = source->RealizeIfNeeded();
215 if (!realized_source) {
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) {
230 if (dest_width < 0) {
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);
252 width =
std::max(width, unit);
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;
261 case CFX_GraphStateData::LineCap::kButt:
262 lineCap = Gdiplus::LineCapFlat;
264 case CFX_GraphStateData::LineCap::kRound:
265 lineCap = Gdiplus::LineCapRound;
266 dashCap = Gdiplus::DashCapRound;
269 case CFX_GraphStateData::LineCap::kSquare:
270 lineCap = Gdiplus::LineCapSquare;
274 CALLFUNC(gdi_plus_ext, GdipSetPenLineCap197819, pPen, lineCap, lineCap,
276 Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped;
278 case CFX_GraphStateData::LineJoin::kMiter:
279 lineJoin = Gdiplus::LineJoinMiterClipped;
281 case CFX_GraphStateData::LineJoin::kRound:
282 lineJoin = Gdiplus::LineJoinRound;
284 case CFX_GraphStateData::LineJoin::kBevel:
285 lineJoin = Gdiplus::LineJoinBevel;
288 CALLFUNC(gdi_plus_ext, GdipSetPenLineJoin, pPen, lineJoin);
289 if (!pGraphState->m_DashArray.empty()) {
291 FX_Alloc(
float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
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];
298 if (i + 1 < pGraphState->m_DashArray.size()) {
299 off_phase = pGraphState->m_DashArray[i + 1];
301 off_phase = on_phase;
305 if (on_phase + off_phase <= 0.00002f) {
316 if (on_phase == 0 || off_phase == 0) {
318 on_leftover += on_phase;
319 off_leftover += off_phase;
322 pDashArray[nCount - 2] += on_phase;
323 pDashArray[nCount - 1] += off_phase;
328 pDashArray[nCount++] = on_phase + on_leftover;
330 pDashArray[nCount++] = off_phase + off_leftover;
335 CALLFUNC(gdi_plus_ext, GdipSetPenDashArray, pPen, pDashArray, nCount);
343 CALLFUNC(gdi_plus_ext, GdipSetPenDashOffset, pPen, phase);
345 pDashArray =
nullptr;
351std::optional<std::pair<size_t, size_t>> IsSmallTriangle(
352 pdfium::span<
const Gdiplus::PointF> points,
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);
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);
373class GpStream
final :
public IStream {
375 GpStream() =
default;
376 ~GpStream() =
default;
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);
387 return E_NOINTERFACE;
389 ULONG STDMETHODCALLTYPE AddRef() override {
390 return (ULONG)InterlockedIncrement(&m_RefCount);
392 ULONG STDMETHODCALLTYPE Release() override {
393 ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
401 HRESULT STDMETHODCALLTYPE Read(
void* output,
403 ULONG* pcbRead) override {
407 if (m_ReadPos >= m_InterStream.tellp())
408 return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
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,
415 m_ReadPos += bytes_out;
417 *pcbRead = (ULONG)bytes_out;
421 HRESULT STDMETHODCALLTYPE Write(
const void* input,
423 ULONG* pcbWritten) override {
429 m_InterStream.write(
reinterpret_cast<
const char*>(input), cb);
436 HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
439 HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
442 ULARGE_INTEGER*) override {
445 HRESULT STDMETHODCALLTYPE Commit(DWORD) override {
return E_NOTIMPL; }
446 HRESULT STDMETHODCALLTYPE Revert() override {
return E_NOTIMPL; }
447 HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
452 HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
457 HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
460 HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
462 ULARGE_INTEGER* lpNewFilePointer) override {
463 std::streamoff start;
464 std::streamoff new_read_position;
466 case STREAM_SEEK_SET:
469 case STREAM_SEEK_CUR:
472 case STREAM_SEEK_END:
473 if (m_InterStream.tellp() < 0)
474 return STG_E_SEEKERROR;
475 start = m_InterStream.tellp();
478 return STG_E_INVALIDFUNCTION;
480 new_read_position = start + liDistanceToMove.QuadPart;
481 if (new_read_position > m_InterStream.tellp())
482 return STG_E_SEEKERROR;
484 m_ReadPos = new_read_position;
485 if (lpNewFilePointer)
486 lpNewFilePointer->QuadPart = m_ReadPos;
490 HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
491 DWORD grfStatFlag) override {
493 return STG_E_INVALIDFUNCTION;
495 ZeroMemory(pStatstg,
sizeof(STATSTG));
497 if (m_InterStream.tellp() < 0)
498 return STG_E_SEEKERROR;
500 pStatstg->cbSize.QuadPart = m_InterStream.tellp();
506 std::streamoff m_ReadPos = 0;
507 fxcrt::ostringstream m_InterStream;
515 FreeLibrary(gdiplus_module_);
520 GetSystemDirectoryA(buf, MAX_PATH);
522 dllpath
+= "\\GDIPLUS.DLL";
523 gdiplus_module_ = LoadLibraryA(dllpath.c_str());
524 if (!gdiplus_module_) {
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;
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);
552 CHECK(source->IsAlphaFormat());
553 Gdiplus::GpGraphics* pGraphics;
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);
562 CALLFUNC(gdi_plus_ext, GdipSetInterpolationMode, pGraphics,
563 Gdiplus::InterpolationModeBilinear);
565 OutputImage(pGraphics,
std::move(source), dest_left, dest_top, dest_width,
567 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
576 uint32_t stroke_argb,
578 pdfium::span<
const CFX_Path::Point> points = path.GetPoints();
582 Gdiplus::GpGraphics* pGraphics =
nullptr;
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);
595 std::vector<Gdiplus::PointF> gp_points(points.size());
596 std::vector<BYTE> gp_types(points.size());
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;
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;
621 gp_types[i] = Gdiplus::PathPointTypeStart;
626 gp_types[i] = Gdiplus::PathPointTypeLine;
628 (i + 1 == points.size() ||
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;
635 if (!bSmooth && gp_points[i].X != gp_points[i - 1].X &&
636 gp_points[i].Y != gp_points[i - 1].Y) {
640 gp_types[i] = Gdiplus::PathPointTypeBezier;
643 if (points[i].m_CloseFigure) {
645 gp_types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
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) {
660 CALLFUNC(gdi_plus_ext, GdipSetSmoothingMode, pGraphics,
661 Gdiplus::SmoothingModeNone);
663 if (!bSmooth && fill)
666 if (bSmooth || (pGraphState && pGraphState
->m_LineWidth > 2)) {
667 CALLFUNC(gdi_plus_ext, GdipSetSmoothingMode, pGraphics,
668 Gdiplus::SmoothingModeAntiAlias);
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);
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);
692 CALLFUNC(gdi_plus_ext, GdipDeleteMatrix, pMatrix);
695 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
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);
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);
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);
720 CALLFUNC(gdi_plus_ext, GdipDrawPath, pGraphics, pPen, pSubPath);
721 CALLFUNC(gdi_plus_ext, GdipDeletePath, pSubPath);
725 CALLFUNC(gdi_plus_ext, GdipDeletePen, pPen);
728 CALLFUNC(gdi_plus_ext, GdipDeleteMatrix, pMatrix);
730 CALLFUNC(gdi_plus_ext, GdipDeletePath, pGpPath);
731 CALLFUNC(gdi_plus_ext, GdipDeleteGraphics, pGraphics);
fxcrt::ByteString ByteString
#define CALLFUNC(gdi_plus_ext, funcname,...)
PlatformIface * GetPlatform() const
static CFX_GEModule * Get()
CFX_PointF Transform(const CFX_PointF &point) 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)
CFX_PTemplate< float > CFX_PointF