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 "core/include/fxge/fx_ge.h"
8
9#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN64_DESKTOP_
10#include <windows.h>
11#include <algorithm>
12
13namespace Gdiplus {
14using std::min;
15using std::max;
16}  // namespace Gdiplus
17
18#include <gdiplus.h>
19#include "core/include/fxge/fx_ge_win32.h"
20#include "win32_int.h"
21
22using namespace Gdiplus;
23using namespace Gdiplus::DllExports;
24
25#define GdiFillType2Gdip(fill_type) \
26  (fill_type == ALTERNATE ? FillModeAlternate : FillModeWinding)
27
28enum {
29  FuncId_GdipCreatePath2,
30  FuncId_GdipSetPenDashStyle,
31  FuncId_GdipSetPenDashArray,
32  FuncId_GdipSetPenDashCap197819,
33  FuncId_GdipSetPenLineJoin,
34  FuncId_GdipSetPenWidth,
35  FuncId_GdipCreateFromHDC,
36  FuncId_GdipSetPageUnit,
37  FuncId_GdipSetSmoothingMode,
38  FuncId_GdipCreateSolidFill,
39  FuncId_GdipFillPath,
40  FuncId_GdipDeleteBrush,
41  FuncId_GdipCreatePen1,
42  FuncId_GdipSetPenMiterLimit,
43  FuncId_GdipDrawPath,
44  FuncId_GdipDeletePen,
45  FuncId_GdipDeletePath,
46  FuncId_GdipDeleteGraphics,
47  FuncId_GdipCreateBitmapFromFileICM,
48  FuncId_GdipCreateBitmapFromStreamICM,
49  FuncId_GdipGetImageHeight,
50  FuncId_GdipGetImageWidth,
51  FuncId_GdipGetImagePixelFormat,
52  FuncId_GdipBitmapLockBits,
53  FuncId_GdipGetImagePaletteSize,
54  FuncId_GdipGetImagePalette,
55  FuncId_GdipBitmapUnlockBits,
56  FuncId_GdipDisposeImage,
57  FuncId_GdipFillRectangle,
58  FuncId_GdipCreateBitmapFromScan0,
59  FuncId_GdipSetImagePalette,
60  FuncId_GdipSetInterpolationMode,
61  FuncId_GdipDrawImagePointsI,
62  FuncId_GdipCreateBitmapFromGdiDib,
63  FuncId_GdiplusStartup,
64  FuncId_GdipDrawLineI,
65  FuncId_GdipResetClip,
66  FuncId_GdipCreatePath,
67  FuncId_GdipAddPathPath,
68  FuncId_GdipSetPathFillMode,
69  FuncId_GdipSetClipPath,
70  FuncId_GdipGetClip,
71  FuncId_GdipCreateRegion,
72  FuncId_GdipGetClipBoundsI,
73  FuncId_GdipSetClipRegion,
74  FuncId_GdipWidenPath,
75  FuncId_GdipAddPathLine,
76  FuncId_GdipAddPathRectangle,
77  FuncId_GdipDeleteRegion,
78  FuncId_GdipGetDC,
79  FuncId_GdipReleaseDC,
80  FuncId_GdipSetPenLineCap197819,
81  FuncId_GdipSetPenDashOffset,
82  FuncId_GdipResetPath,
83  FuncId_GdipCreateRegionPath,
84  FuncId_GdipCreateFont,
85  FuncId_GdipGetFontSize,
86  FuncId_GdipCreateFontFamilyFromName,
87  FuncId_GdipSetTextRenderingHint,
88  FuncId_GdipDrawDriverString,
89  FuncId_GdipCreateMatrix2,
90  FuncId_GdipDeleteMatrix,
91  FuncId_GdipSetWorldTransform,
92  FuncId_GdipResetWorldTransform,
93  FuncId_GdipDeleteFontFamily,
94  FuncId_GdipDeleteFont,
95  FuncId_GdipNewPrivateFontCollection,
96  FuncId_GdipDeletePrivateFontCollection,
97  FuncId_GdipPrivateAddMemoryFont,
98  FuncId_GdipGetFontCollectionFamilyList,
99  FuncId_GdipGetFontCollectionFamilyCount,
100  FuncId_GdipSetTextContrast,
101  FuncId_GdipSetPixelOffsetMode,
102  FuncId_GdipGetImageGraphicsContext,
103  FuncId_GdipDrawImageI,
104  FuncId_GdipDrawImageRectI,
105  FuncId_GdipDrawString,
106  FuncId_GdipSetPenTransform,
107};
108static LPCSTR g_GdipFuncNames[] = {
109    "GdipCreatePath2",
110    "GdipSetPenDashStyle",
111    "GdipSetPenDashArray",
112    "GdipSetPenDashCap197819",
113    "GdipSetPenLineJoin",
114    "GdipSetPenWidth",
115    "GdipCreateFromHDC",
116    "GdipSetPageUnit",
117    "GdipSetSmoothingMode",
118    "GdipCreateSolidFill",
119    "GdipFillPath",
120    "GdipDeleteBrush",
121    "GdipCreatePen1",
122    "GdipSetPenMiterLimit",
123    "GdipDrawPath",
124    "GdipDeletePen",
125    "GdipDeletePath",
126    "GdipDeleteGraphics",
127    "GdipCreateBitmapFromFileICM",
128    "GdipCreateBitmapFromStreamICM",
129    "GdipGetImageHeight",
130    "GdipGetImageWidth",
131    "GdipGetImagePixelFormat",
132    "GdipBitmapLockBits",
133    "GdipGetImagePaletteSize",
134    "GdipGetImagePalette",
135    "GdipBitmapUnlockBits",
136    "GdipDisposeImage",
137    "GdipFillRectangle",
138    "GdipCreateBitmapFromScan0",
139    "GdipSetImagePalette",
140    "GdipSetInterpolationMode",
141    "GdipDrawImagePointsI",
142    "GdipCreateBitmapFromGdiDib",
143    "GdiplusStartup",
144    "GdipDrawLineI",
145    "GdipResetClip",
146    "GdipCreatePath",
147    "GdipAddPathPath",
148    "GdipSetPathFillMode",
149    "GdipSetClipPath",
150    "GdipGetClip",
151    "GdipCreateRegion",
152    "GdipGetClipBoundsI",
153    "GdipSetClipRegion",
154    "GdipWidenPath",
155    "GdipAddPathLine",
156    "GdipAddPathRectangle",
157    "GdipDeleteRegion",
158    "GdipGetDC",
159    "GdipReleaseDC",
160    "GdipSetPenLineCap197819",
161    "GdipSetPenDashOffset",
162    "GdipResetPath",
163    "GdipCreateRegionPath",
164    "GdipCreateFont",
165    "GdipGetFontSize",
166    "GdipCreateFontFamilyFromName",
167    "GdipSetTextRenderingHint",
168    "GdipDrawDriverString",
169    "GdipCreateMatrix2",
170    "GdipDeleteMatrix",
171    "GdipSetWorldTransform",
172    "GdipResetWorldTransform",
173    "GdipDeleteFontFamily",
174    "GdipDeleteFont",
175    "GdipNewPrivateFontCollection",
176    "GdipDeletePrivateFontCollection",
177    "GdipPrivateAddMemoryFont",
178    "GdipGetFontCollectionFamilyList",
179    "GdipGetFontCollectionFamilyCount",
180    "GdipSetTextContrast",
181    "GdipSetPixelOffsetMode",
182    "GdipGetImageGraphicsContext",
183    "GdipDrawImageI",
184    "GdipDrawImageRectI",
185    "GdipDrawString",
186    "GdipSetPenTransform",
187};
188typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePath2)(GDIPCONST GpPointF*,
189                                                       GDIPCONST BYTE*,
190                                                       INT,
191                                                       GpFillMode,
192                                                       GpPath** path);
193typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashStyle)(
194    GpPen* pen,
195    GpDashStyle dashstyle);
196typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashArray)(GpPen* pen,
197                                                           GDIPCONST REAL* dash,
198                                                           INT count);
199typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashCap197819)(
200    GpPen* pen,
201    GpDashCap dashCap);
202typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineJoin)(GpPen* pen,
203                                                          GpLineJoin lineJoin);
204typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenWidth)(GpPen* pen, REAL width);
205typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFromHDC)(HDC hdc,
206                                                         GpGraphics** graphics);
207typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPageUnit)(GpGraphics* graphics,
208                                                       GpUnit unit);
209typedef GpStatus(WINGDIPAPI* FuncType_GdipSetSmoothingMode)(
210    GpGraphics* graphics,
211    SmoothingMode smoothingMode);
212typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateSolidFill)(ARGB color,
213                                                           GpSolidFill** brush);
214typedef GpStatus(WINGDIPAPI* FuncType_GdipFillPath)(GpGraphics* graphics,
215                                                    GpBrush* brush,
216                                                    GpPath* path);
217typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteBrush)(GpBrush* brush);
218typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePen1)(ARGB color,
219                                                      REAL width,
220                                                      GpUnit unit,
221                                                      GpPen** pen);
222typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenMiterLimit)(GpPen* pen,
223                                                            REAL miterLimit);
224typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawPath)(GpGraphics* graphics,
225                                                    GpPen* pen,
226                                                    GpPath* path);
227typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePen)(GpPen* pen);
228typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePath)(GpPath* path);
229typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteGraphics)(GpGraphics* graphics);
230typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromFileICM)(
231    GDIPCONST WCHAR* filename,
232    GpBitmap** bitmap);
233typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromStreamICM)(
234    IStream* stream,
235    GpBitmap** bitmap);
236typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageWidth)(GpImage* image,
237                                                         UINT* width);
238typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageHeight)(GpImage* image,
239                                                          UINT* height);
240typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePixelFormat)(
241    GpImage* image,
242    PixelFormat* format);
243typedef GpStatus(WINGDIPAPI* FuncType_GdipBitmapLockBits)(
244    GpBitmap* bitmap,
245    GDIPCONST GpRect* rect,
246    UINT flags,
247    PixelFormat format,
248    BitmapData* lockedBitmapData);
249typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePalette)(
250    GpImage* image,
251    ColorPalette* palette,
252    INT size);
253typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImagePaletteSize)(GpImage* image,
254                                                               INT* size);
255typedef GpStatus(WINGDIPAPI* FuncType_GdipBitmapUnlockBits)(
256    GpBitmap* bitmap,
257    BitmapData* lockedBitmapData);
258typedef GpStatus(WINGDIPAPI* FuncType_GdipDisposeImage)(GpImage* image);
259typedef GpStatus(WINGDIPAPI* FuncType_GdipFillRectangle)(GpGraphics* graphics,
260                                                         GpBrush* brush,
261                                                         REAL x,
262                                                         REAL y,
263                                                         REAL width,
264                                                         REAL height);
265typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromScan0)(
266    INT width,
267    INT height,
268    INT stride,
269    PixelFormat format,
270    BYTE* scan0,
271    GpBitmap** bitmap);
272typedef GpStatus(WINGDIPAPI* FuncType_GdipSetImagePalette)(
273    GpImage* image,
274    GDIPCONST ColorPalette* palette);
275typedef GpStatus(WINGDIPAPI* FuncType_GdipSetInterpolationMode)(
276    GpGraphics* graphics,
277    InterpolationMode interpolationMode);
278typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImagePointsI)(
279    GpGraphics* graphics,
280    GpImage* image,
281    GDIPCONST GpPoint* dstpoints,
282    INT count);
283typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateBitmapFromGdiDib)(
284    GDIPCONST BITMAPINFO* gdiBitmapInfo,
285    VOID* gdiBitmapData,
286    GpBitmap** bitmap);
287typedef Status(WINAPI* FuncType_GdiplusStartup)(
288    OUT uintptr_t* token,
289    const GdiplusStartupInput* input,
290    OUT GdiplusStartupOutput* output);
291typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawLineI)(GpGraphics* graphics,
292                                                     GpPen* pen,
293                                                     int x1,
294                                                     int y1,
295                                                     int x2,
296                                                     int y2);
297typedef GpStatus(WINGDIPAPI* FuncType_GdipResetClip)(GpGraphics* graphics);
298typedef GpStatus(WINGDIPAPI* FuncType_GdipCreatePath)(GpFillMode brushMode,
299                                                      GpPath** path);
300typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathPath)(
301    GpPath* path,
302    GDIPCONST GpPath* addingPath,
303    BOOL connect);
304typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPathFillMode)(GpPath* path,
305                                                           GpFillMode fillmode);
306typedef GpStatus(WINGDIPAPI* FuncType_GdipSetClipPath)(GpGraphics* graphics,
307                                                       GpPath* path,
308                                                       CombineMode combineMode);
309typedef GpStatus(WINGDIPAPI* FuncType_GdipGetClip)(GpGraphics* graphics,
310                                                   GpRegion* region);
311typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateRegion)(GpRegion** region);
312typedef GpStatus(WINGDIPAPI* FuncType_GdipGetClipBoundsI)(GpGraphics* graphics,
313                                                          GpRect* rect);
314typedef GpStatus(WINGDIPAPI* FuncType_GdipSetClipRegion)(
315    GpGraphics* graphics,
316    GpRegion* region,
317    CombineMode combineMode);
318typedef GpStatus(WINGDIPAPI* FuncType_GdipWidenPath)(GpPath* nativePath,
319                                                     GpPen* pen,
320                                                     GpMatrix* matrix,
321                                                     REAL flatness);
322typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathLine)(GpPath* path,
323                                                       REAL x1,
324                                                       REAL y1,
325                                                       REAL x2,
326                                                       REAL y2);
327typedef GpStatus(WINGDIPAPI* FuncType_GdipAddPathRectangle)(GpPath* path,
328                                                            REAL x,
329                                                            REAL y,
330                                                            REAL width,
331                                                            REAL height);
332typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteRegion)(GpRegion* region);
333typedef GpStatus(WINGDIPAPI* FuncType_GdipGetDC)(GpGraphics* graphics,
334                                                 HDC* hdc);
335typedef GpStatus(WINGDIPAPI* FuncType_GdipReleaseDC)(GpGraphics* graphics,
336                                                     HDC hdc);
337typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenLineCap197819)(
338    GpPen* pen,
339    GpLineCap startCap,
340    GpLineCap endCap,
341    GpDashCap dashCap);
342typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenDashOffset)(GpPen* pen,
343                                                            REAL offset);
344typedef GpStatus(WINGDIPAPI* FuncType_GdipResetPath)(GpPath* path);
345typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateRegionPath)(GpPath* path,
346                                                            GpRegion** region);
347typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFont)(
348    GDIPCONST GpFontFamily* fontFamily,
349    REAL emSize,
350    INT style,
351    Unit unit,
352    GpFont** font);
353typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontSize)(GpFont* font,
354                                                       REAL* size);
355typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateFontFamilyFromName)(
356    GDIPCONST WCHAR* name,
357    GpFontCollection* fontCollection,
358    GpFontFamily** FontFamily);
359typedef GpStatus(WINGDIPAPI* FuncType_GdipSetTextRenderingHint)(
360    GpGraphics* graphics,
361    TextRenderingHint mode);
362typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawDriverString)(
363    GpGraphics* graphics,
364    GDIPCONST UINT16* text,
365    INT length,
366    GDIPCONST GpFont* font,
367    GDIPCONST GpBrush* brush,
368    GDIPCONST PointF* positions,
369    INT flags,
370    GDIPCONST GpMatrix* matrix);
371typedef GpStatus(WINGDIPAPI* FuncType_GdipCreateMatrix2)(REAL m11,
372                                                         REAL m12,
373                                                         REAL m21,
374                                                         REAL m22,
375                                                         REAL dx,
376                                                         REAL dy,
377                                                         GpMatrix** matrix);
378typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteMatrix)(GpMatrix* matrix);
379typedef GpStatus(WINGDIPAPI* FuncType_GdipSetWorldTransform)(
380    GpGraphics* graphics,
381    GpMatrix* matrix);
382typedef GpStatus(WINGDIPAPI* FuncType_GdipResetWorldTransform)(
383    GpGraphics* graphics);
384typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteFontFamily)(
385    GpFontFamily* FontFamily);
386typedef GpStatus(WINGDIPAPI* FuncType_GdipDeleteFont)(GpFont* font);
387typedef GpStatus(WINGDIPAPI* FuncType_GdipNewPrivateFontCollection)(
388    GpFontCollection** fontCollection);
389typedef GpStatus(WINGDIPAPI* FuncType_GdipDeletePrivateFontCollection)(
390    GpFontCollection** fontCollection);
391typedef GpStatus(WINGDIPAPI* FuncType_GdipPrivateAddMemoryFont)(
392    GpFontCollection* fontCollection,
393    GDIPCONST void* memory,
394    INT length);
395typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontCollectionFamilyList)(
396    GpFontCollection* fontCollection,
397    INT numSought,
398    GpFontFamily* gpfamilies[],
399    INT* numFound);
400typedef GpStatus(WINGDIPAPI* FuncType_GdipGetFontCollectionFamilyCount)(
401    GpFontCollection* fontCollection,
402    INT* numFound);
403typedef GpStatus(WINGDIPAPI* FuncType_GdipSetTextContrast)(GpGraphics* graphics,
404                                                           UINT contrast);
405typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPixelOffsetMode)(
406    GpGraphics* graphics,
407    PixelOffsetMode pixelOffsetMode);
408typedef GpStatus(WINGDIPAPI* FuncType_GdipGetImageGraphicsContext)(
409    GpImage* image,
410    GpGraphics** graphics);
411typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImageI)(GpGraphics* graphics,
412                                                      GpImage* image,
413                                                      INT x,
414                                                      INT y);
415typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawImageRectI)(GpGraphics* graphics,
416                                                          GpImage* image,
417                                                          INT x,
418                                                          INT y,
419                                                          INT width,
420                                                          INT height);
421typedef GpStatus(WINGDIPAPI* FuncType_GdipDrawString)(
422    GpGraphics* graphics,
423    GDIPCONST WCHAR* string,
424    INT length,
425    GDIPCONST GpFont* font,
426    GDIPCONST RectF* layoutRect,
427    GDIPCONST GpStringFormat* stringFormat,
428    GDIPCONST GpBrush* brush);
429typedef GpStatus(WINGDIPAPI* FuncType_GdipSetPenTransform)(GpPen* pen,
430                                                           GpMatrix* matrix);
431#define CallFunc(funcname) \
432  ((FuncType_##funcname)GdiplusExt.m_Functions[FuncId_##funcname])
433typedef HANDLE(__stdcall* FuncType_GdiAddFontMemResourceEx)(PVOID pbFont,
434                                                            DWORD cbFont,
435                                                            PVOID pdv,
436                                                            DWORD* pcFonts);
437typedef BOOL(__stdcall* FuncType_GdiRemoveFontMemResourceEx)(HANDLE handle);
438void* CGdiplusExt::GdiAddFontMemResourceEx(void* pFontdata,
439                                           FX_DWORD size,
440                                           void* pdv,
441                                           FX_DWORD* num_face) {
442  if (m_pGdiAddFontMemResourceEx) {
443    return ((FuncType_GdiAddFontMemResourceEx)m_pGdiAddFontMemResourceEx)(
444        (PVOID)pFontdata, (DWORD)size, (PVOID)pdv, (DWORD*)num_face);
445  }
446  return NULL;
447}
448FX_BOOL CGdiplusExt::GdiRemoveFontMemResourceEx(void* handle) {
449  if (m_pGdiRemoveFontMemResourseEx) {
450    return ((FuncType_GdiRemoveFontMemResourceEx)m_pGdiRemoveFontMemResourseEx)(
451        (HANDLE)handle);
452  }
453  return FALSE;
454}
455static GpBrush* _GdipCreateBrush(DWORD argb) {
456  CGdiplusExt& GdiplusExt =
457      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
458  GpSolidFill* solidBrush = NULL;
459  CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);
460  return solidBrush;
461}
462static CFX_DIBitmap* _StretchMonoToGray(int dest_width,
463                                        int dest_height,
464                                        const CFX_DIBitmap* pSource,
465                                        FX_RECT* pClipRect) {
466  FX_BOOL bFlipX = dest_width < 0;
467  if (bFlipX) {
468    dest_width = -dest_width;
469  }
470  FX_BOOL bFlipY = dest_height < 0;
471  if (bFlipY) {
472    dest_height = -dest_height;
473  }
474  int result_width = pClipRect->Width();
475  int result_height = pClipRect->Height();
476  int result_pitch = (result_width + 3) / 4 * 4;
477  CFX_DIBitmap* pStretched = new CFX_DIBitmap;
478  if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb)) {
479    delete pStretched;
480    return NULL;
481  }
482  LPBYTE dest_buf = pStretched->GetBuffer();
483  int src_width = pSource->GetWidth();
484  int src_height = pSource->GetHeight();
485  int y_unit = src_height / dest_height;
486  int x_unit = src_width / dest_width;
487  int area_unit = y_unit * x_unit;
488  LPBYTE src_buf = pSource->GetBuffer();
489  int src_pitch = pSource->GetPitch();
490  for (int dest_y = 0; dest_y < result_height; dest_y++) {
491    LPBYTE dest_scan = dest_buf + dest_y * result_pitch;
492    int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top)
493                             : (dest_y + pClipRect->top);
494    src_y_start = src_y_start * src_height / dest_height;
495    LPBYTE src_scan = src_buf + src_y_start * src_pitch;
496    for (int dest_x = 0; dest_x < result_width; dest_x++) {
497      int sum = 0;
498      int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left)
499                               : (dest_x + pClipRect->left);
500      src_x_start = src_x_start * src_width / dest_width;
501      int src_x_end = src_x_start + x_unit;
502      LPBYTE src_line = src_scan;
503      for (int src_y = 0; src_y < y_unit; src_y++) {
504        for (int src_x = src_x_start; src_x < src_x_end; src_x++) {
505          if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {
506            sum += 255;
507          }
508        }
509        src_line += src_pitch;
510      }
511      dest_scan[dest_x] = 255 - sum / area_unit;
512    }
513  }
514  return pStretched;
515}
516static void OutputImageMask(GpGraphics* pGraphics,
517                            BOOL bMonoDevice,
518                            const CFX_DIBitmap* pBitmap,
519                            int dest_left,
520                            int dest_top,
521                            int dest_width,
522                            int dest_height,
523                            FX_ARGB argb,
524                            const FX_RECT* pClipRect) {
525  ASSERT(pBitmap->GetBPP() == 1);
526  CGdiplusExt& GdiplusExt =
527      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
528  int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();
529  int src_pitch = pBitmap->GetPitch();
530  uint8_t* scan0 = pBitmap->GetBuffer();
531  if (src_width == 1 && src_height == 1) {
532    if ((scan0[0] & 0x80) == 0) {
533      return;
534    }
535    GpSolidFill* solidBrush;
536    CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);
537    if (dest_width < 0) {
538      dest_width = -dest_width;
539      dest_left -= dest_width;
540    }
541    if (dest_height < 0) {
542      dest_height = -dest_height;
543      dest_top -= dest_height;
544    }
545    CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left,
546                                (float)dest_top, (float)dest_width,
547                                (float)dest_height);
548    CallFunc(GdipDeleteBrush)(solidBrush);
549    return;
550  }
551  if (!bMonoDevice && abs(dest_width) < src_width &&
552      abs(dest_height) < src_height) {
553    FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width,
554                       dest_top + dest_height);
555    image_rect.Normalize();
556    FX_RECT image_clip = image_rect;
557    image_clip.Intersect(*pClipRect);
558    if (image_clip.IsEmpty()) {
559      return;
560    }
561    image_clip.Offset(-image_rect.left, -image_rect.top);
562    CFX_DIBitmap* pStretched = NULL;
563    if (src_width * src_height > 10000) {
564      pStretched =
565          _StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);
566    } else {
567      pStretched =
568          pBitmap->StretchTo(dest_width, dest_height, FALSE, &image_clip);
569    }
570    GpBitmap* bitmap;
571    CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
572                                        (image_clip.Width() + 3) / 4 * 4,
573                                        PixelFormat8bppIndexed,
574                                        pStretched->GetBuffer(), &bitmap);
575    int a, r, g, b;
576    ArgbDecode(argb, a, r, g, b);
577    UINT pal[258];
578    pal[0] = 0;
579    pal[1] = 256;
580    for (int i = 0; i < 256; i++) {
581      pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);
582    }
583    CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
584    CallFunc(GdipDrawImageI)(pGraphics, bitmap,
585                             image_rect.left + image_clip.left,
586                             image_rect.top + image_clip.top);
587    CallFunc(GdipDisposeImage)(bitmap);
588    delete pStretched;
589    return;
590  }
591  GpBitmap* bitmap;
592  CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
593                                      PixelFormat1bppIndexed, scan0, &bitmap);
594  UINT palette[4] = {PaletteFlagsHasAlpha, 2, 0, argb};
595  CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)palette);
596  Point destinationPoints[] = {Point(dest_left, dest_top),
597                               Point(dest_left + dest_width, dest_top),
598                               Point(dest_left, dest_top + dest_height)};
599  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
600  CallFunc(GdipDisposeImage)(bitmap);
601}
602static void OutputImage(GpGraphics* pGraphics,
603                        const CFX_DIBitmap* pBitmap,
604                        const FX_RECT* pSrcRect,
605                        int dest_left,
606                        int dest_top,
607                        int dest_width,
608                        int dest_height) {
609  int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
610  CGdiplusExt& GdiplusExt =
611      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
612  if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
613    FX_RECT new_rect(0, 0, src_width, src_height);
614    CFX_DIBitmap* pCloned = pBitmap->Clone(pSrcRect);
615    if (!pCloned) {
616      return;
617    }
618    OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width,
619                dest_height);
620    delete pCloned;
621    return;
622  }
623  int src_pitch = pBitmap->GetPitch();
624  uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch +
625                   pBitmap->GetBPP() * pSrcRect->left / 8;
626  GpBitmap* bitmap = NULL;
627  switch (pBitmap->GetFormat()) {
628    case FXDIB_Argb:
629      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
630                                          PixelFormat32bppARGB, scan0, &bitmap);
631      break;
632    case FXDIB_Rgb32:
633      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
634                                          PixelFormat32bppRGB, scan0, &bitmap);
635      break;
636    case FXDIB_Rgb:
637      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
638                                          PixelFormat24bppRGB, scan0, &bitmap);
639      break;
640    case FXDIB_8bppRgb: {
641      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
642                                          PixelFormat8bppIndexed, scan0,
643                                          &bitmap);
644      UINT pal[258];
645      pal[0] = 0;
646      pal[1] = 256;
647      for (int i = 0; i < 256; i++) {
648        pal[i + 2] = pBitmap->GetPaletteEntry(i);
649      }
650      CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
651      break;
652    }
653    case FXDIB_1bppRgb: {
654      CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
655                                          PixelFormat1bppIndexed, scan0,
656                                          &bitmap);
657      break;
658    }
659  }
660  if (dest_height < 0) {
661    dest_height--;
662  }
663  if (dest_width < 0) {
664    dest_width--;
665  }
666  Point destinationPoints[] = {Point(dest_left, dest_top),
667                               Point(dest_left + dest_width, dest_top),
668                               Point(dest_left, dest_top + dest_height)};
669  CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
670  CallFunc(GdipDisposeImage)(bitmap);
671}
672CGdiplusExt::CGdiplusExt() {
673  m_hModule = NULL;
674  m_GdiModule = NULL;
675  for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i++) {
676    m_Functions[i] = NULL;
677  }
678  m_pGdiAddFontMemResourceEx = NULL;
679  m_pGdiRemoveFontMemResourseEx = NULL;
680}
681void CGdiplusExt::Load() {
682  CFX_ByteString strPlusPath = "";
683  FX_CHAR buf[MAX_PATH];
684  GetSystemDirectoryA(buf, MAX_PATH);
685  strPlusPath += buf;
686  strPlusPath += "\\";
687  strPlusPath += "GDIPLUS.DLL";
688  m_hModule = LoadLibraryA(strPlusPath);
689  if (!m_hModule) {
690    return;
691  }
692  for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i++) {
693    m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
694    if (!m_Functions[i]) {
695      m_hModule = NULL;
696      return;
697    }
698  }
699  uintptr_t gdiplusToken;
700  GdiplusStartupInput gdiplusStartupInput;
701  ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
702      &gdiplusToken, &gdiplusStartupInput, NULL);
703  m_GdiModule = LoadLibraryA("GDI32.DLL");
704  if (!m_GdiModule) {
705    return;
706  }
707  m_pGdiAddFontMemResourceEx =
708      GetProcAddress(m_GdiModule, "AddFontMemResourceEx");
709  m_pGdiRemoveFontMemResourseEx =
710      GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx");
711}
712CGdiplusExt::~CGdiplusExt() {}
713LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, FX_DWORD size) {
714  GpFontCollection* pCollection = NULL;
715  CGdiplusExt& GdiplusExt =
716      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
717  CallFunc(GdipNewPrivateFontCollection)(&pCollection);
718  GpStatus status =
719      CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);
720  if (status == Ok) {
721    return pCollection;
722  }
723  CallFunc(GdipDeletePrivateFontCollection)(&pCollection);
724  return NULL;
725}
726void CGdiplusExt::DeleteMemFont(LPVOID pCollection) {
727  CGdiplusExt& GdiplusExt =
728      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
729  CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);
730}
731FX_BOOL CGdiplusExt::GdipCreateBitmap(CFX_DIBitmap* pBitmap, void** bitmap) {
732  CGdiplusExt& GdiplusExt =
733      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
734  PixelFormat format;
735  switch (pBitmap->GetFormat()) {
736    case FXDIB_Rgb:
737      format = PixelFormat24bppRGB;
738      break;
739    case FXDIB_Rgb32:
740      format = PixelFormat32bppRGB;
741      break;
742    case FXDIB_Argb:
743      format = PixelFormat32bppARGB;
744      break;
745    default:
746      return FALSE;
747  }
748  GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(
749      pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap->GetPitch(), format,
750      pBitmap->GetBuffer(), (GpBitmap**)bitmap);
751  if (status == Ok) {
752    return TRUE;
753  }
754  return FALSE;
755}
756FX_BOOL CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics) {
757  CGdiplusExt& GdiplusExt =
758      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
759  GpStatus status = CallFunc(GdipGetImageGraphicsContext)(
760      (GpBitmap*)bitmap, (GpGraphics**)graphics);
761  if (status == Ok) {
762    return TRUE;
763  }
764  return FALSE;
765}
766FX_BOOL CGdiplusExt::GdipCreateFontFamilyFromName(const FX_WCHAR* name,
767                                                  void* pFontCollection,
768                                                  void** pFamily) {
769  CGdiplusExt& GdiplusExt =
770      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
771  GpStatus status = CallFunc(GdipCreateFontFamilyFromName)(
772      (GDIPCONST WCHAR*)name, (GpFontCollection*)pFontCollection,
773      (GpFontFamily**)pFamily);
774  if (status == Ok) {
775    return TRUE;
776  }
777  return FALSE;
778}
779FX_BOOL CGdiplusExt::GdipCreateFontFromFamily(void* pFamily,
780                                              FX_FLOAT font_size,
781                                              int fontstyle,
782                                              int flag,
783                                              void** pFont) {
784  CGdiplusExt& GdiplusExt =
785      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
786  GpStatus status =
787      CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle,
788                               Unit(flag), (GpFont**)pFont);
789  if (status == Ok) {
790    return TRUE;
791  }
792  return FALSE;
793}
794void CGdiplusExt::GdipGetFontSize(void* pFont, FX_FLOAT* size) {
795  REAL get_size;
796  CGdiplusExt& GdiplusExt =
797      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
798  GpStatus status = CallFunc(GdipGetFontSize)((GpFont*)pFont, (REAL*)&get_size);
799  if (status == Ok) {
800    *size = (FX_FLOAT)get_size;
801  } else {
802    *size = 0;
803  }
804}
805void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode) {
806  CGdiplusExt& GdiplusExt =
807      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
808  CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics,
809                                     (TextRenderingHint)mode);
810}
811void CGdiplusExt::GdipSetPageUnit(void* graphics, FX_DWORD unit) {
812  CGdiplusExt& GdiplusExt =
813      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
814  CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);
815}
816FX_BOOL CGdiplusExt::GdipDrawDriverString(void* graphics,
817                                          unsigned short* text,
818                                          int length,
819                                          void* font,
820                                          void* brush,
821                                          void* positions,
822                                          int flags,
823                                          const void* matrix) {
824  CGdiplusExt& GdiplusExt =
825      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
826  GpStatus status = CallFunc(GdipDrawDriverString)(
827      (GpGraphics*)graphics, (GDIPCONST UINT16*)text, (INT)length,
828      (GDIPCONST GpFont*)font, (GDIPCONST GpBrush*)brush,
829      (GDIPCONST PointF*)positions, (INT)flags, (GDIPCONST GpMatrix*)matrix);
830  if (status == Ok) {
831    return TRUE;
832  }
833  return FALSE;
834}
835void CGdiplusExt::GdipCreateBrush(FX_DWORD fill_argb, void** pBrush) {
836  CGdiplusExt& GdiplusExt =
837      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
838  CallFunc(GdipCreateSolidFill)((ARGB)fill_argb, (GpSolidFill**)pBrush);
839}
840void CGdiplusExt::GdipDeleteBrush(void* pBrush) {
841  CGdiplusExt& GdiplusExt =
842      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
843  CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);
844}
845void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection,
846                                                FX_FLOAT font_size,
847                                                int fontstyle) {
848  CGdiplusExt& GdiplusExt =
849      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
850  int numFamilies = 0;
851  GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)(
852      (GpFontCollection*)pFontCollection, &numFamilies);
853  if (status != Ok) {
854    return NULL;
855  }
856  GpFontFamily* family_list[1];
857  status = CallFunc(GdipGetFontCollectionFamilyList)(
858      (GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
859  if (status != Ok) {
860    return NULL;
861  }
862  GpFont* pFont = NULL;
863  status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle,
864                                    UnitPixel, &pFont);
865  if (status != Ok) {
866    return NULL;
867  }
868  return pFont;
869}
870void CGdiplusExt::GdipCreateMatrix(FX_FLOAT a,
871                                   FX_FLOAT b,
872                                   FX_FLOAT c,
873                                   FX_FLOAT d,
874                                   FX_FLOAT e,
875                                   FX_FLOAT f,
876                                   void** matrix) {
877  CGdiplusExt& GdiplusExt =
878      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
879  CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);
880}
881void CGdiplusExt::GdipDeleteMatrix(void* matrix) {
882  CGdiplusExt& GdiplusExt =
883      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
884  CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);
885}
886void CGdiplusExt::GdipDeleteFontFamily(void* pFamily) {
887  CGdiplusExt& GdiplusExt =
888      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
889  CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);
890}
891void CGdiplusExt::GdipDeleteFont(void* pFont) {
892  CGdiplusExt& GdiplusExt =
893      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
894  CallFunc(GdipDeleteFont)((GpFont*)pFont);
895}
896void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix) {
897  CGdiplusExt& GdiplusExt =
898      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
899  CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);
900}
901void CGdiplusExt::GdipDisposeImage(void* bitmap) {
902  CGdiplusExt& GdiplusExt =
903      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
904  CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);
905}
906void CGdiplusExt::GdipDeleteGraphics(void* graphics) {
907  CGdiplusExt& GdiplusExt =
908      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
909  CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);
910}
911FX_BOOL CGdiplusExt::StretchBitMask(HDC hDC,
912                                    BOOL bMonoDevice,
913                                    const CFX_DIBitmap* pBitmap,
914                                    int dest_left,
915                                    int dest_top,
916                                    int dest_width,
917                                    int dest_height,
918                                    FX_DWORD argb,
919                                    const FX_RECT* pClipRect,
920                                    int flags) {
921  ASSERT(pBitmap->GetBPP() == 1);
922  CGdiplusExt& GdiplusExt =
923      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
924  GpGraphics* pGraphics = NULL;
925  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
926  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
927  if (flags & FXDIB_NOSMOOTH) {
928    CallFunc(GdipSetInterpolationMode)(pGraphics,
929                                       InterpolationModeNearestNeighbor);
930  } else {
931    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
932  }
933  OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top,
934                  dest_width, dest_height, argb, pClipRect);
935  CallFunc(GdipDeleteGraphics)(pGraphics);
936  return TRUE;
937}
938FX_BOOL CGdiplusExt::StretchDIBits(HDC hDC,
939                                   const CFX_DIBitmap* pBitmap,
940                                   int dest_left,
941                                   int dest_top,
942                                   int dest_width,
943                                   int dest_height,
944                                   const FX_RECT* pClipRect,
945                                   int flags) {
946  GpGraphics* pGraphics;
947  CGdiplusExt& GdiplusExt =
948      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
949  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
950  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
951  if (flags & FXDIB_NOSMOOTH) {
952    CallFunc(GdipSetInterpolationMode)(pGraphics,
953                                       InterpolationModeNearestNeighbor);
954  } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
955             pBitmap->GetHeight() > abs(dest_height) / 2) {
956    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
957  } else {
958    CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);
959  }
960  FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
961  OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width,
962              dest_height);
963  CallFunc(GdipDeleteGraphics)(pGraphics);
964  CallFunc(GdipDeleteGraphics)(pGraphics);
965  return TRUE;
966}
967static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState,
968                             const CFX_Matrix* pMatrix,
969                             DWORD argb,
970                             FX_BOOL bTextMode = FALSE) {
971  CGdiplusExt& GdiplusExt =
972      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
973  FX_FLOAT width = pGraphState ? pGraphState->m_LineWidth : 1.0f;
974  if (!bTextMode) {
975    FX_FLOAT unit =
976        pMatrix
977            ? FXSYS_Div(1.0f, (pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
978            : 1.0f;
979    if (width < unit) {
980      width = unit;
981    }
982  }
983  GpPen* pPen = NULL;
984  CallFunc(GdipCreatePen1)((ARGB)argb, width, UnitWorld, &pPen);
985  LineCap lineCap;
986  DashCap dashCap = DashCapFlat;
987  FX_BOOL bDashExtend = FALSE;
988  switch (pGraphState->m_LineCap) {
989    case CFX_GraphStateData::LineCapButt:
990      lineCap = LineCapFlat;
991      break;
992    case CFX_GraphStateData::LineCapRound:
993      lineCap = LineCapRound;
994      dashCap = DashCapRound;
995      bDashExtend = TRUE;
996      break;
997    case CFX_GraphStateData::LineCapSquare:
998      lineCap = LineCapSquare;
999      bDashExtend = TRUE;
1000      break;
1001  }
1002  CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
1003  LineJoin lineJoin;
1004  switch (pGraphState->m_LineJoin) {
1005    case CFX_GraphStateData::LineJoinMiter:
1006      lineJoin = LineJoinMiterClipped;
1007      break;
1008    case CFX_GraphStateData::LineJoinRound:
1009      lineJoin = LineJoinRound;
1010      break;
1011    case CFX_GraphStateData::LineJoinBevel:
1012      lineJoin = LineJoinBevel;
1013      break;
1014  }
1015  CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
1016  if (pGraphState->m_DashCount) {
1017    FX_FLOAT* pDashArray = FX_Alloc(
1018        FX_FLOAT, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);
1019    int nCount = 0;
1020    FX_FLOAT on_leftover = 0, off_leftover = 0;
1021    for (int i = 0; i < pGraphState->m_DashCount; i += 2) {
1022      FX_FLOAT on_phase = pGraphState->m_DashArray[i];
1023      FX_FLOAT off_phase;
1024      if (i == pGraphState->m_DashCount - 1) {
1025        off_phase = on_phase;
1026      } else {
1027        off_phase = pGraphState->m_DashArray[i + 1];
1028      }
1029      on_phase /= width;
1030      off_phase /= width;
1031      if (on_phase + off_phase <= 0.00002f) {
1032        on_phase = 1.0f / 10;
1033        off_phase = 1.0f / 10;
1034      }
1035      if (bDashExtend) {
1036        if (off_phase < 1) {
1037          off_phase = 0;
1038        } else {
1039          off_phase -= 1;
1040        }
1041        on_phase += 1;
1042      }
1043      if (on_phase == 0 || off_phase == 0) {
1044        if (nCount == 0) {
1045          on_leftover += on_phase;
1046          off_leftover += off_phase;
1047        } else {
1048          pDashArray[nCount - 2] += on_phase;
1049          pDashArray[nCount - 1] += off_phase;
1050        }
1051      } else {
1052        pDashArray[nCount++] = on_phase + on_leftover;
1053        on_leftover = 0;
1054        pDashArray[nCount++] = off_phase + off_leftover;
1055        off_leftover = 0;
1056      }
1057    }
1058    CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
1059    FX_FLOAT phase = pGraphState->m_DashPhase;
1060    if (bDashExtend) {
1061      if (phase < 0.5f) {
1062        phase = 0;
1063      } else {
1064        phase -= 0.5f;
1065      }
1066    }
1067    CallFunc(GdipSetPenDashOffset)(pPen, phase);
1068    FX_Free(pDashArray);
1069    pDashArray = NULL;
1070  }
1071  CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
1072  return pPen;
1073}
1074static FX_BOOL IsSmallTriangle(PointF* points,
1075                               const CFX_Matrix* pMatrix,
1076                               int& v1,
1077                               int& v2) {
1078  int pairs[] = {1, 2, 0, 2, 0, 1};
1079  for (int i = 0; i < 3; i++) {
1080    int pair1 = pairs[i * 2];
1081    int pair2 = pairs[i * 2 + 1];
1082    FX_FLOAT x1 = points[pair1].X, x2 = points[pair2].X;
1083    FX_FLOAT y1 = points[pair1].Y, y2 = points[pair2].Y;
1084    if (pMatrix) {
1085      pMatrix->Transform(x1, y1);
1086      pMatrix->Transform(x2, y2);
1087    }
1088    FX_FLOAT dx = x1 - x2;
1089    FX_FLOAT dy = y1 - y2;
1090    FX_FLOAT distance_square = FXSYS_Mul(dx, dx) + FXSYS_Mul(dy, dy);
1091    if (distance_square < (1.0f * 2 + 1.0f / 4)) {
1092      v1 = i;
1093      v2 = pair1;
1094      return TRUE;
1095    }
1096  }
1097  return FALSE;
1098}
1099FX_BOOL CGdiplusExt::DrawPath(HDC hDC,
1100                              const CFX_PathData* pPathData,
1101                              const CFX_Matrix* pObject2Device,
1102                              const CFX_GraphStateData* pGraphState,
1103                              FX_DWORD fill_argb,
1104                              FX_DWORD stroke_argb,
1105                              int fill_mode) {
1106  int nPoints = pPathData->GetPointCount();
1107  if (nPoints == 0) {
1108    return TRUE;
1109  }
1110  FX_PATHPOINT* pPoints = pPathData->GetPoints();
1111  GpGraphics* pGraphics = NULL;
1112  CGdiplusExt& GdiplusExt =
1113      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1114  CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
1115  CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
1116  CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
1117  GpMatrix* pMatrix = NULL;
1118  if (pObject2Device) {
1119    CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
1120                                pObject2Device->c, pObject2Device->d,
1121                                pObject2Device->e, pObject2Device->f, &pMatrix);
1122    CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
1123  }
1124  PointF* points = FX_Alloc(PointF, nPoints);
1125  BYTE* types = FX_Alloc(BYTE, nPoints);
1126  int nSubPathes = 0;
1127  FX_BOOL bSubClose = FALSE;
1128  int pos_subclose = 0;
1129  FX_BOOL bSmooth = FALSE;
1130  int startpoint = 0;
1131  for (int i = 0; i < nPoints; i++) {
1132    points[i].X = pPoints[i].m_PointX;
1133    points[i].Y = pPoints[i].m_PointY;
1134    FX_FLOAT x, y;
1135    if (pObject2Device) {
1136      pObject2Device->Transform(pPoints[i].m_PointX, pPoints[i].m_PointY, x, y);
1137    } else {
1138      x = pPoints[i].m_PointX;
1139      y = pPoints[i].m_PointY;
1140    }
1141    if (x > 50000 * 1.0f) {
1142      points[i].X = 50000 * 1.0f;
1143    }
1144    if (x < -50000 * 1.0f) {
1145      points[i].X = -50000 * 1.0f;
1146    }
1147    if (y > 50000 * 1.0f) {
1148      points[i].Y = 50000 * 1.0f;
1149    }
1150    if (y < -50000 * 1.0f) {
1151      points[i].Y = -50000 * 1.0f;
1152    }
1153    int point_type = pPoints[i].m_Flag & FXPT_TYPE;
1154    if (point_type == FXPT_MOVETO) {
1155      types[i] = PathPointTypeStart;
1156      nSubPathes++;
1157      bSubClose = FALSE;
1158      startpoint = i;
1159    } else if (point_type == FXPT_LINETO) {
1160      types[i] = PathPointTypeLine;
1161      if (pPoints[i - 1].m_Flag == FXPT_MOVETO &&
1162          (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&
1163          points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
1164        points[i].X += 0.01f;
1165        continue;
1166      }
1167      if (!bSmooth && points[i].X != points[i - 1].X &&
1168          points[i].Y != points[i - 1].Y) {
1169        bSmooth = TRUE;
1170      }
1171    } else if (point_type == FXPT_BEZIERTO) {
1172      types[i] = PathPointTypeBezier;
1173      bSmooth = TRUE;
1174    }
1175    if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {
1176      if (bSubClose) {
1177        types[pos_subclose] &= ~PathPointTypeCloseSubpath;
1178      } else {
1179        bSubClose = TRUE;
1180      }
1181      pos_subclose = i;
1182      types[i] |= PathPointTypeCloseSubpath;
1183      if (!bSmooth && points[i].X != points[startpoint].X &&
1184          points[i].Y != points[startpoint].Y) {
1185        bSmooth = TRUE;
1186      }
1187    }
1188  }
1189  if (fill_mode & FXFILL_NOPATHSMOOTH) {
1190    bSmooth = FALSE;
1191    CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
1192  } else if (!(fill_mode & FXFILL_FULLCOVER)) {
1193    if (!bSmooth && (fill_mode & 3)) {
1194      bSmooth = TRUE;
1195    }
1196    if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
1197      CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
1198    }
1199  }
1200  int new_fill_mode = fill_mode & 3;
1201  if (nPoints == 4 && !pGraphState) {
1202    int v1, v2;
1203    if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
1204      GpPen* pPen = NULL;
1205      CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
1206      CallFunc(GdipDrawLineI)(
1207          pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
1208          FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
1209      CallFunc(GdipDeletePen)(pPen);
1210      return TRUE;
1211    }
1212  }
1213  GpPath* pGpPath = NULL;
1214  CallFunc(GdipCreatePath2)(points, types, nPoints,
1215                            GdiFillType2Gdip(new_fill_mode), &pGpPath);
1216  if (!pGpPath) {
1217    if (pMatrix) {
1218      CallFunc(GdipDeleteMatrix)(pMatrix);
1219    }
1220    FX_Free(points);
1221    FX_Free(types);
1222    CallFunc(GdipDeleteGraphics)(pGraphics);
1223    return FALSE;
1224  }
1225  if (new_fill_mode) {
1226    GpBrush* pBrush = _GdipCreateBrush(fill_argb);
1227    CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
1228    CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
1229    CallFunc(GdipDeleteBrush)(pBrush);
1230  }
1231  if (pGraphState && stroke_argb) {
1232    GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb,
1233                                 fill_mode & FX_STROKE_TEXT_MODE);
1234    if (nSubPathes == 1) {
1235      CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
1236    } else {
1237      int iStart = 0;
1238      for (int i = 0; i < nPoints; i++) {
1239        if (i == nPoints - 1 || types[i + 1] == PathPointTypeStart) {
1240          GpPath* pSubPath;
1241          CallFunc(GdipCreatePath2)(points + iStart, types + iStart,
1242                                    i - iStart + 1,
1243                                    GdiFillType2Gdip(new_fill_mode), &pSubPath);
1244          iStart = i + 1;
1245          CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
1246          CallFunc(GdipDeletePath)(pSubPath);
1247        }
1248      }
1249    }
1250    CallFunc(GdipDeletePen)(pPen);
1251  }
1252  if (pMatrix) {
1253    CallFunc(GdipDeleteMatrix)(pMatrix);
1254  }
1255  FX_Free(points);
1256  FX_Free(types);
1257  CallFunc(GdipDeletePath)(pGpPath);
1258  CallFunc(GdipDeleteGraphics)(pGraphics);
1259  return TRUE;
1260}
1261class GpStream final : public IStream {
1262  LONG m_RefCount;
1263  int m_ReadPos;
1264  CFX_ByteTextBuf m_InterStream;
1265
1266 public:
1267  GpStream() {
1268    m_RefCount = 1;
1269    m_ReadPos = 0;
1270  }
1271  virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
1272                                                   void** ppvObject) {
1273    if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
1274        iid == __uuidof(ISequentialStream)) {
1275      *ppvObject = static_cast<IStream*>(this);
1276      AddRef();
1277      return S_OK;
1278    }
1279    return E_NOINTERFACE;
1280  }
1281  virtual ULONG STDMETHODCALLTYPE AddRef(void) {
1282    return (ULONG)InterlockedIncrement(&m_RefCount);
1283  }
1284  virtual ULONG STDMETHODCALLTYPE Release(void) {
1285    ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
1286    if (res == 0) {
1287      delete this;
1288    }
1289    return res;
1290  }
1291
1292 public:
1293  virtual HRESULT STDMETHODCALLTYPE Read(void* Output,
1294                                         ULONG cb,
1295                                         ULONG* pcbRead) {
1296    size_t bytes_left;
1297    size_t bytes_out;
1298    if (pcbRead) {
1299      *pcbRead = 0;
1300    }
1301    if (m_ReadPos == m_InterStream.GetLength()) {
1302      return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
1303    }
1304    bytes_left = m_InterStream.GetLength() - m_ReadPos;
1305    bytes_out = FX_MIN(cb, bytes_left);
1306    FXSYS_memcpy(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);
1307    m_ReadPos += (int32_t)bytes_out;
1308    if (pcbRead) {
1309      *pcbRead = (ULONG)bytes_out;
1310    }
1311    return S_OK;
1312  }
1313  virtual HRESULT STDMETHODCALLTYPE Write(void const* Input,
1314                                          ULONG cb,
1315                                          ULONG* pcbWritten) {
1316    if (cb <= 0) {
1317      if (pcbWritten) {
1318        *pcbWritten = 0;
1319      }
1320      return S_OK;
1321    }
1322    m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);
1323    if (pcbWritten) {
1324      *pcbWritten = cb;
1325    }
1326    return S_OK;
1327  }
1328
1329 public:
1330  virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) {
1331    return E_NOTIMPL;
1332  }
1333  virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
1334                                           ULARGE_INTEGER,
1335                                           ULARGE_INTEGER*,
1336                                           ULARGE_INTEGER*) {
1337    return E_NOTIMPL;
1338  }
1339  virtual HRESULT STDMETHODCALLTYPE Commit(DWORD) { return E_NOTIMPL; }
1340  virtual HRESULT STDMETHODCALLTYPE Revert(void) { return E_NOTIMPL; }
1341  virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
1342                                               ULARGE_INTEGER,
1343                                               DWORD) {
1344    return E_NOTIMPL;
1345  }
1346  virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
1347                                                 ULARGE_INTEGER,
1348                                                 DWORD) {
1349    return E_NOTIMPL;
1350  }
1351  virtual HRESULT STDMETHODCALLTYPE Clone(IStream**) { return E_NOTIMPL; }
1352  virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
1353                                         DWORD dwOrigin,
1354                                         ULARGE_INTEGER* lpNewFilePointer) {
1355    long start = 0;
1356    long new_read_position;
1357    switch (dwOrigin) {
1358      case STREAM_SEEK_SET:
1359        start = 0;
1360        break;
1361      case STREAM_SEEK_CUR:
1362        start = m_ReadPos;
1363        break;
1364      case STREAM_SEEK_END:
1365        start = m_InterStream.GetLength();
1366        break;
1367      default:
1368        return STG_E_INVALIDFUNCTION;
1369        break;
1370    }
1371    new_read_position = start + (long)liDistanceToMove.QuadPart;
1372    if (new_read_position < 0 ||
1373        new_read_position > m_InterStream.GetLength()) {
1374      return STG_E_SEEKERROR;
1375    }
1376    m_ReadPos = new_read_position;
1377    if (lpNewFilePointer) {
1378      lpNewFilePointer->QuadPart = m_ReadPos;
1379    }
1380    return S_OK;
1381  }
1382  virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag) {
1383    if (!pStatstg) {
1384      return STG_E_INVALIDFUNCTION;
1385    }
1386    ZeroMemory(pStatstg, sizeof(STATSTG));
1387    pStatstg->cbSize.QuadPart = m_InterStream.GetLength();
1388    return S_OK;
1389  }
1390};
1391typedef struct {
1392  BITMAPINFO* pbmi;
1393  int Stride;
1394  LPBYTE pScan0;
1395  GpBitmap* pBitmap;
1396  BitmapData* pBitmapData;
1397  GpStream* pStream;
1398} PREVIEW3_DIBITMAP;
1399static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
1400  GpBitmap* pBitmap;
1401  GpStream* pStream = NULL;
1402  CGdiplusExt& GdiplusExt =
1403      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1404  Status status = Ok;
1405  if (args.flags == WINDIB_OPEN_PATHNAME) {
1406    status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name,
1407                                                   &pBitmap);
1408  } else {
1409    if (args.memory_size == 0 || !args.memory_base) {
1410      return NULL;
1411    }
1412    pStream = new GpStream;
1413    pStream->Write(args.memory_base, (ULONG)args.memory_size, NULL);
1414    status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
1415  }
1416  if (status != Ok) {
1417    if (pStream) {
1418      pStream->Release();
1419    }
1420    return NULL;
1421  }
1422  UINT height, width;
1423  CallFunc(GdipGetImageHeight)(pBitmap, &height);
1424  CallFunc(GdipGetImageWidth)(pBitmap, &width);
1425  PixelFormat pixel_format;
1426  CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
1427  int info_size = sizeof(BITMAPINFOHEADER);
1428  int bpp = 24;
1429  int dest_pixel_format = PixelFormat24bppRGB;
1430  if (pixel_format == PixelFormat1bppIndexed) {
1431    info_size += 8;
1432    bpp = 1;
1433    dest_pixel_format = PixelFormat1bppIndexed;
1434  } else if (pixel_format == PixelFormat8bppIndexed) {
1435    info_size += 1024;
1436    bpp = 8;
1437    dest_pixel_format = PixelFormat8bppIndexed;
1438  } else if (pixel_format == PixelFormat32bppARGB) {
1439    bpp = 32;
1440    dest_pixel_format = PixelFormat32bppARGB;
1441  }
1442  LPBYTE buf = FX_Alloc(BYTE, info_size);
1443  BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
1444  pbmih->biBitCount = bpp;
1445  pbmih->biCompression = BI_RGB;
1446  pbmih->biHeight = -(int)height;
1447  pbmih->biPlanes = 1;
1448  pbmih->biWidth = width;
1449  Rect rect(0, 0, width, height);
1450  BitmapData* pBitmapData = FX_Alloc(BitmapData, 1);
1451  CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
1452                               dest_pixel_format, pBitmapData);
1453  if (pixel_format == PixelFormat1bppIndexed ||
1454      pixel_format == PixelFormat8bppIndexed) {
1455    DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
1456    struct {
1457      UINT flags;
1458      UINT Count;
1459      DWORD Entries[256];
1460    } pal;
1461    int size = 0;
1462    CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
1463    CallFunc(GdipGetImagePalette)(pBitmap, (ColorPalette*)&pal, size);
1464    int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
1465    for (int i = 0; i < entries; i++) {
1466      ppal[i] = pal.Entries[i] & 0x00ffffff;
1467    }
1468  }
1469  PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
1470  pInfo->pbmi = (BITMAPINFO*)buf;
1471  pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
1472  pInfo->Stride = pBitmapData->Stride;
1473  pInfo->pBitmap = pBitmap;
1474  pInfo->pBitmapData = pBitmapData;
1475  pInfo->pStream = pStream;
1476  return pInfo;
1477}
1478static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
1479  CGdiplusExt& GdiplusExt =
1480      ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1481  CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
1482  CallFunc(GdipDisposeImage)(pInfo->pBitmap);
1483  FX_Free(pInfo->pBitmapData);
1484  FX_Free((LPBYTE)pInfo->pbmi);
1485  if (pInfo->pStream) {
1486    pInfo->pStream->Release();
1487  }
1488  FX_Free(pInfo);
1489}
1490CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
1491                                         LPVOID pData,
1492                                         FX_BOOL bAlpha);
1493CFX_DIBitmap* CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) {
1494  PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
1495  if (!pInfo) {
1496    return NULL;
1497  }
1498  int height = abs(pInfo->pbmi->bmiHeader.biHeight);
1499  int width = pInfo->pbmi->bmiHeader.biWidth;
1500  int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
1501  LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
1502  if (dest_pitch == pInfo->Stride) {
1503    FXSYS_memcpy(pData, pInfo->pScan0, dest_pitch * height);
1504  } else {
1505    for (int i = 0; i < height; i++) {
1506      FXSYS_memcpy(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i,
1507                   dest_pitch);
1508    }
1509  }
1510  CFX_DIBitmap* pDIBitmap = _FX_WindowsDIB_LoadFromBuf(
1511      pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
1512  FX_Free(pData);
1513  FreeDIBitmap(pInfo);
1514  return pDIBitmap;
1515}
1516#endif
1517