• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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