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