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