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