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