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