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