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