More chromium_code whack-a-mole.
[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 src_count = src_width * src_height;
486   int dest_count = dest_width * dest_height;
487   int y_unit = src_height / dest_height;
488   int x_unit = src_width / dest_width;
489   int area_unit = y_unit * x_unit;
490   LPBYTE src_buf = pSource->GetBuffer();
491   int src_pitch = pSource->GetPitch();
492   for (int dest_y = 0; dest_y < result_height; dest_y++) {
493     LPBYTE dest_scan = dest_buf + dest_y * result_pitch;
494     int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top)
495                              : (dest_y + pClipRect->top);
496     src_y_start = src_y_start * src_height / dest_height;
497     LPBYTE src_scan = src_buf + src_y_start * src_pitch;
498     for (int dest_x = 0; dest_x < result_width; dest_x++) {
499       int sum = 0;
500       int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left)
501                                : (dest_x + pClipRect->left);
502       src_x_start = src_x_start * src_width / dest_width;
503       int src_x_end = src_x_start + x_unit;
504       LPBYTE src_line = src_scan;
505       for (int src_y = 0; src_y < y_unit; src_y++) {
506         for (int src_x = src_x_start; src_x < src_x_end; src_x++) {
507           if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {
508             sum += 255;
509           }
510         }
511         src_line += src_pitch;
512       }
513       dest_scan[dest_x] = 255 - sum / area_unit;
514     }
515   }
516   return pStretched;
517 }
518 static void OutputImageMask(GpGraphics* pGraphics,
519                             BOOL bMonoDevice,
520                             const CFX_DIBitmap* pBitmap,
521                             int dest_left,
522                             int dest_top,
523                             int dest_width,
524                             int dest_height,
525                             FX_ARGB argb,
526                             const FX_RECT* pClipRect) {
527   ASSERT(pBitmap->GetBPP() == 1);
528   CGdiplusExt& GdiplusExt =
529       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
530   int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();
531   int src_pitch = pBitmap->GetPitch();
532   uint8_t* scan0 = pBitmap->GetBuffer();
533   if (src_width == 1 && src_height == 1) {
534     if ((scan0[0] & 0x80) == 0) {
535       return;
536     }
537     GpSolidFill* solidBrush;
538     CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);
539     if (dest_width < 0) {
540       dest_width = -dest_width;
541       dest_left -= dest_width;
542     }
543     if (dest_height < 0) {
544       dest_height = -dest_height;
545       dest_top -= dest_height;
546     }
547     CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left,
548                                 (float)dest_top, (float)dest_width,
549                                 (float)dest_height);
550     CallFunc(GdipDeleteBrush)(solidBrush);
551     return;
552   }
553   if (!bMonoDevice && abs(dest_width) < src_width &&
554       abs(dest_height) < src_height) {
555     FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width,
556                        dest_top + dest_height);
557     image_rect.Normalize();
558     FX_RECT image_clip = image_rect;
559     image_clip.Intersect(*pClipRect);
560     if (image_clip.IsEmpty()) {
561       return;
562     }
563     image_clip.Offset(-image_rect.left, -image_rect.top);
564     CFX_DIBitmap* pStretched = NULL;
565     if (src_width * src_height > 10000) {
566       pStretched =
567           _StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);
568     } else {
569       pStretched =
570           pBitmap->StretchTo(dest_width, dest_height, FALSE, &image_clip);
571     }
572     GpBitmap* bitmap;
573     CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
574                                         (image_clip.Width() + 3) / 4 * 4,
575                                         PixelFormat8bppIndexed,
576                                         pStretched->GetBuffer(), &bitmap);
577     int a, r, g, b;
578     ArgbDecode(argb, a, r, g, b);
579     UINT pal[258];
580     pal[0] = 0;
581     pal[1] = 256;
582     for (int i = 0; i < 256; i++) {
583       pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);
584     }
585     CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
586     CallFunc(GdipDrawImageI)(pGraphics, bitmap,
587                              image_rect.left + image_clip.left,
588                              image_rect.top + image_clip.top);
589     CallFunc(GdipDisposeImage)(bitmap);
590     delete pStretched;
591     return;
592   }
593   GpBitmap* bitmap;
594   CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
595                                       PixelFormat1bppIndexed, scan0, &bitmap);
596   UINT palette[4] = {PaletteFlagsHasAlpha, 2, 0, argb};
597   CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)palette);
598   Point destinationPoints[] = {Point(dest_left, dest_top),
599                                Point(dest_left + dest_width, dest_top),
600                                Point(dest_left, dest_top + dest_height)};
601   CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
602   CallFunc(GdipDisposeImage)(bitmap);
603 }
604 static void OutputImage(GpGraphics* pGraphics,
605                         const CFX_DIBitmap* pBitmap,
606                         const FX_RECT* pSrcRect,
607                         int dest_left,
608                         int dest_top,
609                         int dest_width,
610                         int dest_height) {
611   int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
612   CGdiplusExt& GdiplusExt =
613       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
614   if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
615     FX_RECT new_rect(0, 0, src_width, src_height);
616     CFX_DIBitmap* pCloned = pBitmap->Clone(pSrcRect);
617     if (!pCloned) {
618       return;
619     }
620     OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width,
621                 dest_height);
622     delete pCloned;
623     return;
624   }
625   int src_pitch = pBitmap->GetPitch();
626   uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch +
627                    pBitmap->GetBPP() * pSrcRect->left / 8;
628   GpBitmap* bitmap = NULL;
629   switch (pBitmap->GetFormat()) {
630     case FXDIB_Argb:
631       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
632                                           PixelFormat32bppARGB, scan0, &bitmap);
633       break;
634     case FXDIB_Rgb32:
635       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
636                                           PixelFormat32bppRGB, scan0, &bitmap);
637       break;
638     case FXDIB_Rgb:
639       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
640                                           PixelFormat24bppRGB, scan0, &bitmap);
641       break;
642     case FXDIB_8bppRgb: {
643       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
644                                           PixelFormat8bppIndexed, scan0,
645                                           &bitmap);
646       UINT pal[258];
647       pal[0] = 0;
648       pal[1] = 256;
649       for (int i = 0; i < 256; i++) {
650         pal[i + 2] = pBitmap->GetPaletteEntry(i);
651       }
652       CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
653       break;
654     }
655     case FXDIB_1bppRgb: {
656       CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
657                                           PixelFormat1bppIndexed, scan0,
658                                           &bitmap);
659       break;
660     }
661   }
662   if (dest_height < 0) {
663     dest_height--;
664   }
665   if (dest_width < 0) {
666     dest_width--;
667   }
668   Point destinationPoints[] = {Point(dest_left, dest_top),
669                                Point(dest_left + dest_width, dest_top),
670                                Point(dest_left, dest_top + dest_height)};
671   CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
672   CallFunc(GdipDisposeImage)(bitmap);
673 }
674 CGdiplusExt::CGdiplusExt() {
675   m_hModule = NULL;
676   m_GdiModule = NULL;
677   for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i++) {
678     m_Functions[i] = NULL;
679   }
680   m_pGdiAddFontMemResourceEx = NULL;
681   m_pGdiRemoveFontMemResourseEx = NULL;
682 }
683 void CGdiplusExt::Load() {
684   CFX_ByteString strPlusPath = "";
685   FX_CHAR buf[MAX_PATH];
686   GetSystemDirectoryA(buf, MAX_PATH);
687   strPlusPath += buf;
688   strPlusPath += "\\";
689   strPlusPath += "GDIPLUS.DLL";
690   m_hModule = LoadLibraryA(strPlusPath);
691   if (m_hModule == NULL) {
692     return;
693   }
694   for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i++) {
695     m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
696     if (m_Functions[i] == NULL) {
697       m_hModule = NULL;
698       return;
699     }
700   }
701   uintptr_t gdiplusToken;
702   GdiplusStartupInput gdiplusStartupInput;
703   ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
704       &gdiplusToken, &gdiplusStartupInput, NULL);
705   m_GdiModule = LoadLibraryA("GDI32.DLL");
706   if (m_GdiModule == NULL) {
707     return;
708   }
709   m_pGdiAddFontMemResourceEx =
710       GetProcAddress(m_GdiModule, "AddFontMemResourceEx");
711   m_pGdiRemoveFontMemResourseEx =
712       GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx");
713 }
714 CGdiplusExt::~CGdiplusExt() {}
715 LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, FX_DWORD size) {
716   GpFontCollection* pCollection = NULL;
717   CGdiplusExt& GdiplusExt =
718       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
719   CallFunc(GdipNewPrivateFontCollection)(&pCollection);
720   GpStatus status =
721       CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);
722   if (status == Ok) {
723     return pCollection;
724   }
725   CallFunc(GdipDeletePrivateFontCollection)(&pCollection);
726   return NULL;
727 }
728 void CGdiplusExt::DeleteMemFont(LPVOID pCollection) {
729   CGdiplusExt& GdiplusExt =
730       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
731   CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);
732 }
733 FX_BOOL CGdiplusExt::GdipCreateBitmap(CFX_DIBitmap* pBitmap, void** bitmap) {
734   CGdiplusExt& GdiplusExt =
735       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
736   PixelFormat format;
737   switch (pBitmap->GetFormat()) {
738     case FXDIB_Rgb:
739       format = PixelFormat24bppRGB;
740       break;
741     case FXDIB_Rgb32:
742       format = PixelFormat32bppRGB;
743       break;
744     case FXDIB_Argb:
745       format = PixelFormat32bppARGB;
746       break;
747     default:
748       return FALSE;
749   }
750   GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(
751       pBitmap->GetWidth(), pBitmap->GetHeight(), pBitmap->GetPitch(), format,
752       pBitmap->GetBuffer(), (GpBitmap**)bitmap);
753   if (status == Ok) {
754     return TRUE;
755   }
756   return FALSE;
757 }
758 FX_BOOL CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics) {
759   CGdiplusExt& GdiplusExt =
760       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
761   GpStatus status = CallFunc(GdipGetImageGraphicsContext)(
762       (GpBitmap*)bitmap, (GpGraphics**)graphics);
763   if (status == Ok) {
764     return TRUE;
765   }
766   return FALSE;
767 }
768 FX_BOOL CGdiplusExt::GdipCreateFontFamilyFromName(const FX_WCHAR* name,
769                                                   void* pFontCollection,
770                                                   void** pFamily) {
771   CGdiplusExt& GdiplusExt =
772       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
773   GpStatus status = CallFunc(GdipCreateFontFamilyFromName)(
774       (GDIPCONST WCHAR*)name, (GpFontCollection*)pFontCollection,
775       (GpFontFamily**)pFamily);
776   if (status == Ok) {
777     return TRUE;
778   }
779   return FALSE;
780 }
781 FX_BOOL CGdiplusExt::GdipCreateFontFromFamily(void* pFamily,
782                                               FX_FLOAT font_size,
783                                               int fontstyle,
784                                               int flag,
785                                               void** pFont) {
786   CGdiplusExt& GdiplusExt =
787       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
788   GpStatus status =
789       CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle,
790                                Unit(flag), (GpFont**)pFont);
791   if (status == Ok) {
792     return TRUE;
793   }
794   return FALSE;
795 }
796 void CGdiplusExt::GdipGetFontSize(void* pFont, FX_FLOAT* size) {
797   REAL get_size;
798   CGdiplusExt& GdiplusExt =
799       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
800   GpStatus status = CallFunc(GdipGetFontSize)((GpFont*)pFont, (REAL*)&get_size);
801   if (status == Ok) {
802     *size = (FX_FLOAT)get_size;
803   } else {
804     *size = 0;
805   }
806 }
807 void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode) {
808   CGdiplusExt& GdiplusExt =
809       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
810   CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics,
811                                      (TextRenderingHint)mode);
812 }
813 void CGdiplusExt::GdipSetPageUnit(void* graphics, FX_DWORD unit) {
814   CGdiplusExt& GdiplusExt =
815       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
816   CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);
817 }
818 FX_BOOL CGdiplusExt::GdipDrawDriverString(void* graphics,
819                                           unsigned short* text,
820                                           int length,
821                                           void* font,
822                                           void* brush,
823                                           void* positions,
824                                           int flags,
825                                           const void* matrix) {
826   CGdiplusExt& GdiplusExt =
827       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
828   GpStatus status = CallFunc(GdipDrawDriverString)(
829       (GpGraphics*)graphics, (GDIPCONST UINT16*)text, (INT)length,
830       (GDIPCONST GpFont*)font, (GDIPCONST GpBrush*)brush,
831       (GDIPCONST PointF*)positions, (INT)flags, (GDIPCONST GpMatrix*)matrix);
832   if (status == Ok) {
833     return TRUE;
834   }
835   return FALSE;
836 }
837 void CGdiplusExt::GdipCreateBrush(FX_DWORD fill_argb, void** pBrush) {
838   CGdiplusExt& GdiplusExt =
839       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
840   CallFunc(GdipCreateSolidFill)((ARGB)fill_argb, (GpSolidFill**)pBrush);
841 }
842 void CGdiplusExt::GdipDeleteBrush(void* pBrush) {
843   CGdiplusExt& GdiplusExt =
844       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
845   CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);
846 }
847 void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection,
848                                                 FX_FLOAT font_size,
849                                                 int fontstyle) {
850   CGdiplusExt& GdiplusExt =
851       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
852   int numFamilies = 0;
853   GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)(
854       (GpFontCollection*)pFontCollection, &numFamilies);
855   if (status != Ok) {
856     return NULL;
857   }
858   GpFontFamily* family_list[1];
859   status = CallFunc(GdipGetFontCollectionFamilyList)(
860       (GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
861   if (status != Ok) {
862     return NULL;
863   }
864   GpFont* pFont = NULL;
865   status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle,
866                                     UnitPixel, &pFont);
867   if (status != Ok) {
868     return NULL;
869   }
870   return pFont;
871 }
872 void CGdiplusExt::GdipCreateMatrix(FX_FLOAT a,
873                                    FX_FLOAT b,
874                                    FX_FLOAT c,
875                                    FX_FLOAT d,
876                                    FX_FLOAT e,
877                                    FX_FLOAT f,
878                                    void** matrix) {
879   CGdiplusExt& GdiplusExt =
880       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
881   CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);
882 }
883 void CGdiplusExt::GdipDeleteMatrix(void* matrix) {
884   CGdiplusExt& GdiplusExt =
885       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
886   CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);
887 }
888 void CGdiplusExt::GdipDeleteFontFamily(void* pFamily) {
889   CGdiplusExt& GdiplusExt =
890       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
891   CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);
892 }
893 void CGdiplusExt::GdipDeleteFont(void* pFont) {
894   CGdiplusExt& GdiplusExt =
895       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
896   CallFunc(GdipDeleteFont)((GpFont*)pFont);
897 }
898 void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix) {
899   CGdiplusExt& GdiplusExt =
900       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
901   CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);
902 }
903 void CGdiplusExt::GdipDisposeImage(void* bitmap) {
904   CGdiplusExt& GdiplusExt =
905       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
906   CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);
907 }
908 void CGdiplusExt::GdipDeleteGraphics(void* graphics) {
909   CGdiplusExt& GdiplusExt =
910       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
911   CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);
912 }
913 FX_BOOL CGdiplusExt::StretchBitMask(HDC hDC,
914                                     BOOL bMonoDevice,
915                                     const CFX_DIBitmap* pBitmap,
916                                     int dest_left,
917                                     int dest_top,
918                                     int dest_width,
919                                     int dest_height,
920                                     FX_DWORD argb,
921                                     const FX_RECT* pClipRect,
922                                     int flags) {
923   ASSERT(pBitmap->GetBPP() == 1);
924   CGdiplusExt& GdiplusExt =
925       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
926   GpGraphics* pGraphics = NULL;
927   CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
928   CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
929   if (flags & FXDIB_NOSMOOTH) {
930     CallFunc(GdipSetInterpolationMode)(pGraphics,
931                                        InterpolationModeNearestNeighbor);
932   } else {
933     CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
934   }
935   OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top,
936                   dest_width, dest_height, argb, pClipRect);
937   CallFunc(GdipDeleteGraphics)(pGraphics);
938   return TRUE;
939 }
940 FX_BOOL CGdiplusExt::StretchDIBits(HDC hDC,
941                                    const CFX_DIBitmap* pBitmap,
942                                    int dest_left,
943                                    int dest_top,
944                                    int dest_width,
945                                    int dest_height,
946                                    const FX_RECT* pClipRect,
947                                    int flags) {
948   GpGraphics* pGraphics;
949   CGdiplusExt& GdiplusExt =
950       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
951   CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
952   CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
953   if (flags & FXDIB_NOSMOOTH) {
954     CallFunc(GdipSetInterpolationMode)(pGraphics,
955                                        InterpolationModeNearestNeighbor);
956   } else if (pBitmap->GetWidth() > abs(dest_width) / 2 ||
957              pBitmap->GetHeight() > abs(dest_height) / 2) {
958     CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
959   } else {
960     CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);
961   }
962   FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
963   OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width,
964               dest_height);
965   CallFunc(GdipDeleteGraphics)(pGraphics);
966   CallFunc(GdipDeleteGraphics)(pGraphics);
967   return TRUE;
968 }
969 static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState,
970                              const CFX_AffineMatrix* pMatrix,
971                              DWORD argb,
972                              FX_BOOL bTextMode = FALSE) {
973   CGdiplusExt& GdiplusExt =
974       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
975   FX_FLOAT width = pGraphState ? pGraphState->m_LineWidth : 1.0f;
976   if (!bTextMode) {
977     FX_FLOAT unit =
978         pMatrix == NULL
979             ? 1.0f
980             : FXSYS_Div(1.0f, (pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2);
981     if (width < unit) {
982       width = unit;
983     }
984   }
985   GpPen* pPen = NULL;
986   CallFunc(GdipCreatePen1)((ARGB)argb, width, UnitWorld, &pPen);
987   LineCap lineCap;
988   DashCap dashCap = DashCapFlat;
989   FX_BOOL bDashExtend = FALSE;
990   switch (pGraphState->m_LineCap) {
991     case CFX_GraphStateData::LineCapButt:
992       lineCap = LineCapFlat;
993       break;
994     case CFX_GraphStateData::LineCapRound:
995       lineCap = LineCapRound;
996       dashCap = DashCapRound;
997       bDashExtend = TRUE;
998       break;
999     case CFX_GraphStateData::LineCapSquare:
1000       lineCap = LineCapSquare;
1001       bDashExtend = TRUE;
1002       break;
1003   }
1004   CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
1005   LineJoin lineJoin;
1006   switch (pGraphState->m_LineJoin) {
1007     case CFX_GraphStateData::LineJoinMiter:
1008       lineJoin = LineJoinMiterClipped;
1009       break;
1010     case CFX_GraphStateData::LineJoinRound:
1011       lineJoin = LineJoinRound;
1012       break;
1013     case CFX_GraphStateData::LineJoinBevel:
1014       lineJoin = LineJoinBevel;
1015       break;
1016   }
1017   CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
1018   if (pGraphState->m_DashCount) {
1019     FX_FLOAT* pDashArray = FX_Alloc(
1020         FX_FLOAT, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);
1021     int nCount = 0;
1022     FX_FLOAT on_leftover = 0, off_leftover = 0;
1023     for (int i = 0; i < pGraphState->m_DashCount; i += 2) {
1024       FX_FLOAT on_phase = pGraphState->m_DashArray[i];
1025       FX_FLOAT off_phase;
1026       if (i == pGraphState->m_DashCount - 1) {
1027         off_phase = on_phase;
1028       } else {
1029         off_phase = pGraphState->m_DashArray[i + 1];
1030       }
1031       on_phase /= width;
1032       off_phase /= width;
1033       if (on_phase + off_phase <= 0.00002f) {
1034         on_phase = 1.0f / 10;
1035         off_phase = 1.0f / 10;
1036       }
1037       if (bDashExtend) {
1038         if (off_phase < 1) {
1039           off_phase = 0;
1040         } else {
1041           off_phase -= 1;
1042         }
1043         on_phase += 1;
1044       }
1045       if (on_phase == 0 || off_phase == 0) {
1046         if (nCount == 0) {
1047           on_leftover += on_phase;
1048           off_leftover += off_phase;
1049         } else {
1050           pDashArray[nCount - 2] += on_phase;
1051           pDashArray[nCount - 1] += off_phase;
1052         }
1053       } else {
1054         pDashArray[nCount++] = on_phase + on_leftover;
1055         on_leftover = 0;
1056         pDashArray[nCount++] = off_phase + off_leftover;
1057         off_leftover = 0;
1058       }
1059     }
1060     CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
1061     FX_FLOAT phase = pGraphState->m_DashPhase;
1062     if (bDashExtend) {
1063       if (phase < 0.5f) {
1064         phase = 0;
1065       } else {
1066         phase -= 0.5f;
1067       }
1068     }
1069     CallFunc(GdipSetPenDashOffset)(pPen, phase);
1070     FX_Free(pDashArray);
1071     pDashArray = NULL;
1072   }
1073   CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
1074   return pPen;
1075 }
1076 static FX_BOOL IsSmallTriangle(PointF* points,
1077                                const CFX_AffineMatrix* pMatrix,
1078                                int& v1,
1079                                int& v2) {
1080   int pairs[] = {1, 2, 0, 2, 0, 1};
1081   for (int i = 0; i < 3; i++) {
1082     int pair1 = pairs[i * 2];
1083     int pair2 = pairs[i * 2 + 1];
1084     FX_FLOAT x1 = points[pair1].X, x2 = points[pair2].X;
1085     FX_FLOAT y1 = points[pair1].Y, y2 = points[pair2].Y;
1086     if (pMatrix) {
1087       pMatrix->Transform(x1, y1);
1088       pMatrix->Transform(x2, y2);
1089     }
1090     FX_FLOAT dx = x1 - x2;
1091     FX_FLOAT dy = y1 - y2;
1092     FX_FLOAT distance_square = FXSYS_Mul(dx, dx) + FXSYS_Mul(dy, dy);
1093     if (distance_square < (1.0f * 2 + 1.0f / 4)) {
1094       v1 = i;
1095       v2 = pair1;
1096       return TRUE;
1097     }
1098   }
1099   return FALSE;
1100 }
1101 FX_BOOL CGdiplusExt::DrawPath(HDC hDC,
1102                               const CFX_PathData* pPathData,
1103                               const CFX_AffineMatrix* pObject2Device,
1104                               const CFX_GraphStateData* pGraphState,
1105                               FX_DWORD fill_argb,
1106                               FX_DWORD stroke_argb,
1107                               int fill_mode) {
1108   int nPoints = pPathData->GetPointCount();
1109   if (nPoints == 0) {
1110     return TRUE;
1111   }
1112   FX_PATHPOINT* pPoints = pPathData->GetPoints();
1113   GpGraphics* pGraphics = NULL;
1114   CGdiplusExt& GdiplusExt =
1115       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1116   CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
1117   CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
1118   CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
1119   GpMatrix* pMatrix = NULL;
1120   if (pObject2Device) {
1121     CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b,
1122                                 pObject2Device->c, pObject2Device->d,
1123                                 pObject2Device->e, pObject2Device->f, &pMatrix);
1124     CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
1125   }
1126   PointF* points = FX_Alloc(PointF, nPoints);
1127   BYTE* types = FX_Alloc(BYTE, nPoints);
1128   int nSubPathes = 0;
1129   FX_BOOL bSubClose = FALSE;
1130   int pos_subclose = 0;
1131   FX_BOOL bSmooth = FALSE;
1132   int startpoint = 0;
1133   for (int i = 0; i < nPoints; i++) {
1134     points[i].X = pPoints[i].m_PointX;
1135     points[i].Y = pPoints[i].m_PointY;
1136     FX_FLOAT x, y;
1137     if (pObject2Device) {
1138       pObject2Device->Transform(pPoints[i].m_PointX, pPoints[i].m_PointY, x, y);
1139     } else {
1140       x = pPoints[i].m_PointX;
1141       y = pPoints[i].m_PointY;
1142     }
1143     if (x > 50000 * 1.0f) {
1144       points[i].X = 50000 * 1.0f;
1145     }
1146     if (x < -50000 * 1.0f) {
1147       points[i].X = -50000 * 1.0f;
1148     }
1149     if (y > 50000 * 1.0f) {
1150       points[i].Y = 50000 * 1.0f;
1151     }
1152     if (y < -50000 * 1.0f) {
1153       points[i].Y = -50000 * 1.0f;
1154     }
1155     int point_type = pPoints[i].m_Flag & FXPT_TYPE;
1156     if (point_type == FXPT_MOVETO) {
1157       types[i] = PathPointTypeStart;
1158       nSubPathes++;
1159       bSubClose = FALSE;
1160       startpoint = i;
1161     } else if (point_type == FXPT_LINETO) {
1162       types[i] = PathPointTypeLine;
1163       if (pPoints[i - 1].m_Flag == FXPT_MOVETO &&
1164           (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&
1165           points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
1166         points[i].X += 0.01f;
1167         continue;
1168       }
1169       if (!bSmooth && points[i].X != points[i - 1].X &&
1170           points[i].Y != points[i - 1].Y) {
1171         bSmooth = TRUE;
1172       }
1173     } else if (point_type == FXPT_BEZIERTO) {
1174       types[i] = PathPointTypeBezier;
1175       bSmooth = TRUE;
1176     }
1177     if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {
1178       if (bSubClose) {
1179         types[pos_subclose] &= ~PathPointTypeCloseSubpath;
1180       } else {
1181         bSubClose = TRUE;
1182       }
1183       pos_subclose = i;
1184       types[i] |= PathPointTypeCloseSubpath;
1185       if (!bSmooth && points[i].X != points[startpoint].X &&
1186           points[i].Y != points[startpoint].Y) {
1187         bSmooth = TRUE;
1188       }
1189     }
1190   }
1191   if (fill_mode & FXFILL_NOPATHSMOOTH) {
1192     bSmooth = FALSE;
1193     CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
1194   } else if (!(fill_mode & FXFILL_FULLCOVER)) {
1195     if (!bSmooth && (fill_mode & 3)) {
1196       bSmooth = TRUE;
1197     }
1198     if (bSmooth || (pGraphState && pGraphState->m_LineWidth > 2)) {
1199       CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
1200     }
1201   }
1202   int new_fill_mode = fill_mode & 3;
1203   if (nPoints == 4 && pGraphState == NULL) {
1204     int v1, v2;
1205     if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
1206       GpPen* pPen = NULL;
1207       CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
1208       CallFunc(GdipDrawLineI)(
1209           pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
1210           FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
1211       CallFunc(GdipDeletePen)(pPen);
1212       return TRUE;
1213     }
1214   }
1215   GpPath* pGpPath = NULL;
1216   CallFunc(GdipCreatePath2)(points, types, nPoints,
1217                             GdiFillType2Gdip(new_fill_mode), &pGpPath);
1218   if (!pGpPath) {
1219     if (pMatrix) {
1220       CallFunc(GdipDeleteMatrix)(pMatrix);
1221     }
1222     FX_Free(points);
1223     FX_Free(types);
1224     CallFunc(GdipDeleteGraphics)(pGraphics);
1225     return FALSE;
1226   }
1227   if (new_fill_mode) {
1228     GpBrush* pBrush = _GdipCreateBrush(fill_argb);
1229     CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
1230     CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
1231     CallFunc(GdipDeleteBrush)(pBrush);
1232   }
1233   if (pGraphState && stroke_argb) {
1234     GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb,
1235                                  fill_mode & FX_STROKE_TEXT_MODE);
1236     if (nSubPathes == 1) {
1237       CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
1238     } else {
1239       int iStart = 0;
1240       for (int i = 0; i < nPoints; i++) {
1241         if (i == nPoints - 1 || types[i + 1] == PathPointTypeStart) {
1242           GpPath* pSubPath;
1243           CallFunc(GdipCreatePath2)(points + iStart, types + iStart,
1244                                     i - iStart + 1,
1245                                     GdiFillType2Gdip(new_fill_mode), &pSubPath);
1246           iStart = i + 1;
1247           CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
1248           CallFunc(GdipDeletePath)(pSubPath);
1249         }
1250       }
1251     }
1252     CallFunc(GdipDeletePen)(pPen);
1253   }
1254   if (pMatrix) {
1255     CallFunc(GdipDeleteMatrix)(pMatrix);
1256   }
1257   FX_Free(points);
1258   FX_Free(types);
1259   CallFunc(GdipDeletePath)(pGpPath);
1260   CallFunc(GdipDeleteGraphics)(pGraphics);
1261   return TRUE;
1262 }
1263 class GpStream final : public IStream {
1264   LONG m_RefCount;
1265   int m_ReadPos;
1266   CFX_ByteTextBuf m_InterStream;
1267
1268  public:
1269   GpStream() {
1270     m_RefCount = 1;
1271     m_ReadPos = 0;
1272   }
1273   virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
1274                                                    void** ppvObject) {
1275     if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
1276         iid == __uuidof(ISequentialStream)) {
1277       *ppvObject = static_cast<IStream*>(this);
1278       AddRef();
1279       return S_OK;
1280     }
1281     return E_NOINTERFACE;
1282   }
1283   virtual ULONG STDMETHODCALLTYPE AddRef(void) {
1284     return (ULONG)InterlockedIncrement(&m_RefCount);
1285   }
1286   virtual ULONG STDMETHODCALLTYPE Release(void) {
1287     ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
1288     if (res == 0) {
1289       delete this;
1290     }
1291     return res;
1292   }
1293
1294  public:
1295   virtual HRESULT STDMETHODCALLTYPE Read(void* Output,
1296                                          ULONG cb,
1297                                          ULONG* pcbRead) {
1298     size_t bytes_left;
1299     size_t bytes_out;
1300     if (pcbRead != NULL) {
1301       *pcbRead = 0;
1302     }
1303     if (m_ReadPos == m_InterStream.GetLength()) {
1304       return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
1305     }
1306     bytes_left = m_InterStream.GetLength() - m_ReadPos;
1307     bytes_out = FX_MIN(cb, bytes_left);
1308     FXSYS_memcpy(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);
1309     m_ReadPos += (int32_t)bytes_out;
1310     if (pcbRead != NULL) {
1311       *pcbRead = (ULONG)bytes_out;
1312     }
1313     return S_OK;
1314   }
1315   virtual HRESULT STDMETHODCALLTYPE Write(void const* Input,
1316                                           ULONG cb,
1317                                           ULONG* pcbWritten) {
1318     if (cb <= 0) {
1319       if (pcbWritten != NULL) {
1320         *pcbWritten = 0;
1321       }
1322       return S_OK;
1323     }
1324     m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);
1325     if (pcbWritten != NULL) {
1326       *pcbWritten = cb;
1327     }
1328     return S_OK;
1329   }
1330
1331  public:
1332   virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) {
1333     return E_NOTIMPL;
1334   }
1335   virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
1336                                            ULARGE_INTEGER,
1337                                            ULARGE_INTEGER*,
1338                                            ULARGE_INTEGER*) {
1339     return E_NOTIMPL;
1340   }
1341   virtual HRESULT STDMETHODCALLTYPE Commit(DWORD) { return E_NOTIMPL; }
1342   virtual HRESULT STDMETHODCALLTYPE Revert(void) { return E_NOTIMPL; }
1343   virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
1344                                                ULARGE_INTEGER,
1345                                                DWORD) {
1346     return E_NOTIMPL;
1347   }
1348   virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
1349                                                  ULARGE_INTEGER,
1350                                                  DWORD) {
1351     return E_NOTIMPL;
1352   }
1353   virtual HRESULT STDMETHODCALLTYPE Clone(IStream**) { return E_NOTIMPL; }
1354   virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
1355                                          DWORD dwOrigin,
1356                                          ULARGE_INTEGER* lpNewFilePointer) {
1357     long start = 0;
1358     long new_read_position;
1359     switch (dwOrigin) {
1360       case STREAM_SEEK_SET:
1361         start = 0;
1362         break;
1363       case STREAM_SEEK_CUR:
1364         start = m_ReadPos;
1365         break;
1366       case STREAM_SEEK_END:
1367         start = m_InterStream.GetLength();
1368         break;
1369       default:
1370         return STG_E_INVALIDFUNCTION;
1371         break;
1372     }
1373     new_read_position = start + (long)liDistanceToMove.QuadPart;
1374     if (new_read_position < 0 ||
1375         new_read_position > m_InterStream.GetLength()) {
1376       return STG_E_SEEKERROR;
1377     }
1378     m_ReadPos = new_read_position;
1379     if (lpNewFilePointer != NULL) {
1380       lpNewFilePointer->QuadPart = m_ReadPos;
1381     }
1382     return S_OK;
1383   }
1384   virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag) {
1385     if (pStatstg == NULL) {
1386       return STG_E_INVALIDFUNCTION;
1387     }
1388     ZeroMemory(pStatstg, sizeof(STATSTG));
1389     pStatstg->cbSize.QuadPart = m_InterStream.GetLength();
1390     return S_OK;
1391   }
1392 };
1393 typedef struct {
1394   BITMAPINFO* pbmi;
1395   int Stride;
1396   LPBYTE pScan0;
1397   GpBitmap* pBitmap;
1398   BitmapData* pBitmapData;
1399   GpStream* pStream;
1400 } PREVIEW3_DIBITMAP;
1401 static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args) {
1402   GpBitmap* pBitmap;
1403   GpStream* pStream = NULL;
1404   CGdiplusExt& GdiplusExt =
1405       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1406   Status status = Ok;
1407   if (args.flags == WINDIB_OPEN_PATHNAME) {
1408     status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name,
1409                                                    &pBitmap);
1410   } else {
1411     if (args.memory_size == 0 || !args.memory_base) {
1412       return NULL;
1413     }
1414     pStream = new GpStream;
1415     pStream->Write(args.memory_base, (ULONG)args.memory_size, NULL);
1416     status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
1417   }
1418   if (status != Ok) {
1419     if (pStream) {
1420       pStream->Release();
1421     }
1422     return NULL;
1423   }
1424   UINT height, width;
1425   CallFunc(GdipGetImageHeight)(pBitmap, &height);
1426   CallFunc(GdipGetImageWidth)(pBitmap, &width);
1427   PixelFormat pixel_format;
1428   CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
1429   int info_size = sizeof(BITMAPINFOHEADER);
1430   int bpp = 24;
1431   int dest_pixel_format = PixelFormat24bppRGB;
1432   if (pixel_format == PixelFormat1bppIndexed) {
1433     info_size += 8;
1434     bpp = 1;
1435     dest_pixel_format = PixelFormat1bppIndexed;
1436   } else if (pixel_format == PixelFormat8bppIndexed) {
1437     info_size += 1024;
1438     bpp = 8;
1439     dest_pixel_format = PixelFormat8bppIndexed;
1440   } else if (pixel_format == PixelFormat32bppARGB) {
1441     bpp = 32;
1442     dest_pixel_format = PixelFormat32bppARGB;
1443   }
1444   LPBYTE buf = FX_Alloc(BYTE, info_size);
1445   BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
1446   pbmih->biBitCount = bpp;
1447   pbmih->biCompression = BI_RGB;
1448   pbmih->biHeight = -(int)height;
1449   pbmih->biPlanes = 1;
1450   pbmih->biWidth = width;
1451   Rect rect(0, 0, width, height);
1452   BitmapData* pBitmapData = FX_Alloc(BitmapData, 1);
1453   CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
1454                                dest_pixel_format, pBitmapData);
1455   if (pixel_format == PixelFormat1bppIndexed ||
1456       pixel_format == PixelFormat8bppIndexed) {
1457     DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
1458     struct {
1459       UINT flags;
1460       UINT Count;
1461       DWORD Entries[256];
1462     } pal;
1463     int size = 0;
1464     CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
1465     CallFunc(GdipGetImagePalette)(pBitmap, (ColorPalette*)&pal, size);
1466     int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
1467     for (int i = 0; i < entries; i++) {
1468       ppal[i] = pal.Entries[i] & 0x00ffffff;
1469     }
1470   }
1471   PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
1472   pInfo->pbmi = (BITMAPINFO*)buf;
1473   pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
1474   pInfo->Stride = pBitmapData->Stride;
1475   pInfo->pBitmap = pBitmap;
1476   pInfo->pBitmapData = pBitmapData;
1477   pInfo->pStream = pStream;
1478   return pInfo;
1479 }
1480 static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo) {
1481   CGdiplusExt& GdiplusExt =
1482       ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
1483   CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
1484   CallFunc(GdipDisposeImage)(pInfo->pBitmap);
1485   FX_Free(pInfo->pBitmapData);
1486   FX_Free((LPBYTE)pInfo->pbmi);
1487   if (pInfo->pStream) {
1488     pInfo->pStream->Release();
1489   }
1490   FX_Free(pInfo);
1491 }
1492 CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi,
1493                                          LPVOID pData,
1494                                          FX_BOOL bAlpha);
1495 CFX_DIBitmap* CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args) {
1496   PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
1497   if (pInfo == NULL) {
1498     return NULL;
1499   }
1500   int height = abs(pInfo->pbmi->bmiHeader.biHeight);
1501   int width = pInfo->pbmi->bmiHeader.biWidth;
1502   int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
1503   LPBYTE pData = FX_Alloc2D(BYTE, dest_pitch, height);
1504   if (dest_pitch == pInfo->Stride) {
1505     FXSYS_memcpy(pData, pInfo->pScan0, dest_pitch * height);
1506   } else {
1507     for (int i = 0; i < height; i++) {
1508       FXSYS_memcpy(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i,
1509                    dest_pitch);
1510     }
1511   }
1512   CFX_DIBitmap* pDIBitmap = _FX_WindowsDIB_LoadFromBuf(
1513       pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
1514   FX_Free(pData);
1515   FreeDIBitmap(pInfo);
1516   return pDIBitmap;
1517 }
1518 #endif