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