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