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