Add safe FX_Alloc2D() macro
[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_
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 = new CFX_DIBitmap;
306     if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb)) {
307         delete pStretched;
308         return NULL;
309     }
310     LPBYTE dest_buf = pStretched->GetBuffer();
311     int src_width = pSource->GetWidth();
312     int src_height = pSource->GetHeight();
313     int src_count = src_width * src_height;
314     int dest_count = dest_width * dest_height;
315     int ratio = 255 * dest_count / src_count;
316     int y_unit = src_height / dest_height;
317     int x_unit = src_width / dest_width;
318     int area_unit = y_unit * x_unit;
319     LPBYTE src_buf = pSource->GetBuffer();
320     int src_pitch = pSource->GetPitch();
321     for (int dest_y = 0; dest_y < result_height; dest_y ++) {
322         LPBYTE dest_scan = dest_buf + dest_y * result_pitch;
323         int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top) : (dest_y + pClipRect->top);
324         src_y_start = src_y_start * src_height / dest_height;
325         LPBYTE src_scan = src_buf + src_y_start * src_pitch;
326         for (int dest_x = 0; dest_x < result_width; dest_x ++) {
327             int sum = 0;
328             int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left) : (dest_x + pClipRect->left);
329             src_x_start = src_x_start * src_width / dest_width;
330             int src_x_end = src_x_start + x_unit;
331             LPBYTE src_line = src_scan;
332             for (int src_y = 0; src_y < y_unit; src_y ++) {
333                 for (int src_x = src_x_start; src_x < src_x_end; src_x ++) {
334                     if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {
335                         sum += 255;
336                     }
337                 }
338                 src_line += src_pitch;
339             }
340             dest_scan[dest_x] = 255 - sum / area_unit;
341         }
342     }
343     return pStretched;
344 }
345 static void OutputImageMask(GpGraphics* pGraphics, BOOL bMonoDevice, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,
346                             int dest_width, int dest_height, FX_ARGB argb, const FX_RECT* pClipRect)
347 {
348     ASSERT(pBitmap->GetBPP() == 1);
349     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
350     int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();
351     int src_pitch = pBitmap->GetPitch();
352     FX_LPBYTE scan0 = pBitmap->GetBuffer();
353     if (src_width == 1 && src_height == 1) {
354         if ((scan0[0] & 0x80) == 0) {
355             return;
356         }
357         GpSolidFill* solidBrush;
358         CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);
359         if (dest_width < 0) {
360             dest_width = -dest_width;
361             dest_left -= dest_width;
362         }
363         if (dest_height < 0) {
364             dest_height = -dest_height;
365             dest_top -= dest_height;
366         }
367         CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left, (float)dest_top,
368                                     (float)dest_width, (float)dest_height);
369         CallFunc(GdipDeleteBrush)(solidBrush);
370         return;
371     }
372     if (!bMonoDevice && abs(dest_width) < src_width && abs(dest_height) < src_height) {
373         FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width, dest_top + dest_height);
374         image_rect.Normalize();
375         FX_RECT image_clip = image_rect;
376         image_clip.Intersect(*pClipRect);
377         if (image_clip.IsEmpty()) {
378             return;
379         }
380         image_clip.Offset(-image_rect.left, -image_rect.top);
381         CFX_DIBitmap* pStretched = NULL;
382         if (src_width * src_height > 10000) {
383             pStretched = _StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);
384         } else {
385             pStretched = pBitmap->StretchTo(dest_width, dest_height, FALSE, &image_clip);
386         }
387         GpBitmap* bitmap;
388         CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
389                                             (image_clip.Width() + 3) / 4 * 4, PixelFormat8bppIndexed, pStretched->GetBuffer(), &bitmap);
390         int a, r, g, b;
391         ArgbDecode(argb, a, r, g, b);
392         UINT pal[258];
393         pal[0] = 0;
394         pal[1] = 256;
395         for (int i = 0; i < 256; i ++) {
396             pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);
397         }
398         CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
399         CallFunc(GdipDrawImageI)(pGraphics, bitmap, image_rect.left + image_clip.left,
400                                  image_rect.top + image_clip.top);
401         CallFunc(GdipDisposeImage)(bitmap);
402         delete pStretched;
403         return;
404     }
405     GpBitmap* bitmap;
406     CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, PixelFormat1bppIndexed, scan0, &bitmap);
407     UINT palette[4] = { PaletteFlagsHasAlpha, 2, 0, argb };
408     CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)palette);
409     Point destinationPoints[] = {
410         Point(dest_left, dest_top),
411         Point(dest_left + dest_width, dest_top),
412         Point(dest_left, dest_top + dest_height)
413     };
414     CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
415     CallFunc(GdipDisposeImage)(bitmap);
416 }
417 static void OutputImage(GpGraphics* pGraphics, const CFX_DIBitmap* pBitmap, const FX_RECT* pSrcRect,
418                         int dest_left, int dest_top, int dest_width, int dest_height)
419 {
420     int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
421     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
422     if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
423         FX_RECT new_rect(0, 0, src_width, src_height);
424         CFX_DIBitmap* pCloned = pBitmap->Clone(pSrcRect);
425         if (!pCloned) {
426             return;
427         }
428         OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width, dest_height);
429         delete pCloned;
430         return;
431     }
432     int src_pitch = pBitmap->GetPitch();
433     FX_LPBYTE scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch + pBitmap->GetBPP() * pSrcRect->left / 8;
434     GpBitmap* bitmap = NULL;
435     switch (pBitmap->GetFormat()) {
436         case FXDIB_Argb:
437             CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
438                                                 PixelFormat32bppARGB, scan0, &bitmap);
439             break;
440         case FXDIB_Rgb32:
441             CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
442                                                 PixelFormat32bppRGB, scan0, &bitmap);
443             break;
444         case FXDIB_Rgb:
445             CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
446                                                 PixelFormat24bppRGB, scan0, &bitmap);
447             break;
448         case FXDIB_8bppRgb: {
449                 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
450                                                     PixelFormat8bppIndexed, scan0, &bitmap);
451                 UINT pal[258];
452                 pal[0] = 0;
453                 pal[1] = 256;
454                 for (int i = 0; i < 256; i ++) {
455                     pal[i + 2] = pBitmap->GetPaletteEntry(i);
456                 }
457                 CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
458                 break;
459             }
460         case FXDIB_1bppRgb: {
461                 CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
462                                                     PixelFormat1bppIndexed, scan0, &bitmap);
463                 break;
464             }
465     }
466     if (dest_height < 0) {
467         dest_height --;
468     }
469     if (dest_width < 0) {
470         dest_width --;
471     }
472     Point destinationPoints[] = {
473         Point(dest_left, dest_top),
474         Point(dest_left + dest_width, dest_top),
475         Point(dest_left, dest_top + dest_height)
476     };
477     CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
478     CallFunc(GdipDisposeImage)(bitmap);
479 }
480 CGdiplusExt::CGdiplusExt()
481 {
482     m_hModule = NULL;
483     m_GdiModule = NULL;
484     for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i ++) {
485         m_Functions[i] = NULL;
486     }
487     m_pGdiAddFontMemResourceEx = NULL;
488     m_pGdiRemoveFontMemResourseEx = NULL;
489 }
490 void CGdiplusExt::Load()
491 {
492     CFX_ByteString strPlusPath = "";
493     FX_CHAR buf[MAX_PATH];
494     GetSystemDirectoryA(buf, MAX_PATH);
495     strPlusPath += buf;
496     strPlusPath += "\\";
497     strPlusPath += "GDIPLUS.DLL";
498     m_hModule = LoadLibraryA(strPlusPath);
499     if (m_hModule == NULL) {
500         return;
501     }
502     for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i ++) {
503         m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
504         if (m_Functions[i] == NULL) {
505             m_hModule = NULL;
506             return;
507         }
508     }
509     FX_UINTPTR gdiplusToken;
510     GdiplusStartupInput gdiplusStartupInput;
511     ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(&gdiplusToken, &gdiplusStartupInput, NULL);
512     m_GdiModule = LoadLibraryA("GDI32.DLL");
513     if (m_GdiModule == NULL) {
514         return;
515     }
516     m_pGdiAddFontMemResourceEx = GetProcAddress(m_GdiModule, "AddFontMemResourceEx");
517     m_pGdiRemoveFontMemResourseEx = GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx");
518 }
519 CGdiplusExt::~CGdiplusExt()
520 {
521 }
522 LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, FX_DWORD size)
523 {
524     GpFontCollection* pCollection = NULL;
525     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
526     CallFunc(GdipNewPrivateFontCollection)(&pCollection);
527     GpStatus status = CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);
528     if (status == Ok) {
529         return pCollection;
530     }
531     CallFunc(GdipDeletePrivateFontCollection)(&pCollection);
532     return NULL;
533 }
534 void CGdiplusExt::DeleteMemFont(LPVOID pCollection)
535 {
536     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
537     CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);
538 }
539 FX_BOOL CGdiplusExt::GdipCreateBitmap(CFX_DIBitmap* pBitmap, void**bitmap)
540 {
541     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
542     PixelFormat format;
543     switch (pBitmap->GetFormat()) {
544         case FXDIB_Rgb:
545             format = PixelFormat24bppRGB;
546             break;
547         case FXDIB_Rgb32:
548             format = PixelFormat32bppRGB;
549             break;
550         case FXDIB_Argb:
551             format = PixelFormat32bppARGB;
552             break;
553         default:
554             return FALSE;
555     }
556     GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(pBitmap->GetWidth(), pBitmap->GetHeight(),
557                       pBitmap->GetPitch(), format, pBitmap->GetBuffer(), (GpBitmap**)bitmap);
558     if (status == Ok) {
559         return TRUE;
560     }
561     return FALSE;
562 }
563 FX_BOOL CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics)
564 {
565     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
566     GpStatus status = CallFunc(GdipGetImageGraphicsContext)((GpBitmap*)bitmap, (GpGraphics**)graphics);
567     if (status == Ok) {
568         return TRUE;
569     }
570     return FALSE;
571 }
572 FX_BOOL CGdiplusExt::GdipCreateFontFamilyFromName(FX_LPCWSTR name, void* pFontCollection, void**pFamily)
573 {
574     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
575     GpStatus status = CallFunc(GdipCreateFontFamilyFromName)((GDIPCONST WCHAR *)name, (GpFontCollection*)pFontCollection, (GpFontFamily**)pFamily);
576     if (status == Ok) {
577         return TRUE;
578     }
579     return FALSE;
580 }
581 FX_BOOL CGdiplusExt::GdipCreateFontFromFamily(void* pFamily, FX_FLOAT font_size, int fontstyle, int flag, void** pFont)
582 {
583     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
584     GpStatus status = CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle, Unit(flag), (GpFont**)pFont);
585     if (status == Ok) {
586         return TRUE;
587     }
588     return FALSE;
589 }
590 void CGdiplusExt::GdipGetFontSize(void *pFont, FX_FLOAT *size)
591 {
592     REAL get_size;
593     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
594     GpStatus status = CallFunc(GdipGetFontSize)((GpFont *)pFont, (REAL*)&get_size);
595     if (status == Ok) {
596         *size = (FX_FLOAT)get_size;
597     } else {
598         *size = 0;
599     }
600 }
601 void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode)
602 {
603     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
604     CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics, (TextRenderingHint)mode);
605 }
606 void CGdiplusExt::GdipSetPageUnit(void* graphics, FX_DWORD unit)
607 {
608     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
609     CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);
610 }
611 FX_BOOL CGdiplusExt::GdipDrawDriverString(void *graphics,  unsigned short *text, int length,
612         void *font, void* brush, void *positions, int flags, const void *matrix)
613 {
614     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
615     GpStatus status = CallFunc(GdipDrawDriverString)((GpGraphics*)graphics, (GDIPCONST UINT16 *)text, (INT)length, (GDIPCONST GpFont *)font, (GDIPCONST GpBrush*)brush,
616                       (GDIPCONST PointF *)positions, (INT)flags, (GDIPCONST GpMatrix *)matrix);
617     if (status == Ok) {
618         return TRUE;
619     }
620     return FALSE;
621 }
622 void CGdiplusExt::GdipCreateBrush(FX_DWORD fill_argb, void** pBrush)
623 {
624     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
625     CallFunc(GdipCreateSolidFill)((ARGB)fill_argb, (GpSolidFill**)pBrush);
626 }
627 void CGdiplusExt::GdipDeleteBrush(void* pBrush)
628 {
629     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
630     CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);
631 }
632 void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection, FX_FLOAT font_size, int fontstyle)
633 {
634     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
635     int numFamilies = 0;
636     GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)((GpFontCollection*)pFontCollection, &numFamilies);
637     if (status != Ok) {
638         return NULL;
639     }
640     GpFontFamily* family_list[1];
641     status = CallFunc(GdipGetFontCollectionFamilyList)((GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
642     if (status != Ok) {
643         return NULL;
644     }
645     GpFont* pFont = NULL;
646     status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle, UnitPixel, &pFont);
647     if (status != Ok) {
648         return NULL;
649     }
650     return pFont;
651 }
652 void CGdiplusExt::GdipCreateMatrix(FX_FLOAT a, FX_FLOAT b, FX_FLOAT c, FX_FLOAT d, FX_FLOAT e, FX_FLOAT f, void** matrix)
653 {
654     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
655     CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);
656 }
657 void CGdiplusExt::GdipDeleteMatrix(void* matrix)
658 {
659     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
660     CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);
661 }
662 void CGdiplusExt::GdipDeleteFontFamily(void* pFamily)
663 {
664     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
665     CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);
666 }
667 void CGdiplusExt::GdipDeleteFont(void* pFont)
668 {
669     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
670     CallFunc(GdipDeleteFont)((GpFont*)pFont);
671 }
672 void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix)
673 {
674     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
675     CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);
676 }
677 void CGdiplusExt::GdipDisposeImage(void* bitmap)
678 {
679     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
680     CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);
681 }
682 void CGdiplusExt::GdipDeleteGraphics(void* graphics)
683 {
684     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
685     CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);
686 }
687 FX_BOOL CGdiplusExt::StretchBitMask(HDC hDC, BOOL bMonoDevice, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,
688                                     int dest_width, int dest_height, FX_DWORD argb, const FX_RECT* pClipRect, int flags)
689 {
690     ASSERT(pBitmap->GetBPP() == 1);
691     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
692     GpGraphics* pGraphics = NULL;
693     CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
694     CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
695     if (flags & FXDIB_NOSMOOTH) {
696         CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeNearestNeighbor);
697     } else {
698         CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
699     }
700     OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top, dest_width, dest_height, argb, pClipRect);
701     CallFunc(GdipDeleteGraphics)(pGraphics);
702     return TRUE;
703 }
704 FX_BOOL CGdiplusExt::StretchDIBits(HDC hDC, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,
705                                    int dest_width, int dest_height, const FX_RECT* pClipRect, int flags)
706 {
707     GpGraphics* pGraphics;
708     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
709     CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
710     CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
711     if (flags & FXDIB_NOSMOOTH) {
712         CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeNearestNeighbor);
713     } else if (pBitmap->GetWidth() > abs(dest_width) / 2 || pBitmap->GetHeight() > abs(dest_height) / 2) {
714         CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
715     } else {
716         CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);
717     }
718     FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
719     OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width, dest_height);
720     CallFunc(GdipDeleteGraphics)(pGraphics);
721     CallFunc(GdipDeleteGraphics)(pGraphics);
722     return TRUE;
723 }
724 static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState, const CFX_AffineMatrix* pMatrix, DWORD argb, FX_BOOL bTextMode = FALSE)
725 {
726     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
727     FX_FLOAT width = pGraphState ? pGraphState->m_LineWidth : 1.0f;
728     if (!bTextMode) {
729         FX_FLOAT unit = pMatrix == NULL ? 1.0f : FXSYS_Div(1.0f, (pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2);
730         if (width < unit) {
731             width = unit;
732         }
733     }
734     GpPen* pPen = NULL;
735     CallFunc(GdipCreatePen1)((ARGB)argb, width, UnitWorld, &pPen);
736     LineCap lineCap;
737     DashCap dashCap = DashCapFlat;
738     FX_BOOL bDashExtend = FALSE;
739     switch(pGraphState->m_LineCap) {
740         case CFX_GraphStateData::LineCapButt:
741             lineCap = LineCapFlat;
742             break;
743         case CFX_GraphStateData::LineCapRound:
744             lineCap = LineCapRound;
745             dashCap = DashCapRound;
746             bDashExtend = TRUE;
747             break;
748         case CFX_GraphStateData::LineCapSquare:
749             lineCap = LineCapSquare;
750             bDashExtend = TRUE;
751             break;
752     }
753     CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
754     LineJoin lineJoin;
755     switch(pGraphState->m_LineJoin) {
756         case CFX_GraphStateData::LineJoinMiter:
757             lineJoin = LineJoinMiterClipped;
758             break;
759         case CFX_GraphStateData::LineJoinRound:
760             lineJoin = LineJoinRound;
761             break;
762         case CFX_GraphStateData::LineJoinBevel:
763             lineJoin = LineJoinBevel;
764             break;
765     }
766     CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
767     if(pGraphState->m_DashCount) {
768         FX_FLOAT* pDashArray = FX_Alloc(FX_FLOAT, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);
769         if (!pDashArray) {
770             return NULL;
771         }
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     if (!points) {
874         return FALSE;
875     }
876     BYTE * types  = FX_Alloc(BYTE, nPoints);
877     if (!types) {
878         FX_Free(points);
879         return FALSE;
880     }
881     int nSubPathes = 0;
882     FX_BOOL bSubClose = FALSE;
883     int pos_subclose = 0;
884     FX_BOOL bSmooth = FALSE;
885     int startpoint = 0;
886     for(int i = 0; i < nPoints; i++) {
887         points[i].X = pPoints[i].m_PointX;
888         points[i].Y = pPoints[i].m_PointY;
889         FX_FLOAT x, y;
890         if (pObject2Device) {
891             pObject2Device->Transform(pPoints[i].m_PointX, pPoints[i].m_PointY, x, y);
892         } else {
893             x = pPoints[i].m_PointX;
894             y = pPoints[i].m_PointY;
895         }
896         if (x > 50000 * 1.0f) {
897             points[i].X = 50000 * 1.0f;
898         }
899         if (x < -50000 * 1.0f) {
900             points[i].X = -50000 * 1.0f;
901         }
902         if (y > 50000 * 1.0f) {
903             points[i].Y = 50000 * 1.0f;
904         }
905         if (y < -50000 * 1.0f) {
906             points[i].Y = -50000 * 1.0f;
907         }
908         int point_type = pPoints[i].m_Flag & FXPT_TYPE;
909         if(point_type == FXPT_MOVETO) {
910             types[i] = PathPointTypeStart;
911             nSubPathes ++;
912             bSubClose = FALSE;
913             startpoint = i;
914         } else if (point_type == FXPT_LINETO) {
915             types[i] = PathPointTypeLine;
916             if (pPoints[i - 1].m_Flag == FXPT_MOVETO && (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&
917                     points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
918                 points[i].X += 0.01f;
919                 continue;
920             }
921             if (!bSmooth && points[i].X != points[i - 1].X && points[i].Y != points[i - 1].Y) {
922                 bSmooth = TRUE;
923             }
924         } else if (point_type == FXPT_BEZIERTO) {
925             types[i] = PathPointTypeBezier;
926             bSmooth = TRUE;
927         }
928         if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {
929             if (bSubClose) {
930                 types[pos_subclose] &= ~PathPointTypeCloseSubpath;
931             } else {
932                 bSubClose = TRUE;
933             }
934             pos_subclose = i;
935             types[i] |= PathPointTypeCloseSubpath;
936             if (!bSmooth && points[i].X != points[startpoint].X && points[i].Y != points[startpoint].Y) {
937                 bSmooth = TRUE;
938             }
939         }
940     }
941     if (fill_mode & FXFILL_NOPATHSMOOTH) {
942         bSmooth = FALSE;
943         CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
944     } else if (!(fill_mode & FXFILL_FULLCOVER)) {
945         if (!bSmooth && (fill_mode & 3)) {
946             bSmooth = TRUE;
947         }
948         if (bSmooth || pGraphState && pGraphState->m_LineWidth > 2) {
949             CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
950         }
951     }
952     int new_fill_mode = fill_mode & 3;
953     if (nPoints == 4 && pGraphState == NULL) {
954         int v1, v2;
955         if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
956             GpPen* pPen = NULL;
957             CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
958             CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
959                                     FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
960             CallFunc(GdipDeletePen)(pPen);
961             return TRUE;
962         }
963     }
964     GpPath* pGpPath = NULL;
965     CallFunc(GdipCreatePath2)(points, types, nPoints, GdiFillType2Gdip(new_fill_mode), &pGpPath);
966     if (!pGpPath) {
967         if (pMatrix) {
968             CallFunc(GdipDeleteMatrix)(pMatrix);
969         }
970         FX_Free(points);
971         FX_Free(types);
972         CallFunc(GdipDeleteGraphics)(pGraphics);
973         return FALSE;
974     }
975     if (new_fill_mode) {
976         GpBrush* pBrush = _GdipCreateBrush(fill_argb);
977         CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
978         CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
979         CallFunc(GdipDeleteBrush)(pBrush);
980     }
981     if (pGraphState && stroke_argb) {
982         GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb, fill_mode & FX_STROKE_TEXT_MODE);
983         if (nSubPathes == 1) {
984             CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
985         } else {
986             int iStart = 0;
987             for (int i = 0; i < nPoints; i ++) {
988                 if (i == nPoints - 1 || types[i + 1] == PathPointTypeStart) {
989                     GpPath* pSubPath;
990                     CallFunc(GdipCreatePath2)(points + iStart, types + iStart, i - iStart + 1, GdiFillType2Gdip(new_fill_mode), &pSubPath);
991                     iStart = i + 1;
992                     CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
993                     CallFunc(GdipDeletePath)(pSubPath);
994                 }
995             }
996         }
997         CallFunc(GdipDeletePen)(pPen);
998     }
999     if (pMatrix) {
1000         CallFunc(GdipDeleteMatrix)(pMatrix);
1001     }
1002     FX_Free(points);
1003     FX_Free(types);
1004     CallFunc(GdipDeletePath)(pGpPath);
1005     CallFunc(GdipDeleteGraphics)(pGraphics);
1006     return TRUE;
1007 }
1008 class GpStream FX_FINAL : public IStream
1009 {
1010     LONG        m_RefCount;
1011     int     m_ReadPos;
1012     CFX_ByteTextBuf     m_InterStream;
1013 public:
1014     GpStream()
1015     {
1016         m_RefCount = 1;
1017         m_ReadPos = 0;
1018     }
1019     virtual HRESULT STDMETHODCALLTYPE
1020     QueryInterface(REFIID iid, void ** ppvObject)
1021     {
1022         if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
1023                 iid == __uuidof(ISequentialStream))     {
1024             *ppvObject = static_cast<IStream*>(this);
1025             AddRef();
1026             return S_OK;
1027         } else {
1028             return E_NOINTERFACE;
1029         }
1030     }
1031     virtual ULONG STDMETHODCALLTYPE AddRef(void)
1032     {
1033         return (ULONG)InterlockedIncrement(&m_RefCount);
1034     }
1035     virtual ULONG STDMETHODCALLTYPE Release(void)
1036     {
1037         ULONG res = (ULONG) InterlockedDecrement(&m_RefCount);
1038         if (res == 0) {
1039             delete this;
1040         }
1041         return res;
1042     }
1043 public:
1044     virtual HRESULT STDMETHODCALLTYPE Read(void* Output, ULONG cb, ULONG* pcbRead)
1045     {
1046         size_t  bytes_left;
1047         size_t  bytes_out;
1048         if (pcbRead != NULL) {
1049             *pcbRead = 0;
1050         }
1051         if (m_ReadPos == m_InterStream.GetLength()) {
1052             return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
1053         }
1054         bytes_left = m_InterStream.GetLength() - m_ReadPos;
1055         bytes_out = FX_MIN(cb, bytes_left);
1056         FXSYS_memcpy32(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);
1057         m_ReadPos += (FX_INT32)bytes_out;
1058         if (pcbRead != NULL) {
1059             *pcbRead = (ULONG)bytes_out;
1060         }
1061         return S_OK;
1062     }
1063     virtual HRESULT STDMETHODCALLTYPE Write(void const* Input, ULONG cb, ULONG* pcbWritten)
1064     {
1065         if (cb <= 0) {
1066             if (pcbWritten != NULL) {
1067                 *pcbWritten = 0;
1068             }
1069             return S_OK;
1070         }
1071         m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);
1072         if (pcbWritten != NULL) {
1073             *pcbWritten = cb;
1074         }
1075         return S_OK;
1076     }
1077 public:
1078     virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER)
1079     {
1080         return E_NOTIMPL;
1081     }
1082     virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*)
1083     {
1084         return E_NOTIMPL;
1085     }
1086     virtual HRESULT STDMETHODCALLTYPE Commit(DWORD)
1087     {
1088         return E_NOTIMPL;
1089     }
1090     virtual HRESULT STDMETHODCALLTYPE Revert(void)
1091     {
1092         return E_NOTIMPL;
1093     }
1094     virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
1095     {
1096         return E_NOTIMPL;
1097     }
1098     virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
1099     {
1100         return E_NOTIMPL;
1101     }
1102     virtual HRESULT STDMETHODCALLTYPE Clone(IStream **)
1103     {
1104         return E_NOTIMPL;
1105     }
1106     virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, DWORD dwOrigin, ULARGE_INTEGER* lpNewFilePointer)
1107     {
1108         long    start = 0;
1109         long    new_read_position;
1110         switch(dwOrigin) {
1111             case STREAM_SEEK_SET:
1112                 start = 0;
1113                 break;
1114             case STREAM_SEEK_CUR:
1115                 start = m_ReadPos;
1116                 break;
1117             case STREAM_SEEK_END:
1118                 start = m_InterStream.GetLength();
1119                 break;
1120             default:
1121                 return STG_E_INVALIDFUNCTION;
1122                 break;
1123         }
1124         new_read_position = start + (long)liDistanceToMove.QuadPart;
1125         if (new_read_position < 0 || new_read_position > m_InterStream.GetLength()) {
1126             return STG_E_SEEKERROR;
1127         }
1128         m_ReadPos = new_read_position;
1129         if (lpNewFilePointer != NULL) {
1130             lpNewFilePointer->QuadPart = m_ReadPos;
1131         }
1132         return S_OK;
1133     }
1134     virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag)
1135     {
1136         if (pStatstg == NULL) {
1137             return STG_E_INVALIDFUNCTION;
1138         }
1139         ZeroMemory(pStatstg, sizeof(STATSTG));
1140         pStatstg->cbSize.QuadPart = m_InterStream.GetLength();
1141         return S_OK;
1142     }
1143 };
1144 typedef struct {
1145     BITMAPINFO*         pbmi;
1146     int                         Stride;
1147     LPBYTE                      pScan0;
1148     GpBitmap*           pBitmap;
1149     BitmapData*         pBitmapData;
1150     GpStream*       pStream;
1151 } PREVIEW3_DIBITMAP;
1152 static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args)
1153 {
1154     GpBitmap* pBitmap;
1155     GpStream* pStream = NULL;
1156     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1157     Status status = Ok;
1158     if (args.flags == WINDIB_OPEN_PATHNAME) {
1159         status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name, &pBitmap);
1160     } else {
1161         if (args.memory_size == 0 || !args.memory_base) {
1162             return NULL;
1163         }
1164         pStream = new GpStream;
1165         pStream->Write(args.memory_base, (ULONG)args.memory_size, NULL);
1166         status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
1167     }
1168     if (status != Ok) {
1169         if (pStream) {
1170             pStream->Release();
1171         }
1172         return NULL;
1173     }
1174     UINT height, width;
1175     CallFunc(GdipGetImageHeight)(pBitmap, &height);
1176     CallFunc(GdipGetImageWidth)(pBitmap, &width);
1177     PixelFormat pixel_format;
1178     CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
1179     int info_size = sizeof(BITMAPINFOHEADER);
1180     int bpp = 24;
1181     int dest_pixel_format = PixelFormat24bppRGB;
1182     if (pixel_format == PixelFormat1bppIndexed) {
1183         info_size += 8;
1184         bpp = 1;
1185         dest_pixel_format = PixelFormat1bppIndexed;
1186     } else if (pixel_format == PixelFormat8bppIndexed) {
1187         info_size += 1024;
1188         bpp = 8;
1189         dest_pixel_format = PixelFormat8bppIndexed;
1190     } else if (pixel_format == PixelFormat32bppARGB) {
1191         bpp = 32;
1192         dest_pixel_format = PixelFormat32bppARGB;
1193     }
1194     LPBYTE buf = FX_Alloc(BYTE, info_size);
1195     if (!buf) {
1196         if (pStream) {
1197             pStream->Release();
1198         }
1199         return NULL;
1200     }
1201     BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
1202     pbmih->biBitCount = bpp;
1203     pbmih->biCompression = BI_RGB;
1204     pbmih->biHeight = -(int)height;
1205     pbmih->biPlanes = 1;
1206     pbmih->biWidth = width;
1207     Rect rect(0, 0, width, height);
1208     BitmapData* pBitmapData = FX_Alloc(BitmapData, 1);
1209     if (!pBitmapData) {
1210         if (pStream) {
1211             pStream->Release();
1212         }
1213         return NULL;
1214     }
1215     CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
1216                                  dest_pixel_format, pBitmapData);
1217     if (pixel_format == PixelFormat1bppIndexed || pixel_format == PixelFormat8bppIndexed) {
1218         DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
1219         struct {
1220             UINT flags;
1221             UINT Count;
1222             DWORD Entries[256];
1223         } pal;
1224         int size = 0;
1225         CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
1226         CallFunc(GdipGetImagePalette)(pBitmap, (ColorPalette*)&pal, size);
1227         int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
1228         for (int i = 0; i < entries; i ++) {
1229             ppal[i] = pal.Entries[i] & 0x00ffffff;
1230         }
1231     }
1232     PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
1233     if (!pInfo) {
1234         if (pStream) {
1235             pStream->Release();
1236         }
1237         return NULL;
1238     }
1239     pInfo->pbmi = (BITMAPINFO*)buf;
1240     pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
1241     pInfo->Stride = pBitmapData->Stride;
1242     pInfo->pBitmap = pBitmap;
1243     pInfo->pBitmapData = pBitmapData;
1244     pInfo->pStream = pStream;
1245     return pInfo;
1246 }
1247 static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo)
1248 {
1249     CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1250     CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
1251     CallFunc(GdipDisposeImage)(pInfo->pBitmap);
1252     FX_Free(pInfo->pBitmapData);
1253     FX_Free((LPBYTE)pInfo->pbmi);
1254     if (pInfo->pStream) {
1255         pInfo->pStream->Release();
1256     }
1257     FX_Free(pInfo);
1258 }
1259 CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, LPVOID pData, FX_BOOL bAlpha);
1260 CFX_DIBitmap* CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args)
1261 {
1262     PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
1263     if (pInfo == NULL) {
1264         return NULL;
1265     }
1266     int height = abs(pInfo->pbmi->bmiHeader.biHeight);
1267     int width = pInfo->pbmi->bmiHeader.biWidth;
1268     int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
1269     LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
1270     if (dest_pitch == pInfo->Stride) {
1271         FXSYS_memcpy32(pData, pInfo->pScan0, dest_pitch * height);
1272     } else {
1273         for (int i = 0; i < height; i ++) {
1274             FXSYS_memcpy32(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i, dest_pitch);
1275         }
1276     }
1277     CFX_DIBitmap* pDIBitmap = _FX_WindowsDIB_LoadFromBuf(pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
1278     FX_Free(pData);
1279     FreeDIBitmap(pInfo);
1280     return pDIBitmap;
1281 }
1282 #endif