1 // Copyright 2014 PDFium Authors. All rights reserved.
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 <windows.h>
8
9 #include <objidl.h>
10
11 #include <algorithm>
12 #include <memory>
13 #include <sstream>
14 #include <utility>
15 #include <vector>
16
17 #include "core/fxcrt/fx_system.h"
18 #include "core/fxge/cfx_gemodule.h"
19 #include "core/fxge/cfx_graphstatedata.h"
20 #include "core/fxge/cfx_pathdata.h"
21 #include "core/fxge/win32/cfx_windowsdib.h"
22 #include "core/fxge/win32/win32_int.h"
23 #include "third_party/base/span.h"
24
25 // Has to come before gdiplus.h
26 namespace Gdiplus {
27 using std::max;
28 using std::min;
29 } // namespace Gdiplus
30
31 #include <gdiplus.h> // NOLINT
32
33 namespace {
34
35 enum {
36 FuncId_GdipCreatePath2,
37 FuncId_GdipSetPenDashArray,
38 FuncId_GdipSetPenLineJoin,
39 FuncId_GdipCreateFromHDC,
40 FuncId_GdipSetPageUnit,
41 FuncId_GdipSetSmoothingMode,
42 FuncId_GdipCreateSolidFill,
43 FuncId_GdipFillPath,
44 FuncId_GdipDeleteBrush,
45 FuncId_GdipCreatePen1,
46 FuncId_GdipSetPenMiterLimit,
47 FuncId_GdipDrawPath,
48 FuncId_GdipDeletePen,
49 FuncId_GdipDeletePath,
50 FuncId_GdipDeleteGraphics,
51 FuncId_GdipCreateBitmapFromFileICM,
52 FuncId_GdipCreateBitmapFromStreamICM,
53 FuncId_GdipGetImageHeight,
54 FuncId_GdipGetImageWidth,
55 FuncId_GdipGetImagePixelFormat,
56 FuncId_GdipBitmapLockBits,
57 FuncId_GdipGetImagePaletteSize,
58 FuncId_GdipGetImagePalette,
59 FuncId_GdipBitmapUnlockBits,
60 FuncId_GdipDisposeImage,
61 FuncId_GdipCreateBitmapFromScan0,
62 FuncId_GdipSetImagePalette,
63 FuncId_GdipSetInterpolationMode,
64 FuncId_GdipDrawImagePointsI,
65 FuncId_GdiplusStartup,
66 FuncId_GdipDrawLineI,
67 FuncId_GdipCreatePath,
68 FuncId_GdipSetPathFillMode,
69 FuncId_GdipSetClipRegion,
70 FuncId_GdipWidenPath,
71 FuncId_GdipAddPathLine,
72 FuncId_GdipAddPathRectangle,
73 FuncId_GdipDeleteRegion,
74 FuncId_GdipSetPenLineCap197819,
75 FuncId_GdipSetPenDashOffset,
76 FuncId_GdipCreateMatrix2,
77 FuncId_GdipDeleteMatrix,
78 FuncId_GdipSetWorldTransform,
79 FuncId_GdipSetPixelOffsetMode,
80 };
81
82 LPCSTR g_GdipFuncNames[] = {
83 "GdipCreatePath2",
84 "GdipSetPenDashArray",
85 "GdipSetPenLineJoin",
86 "GdipCreateFromHDC",
87 "GdipSetPageUnit",
88 "GdipSetSmoothingMode",
89 "GdipCreateSolidFill",
90 "GdipFillPath",
91 "GdipDeleteBrush",
92 "GdipCreatePen1",
93 "GdipSetPenMiterLimit",
94 "GdipDrawPath",
95 "GdipDeletePen",
96 "GdipDeletePath",
97 "GdipDeleteGraphics",
98 "GdipCreateBitmapFromFileICM",
99 "GdipCreateBitmapFromStreamICM",
100 "GdipGetImageHeight",
101 "GdipGetImageWidth",
102 "GdipGetImagePixelFormat",
103 "GdipBitmapLockBits",
104 "GdipGetImagePaletteSize",
105 "GdipGetImagePalette",
106 "GdipBitmapUnlockBits",
107 "GdipDisposeImage",
108 "GdipCreateBitmapFromScan0",
109 "GdipSetImagePalette",
110 "GdipSetInterpolationMode",
111 "GdipDrawImagePointsI",
112 "GdiplusStartup",
113 "GdipDrawLineI",
114 "GdipCreatePath",
115 "GdipSetPathFillMode",
116 "GdipSetClipRegion",
117 "GdipWidenPath",
118 "GdipAddPathLine",
119 "GdipAddPathRectangle",
120 "GdipDeleteRegion",
121 "GdipSetPenLineCap197819",
122 "GdipSetPenDashOffset",
123 "GdipCreateMatrix2",
124 "GdipDeleteMatrix",
125 "GdipSetWorldTransform",
126 "GdipSetPixelOffsetMode",
127 };
128 static_assert(FX_ArraySize(g_GdipFuncNames) ==
129 static_cast<size_t>(FuncId_GdipSetPixelOffsetMode) + 1,
130 "g_GdipFuncNames has wrong size");
131
132 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)(
133 GDIPCONST Gdiplus::GpPointF*,
134 GDIPCONST BYTE*,
135 INT,
136 Gdiplus::GpFillMode,
137 Gdiplus::GpPath** path);
138 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)(
139 Gdiplus::GpPen* pen,
140 GDIPCONST Gdiplus::REAL* dash,
141 INT count);
142 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)(
143 Gdiplus::GpPen* pen,
144 Gdiplus::GpLineJoin lineJoin);
145 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)(
146 HDC hdc,
147 Gdiplus::GpGraphics** graphics);
148 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)(
149 Gdiplus::GpGraphics* graphics,
150 Gdiplus::GpUnit unit);
151 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)(
152 Gdiplus::GpGraphics* graphics,
153 Gdiplus::SmoothingMode smoothingMode);
154 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)(
155 Gdiplus::ARGB color,
156 Gdiplus::GpSolidFill** brush);
157 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipFillPath)(
158 Gdiplus::GpGraphics* graphics,
159 Gdiplus::GpBrush* brush,
160 Gdiplus::GpPath* path);
161 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)(
162 Gdiplus::GpBrush* brush);
163 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)(
164 Gdiplus::ARGB color,
165 Gdiplus::REAL width,
166 Gdiplus::GpUnit unit,
167 Gdiplus::GpPen** pen);
168 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)(
169 Gdiplus::GpPen* pen,
170 Gdiplus::REAL miterLimit);
171 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)(
172 Gdiplus::GpGraphics* graphics,
173 Gdiplus::GpPen* pen,
174 Gdiplus::GpPath* path);
175 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)(
176 Gdiplus::GpPen* pen);
177 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)(
178 Gdiplus::GpPath* path);
179 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)(
180 Gdiplus::GpGraphics* graphics);
181 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)(
182 GDIPCONST WCHAR* filename,
183 Gdiplus::GpBitmap** bitmap);
184 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)(
185 IStream* stream,
186 Gdiplus::GpBitmap** bitmap);
187 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)(
188 Gdiplus::GpImage* image,
189 UINT* width);
190 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)(
191 Gdiplus::GpImage* image,
192 UINT* height);
193 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)(
194 Gdiplus::GpImage* image,
195 Gdiplus::PixelFormat* format);
196 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)(
197 Gdiplus::GpBitmap* bitmap,
198 GDIPCONST Gdiplus::GpRect* rect,
199 UINT flags,
200 Gdiplus::PixelFormat format,
201 Gdiplus::BitmapData* lockedBitmapData);
202 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)(
203 Gdiplus::GpImage* image,
204 Gdiplus::ColorPalette* palette,
205 INT size);
206 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)(
207 Gdiplus::GpImage* image,
208 INT* size);
209 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)(
210 Gdiplus::GpBitmap* bitmap,
211 Gdiplus::BitmapData* lockedBitmapData);
212 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)(
213 Gdiplus::GpImage* image);
214 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)(
215 INT width,
216 INT height,
217 INT stride,
218 Gdiplus::PixelFormat format,
219 BYTE* scan0,
220 Gdiplus::GpBitmap** bitmap);
221 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)(
222 Gdiplus::GpImage* image,
223 GDIPCONST Gdiplus::ColorPalette* palette);
224 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)(
225 Gdiplus::GpGraphics* graphics,
226 Gdiplus::InterpolationMode interpolationMode);
227 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)(
228 Gdiplus::GpGraphics* graphics,
229 Gdiplus::GpImage* image,
230 GDIPCONST Gdiplus::GpPoint* dstpoints,
231 INT count);
232 typedef Gdiplus::Status(WINAPI* FuncType_GdiplusStartup)(
233 OUT uintptr_t* token,
234 const Gdiplus::GdiplusStartupInput* input,
235 OUT Gdiplus::GdiplusStartupOutput* output);
236 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)(
237 Gdiplus::GpGraphics* graphics,
238 Gdiplus::GpPen* pen,
239 int x1,
240 int y1,
241 int x2,
242 int y2);
243 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)(
244 Gdiplus::GpFillMode brushMode,
245 Gdiplus::GpPath** path);
246 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)(
247 Gdiplus::GpPath* path,
248 Gdiplus::GpFillMode fillmode);
249 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)(
250 Gdiplus::GpGraphics* graphics,
251 Gdiplus::GpRegion* region,
252 Gdiplus::CombineMode combineMode);
253 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)(
254 Gdiplus::GpPath* nativePath,
255 Gdiplus::GpPen* pen,
256 Gdiplus::GpMatrix* matrix,
257 Gdiplus::REAL flatness);
258 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)(
259 Gdiplus::GpPath* path,
260 Gdiplus::REAL x1,
261 Gdiplus::REAL y1,
262 Gdiplus::REAL x2,
263 Gdiplus::REAL y2);
264 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)(
265 Gdiplus::GpPath* path,
266 Gdiplus::REAL x,
267 Gdiplus::REAL y,
268 Gdiplus::REAL width,
269 Gdiplus::REAL height);
270 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)(
271 Gdiplus::GpRegion* region);
272 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)(
273 Gdiplus::GpPen* pen,
274 Gdiplus::GpLineCap startCap,
275 Gdiplus::GpLineCap endCap,
276 Gdiplus::GpDashCap dashCap);
277 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)(
278 Gdiplus::GpPen* pen,
279 Gdiplus::REAL offset);
280 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)(
281 Gdiplus::REAL m11,
282 Gdiplus::REAL m12,
283 Gdiplus::REAL m21,
284 Gdiplus::REAL m22,
285 Gdiplus::REAL dx,
286 Gdiplus::REAL dy,
287 Gdiplus::GpMatrix** matrix);
288 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)(
289 Gdiplus::GpMatrix* matrix);
290 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)(
291 Gdiplus::GpGraphics* graphics,
292 Gdiplus::GpMatrix* matrix);
293 typedef Gdiplus::GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)(
294 Gdiplus::GpGraphics* graphics,
295 Gdiplus::PixelOffsetMode pixelOffsetMode);
296 #define CallFunc(funcname) \
297 reinterpret_cast<FuncType_##funcname>( \
298 GdiplusExt.m_Functions[FuncId_##funcname])
299
GdiFillType2Gdip(int fill_type)300 Gdiplus::GpFillMode GdiFillType2Gdip(int fill_type) {
301 return fill_type == ALTERNATE ? Gdiplus::FillModeAlternate
302 : Gdiplus::FillModeWinding;
303 }
304
GetGdiplusExt()305 const CGdiplusExt& GetGdiplusExt() {
306 auto* pData =
307 static_cast<CWin32Platform*>(CFX_GEModule::Get()->GetPlatform());
308 return pData->m_GdiplusExt;
309 }
310
GdipCreateBrushImpl(DWORD argb)311 Gdiplus::GpBrush* GdipCreateBrushImpl(DWORD argb) {
312 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
313 Gdiplus::GpSolidFill* solidBrush = nullptr;
314 CallFunc(GdipCreateSolidFill)((Gdiplus::ARGB)argb, &solidBrush);
315 return solidBrush;
316 }
317
OutputImage(Gdiplus::GpGraphics * pGraphics,const RetainPtr<CFX_DIBitmap> & pBitmap,const FX_RECT * pSrcRect,int dest_left,int dest_top,int dest_width,int dest_height)318 void OutputImage(Gdiplus::GpGraphics* pGraphics,
319 const RetainPtr<CFX_DIBitmap>& pBitmap,
320 const FX_RECT* pSrcRect,
321 int dest_left,
322 int dest_top,
323 int dest_width,
324 int dest_height) {
325 int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
326 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
327 if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
328 FX_RECT new_rect(0, 0, src_width, src_height);
329 RetainPtr<CFX_DIBitmap> pCloned = pBitmap->Clone(pSrcRect);
330 if (!pCloned)
331 return;
332 OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width,
333 dest_height);
334 return;
335 }
336 int src_pitch = pBitmap->GetPitch();
337 uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch +
338 pBitmap->GetBPP() * pSrcRect->left / 8;
339 Gdiplus::GpBitmap* bitmap = nullptr;
340 switch (pBitmap->GetFormat()) {
341 case FXDIB_Argb:
342 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
343 PixelFormat32bppARGB, scan0, &bitmap);
344 break;
345 case FXDIB_Rgb32:
346 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
347 PixelFormat32bppRGB, scan0, &bitmap);
348 break;
349 case FXDIB_Rgb:
350 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
351 PixelFormat24bppRGB, scan0, &bitmap);
352 break;
353 case FXDIB_8bppRgb: {
354 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
355 PixelFormat8bppIndexed, scan0,
356 &bitmap);
357 UINT pal[258];
358 pal[0] = 0;
359 pal[1] = 256;
360 for (int i = 0; i < 256; i++)
361 pal[i + 2] = pBitmap->GetPaletteArgb(i);
362 CallFunc(GdipSetImagePalette)(bitmap, (Gdiplus::ColorPalette*)pal);
363 break;
364 }
365 case FXDIB_1bppRgb: {
366 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
367 PixelFormat1bppIndexed, scan0,
368 &bitmap);
369 break;
370 }
371 }
372 if (dest_height < 0) {
373 dest_height--;
374 }
375 if (dest_width < 0) {
376 dest_width--;
377 }
378 Gdiplus::Point destinationPoints[] = {
379 Gdiplus::Point(dest_left, dest_top),
380 Gdiplus::Point(dest_left + dest_width, dest_top),
381 Gdiplus::Point(dest_left, dest_top + dest_height)};
382 CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
383 CallFunc(GdipDisposeImage)(bitmap);
384 }
385
GdipCreatePenImpl(const CFX_GraphStateData * pGraphState,const CFX_Matrix * pMatrix,DWORD argb,bool bTextMode)386 Gdiplus::GpPen* GdipCreatePenImpl(const CFX_GraphStateData* pGraphState,
387 const CFX_Matrix* pMatrix,
388 DWORD argb,
389 bool bTextMode) {
390 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
391 float width = pGraphState->m_LineWidth;
392 if (!bTextMode) {
393 float unit = pMatrix
394 ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
395 : 1.0f;
396 width = std::max(width, unit);
397 }
398 Gdiplus::GpPen* pPen = nullptr;
399 CallFunc(GdipCreatePen1)((Gdiplus::ARGB)argb, width, Gdiplus::UnitWorld,
400 &pPen);
401 Gdiplus::LineCap lineCap = Gdiplus::LineCapFlat;
402 Gdiplus::DashCap dashCap = Gdiplus::DashCapFlat;
403 bool bDashExtend = false;
404 switch (pGraphState->m_LineCap) {
405 case CFX_GraphStateData::LineCapButt:
406 lineCap = Gdiplus::LineCapFlat;
407 break;
408 case CFX_GraphStateData::LineCapRound:
409 lineCap = Gdiplus::LineCapRound;
410 dashCap = Gdiplus::DashCapRound;
411 bDashExtend = true;
412 break;
413 case CFX_GraphStateData::LineCapSquare:
414 lineCap = Gdiplus::LineCapSquare;
415 bDashExtend = true;
416 break;
417 }
418 CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
419 Gdiplus::LineJoin lineJoin = Gdiplus::LineJoinMiterClipped;
420 switch (pGraphState->m_LineJoin) {
421 case CFX_GraphStateData::LineJoinMiter:
422 lineJoin = Gdiplus::LineJoinMiterClipped;
423 break;
424 case CFX_GraphStateData::LineJoinRound:
425 lineJoin = Gdiplus::LineJoinRound;
426 break;
427 case CFX_GraphStateData::LineJoinBevel:
428 lineJoin = Gdiplus::LineJoinBevel;
429 break;
430 }
431 CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
432 if (!pGraphState->m_DashArray.empty()) {
433 float* pDashArray =
434 FX_Alloc(float, FxAlignToBoundary<2>(pGraphState->m_DashArray.size()));
435 int nCount = 0;
436 float on_leftover = 0, off_leftover = 0;
437 for (size_t i = 0; i < pGraphState->m_DashArray.size(); i += 2) {
438 float on_phase = pGraphState->m_DashArray[i];
439 float off_phase;
440 if (i == pGraphState->m_DashArray.size() - 1)
441 off_phase = on_phase;
442 else
443 off_phase = pGraphState->m_DashArray[i + 1];
444 on_phase /= width;
445 off_phase /= width;
446 if (on_phase + off_phase <= 0.00002f) {
447 on_phase = 1.0f / 10;
448 off_phase = 1.0f / 10;
449 }
450 if (bDashExtend) {
451 if (off_phase < 1)
452 off_phase = 0;
453 else
454 off_phase -= 1;
455 on_phase += 1;
456 }
457 if (on_phase == 0 || off_phase == 0) {
458 if (nCount == 0) {
459 on_leftover += on_phase;
460 off_leftover += off_phase;
461 } else {
462 pDashArray[nCount - 2] += on_phase;
463 pDashArray[nCount - 1] += off_phase;
464 }
465 } else {
466 pDashArray[nCount++] = on_phase + on_leftover;
467 on_leftover = 0;
468 pDashArray[nCount++] = off_phase + off_leftover;
469 off_leftover = 0;
470 }
471 }
472 CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
473 float phase = pGraphState->m_DashPhase;
474 if (bDashExtend) {
475 if (phase < 0.5f)
476 phase = 0;
477 else
478 phase -= 0.5f;
479 }
480 CallFunc(GdipSetPenDashOffset)(pPen, phase);
481 FX_Free(pDashArray);
482 pDashArray = nullptr;
483 }
484 CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
485 return pPen;
486 }
487
IsSmallTriangle(pdfium::span<const Gdiplus::PointF> points,const CFX_Matrix * pMatrix)488 Optional<std::pair<size_t, size_t>> IsSmallTriangle(
489 pdfium::span<const Gdiplus::PointF> points,
490 const CFX_Matrix* pMatrix) {
491 static constexpr size_t kPairs[3][2] = {{1, 2}, {0, 2}, {0, 1}};
492 for (size_t i = 0; i < FX_ArraySize(kPairs); ++i) {
493 size_t pair1 = kPairs[i][0];
494 size_t pair2 = kPairs[i][1];
495
496 CFX_PointF p1(points[pair1].X, points[pair1].Y);
497 CFX_PointF p2(points[pair2].X, points[pair2].Y);
498 if (pMatrix) {
499 p1 = pMatrix->Transform(p1);
500 p2 = pMatrix->Transform(p2);
501 }
502
503 CFX_PointF diff = p1 - p2;
504 float distance_square = (diff.x * diff.x) + (diff.y * diff.y);
505 if (distance_square < 2.25f)
506 return std::make_pair(i, pair1);
507 }
508 return {};
509 }
510
511 class GpStream final : public IStream {
512 public:
GpStream()513 GpStream() : m_RefCount(1), m_ReadPos(0) {}
514 ~GpStream() = default;
515
516 // IUnknown
QueryInterface(REFIID iid,void ** ppvObject)517 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
518 void** ppvObject) override {
519 if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
520 iid == __uuidof(ISequentialStream)) {
521 *ppvObject = static_cast<IStream*>(this);
522 AddRef();
523 return S_OK;
524 }
525 return E_NOINTERFACE;
526 }
AddRef()527 ULONG STDMETHODCALLTYPE AddRef() override {
528 return (ULONG)InterlockedIncrement(&m_RefCount);
529 }
Release()530 ULONG STDMETHODCALLTYPE Release() override {
531 ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
532 if (res == 0) {
533 delete this;
534 }
535 return res;
536 }
537
538 // ISequentialStream
Read(void * output,ULONG cb,ULONG * pcbRead)539 HRESULT STDMETHODCALLTYPE Read(void* output,
540 ULONG cb,
541 ULONG* pcbRead) override {
542 if (pcbRead)
543 *pcbRead = 0;
544
545 if (m_ReadPos >= m_InterStream.tellp())
546 return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
547
548 size_t bytes_left = m_InterStream.tellp() - m_ReadPos;
549 size_t bytes_out =
550 std::min(pdfium::base::checked_cast<size_t>(cb), bytes_left);
551 memcpy(output, m_InterStream.str().c_str() + m_ReadPos, bytes_out);
552 m_ReadPos += bytes_out;
553 if (pcbRead)
554 *pcbRead = (ULONG)bytes_out;
555
556 return S_OK;
557 }
Write(const void * input,ULONG cb,ULONG * pcbWritten)558 HRESULT STDMETHODCALLTYPE Write(const void* input,
559 ULONG cb,
560 ULONG* pcbWritten) override {
561 if (cb <= 0) {
562 if (pcbWritten)
563 *pcbWritten = 0;
564 return S_OK;
565 }
566 m_InterStream.write(reinterpret_cast<const char*>(input), cb);
567 if (pcbWritten)
568 *pcbWritten = cb;
569 return S_OK;
570 }
571
572 // IStream
SetSize(ULARGE_INTEGER)573 HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
574 return E_NOTIMPL;
575 }
CopyTo(IStream *,ULARGE_INTEGER,ULARGE_INTEGER *,ULARGE_INTEGER *)576 HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
577 ULARGE_INTEGER,
578 ULARGE_INTEGER*,
579 ULARGE_INTEGER*) override {
580 return E_NOTIMPL;
581 }
Commit(DWORD)582 HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
Revert()583 HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
LockRegion(ULARGE_INTEGER,ULARGE_INTEGER,DWORD)584 HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
585 ULARGE_INTEGER,
586 DWORD) override {
587 return E_NOTIMPL;
588 }
UnlockRegion(ULARGE_INTEGER,ULARGE_INTEGER,DWORD)589 HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
590 ULARGE_INTEGER,
591 DWORD) override {
592 return E_NOTIMPL;
593 }
Clone(IStream ** stream)594 HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
595 return E_NOTIMPL;
596 }
Seek(LARGE_INTEGER liDistanceToMove,DWORD dwOrigin,ULARGE_INTEGER * lpNewFilePointer)597 HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
598 DWORD dwOrigin,
599 ULARGE_INTEGER* lpNewFilePointer) override {
600 std::streamoff start;
601 std::streamoff new_read_position;
602 switch (dwOrigin) {
603 case STREAM_SEEK_SET:
604 start = 0;
605 break;
606 case STREAM_SEEK_CUR:
607 start = m_ReadPos;
608 break;
609 case STREAM_SEEK_END:
610 if (m_InterStream.tellp() < 0)
611 return STG_E_SEEKERROR;
612 start = m_InterStream.tellp();
613 break;
614 default:
615 return STG_E_INVALIDFUNCTION;
616 }
617 new_read_position = start + liDistanceToMove.QuadPart;
618 if (new_read_position > m_InterStream.tellp())
619 return STG_E_SEEKERROR;
620
621 m_ReadPos = new_read_position;
622 if (lpNewFilePointer)
623 lpNewFilePointer->QuadPart = m_ReadPos;
624
625 return S_OK;
626 }
Stat(STATSTG * pStatstg,DWORD grfStatFlag)627 HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
628 DWORD grfStatFlag) override {
629 if (!pStatstg)
630 return STG_E_INVALIDFUNCTION;
631
632 ZeroMemory(pStatstg, sizeof(STATSTG));
633
634 if (m_InterStream.tellp() < 0)
635 return STG_E_SEEKERROR;
636
637 pStatstg->cbSize.QuadPart = m_InterStream.tellp();
638 return S_OK;
639 }
640
641 private:
642 LONG m_RefCount;
643 std::streamoff m_ReadPos;
644 std::ostringstream m_InterStream;
645 };
646
647 struct PREVIEW3_DIBITMAP {
648 BITMAPINFO* pbmi;
649 int Stride;
650 LPBYTE pScan0;
651 Gdiplus::GpBitmap* pBitmap;
652 Gdiplus::BitmapData* pBitmapData;
653 GpStream* pStream;
654 };
655
LoadDIBitmap(WINDIB_Open_Args_ args)656 PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
657 Gdiplus::GpBitmap* pBitmap;
658 GpStream* pStream = nullptr;
659 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
660 Gdiplus::Status status = Gdiplus::Ok;
661 if (args.flags == WINDIB_OPEN_PATHNAME) {
662 status = CallFunc(GdipCreateBitmapFromFileICM)(args.path_name, &pBitmap);
663 } else {
664 if (args.memory_size == 0 || !args.memory_base)
665 return nullptr;
666
667 pStream = new GpStream;
668 pStream->Write(args.memory_base, (ULONG)args.memory_size, nullptr);
669 status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
670 }
671 if (status != Gdiplus::Ok) {
672 if (pStream)
673 pStream->Release();
674
675 return nullptr;
676 }
677 UINT height, width;
678 CallFunc(GdipGetImageHeight)(pBitmap, &height);
679 CallFunc(GdipGetImageWidth)(pBitmap, &width);
680 Gdiplus::PixelFormat pixel_format;
681 CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
682 int info_size = sizeof(BITMAPINFOHEADER);
683 int bpp = 24;
684 int dest_pixel_format = PixelFormat24bppRGB;
685 if (pixel_format == PixelFormat1bppIndexed) {
686 info_size += 8;
687 bpp = 1;
688 dest_pixel_format = PixelFormat1bppIndexed;
689 } else if (pixel_format == PixelFormat8bppIndexed) {
690 info_size += 1024;
691 bpp = 8;
692 dest_pixel_format = PixelFormat8bppIndexed;
693 } else if (pixel_format == PixelFormat32bppARGB) {
694 bpp = 32;
695 dest_pixel_format = PixelFormat32bppARGB;
696 }
697 LPBYTE buf = FX_Alloc(BYTE, info_size);
698 BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
699 pbmih->biBitCount = bpp;
700 pbmih->biCompression = BI_RGB;
701 pbmih->biHeight = -(int)height;
702 pbmih->biPlanes = 1;
703 pbmih->biWidth = width;
704 Gdiplus::Rect rect(0, 0, width, height);
705 Gdiplus::BitmapData* pBitmapData = FX_Alloc(Gdiplus::BitmapData, 1);
706 CallFunc(GdipBitmapLockBits)(pBitmap, &rect, Gdiplus::ImageLockModeRead,
707 dest_pixel_format, pBitmapData);
708 if (pixel_format == PixelFormat1bppIndexed ||
709 pixel_format == PixelFormat8bppIndexed) {
710 DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
711 struct {
712 UINT flags;
713 UINT Count;
714 DWORD Entries[256];
715 } pal;
716 int size = 0;
717 CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
718 CallFunc(GdipGetImagePalette)(pBitmap, (Gdiplus::ColorPalette*)&pal, size);
719 int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
720 for (int i = 0; i < entries; i++) {
721 ppal[i] = pal.Entries[i] & 0x00ffffff;
722 }
723 }
724 PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
725 pInfo->pbmi = (BITMAPINFO*)buf;
726 pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
727 pInfo->Stride = pBitmapData->Stride;
728 pInfo->pBitmap = pBitmap;
729 pInfo->pBitmapData = pBitmapData;
730 pInfo->pStream = pStream;
731 return pInfo;
732 }
733
FreeDIBitmap(PREVIEW3_DIBITMAP * pInfo)734 void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
735 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
736 CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
737 CallFunc(GdipDisposeImage)(pInfo->pBitmap);
738 FX_Free(pInfo->pBitmapData);
739 FX_Free((LPBYTE)pInfo->pbmi);
740 if (pInfo->pStream)
741 pInfo->pStream->Release();
742 FX_Free(pInfo);
743 }
744
745 } // namespace
746
CGdiplusExt()747 CGdiplusExt::CGdiplusExt() {}
748
~CGdiplusExt()749 CGdiplusExt::~CGdiplusExt() {
750 FreeLibrary(m_GdiModule);
751 FreeLibrary(m_hModule);
752 }
753
Load()754 void CGdiplusExt::Load() {
755 char buf[MAX_PATH];
756 GetSystemDirectoryA(buf, MAX_PATH);
757 ByteString dllpath = buf;
758 dllpath += "\\GDIPLUS.DLL";
759 m_hModule = LoadLibraryA(dllpath.c_str());
760 if (!m_hModule)
761 return;
762
763 m_Functions.resize(FX_ArraySize(g_GdipFuncNames));
764 for (size_t i = 0; i < FX_ArraySize(g_GdipFuncNames); ++i) {
765 m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
766 if (!m_Functions[i]) {
767 m_hModule = nullptr;
768 return;
769 }
770 }
771
772 uintptr_t gdiplusToken;
773 Gdiplus::GdiplusStartupInput gdiplusStartupInput;
774 ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
775 &gdiplusToken, &gdiplusStartupInput, nullptr);
776 m_GdiModule = LoadLibraryA("GDI32.DLL");
777 }
778
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)779 bool CGdiplusExt::StretchDIBits(HDC hDC,
780 const RetainPtr<CFX_DIBitmap>& pBitmap,
781 int dest_left,
782 int dest_top,
783 int dest_width,
784 int dest_height,
785 const FX_RECT* pClipRect,
786 const FXDIB_ResampleOptions& options) {
787 Gdiplus::GpGraphics* pGraphics;
788 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
789 CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
790 CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
791 if (options.bNoSmoothing) {
792 CallFunc(GdipSetInterpolationMode)(
793 pGraphics, Gdiplus::InterpolationModeNearestNeighbor);
794 } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
795 pBitmap->GetHeight() > abs(dest_height) / 2) {
796 CallFunc(GdipSetInterpolationMode)(pGraphics,
797 Gdiplus::InterpolationModeHighQuality);
798 } else {
799 CallFunc(GdipSetInterpolationMode)(pGraphics,
800 Gdiplus::InterpolationModeBilinear);
801 }
802 FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
803 OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width,
804 dest_height);
805 CallFunc(GdipDeleteGraphics)(pGraphics);
806 CallFunc(GdipDeleteGraphics)(pGraphics);
807 return true;
808 }
809
DrawPath(HDC hDC,const CFX_PathData * pPathData,const CFX_Matrix * pObject2Device,const CFX_GraphStateData * pGraphState,uint32_t fill_argb,uint32_t stroke_argb,int fill_mode)810 bool CGdiplusExt::DrawPath(HDC hDC,
811 const CFX_PathData* pPathData,
812 const CFX_Matrix* pObject2Device,
813 const CFX_GraphStateData* pGraphState,
814 uint32_t fill_argb,
815 uint32_t stroke_argb,
816 int fill_mode) {
817 pdfium::span<const FX_PATHPOINT> points = pPathData->GetPoints();
818 if (points.empty())
819 return true;
820
821 Gdiplus::GpGraphics* pGraphics = nullptr;
822 const CGdiplusExt& GdiplusExt = GetGdiplusExt();
823 CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
824 CallFunc(GdipSetPageUnit)(pGraphics, Gdiplus::UnitPixel);
825 CallFunc(GdipSetPixelOffsetMode)(pGraphics, Gdiplus::PixelOffsetModeHalf);
826 Gdiplus::GpMatrix* pMatrix = nullptr;
827 if (pObject2Device) {
828 CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
829 pObject2Device->c, pObject2Device->d,
830 pObject2Device->e, pObject2Device->f, &pMatrix);
831 CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
832 }
833 std::vector<Gdiplus::PointF> gp_points(points.size());
834 std::vector<BYTE> gp_types(points.size());
835 int nSubPathes = 0;
836 bool bSubClose = false;
837 int pos_subclose = 0;
838 bool bSmooth = false;
839 int startpoint = 0;
840 for (size_t i = 0; i < points.size(); ++i) {
841 gp_points[i].X = points[i].m_Point.x;
842 gp_points[i].Y = points[i].m_Point.y;
843
844 CFX_PointF pos = points[i].m_Point;
845 if (pObject2Device)
846 pos = pObject2Device->Transform(pos);
847
848 if (pos.x > 50000.0f)
849 gp_points[i].X = 50000.0f;
850 if (pos.x < -50000.0f)
851 gp_points[i].X = -50000.0f;
852 if (pos.y > 50000.0f)
853 gp_points[i].Y = 50000.0f;
854 if (pos.y < -50000.0f)
855 gp_points[i].Y = -50000.0f;
856
857 FXPT_TYPE point_type = points[i].m_Type;
858 if (point_type == FXPT_TYPE::MoveTo) {
859 gp_types[i] = Gdiplus::PathPointTypeStart;
860 nSubPathes++;
861 bSubClose = false;
862 startpoint = i;
863 } else if (point_type == FXPT_TYPE::LineTo) {
864 gp_types[i] = Gdiplus::PathPointTypeLine;
865 if (points[i - 1].IsTypeAndOpen(FXPT_TYPE::MoveTo) &&
866 (i == points.size() - 1 ||
867 points[i + 1].IsTypeAndOpen(FXPT_TYPE::MoveTo)) &&
868 gp_points[i].Y == gp_points[i - 1].Y &&
869 gp_points[i].X == gp_points[i - 1].X) {
870 gp_points[i].X += 0.01f;
871 continue;
872 }
873 if (!bSmooth && gp_points[i].X != gp_points[i - 1].X &&
874 gp_points[i].Y != gp_points[i - 1].Y) {
875 bSmooth = true;
876 }
877 } else if (point_type == FXPT_TYPE::BezierTo) {
878 gp_types[i] = Gdiplus::PathPointTypeBezier;
879 bSmooth = true;
880 }
881 if (points[i].m_CloseFigure) {
882 if (bSubClose)
883 gp_types[pos_subclose] &= ~Gdiplus::PathPointTypeCloseSubpath;
884 else
885 bSubClose = true;
886 pos_subclose = i;
887 gp_types[i] |= Gdiplus::PathPointTypeCloseSubpath;
888 if (!bSmooth && gp_points[i].X != gp_points[startpoint].X &&
889 gp_points[i].Y != gp_points[startpoint].Y) {
890 bSmooth = true;
891 }
892 }
893 }
894 if (fill_mode & FXFILL_NOPATHSMOOTH) {
895 bSmooth = false;
896 CallFunc(GdipSetSmoothingMode)(pGraphics, Gdiplus::SmoothingModeNone);
897 } else if (!(fill_mode & FXFILL_FULLCOVER)) {
898 if (!bSmooth && (fill_mode & 3))
899 bSmooth = true;
900
901 if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
902 CallFunc(GdipSetSmoothingMode)(pGraphics,
903 Gdiplus::SmoothingModeAntiAlias);
904 }
905 }
906 int new_fill_mode = fill_mode & 3;
907 if (points.size() == 4 && !pGraphState) {
908 auto indices = IsSmallTriangle(gp_points, pObject2Device);
909 if (indices.has_value()) {
910 size_t v1;
911 size_t v2;
912 std::tie(v1, v2) = indices.value();
913 Gdiplus::GpPen* pPen = nullptr;
914 CallFunc(GdipCreatePen1)(fill_argb, 1.0f, Gdiplus::UnitPixel, &pPen);
915 CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_roundf(gp_points[v1].X),
916 FXSYS_roundf(gp_points[v1].Y),
917 FXSYS_roundf(gp_points[v2].X),
918 FXSYS_roundf(gp_points[v2].Y));
919 CallFunc(GdipDeletePen)(pPen);
920 return true;
921 }
922 }
923 Gdiplus::GpPath* pGpPath = nullptr;
924 CallFunc(GdipCreatePath2)(gp_points.data(), gp_types.data(), points.size(),
925 GdiFillType2Gdip(new_fill_mode), &pGpPath);
926 if (!pGpPath) {
927 if (pMatrix)
928 CallFunc(GdipDeleteMatrix)(pMatrix);
929
930 CallFunc(GdipDeleteGraphics)(pGraphics);
931 return false;
932 }
933 if (new_fill_mode) {
934 Gdiplus::GpBrush* pBrush = GdipCreateBrushImpl(fill_argb);
935 CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
936 CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
937 CallFunc(GdipDeleteBrush)(pBrush);
938 }
939 if (pGraphState && stroke_argb) {
940 Gdiplus::GpPen* pPen =
941 GdipCreatePenImpl(pGraphState, pObject2Device, stroke_argb,
942 !!(fill_mode & FX_STROKE_TEXT_MODE));
943 if (nSubPathes == 1) {
944 CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
945 } else {
946 int iStart = 0;
947 for (size_t i = 0; i < points.size(); ++i) {
948 if (i == points.size() - 1 ||
949 gp_types[i + 1] == Gdiplus::PathPointTypeStart) {
950 Gdiplus::GpPath* pSubPath;
951 CallFunc(GdipCreatePath2)(&gp_points[iStart], &gp_types[iStart],
952 i - iStart + 1,
953 GdiFillType2Gdip(new_fill_mode), &pSubPath);
954 iStart = i + 1;
955 CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
956 CallFunc(GdipDeletePath)(pSubPath);
957 }
958 }
959 }
960 CallFunc(GdipDeletePen)(pPen);
961 }
962 if (pMatrix)
963 CallFunc(GdipDeleteMatrix)(pMatrix);
964 CallFunc(GdipDeletePath)(pGpPath);
965 CallFunc(GdipDeleteGraphics)(pGraphics);
966 return true;
967 }
968
LoadDIBitmap(WINDIB_Open_Args_ args)969 RetainPtr<CFX_DIBitmap> CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) {
970 PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
971 if (!pInfo)
972 return nullptr;
973
974 int height = abs(pInfo->pbmi->bmiHeader.biHeight);
975 int width = pInfo->pbmi->bmiHeader.biWidth;
976 int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
977 LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
978 if (dest_pitch == pInfo->Stride) {
979 memcpy(pData, pInfo->pScan0, dest_pitch * height);
980 } else {
981 for (int i = 0; i < height; i++) {
982 memcpy(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i,
983 dest_pitch);
984 }
985 }
986 RetainPtr<CFX_DIBitmap> pDIBitmap = FX_WindowsDIB_LoadFromBuf(
987 pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
988 FX_Free(pData);
989 FreeDIBitmap(pInfo);
990 return pDIBitmap;
991 }
992