Merge to XFA: Remove more cruft from fx_system.h
[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 FX_UINTPTR *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     FX_LPBYTE 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     FX_LPBYTE 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     FX_UINTPTR 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(FX_LPCWSTR 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         CallFunc(GdipSetPenDashOffset)(pPen, phase);
820         FX_Free(pDashArray);
821         pDashArray = NULL;
822     }
823     CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
824     return pPen;
825 }
826 static BOOL IsSmallTriangle(PointF* points, const CFX_AffineMatrix* pMatrix, int& v1, int& v2)
827 {
828     int pairs[] = {1, 2, 0, 2, 0, 1};
829     for (int i = 0; i < 3; i ++) {
830         int pair1 = pairs[i * 2];
831         int pair2 = pairs[i * 2 + 1];
832         FX_FLOAT x1 = points[pair1].X, x2 = points[pair2].X;
833         FX_FLOAT y1 = points[pair1].Y, y2 = points[pair2].Y;
834         if (pMatrix) {
835             pMatrix->Transform(x1, y1);
836             pMatrix->Transform(x2, y2);
837         }
838         FX_FLOAT dx = x1 - x2;
839         FX_FLOAT dy = y1 - y2;
840         FX_FLOAT distance_square = FXSYS_Mul(dx, dx) + FXSYS_Mul(dy, dy);
841         if (distance_square < (1.0f * 2 + 1.0f / 4)) {
842             v1 = i;
843             v2 = pair1;
844             return TRUE;
845         }
846     }
847     return FALSE;
848 }
849 BOOL CGdiplusExt::DrawPath(HDC hDC, const CFX_PathData* pPathData,
850                            const CFX_AffineMatrix* pObject2Device,
851                            const CFX_GraphStateData* pGraphState,
852                            FX_DWORD fill_argb,
853                            FX_DWORD stroke_argb,
854                            int fill_mode
855                           )
856 {
857     int nPoints = pPathData->GetPointCount();
858     if (nPoints == 0) {
859         return TRUE;
860     }
861     FX_PATHPOINT* pPoints = pPathData->GetPoints();
862     GpGraphics* pGraphics = NULL;
863     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
864     CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
865     CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
866     CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
867     GpMatrix* pMatrix = NULL;
868     if (pObject2Device) {
869         CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b, pObject2Device->c, pObject2Device->d, pObject2Device->e, pObject2Device->f, &pMatrix);
870         CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
871     }
872     PointF *points = FX_Alloc(PointF, nPoints);
873     BYTE * types  = FX_Alloc(BYTE, nPoints);
874     int nSubPathes = 0;
875     FX_BOOL bSubClose = FALSE;
876     int pos_subclose = 0;
877     FX_BOOL bSmooth = FALSE;
878     int startpoint = 0;
879     for(int i = 0; i < nPoints; i++) {
880         points[i].X = pPoints[i].m_PointX;
881         points[i].Y = pPoints[i].m_PointY;
882         FX_FLOAT x, y;
883         if (pObject2Device) {
884             pObject2Device->Transform(pPoints[i].m_PointX, pPoints[i].m_PointY, x, y);
885         } else {
886             x = pPoints[i].m_PointX;
887             y = pPoints[i].m_PointY;
888         }
889         if (x > 50000 * 1.0f) {
890             points[i].X = 50000 * 1.0f;
891         }
892         if (x < -50000 * 1.0f) {
893             points[i].X = -50000 * 1.0f;
894         }
895         if (y > 50000 * 1.0f) {
896             points[i].Y = 50000 * 1.0f;
897         }
898         if (y < -50000 * 1.0f) {
899             points[i].Y = -50000 * 1.0f;
900         }
901         int point_type = pPoints[i].m_Flag & FXPT_TYPE;
902         if(point_type == FXPT_MOVETO) {
903             types[i] = PathPointTypeStart;
904             nSubPathes ++;
905             bSubClose = FALSE;
906             startpoint = i;
907         } else if (point_type == FXPT_LINETO) {
908             types[i] = PathPointTypeLine;
909             if (pPoints[i - 1].m_Flag == FXPT_MOVETO && (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&
910                     points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
911                 points[i].X += 0.01f;
912                 continue;
913             }
914             if (!bSmooth && points[i].X != points[i - 1].X && points[i].Y != points[i - 1].Y) {
915                 bSmooth = TRUE;
916             }
917         } else if (point_type == FXPT_BEZIERTO) {
918             types[i] = PathPointTypeBezier;
919             bSmooth = TRUE;
920         }
921         if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {
922             if (bSubClose) {
923                 types[pos_subclose] &= ~PathPointTypeCloseSubpath;
924             } else {
925                 bSubClose = TRUE;
926             }
927             pos_subclose = i;
928             types[i] |= PathPointTypeCloseSubpath;
929             if (!bSmooth && points[i].X != points[startpoint].X && points[i].Y != points[startpoint].Y) {
930                 bSmooth = TRUE;
931             }
932         }
933     }
934     if (fill_mode & FXFILL_NOPATHSMOOTH) {
935         bSmooth = FALSE;
936         CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
937     } else if (!(fill_mode & FXFILL_FULLCOVER)) {
938         if (!bSmooth && (fill_mode & 3)) {
939             bSmooth = TRUE;
940         }
941         if (bSmooth || pGraphState && pGraphState->m_LineWidth > 2) {
942             CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
943         }
944     }
945     int new_fill_mode = fill_mode & 3;
946     if (nPoints == 4 && pGraphState == NULL) {
947         int v1, v2;
948         if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
949             GpPen* pPen = NULL;
950             CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
951             CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
952                                     FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
953             CallFunc(GdipDeletePen)(pPen);
954             return TRUE;
955         }
956     }
957     GpPath* pGpPath = NULL;
958     CallFunc(GdipCreatePath2)(points, types, nPoints, GdiFillType2Gdip(new_fill_mode), &pGpPath);
959     if (!pGpPath) {
960         if (pMatrix) {
961             CallFunc(GdipDeleteMatrix)(pMatrix);
962         }
963         FX_Free(points);
964         FX_Free(types);
965         CallFunc(GdipDeleteGraphics)(pGraphics);
966         return FALSE;
967     }
968     if (new_fill_mode) {
969         GpBrush* pBrush = _GdipCreateBrush(fill_argb);
970         CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
971         CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
972         CallFunc(GdipDeleteBrush)(pBrush);
973     }
974     if (pGraphState && stroke_argb) {
975         GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb, fill_mode & FX_STROKE_TEXT_MODE);
976         if (nSubPathes == 1) {
977             CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
978         } else {
979             int iStart = 0;
980             for (int i = 0; i < nPoints; i ++) {
981                 if (i == nPoints - 1 || types[i + 1] == PathPointTypeStart) {
982                     GpPath* pSubPath;
983                     CallFunc(GdipCreatePath2)(points + iStart, types + iStart, i - iStart + 1, GdiFillType2Gdip(new_fill_mode), &pSubPath);
984                     iStart = i + 1;
985                     CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
986                     CallFunc(GdipDeletePath)(pSubPath);
987                 }
988             }
989         }
990         CallFunc(GdipDeletePen)(pPen);
991     }
992     if (pMatrix) {
993         CallFunc(GdipDeleteMatrix)(pMatrix);
994     }
995     FX_Free(points);
996     FX_Free(types);
997     CallFunc(GdipDeletePath)(pGpPath);
998     CallFunc(GdipDeleteGraphics)(pGraphics);
999     return TRUE;
1000 }
1001 class GpStream final : public IStream
1002 {
1003     LONG        m_RefCount;
1004     int     m_ReadPos;
1005     CFX_ByteTextBuf     m_InterStream;
1006 public:
1007     GpStream()
1008     {
1009         m_RefCount = 1;
1010         m_ReadPos = 0;
1011     }
1012     virtual HRESULT STDMETHODCALLTYPE
1013     QueryInterface(REFIID iid, void ** ppvObject)
1014     {
1015         if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
1016                 iid == __uuidof(ISequentialStream))     {
1017             *ppvObject = static_cast<IStream*>(this);
1018             AddRef();
1019             return S_OK;
1020         } else {
1021             return E_NOINTERFACE;
1022         }
1023     }
1024     virtual ULONG STDMETHODCALLTYPE AddRef(void)
1025     {
1026         return (ULONG)InterlockedIncrement(&m_RefCount);
1027     }
1028     virtual ULONG STDMETHODCALLTYPE Release(void)
1029     {
1030         ULONG res = (ULONG) InterlockedDecrement(&m_RefCount);
1031         if (res == 0) {
1032             delete this;
1033         }
1034         return res;
1035     }
1036 public:
1037     virtual HRESULT STDMETHODCALLTYPE Read(void* Output, ULONG cb, ULONG* pcbRead)
1038     {
1039         size_t  bytes_left;
1040         size_t  bytes_out;
1041         if (pcbRead != NULL) {
1042             *pcbRead = 0;
1043         }
1044         if (m_ReadPos == m_InterStream.GetLength()) {
1045             return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
1046         }
1047         bytes_left = m_InterStream.GetLength() - m_ReadPos;
1048         bytes_out = FX_MIN(cb, bytes_left);
1049         FXSYS_memcpy32(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);
1050         m_ReadPos += (FX_INT32)bytes_out;
1051         if (pcbRead != NULL) {
1052             *pcbRead = (ULONG)bytes_out;
1053         }
1054         return S_OK;
1055     }
1056     virtual HRESULT STDMETHODCALLTYPE Write(void const* Input, ULONG cb, ULONG* pcbWritten)
1057     {
1058         if (cb <= 0) {
1059             if (pcbWritten != NULL) {
1060                 *pcbWritten = 0;
1061             }
1062             return S_OK;
1063         }
1064         m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);
1065         if (pcbWritten != NULL) {
1066             *pcbWritten = cb;
1067         }
1068         return S_OK;
1069     }
1070 public:
1071     virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER)
1072     {
1073         return E_NOTIMPL;
1074     }
1075     virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*)
1076     {
1077         return E_NOTIMPL;
1078     }
1079     virtual HRESULT STDMETHODCALLTYPE Commit(DWORD)
1080     {
1081         return E_NOTIMPL;
1082     }
1083     virtual HRESULT STDMETHODCALLTYPE Revert(void)
1084     {
1085         return E_NOTIMPL;
1086     }
1087     virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
1088     {
1089         return E_NOTIMPL;
1090     }
1091     virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
1092     {
1093         return E_NOTIMPL;
1094     }
1095     virtual HRESULT STDMETHODCALLTYPE Clone(IStream **)
1096     {
1097         return E_NOTIMPL;
1098     }
1099     virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, DWORD dwOrigin, ULARGE_INTEGER* lpNewFilePointer)
1100     {
1101         long    start = 0;
1102         long    new_read_position;
1103         switch(dwOrigin) {
1104             case STREAM_SEEK_SET:
1105                 start = 0;
1106                 break;
1107             case STREAM_SEEK_CUR:
1108                 start = m_ReadPos;
1109                 break;
1110             case STREAM_SEEK_END:
1111                 start = m_InterStream.GetLength();
1112                 break;
1113             default:
1114                 return STG_E_INVALIDFUNCTION;
1115                 break;
1116         }
1117         new_read_position = start + (long)liDistanceToMove.QuadPart;
1118         if (new_read_position < 0 || new_read_position > m_InterStream.GetLength()) {
1119             return STG_E_SEEKERROR;
1120         }
1121         m_ReadPos = new_read_position;
1122         if (lpNewFilePointer != NULL) {
1123             lpNewFilePointer->QuadPart = m_ReadPos;
1124         }
1125         return S_OK;
1126     }
1127     virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag)
1128     {
1129         if (pStatstg == NULL) {
1130             return STG_E_INVALIDFUNCTION;
1131         }
1132         ZeroMemory(pStatstg, sizeof(STATSTG));
1133         pStatstg->cbSize.QuadPart = m_InterStream.GetLength();
1134         return S_OK;
1135     }
1136 };
1137 typedef struct {
1138     BITMAPINFO*         pbmi;
1139     int                         Stride;
1140     LPBYTE                      pScan0;
1141     GpBitmap*           pBitmap;
1142     BitmapData*         pBitmapData;
1143     GpStream*       pStream;
1144 } PREVIEW3_DIBITMAP;
1145 static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args)
1146 {
1147     GpBitmap* pBitmap;
1148     GpStream* pStream = NULL;
1149     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1150     Status status = Ok;
1151     if (args.flags == WINDIB_OPEN_PATHNAME) {
1152         status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name, &pBitmap);
1153     } else {
1154         if (args.memory_size == 0 || !args.memory_base) {
1155             return NULL;
1156         }
1157         pStream = FX_NEW GpStream;
1158         if (!pStream) {
1159             return NULL;
1160         }
1161         pStream->Write(args.memory_base, (ULONG)args.memory_size, NULL);
1162         status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
1163     }
1164     if (status != Ok) {
1165         if (pStream) {
1166             pStream->Release();
1167         }
1168         return NULL;
1169     }
1170     UINT height, width;
1171     CallFunc(GdipGetImageHeight)(pBitmap, &height);
1172     CallFunc(GdipGetImageWidth)(pBitmap, &width);
1173     PixelFormat pixel_format;
1174     CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
1175     int info_size = sizeof(BITMAPINFOHEADER);
1176     int bpp = 24;
1177     int dest_pixel_format = PixelFormat24bppRGB;
1178     if (pixel_format == PixelFormat1bppIndexed) {
1179         info_size += 8;
1180         bpp = 1;
1181         dest_pixel_format = PixelFormat1bppIndexed;
1182     } else if (pixel_format == PixelFormat8bppIndexed) {
1183         info_size += 1024;
1184         bpp = 8;
1185         dest_pixel_format = PixelFormat8bppIndexed;
1186     } else if (pixel_format == PixelFormat32bppARGB) {
1187         bpp = 32;
1188         dest_pixel_format = PixelFormat32bppARGB;
1189     }
1190     LPBYTE buf = FX_Alloc(BYTE, info_size);
1191     BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
1192     pbmih->biBitCount = bpp;
1193     pbmih->biCompression = BI_RGB;
1194     pbmih->biHeight = -(int)height;
1195     pbmih->biPlanes = 1;
1196     pbmih->biWidth = width;
1197     Rect rect(0, 0, width, height);
1198     BitmapData* pBitmapData = FX_Alloc(BitmapData, 1);
1199     CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
1200                                  dest_pixel_format, pBitmapData);
1201     if (pixel_format == PixelFormat1bppIndexed || pixel_format == PixelFormat8bppIndexed) {
1202         DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
1203         struct {
1204             UINT flags;
1205             UINT Count;
1206             DWORD Entries[256];
1207         } pal;
1208         int size = 0;
1209         CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
1210         CallFunc(GdipGetImagePalette)(pBitmap, (ColorPalette*)&pal, size);
1211         int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
1212         for (int i = 0; i < entries; i ++) {
1213             ppal[i] = pal.Entries[i] & 0x00ffffff;
1214         }
1215     }
1216     PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
1217     pInfo->pbmi = (BITMAPINFO*)buf;
1218     pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
1219     pInfo->Stride = pBitmapData->Stride;
1220     pInfo->pBitmap = pBitmap;
1221     pInfo->pBitmapData = pBitmapData;
1222     pInfo->pStream = pStream;
1223     return pInfo;
1224 }
1225 static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo)
1226 {
1227     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1228     CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
1229     CallFunc(GdipDisposeImage)(pInfo->pBitmap);
1230     FX_Free(pInfo->pBitmapData);
1231     FX_Free((LPBYTE)pInfo->pbmi);
1232     if (pInfo->pStream) {
1233         pInfo->pStream->Release();
1234     }
1235     FX_Free(pInfo);
1236 }
1237 CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, LPVOID pData, FX_BOOL bAlpha);
1238 CFX_DIBitmap* CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args)
1239 {
1240     PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
1241     if (pInfo == NULL) {
1242         return NULL;
1243     }
1244     int height = abs(pInfo->pbmi->bmiHeader.biHeight);
1245     int width = pInfo->pbmi->bmiHeader.biWidth;
1246     int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
1247     LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
1248     if (dest_pitch == pInfo->Stride) {
1249         FXSYS_memcpy32(pData, pInfo->pScan0, dest_pitch * height);
1250     } else {
1251         for (int i = 0; i < height; i ++) {
1252             FXSYS_memcpy32(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i, dest_pitch);
1253         }
1254     }
1255     CFX_DIBitmap* pDIBitmap = _FX_WindowsDIB_LoadFromBuf(pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
1256     FX_Free(pData);
1257     FreeDIBitmap(pInfo);
1258     return pDIBitmap;
1259 }
1260 #endif