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