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