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