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