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