Don't stretch bitmaps if destination size is empty.
[pdfium.git] / core / src / fxge / win32 / fx_win32_gdipext.cpp
index 7f92406..0f13721 100644 (file)
-// Copyright 2014 PDFium Authors. All rights reserved.\r
-// Use of this source code is governed by a BSD-style license that can be\r
-// found in the LICENSE file.\r
\r
-// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com\r
-\r
-#include "../../../include/fxge/fx_ge.h"\r
-#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN64_\r
-#include <windows.h>\r
-#include "../../../include/fxge/fx_ge_win32.h"\r
-#include "win32_int.h"\r
-#include "../Microsoft SDK/include/GdiPlus.h"\r
-using namespace Gdiplus;\r
-using namespace Gdiplus::DllExports;\r
-#define GdiFillType2Gdip(fill_type) (fill_type == ALTERNATE ? FillModeAlternate : FillModeWinding)\r
-static CombineMode GdiCombineMode2Gdip(int mode)\r
-{\r
-    switch (mode) {\r
-        case RGN_AND:\r
-            return CombineModeIntersect;\r
-    }\r
-    return CombineModeIntersect;\r
-}\r
-enum {\r
-    FuncId_GdipCreatePath2,\r
-    FuncId_GdipSetPenDashStyle,\r
-    FuncId_GdipSetPenDashArray,\r
-    FuncId_GdipSetPenDashCap197819,\r
-    FuncId_GdipSetPenLineJoin,\r
-    FuncId_GdipSetPenWidth,\r
-    FuncId_GdipCreateFromHDC,\r
-    FuncId_GdipSetPageUnit,\r
-    FuncId_GdipSetSmoothingMode,\r
-    FuncId_GdipCreateSolidFill,\r
-    FuncId_GdipFillPath,\r
-    FuncId_GdipDeleteBrush,\r
-    FuncId_GdipCreatePen1,\r
-    FuncId_GdipSetPenMiterLimit,\r
-    FuncId_GdipDrawPath,\r
-    FuncId_GdipDeletePen,\r
-    FuncId_GdipDeletePath,\r
-    FuncId_GdipDeleteGraphics,\r
-    FuncId_GdipCreateBitmapFromFileICM,\r
-    FuncId_GdipCreateBitmapFromStreamICM,\r
-    FuncId_GdipGetImageHeight,\r
-    FuncId_GdipGetImageWidth,\r
-    FuncId_GdipGetImagePixelFormat,\r
-    FuncId_GdipBitmapLockBits,\r
-    FuncId_GdipGetImagePaletteSize,\r
-    FuncId_GdipGetImagePalette,\r
-    FuncId_GdipBitmapUnlockBits,\r
-    FuncId_GdipDisposeImage,\r
-    FuncId_GdipFillRectangle,\r
-    FuncId_GdipCreateBitmapFromScan0,\r
-    FuncId_GdipSetImagePalette,\r
-    FuncId_GdipSetInterpolationMode,\r
-    FuncId_GdipDrawImagePointsI,\r
-    FuncId_GdipCreateBitmapFromGdiDib,\r
-    FuncId_GdiplusStartup,\r
-    FuncId_GdipDrawLineI,\r
-    FuncId_GdipResetClip,\r
-    FuncId_GdipCreatePath,\r
-    FuncId_GdipAddPathPath,\r
-    FuncId_GdipSetPathFillMode,\r
-    FuncId_GdipSetClipPath,\r
-    FuncId_GdipGetClip,\r
-    FuncId_GdipCreateRegion,\r
-    FuncId_GdipGetClipBoundsI,\r
-    FuncId_GdipSetClipRegion,\r
-    FuncId_GdipWidenPath,\r
-    FuncId_GdipAddPathLine,\r
-    FuncId_GdipAddPathRectangle,\r
-    FuncId_GdipDeleteRegion,\r
-    FuncId_GdipGetDC,\r
-    FuncId_GdipReleaseDC,\r
-    FuncId_GdipSetPenLineCap197819,\r
-    FuncId_GdipSetPenDashOffset,\r
-    FuncId_GdipResetPath,\r
-    FuncId_GdipCreateRegionPath,\r
-    FuncId_GdipCreateFont,\r
-    FuncId_GdipGetFontSize,\r
-    FuncId_GdipCreateFontFamilyFromName,\r
-    FuncId_GdipSetTextRenderingHint,\r
-    FuncId_GdipDrawDriverString,\r
-    FuncId_GdipCreateMatrix2,\r
-    FuncId_GdipDeleteMatrix,\r
-    FuncId_GdipSetWorldTransform,\r
-    FuncId_GdipResetWorldTransform,\r
-    FuncId_GdipDeleteFontFamily,\r
-    FuncId_GdipDeleteFont,\r
-    FuncId_GdipNewPrivateFontCollection,\r
-    FuncId_GdipDeletePrivateFontCollection,\r
-    FuncId_GdipPrivateAddMemoryFont,\r
-    FuncId_GdipGetFontCollectionFamilyList,\r
-    FuncId_GdipGetFontCollectionFamilyCount,\r
-    FuncId_GdipSetTextContrast,\r
-    FuncId_GdipSetPixelOffsetMode,\r
-    FuncId_GdipGetImageGraphicsContext,\r
-    FuncId_GdipDrawImageI,\r
-    FuncId_GdipDrawImageRectI,\r
-    FuncId_GdipDrawString,\r
-    FuncId_GdipSetPenTransform,\r
-};\r
-static LPCSTR g_GdipFuncNames[] = {\r
-    "GdipCreatePath2",\r
-    "GdipSetPenDashStyle",\r
-    "GdipSetPenDashArray",\r
-    "GdipSetPenDashCap197819",\r
-    "GdipSetPenLineJoin",\r
-    "GdipSetPenWidth",\r
-    "GdipCreateFromHDC",\r
-    "GdipSetPageUnit",\r
-    "GdipSetSmoothingMode",\r
-    "GdipCreateSolidFill",\r
-    "GdipFillPath",\r
-    "GdipDeleteBrush",\r
-    "GdipCreatePen1",\r
-    "GdipSetPenMiterLimit",\r
-    "GdipDrawPath",\r
-    "GdipDeletePen",\r
-    "GdipDeletePath",\r
-    "GdipDeleteGraphics",\r
-    "GdipCreateBitmapFromFileICM",\r
-    "GdipCreateBitmapFromStreamICM",\r
-    "GdipGetImageHeight",\r
-    "GdipGetImageWidth",\r
-    "GdipGetImagePixelFormat",\r
-    "GdipBitmapLockBits",\r
-    "GdipGetImagePaletteSize",\r
-    "GdipGetImagePalette",\r
-    "GdipBitmapUnlockBits",\r
-    "GdipDisposeImage",\r
-    "GdipFillRectangle",\r
-    "GdipCreateBitmapFromScan0",\r
-    "GdipSetImagePalette",\r
-    "GdipSetInterpolationMode",\r
-    "GdipDrawImagePointsI",\r
-    "GdipCreateBitmapFromGdiDib",\r
-    "GdiplusStartup",\r
-    "GdipDrawLineI",\r
-    "GdipResetClip",\r
-    "GdipCreatePath",\r
-    "GdipAddPathPath",\r
-    "GdipSetPathFillMode",\r
-    "GdipSetClipPath",\r
-    "GdipGetClip",\r
-    "GdipCreateRegion",\r
-    "GdipGetClipBoundsI",\r
-    "GdipSetClipRegion",\r
-    "GdipWidenPath",\r
-    "GdipAddPathLine",\r
-    "GdipAddPathRectangle",\r
-    "GdipDeleteRegion",\r
-    "GdipGetDC",\r
-    "GdipReleaseDC",\r
-    "GdipSetPenLineCap197819",\r
-    "GdipSetPenDashOffset",\r
-    "GdipResetPath",\r
-    "GdipCreateRegionPath",\r
-    "GdipCreateFont",\r
-    "GdipGetFontSize",\r
-    "GdipCreateFontFamilyFromName",\r
-    "GdipSetTextRenderingHint",\r
-    "GdipDrawDriverString",\r
-    "GdipCreateMatrix2",\r
-    "GdipDeleteMatrix",\r
-    "GdipSetWorldTransform",\r
-    "GdipResetWorldTransform",\r
-    "GdipDeleteFontFamily",\r
-    "GdipDeleteFont",\r
-    "GdipNewPrivateFontCollection",\r
-    "GdipDeletePrivateFontCollection",\r
-    "GdipPrivateAddMemoryFont",\r
-    "GdipGetFontCollectionFamilyList",\r
-    "GdipGetFontCollectionFamilyCount",\r
-    "GdipSetTextContrast",\r
-    "GdipSetPixelOffsetMode",\r
-    "GdipGetImageGraphicsContext",\r
-    "GdipDrawImageI",\r
-    "GdipDrawImageRectI",\r
-    "GdipDrawString",\r
-    "GdipSetPenTransform",\r
-};\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreatePath2)(GDIPCONST GpPointF*, GDIPCONST BYTE*, INT, GpFillMode, GpPath **path);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashStyle)(GpPen *pen, GpDashStyle dashstyle);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashArray)(GpPen *pen, GDIPCONST REAL *dash, INT count);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashCap197819)(GpPen *pen, GpDashCap dashCap);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenLineJoin)(GpPen *pen, GpLineJoin lineJoin);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenWidth)(GpPen *pen, REAL width);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateFromHDC)(HDC hdc, GpGraphics **graphics);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPageUnit)(GpGraphics *graphics, GpUnit unit);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetSmoothingMode)(GpGraphics *graphics, SmoothingMode smoothingMode);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateSolidFill)(ARGB color, GpSolidFill **brush);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipFillPath)(GpGraphics *graphics, GpBrush *brush, GpPath *path);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteBrush)(GpBrush *brush);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreatePen1)(ARGB color, REAL width, GpUnit unit, GpPen **pen);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenMiterLimit)(GpPen *pen, REAL miterLimit);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawPath)(GpGraphics *graphics, GpPen *pen, GpPath *path);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeletePen)(GpPen *pen);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeletePath)(GpPath* path);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteGraphics)(GpGraphics *graphics);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromFileICM)(GDIPCONST WCHAR* filename, GpBitmap **bitmap);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromStreamICM)(IStream* stream, GpBitmap **bitmap);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImageWidth)(GpImage *image, UINT *width);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImageHeight)(GpImage *image, UINT *height);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImagePixelFormat)(GpImage *image, PixelFormat *format);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipBitmapLockBits)(GpBitmap* bitmap, GDIPCONST GpRect* rect, UINT flags, PixelFormat format, BitmapData* lockedBitmapData);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImagePalette)(GpImage *image, ColorPalette *palette, INT size);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImagePaletteSize)(GpImage *image, INT *size);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipBitmapUnlockBits)(GpBitmap* bitmap, BitmapData* lockedBitmapData);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDisposeImage)(GpImage *image);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipFillRectangle)(GpGraphics *graphics, GpBrush *brush, REAL x, REAL y, REAL width, REAL height);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromScan0)(INT width, INT height, INT stride, PixelFormat format, BYTE* scan0, GpBitmap** bitmap);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetImagePalette)(GpImage *image, GDIPCONST ColorPalette *palette);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetInterpolationMode)(GpGraphics *graphics, InterpolationMode interpolationMode);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawImagePointsI)(GpGraphics *graphics, GpImage *image, GDIPCONST GpPoint *dstpoints, INT count);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromGdiDib)(GDIPCONST BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData, GpBitmap** bitmap);\r
-typedef Status (WINAPI *FuncType_GdiplusStartup)(OUT FX_UINTPTR *token, const GdiplusStartupInput *input, OUT GdiplusStartupOutput *output);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawLineI)(GpGraphics *graphics, GpPen *pen, int x1, int y1, int x2, int y2);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipResetClip)(GpGraphics *graphics);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreatePath)(GpFillMode brushMode, GpPath **path);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipAddPathPath)(GpPath *path, GDIPCONST GpPath* addingPath, BOOL connect);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPathFillMode)(GpPath *path, GpFillMode fillmode);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetClipPath)(GpGraphics *graphics, GpPath *path, CombineMode combineMode);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetClip)(GpGraphics *graphics, GpRegion *region);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateRegion)(GpRegion **region);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetClipBoundsI)(GpGraphics *graphics, GpRect *rect);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetClipRegion)(GpGraphics *graphics, GpRegion *region, CombineMode combineMode);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipWidenPath)(GpPath *nativePath, GpPen *pen, GpMatrix *matrix, REAL flatness);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipAddPathLine)(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipAddPathRectangle)(GpPath *path, REAL x, REAL y, REAL width, REAL height);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteRegion)(GpRegion *region);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetDC)(GpGraphics* graphics, HDC * hdc);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipReleaseDC)(GpGraphics* graphics, HDC hdc);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenLineCap197819)(GpPen *pen, GpLineCap startCap, GpLineCap endCap, GpDashCap dashCap);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashOffset)(GpPen *pen, REAL offset);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipResetPath)(GpPath *path);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateRegionPath)(GpPath *path, GpRegion **region);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateFont)(GDIPCONST GpFontFamily *fontFamily, REAL emSize, INT style, Unit unit, GpFont **font);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetFontSize)(GpFont *font, REAL *size);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateFontFamilyFromName)(GDIPCONST WCHAR *name, GpFontCollection *fontCollection, GpFontFamily **FontFamily);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetTextRenderingHint)(GpGraphics *graphics, TextRenderingHint mode);\r
-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);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateMatrix2)(REAL m11, REAL m12, REAL m21, REAL m22, REAL dx, REAL dy, GpMatrix **matrix);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteMatrix)(GpMatrix *matrix);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetWorldTransform)(GpGraphics *graphics, GpMatrix *matrix);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipResetWorldTransform)(GpGraphics *graphics);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteFontFamily)(GpFontFamily *FontFamily);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteFont)(GpFont* font);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipNewPrivateFontCollection)(GpFontCollection** fontCollection);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDeletePrivateFontCollection)(GpFontCollection** fontCollection);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipPrivateAddMemoryFont)(GpFontCollection* fontCollection, GDIPCONST void* memory, INT length);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetFontCollectionFamilyList)(GpFontCollection* fontCollection, INT numSought, GpFontFamily* gpfamilies[], INT* numFound);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetFontCollectionFamilyCount)(GpFontCollection* fontCollection, INT* numFound);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetTextContrast)(GpGraphics *graphics, UINT contrast);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPixelOffsetMode)(GpGraphics* graphics, PixelOffsetMode pixelOffsetMode);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImageGraphicsContext)(GpImage *image, GpGraphics **graphics);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawImageI)(GpGraphics *graphics, GpImage *image, INT x, INT y);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawImageRectI)(GpGraphics *graphics, GpImage *image, INT x, INT y, INT width, INT height);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawString)(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, GDIPCONST GpBrush *brush);\r
-typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenTransform)(GpPen *pen, GpMatrix *matrix);\r
-#define CallFunc(funcname) ((FuncType_##funcname)GdiplusExt.m_Functions[FuncId_##funcname])\r
-typedef HANDLE   (__stdcall *FuncType_GdiAddFontMemResourceEx)(PVOID pbFont, DWORD cbFont, PVOID pdv, DWORD *pcFonts);\r
-typedef BOOL     (__stdcall *FuncType_GdiRemoveFontMemResourceEx)(HANDLE handle);\r
-void* CGdiplusExt::GdiAddFontMemResourceEx(void *pFontdata, FX_DWORD size, void* pdv, FX_DWORD* num_face)\r
-{\r
-    if (m_pGdiAddFontMemResourceEx) {\r
-        return ((FuncType_GdiAddFontMemResourceEx)m_pGdiAddFontMemResourceEx)((PVOID)pFontdata, (DWORD)size, (PVOID)pdv, (DWORD*)num_face);\r
-    }\r
-    return NULL;\r
-}\r
-FX_BOOL CGdiplusExt::GdiRemoveFontMemResourceEx(void* handle)\r
-{\r
-    if (m_pGdiRemoveFontMemResourseEx) {\r
-        return ((FuncType_GdiRemoveFontMemResourceEx)m_pGdiRemoveFontMemResourseEx)((HANDLE)handle);\r
-    }\r
-    return FALSE;\r
-}\r
-static GpBrush* _GdipCreateBrush(DWORD argb)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpSolidFill* solidBrush = NULL;\r
-    CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);\r
-    return solidBrush;\r
-}\r
-static CFX_DIBitmap* _StretchMonoToGray(int dest_width, int dest_height,\r
-                                        const CFX_DIBitmap* pSource, FX_RECT* pClipRect)\r
-{\r
-    FX_BOOL bFlipX = dest_width < 0;\r
-    if (bFlipX) {\r
-        dest_width = -dest_width;\r
-    }\r
-    FX_BOOL bFlipY = dest_height < 0;\r
-    if (bFlipY) {\r
-        dest_height = -dest_height;\r
-    }\r
-    int result_width = pClipRect->Width();\r
-    int result_height = pClipRect->Height();\r
-    int result_pitch = (result_width + 3) / 4 * 4;\r
-    CFX_DIBitmap* pStretched = FX_NEW CFX_DIBitmap;\r
-    if (!pStretched) {\r
-        return NULL;\r
-    }\r
-    if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb)) {\r
-        delete pStretched;\r
-        return NULL;\r
-    }\r
-    LPBYTE dest_buf = pStretched->GetBuffer();\r
-    int src_width = pSource->GetWidth();\r
-    int src_height = pSource->GetHeight();\r
-    int src_count = src_width * src_height;\r
-    int dest_count = dest_width * dest_height;\r
-    int ratio = 255 * dest_count / src_count;\r
-    int y_unit = src_height / dest_height;\r
-    int x_unit = src_width / dest_width;\r
-    int area_unit = y_unit * x_unit;\r
-    LPBYTE src_buf = pSource->GetBuffer();\r
-    int src_pitch = pSource->GetPitch();\r
-    for (int dest_y = 0; dest_y < result_height; dest_y ++) {\r
-        LPBYTE dest_scan = dest_buf + dest_y * result_pitch;\r
-        int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top) : (dest_y + pClipRect->top);\r
-        src_y_start = src_y_start * src_height / dest_height;\r
-        LPBYTE src_scan = src_buf + src_y_start * src_pitch;\r
-        for (int dest_x = 0; dest_x < result_width; dest_x ++) {\r
-            int sum = 0;\r
-            int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left) : (dest_x + pClipRect->left);\r
-            src_x_start = src_x_start * src_width / dest_width;\r
-            int src_x_end = src_x_start + x_unit;\r
-            LPBYTE src_line = src_scan;\r
-            for (int src_y = 0; src_y < y_unit; src_y ++) {\r
-                for (int src_x = src_x_start; src_x < src_x_end; src_x ++) {\r
-                    if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {\r
-                        sum += 255;\r
-                    }\r
-                }\r
-                src_line += src_pitch;\r
-            }\r
-            dest_scan[dest_x] = 255 - sum / area_unit;\r
-        }\r
-    }\r
-    return pStretched;\r
-}\r
-static void OutputImageMask(GpGraphics* pGraphics, BOOL bMonoDevice, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,\r
-                            int dest_width, int dest_height, FX_ARGB argb, const FX_RECT* pClipRect)\r
-{\r
-    ASSERT(pBitmap->GetBPP() == 1);\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();\r
-    int src_pitch = pBitmap->GetPitch();\r
-    FX_LPBYTE scan0 = pBitmap->GetBuffer();\r
-    if (src_width == 1 && src_height == 1) {\r
-        if ((scan0[0] & 0x80) == 0) {\r
-            return;\r
-        }\r
-        GpSolidFill* solidBrush;\r
-        CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);\r
-        if (dest_width < 0) {\r
-            dest_width = -dest_width;\r
-            dest_left -= dest_width;\r
-        }\r
-        if (dest_height < 0) {\r
-            dest_height = -dest_height;\r
-            dest_top -= dest_height;\r
-        }\r
-        CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left, (float)dest_top,\r
-                                    (float)dest_width, (float)dest_height);\r
-        CallFunc(GdipDeleteBrush)(solidBrush);\r
-        return;\r
-    }\r
-    if (!bMonoDevice && abs(dest_width) < src_width && abs(dest_height) < src_height) {\r
-        FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width, dest_top + dest_height);\r
-        image_rect.Normalize();\r
-        FX_RECT image_clip = image_rect;\r
-        image_clip.Intersect(*pClipRect);\r
-        if (image_clip.IsEmpty()) {\r
-            return;\r
-        }\r
-        image_clip.Offset(-image_rect.left, -image_rect.top);\r
-        CFX_DIBitmap* pStretched = NULL;\r
-        if (src_width * src_height > 10000) {\r
-            pStretched = _StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);\r
-        } else {\r
-            pStretched = pBitmap->StretchTo(dest_width, dest_height, FALSE, &image_clip);\r
-        }\r
-        GpBitmap* bitmap;\r
-        CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),\r
-                                            (image_clip.Width() + 3) / 4 * 4, PixelFormat8bppIndexed, pStretched->GetBuffer(), &bitmap);\r
-        int a, r, g, b;\r
-        ArgbDecode(argb, a, r, g, b);\r
-        UINT pal[258];\r
-        pal[0] = 0;\r
-        pal[1] = 256;\r
-        for (int i = 0; i < 256; i ++) {\r
-            pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);\r
-        }\r
-        CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);\r
-        CallFunc(GdipDrawImageI)(pGraphics, bitmap, image_rect.left + image_clip.left,\r
-                                 image_rect.top + image_clip.top);\r
-        CallFunc(GdipDisposeImage)(bitmap);\r
-        delete pStretched;\r
-        return;\r
-    }\r
-    GpBitmap* bitmap;\r
-    CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, PixelFormat1bppIndexed, scan0, &bitmap);\r
-    UINT palette[4] = { PaletteFlagsHasAlpha, 2, 0, argb };\r
-    CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)palette);\r
-    Point destinationPoints[] = {\r
-        Point(dest_left, dest_top),\r
-        Point(dest_left + dest_width, dest_top),\r
-        Point(dest_left, dest_top + dest_height)\r
-    };\r
-    CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);\r
-    CallFunc(GdipDisposeImage)(bitmap);\r
-}\r
-static void OutputImage(GpGraphics* pGraphics, const CFX_DIBitmap* pBitmap, const FX_RECT* pSrcRect,\r
-                        int dest_left, int dest_top, int dest_width, int dest_height)\r
-{\r
-    int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {\r
-        FX_RECT new_rect(0, 0, src_width, src_height);\r
-        CFX_DIBitmap* pCloned = pBitmap->Clone(pSrcRect);\r
-        if (!pCloned) {\r
-            return;\r
-        }\r
-        OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width, dest_height);\r
-        delete pCloned;\r
-        return;\r
-    }\r
-    int src_pitch = pBitmap->GetPitch();\r
-    FX_LPBYTE scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch + pBitmap->GetBPP() * pSrcRect->left / 8;\r
-    GpBitmap* bitmap = NULL;\r
-    switch (pBitmap->GetFormat()) {\r
-        case FXDIB_Argb:\r
-            CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,\r
-                                                PixelFormat32bppARGB, scan0, &bitmap);\r
-            break;\r
-        case FXDIB_Rgb32:\r
-            CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,\r
-                                                PixelFormat32bppRGB, scan0, &bitmap);\r
-            break;\r
-        case FXDIB_Rgb:\r
-            CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,\r
-                                                PixelFormat24bppRGB, scan0, &bitmap);\r
-            break;\r
-        case FXDIB_8bppRgb: {\r
-                CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,\r
-                                                    PixelFormat8bppIndexed, scan0, &bitmap);\r
-                UINT pal[258];\r
-                pal[0] = 0;\r
-                pal[1] = 256;\r
-                for (int i = 0; i < 256; i ++) {\r
-                    pal[i + 2] = pBitmap->GetPaletteEntry(i);\r
-                }\r
-                CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);\r
-                break;\r
-            }\r
-        case FXDIB_1bppRgb: {\r
-                CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,\r
-                                                    PixelFormat1bppIndexed, scan0, &bitmap);\r
-                break;\r
-            }\r
-    }\r
-    if (dest_height < 0) {\r
-        dest_height --;\r
-    }\r
-    if (dest_width < 0) {\r
-        dest_width --;\r
-    }\r
-    Point destinationPoints[] = {\r
-        Point(dest_left, dest_top),\r
-        Point(dest_left + dest_width, dest_top),\r
-        Point(dest_left, dest_top + dest_height)\r
-    };\r
-    CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);\r
-    CallFunc(GdipDisposeImage)(bitmap);\r
-}\r
-CGdiplusExt::CGdiplusExt()\r
-{\r
-    m_hModule = NULL;\r
-    m_GdiModule = NULL;\r
-    for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i ++) {\r
-        m_Functions[i] = NULL;\r
-    }\r
-    m_pGdiAddFontMemResourceEx = NULL;\r
-    m_pGdiRemoveFontMemResourseEx = NULL;\r
-}\r
-void CGdiplusExt::Load()\r
-{\r
-    CFX_ByteString strPlusPath = "";\r
-    FX_CHAR buf[MAX_PATH];\r
-    GetSystemDirectoryA(buf, MAX_PATH);\r
-    strPlusPath += buf;\r
-    strPlusPath += "\\";\r
-    strPlusPath += "GDIPLUS.DLL";\r
-    m_hModule = LoadLibraryA(strPlusPath);\r
-    if (m_hModule == NULL) {\r
-        return;\r
-    }\r
-    for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i ++) {\r
-        m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);\r
-        if (m_Functions[i] == NULL) {\r
-            m_hModule = NULL;\r
-            return;\r
-        }\r
-    }\r
-    FX_UINTPTR gdiplusToken;\r
-    GdiplusStartupInput gdiplusStartupInput;\r
-    ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(&gdiplusToken, &gdiplusStartupInput, NULL);\r
-    m_GdiModule = LoadLibraryA("GDI32.DLL");\r
-    if (m_GdiModule == NULL) {\r
-        return;\r
-    }\r
-    m_pGdiAddFontMemResourceEx = GetProcAddress(m_GdiModule, "AddFontMemResourceEx");\r
-    m_pGdiRemoveFontMemResourseEx = GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx");\r
-}\r
-CGdiplusExt::~CGdiplusExt()\r
-{\r
-}\r
-LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, FX_DWORD size)\r
-{\r
-    GpFontCollection* pCollection = NULL;\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipNewPrivateFontCollection)(&pCollection);\r
-    GpStatus status = CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);\r
-    if (status == Ok) {\r
-        return pCollection;\r
-    }\r
-    CallFunc(GdipDeletePrivateFontCollection)(&pCollection);\r
-    return NULL;\r
-}\r
-void CGdiplusExt::DeleteMemFont(LPVOID pCollection)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);\r
-}\r
-FX_BOOL CGdiplusExt::GdipCreateBitmap(CFX_DIBitmap* pBitmap, void**bitmap)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    PixelFormat format;\r
-    switch (pBitmap->GetFormat()) {\r
-        case FXDIB_Rgb:\r
-            format = PixelFormat24bppRGB;\r
-            break;\r
-        case FXDIB_Rgb32:\r
-            format = PixelFormat32bppRGB;\r
-            break;\r
-        case FXDIB_Argb:\r
-            format = PixelFormat32bppARGB;\r
-            break;\r
-        default:\r
-            return FALSE;\r
-    }\r
-    GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(pBitmap->GetWidth(), pBitmap->GetHeight(),\r
-                      pBitmap->GetPitch(), format, pBitmap->GetBuffer(), (GpBitmap**)bitmap);\r
-    if (status == Ok) {\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-FX_BOOL CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpStatus status = CallFunc(GdipGetImageGraphicsContext)((GpBitmap*)bitmap, (GpGraphics**)graphics);\r
-    if (status == Ok) {\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-FX_BOOL CGdiplusExt::GdipCreateFontFamilyFromName(FX_LPCWSTR name, void* pFontCollection, void**pFamily)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpStatus status = CallFunc(GdipCreateFontFamilyFromName)((GDIPCONST WCHAR *)name, (GpFontCollection*)pFontCollection, (GpFontFamily**)pFamily);\r
-    if (status == Ok) {\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-FX_BOOL CGdiplusExt::GdipCreateFontFromFamily(void* pFamily, FX_FLOAT font_size, int fontstyle, int flag, void** pFont)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpStatus status = CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle, Unit(flag), (GpFont**)pFont);\r
-    if (status == Ok) {\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-void CGdiplusExt::GdipGetFontSize(void *pFont, FX_FLOAT *size)\r
-{\r
-    REAL get_size;\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpStatus status = CallFunc(GdipGetFontSize)((GpFont *)pFont, (REAL*)&get_size);\r
-    if (status == Ok) {\r
-        *size = (FX_FLOAT)get_size;\r
-    } else {\r
-        *size = 0;\r
-    }\r
-}\r
-void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics, (TextRenderingHint)mode);\r
-}\r
-void CGdiplusExt::GdipSetPageUnit(void* graphics, FX_DWORD unit)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);\r
-}\r
-FX_BOOL CGdiplusExt::GdipDrawDriverString(void *graphics,  unsigned short *text, int length,\r
-        void *font, void* brush, void *positions, int flags, const void *matrix)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpStatus status = CallFunc(GdipDrawDriverString)((GpGraphics*)graphics, (GDIPCONST UINT16 *)text, (INT)length, (GDIPCONST GpFont *)font, (GDIPCONST GpBrush*)brush,\r
-                      (GDIPCONST PointF *)positions, (INT)flags, (GDIPCONST GpMatrix *)matrix);\r
-    if (status == Ok) {\r
-        return TRUE;\r
-    }\r
-    return FALSE;\r
-}\r
-void CGdiplusExt::GdipCreateBrush(FX_DWORD fill_argb, void** pBrush)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipCreateSolidFill)((ARGB)fill_argb, (GpSolidFill**)pBrush);\r
-}\r
-void CGdiplusExt::GdipDeleteBrush(void* pBrush)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);\r
-}\r
-void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection, FX_FLOAT font_size, int fontstyle)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    int numFamilies = 0;\r
-    GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)((GpFontCollection*)pFontCollection, &numFamilies);\r
-    if (status != Ok) {\r
-        return NULL;\r
-    }\r
-    GpFontFamily* family_list[1];\r
-    status = CallFunc(GdipGetFontCollectionFamilyList)((GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);\r
-    if (status != Ok) {\r
-        return NULL;\r
-    }\r
-    GpFont* pFont = NULL;\r
-    status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle, UnitPixel, &pFont);\r
-    if (status != Ok) {\r
-        return NULL;\r
-    }\r
-    return pFont;\r
-}\r
-void CGdiplusExt::GdipCreateMatrix(FX_FLOAT a, FX_FLOAT b, FX_FLOAT c, FX_FLOAT d, FX_FLOAT e, FX_FLOAT f, void** matrix)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);\r
-}\r
-void CGdiplusExt::GdipDeleteMatrix(void* matrix)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);\r
-}\r
-void CGdiplusExt::GdipDeleteFontFamily(void* pFamily)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);\r
-}\r
-void CGdiplusExt::GdipDeleteFont(void* pFont)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDeleteFont)((GpFont*)pFont);\r
-}\r
-void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);\r
-}\r
-void CGdiplusExt::GdipDisposeImage(void* bitmap)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);\r
-}\r
-void CGdiplusExt::GdipDeleteGraphics(void* graphics)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);\r
-}\r
-FX_BOOL CGdiplusExt::StretchBitMask(HDC hDC, BOOL bMonoDevice, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,\r
-                                    int dest_width, int dest_height, FX_DWORD argb, const FX_RECT* pClipRect, int flags)\r
-{\r
-    ASSERT(pBitmap->GetBPP() == 1);\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    GpGraphics* pGraphics = NULL;\r
-    CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);\r
-    CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);\r
-    if (flags & FXDIB_NOSMOOTH) {\r
-        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeNearestNeighbor);\r
-    } else {\r
-        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);\r
-    }\r
-    OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top, dest_width, dest_height, argb, pClipRect);\r
-    CallFunc(GdipDeleteGraphics)(pGraphics);\r
-    return TRUE;\r
-}\r
-FX_BOOL CGdiplusExt::StretchDIBits(HDC hDC, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,\r
-                                   int dest_width, int dest_height, const FX_RECT* pClipRect, int flags)\r
-{\r
-    GpGraphics* pGraphics;\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);\r
-    CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);\r
-    if (flags & FXDIB_NOSMOOTH) {\r
-        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeNearestNeighbor);\r
-    } else if (pBitmap->GetWidth() > abs(dest_width) / 2 || pBitmap->GetHeight() > abs(dest_height) / 2) {\r
-        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);\r
-    } else {\r
-        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);\r
-    }\r
-    FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());\r
-    OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width, dest_height);\r
-    CallFunc(GdipDeleteGraphics)(pGraphics);\r
-    CallFunc(GdipDeleteGraphics)(pGraphics);\r
-    return TRUE;\r
-}\r
-static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState, const CFX_AffineMatrix* pMatrix, DWORD argb, FX_BOOL bTextMode = FALSE)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    FX_FLOAT width = pGraphState ? pGraphState->m_LineWidth : 1.0f;\r
-    if (!bTextMode) {\r
-        FX_FLOAT unit = pMatrix == NULL ? 1.0f : FXSYS_Div(1.0f, (pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2);\r
-        if (width < unit) {\r
-            width = unit;\r
-        }\r
-    }\r
-    GpPen* pPen = NULL;\r
-    CallFunc(GdipCreatePen1)((ARGB)argb, width, UnitWorld, &pPen);\r
-    LineCap lineCap;\r
-    DashCap dashCap = DashCapFlat;\r
-    FX_BOOL bDashExtend = FALSE;\r
-    switch(pGraphState->m_LineCap) {\r
-        case CFX_GraphStateData::LineCapButt:\r
-            lineCap = LineCapFlat;\r
-            break;\r
-        case CFX_GraphStateData::LineCapRound:\r
-            lineCap = LineCapRound;\r
-            dashCap = DashCapRound;\r
-            bDashExtend = TRUE;\r
-            break;\r
-        case CFX_GraphStateData::LineCapSquare:\r
-            lineCap = LineCapSquare;\r
-            bDashExtend = TRUE;\r
-            break;\r
-    }\r
-    CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);\r
-    LineJoin lineJoin;\r
-    switch(pGraphState->m_LineJoin) {\r
-        case CFX_GraphStateData::LineJoinMiter:\r
-            lineJoin = LineJoinMiterClipped;\r
-            break;\r
-        case CFX_GraphStateData::LineJoinRound:\r
-            lineJoin = LineJoinRound;\r
-            break;\r
-        case CFX_GraphStateData::LineJoinBevel:\r
-            lineJoin = LineJoinBevel;\r
-            break;\r
-    }\r
-    CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);\r
-    if(pGraphState->m_DashCount) {\r
-        FX_FLOAT* pDashArray = FX_Alloc(FX_FLOAT, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);\r
-        if (!pDashArray) {\r
-            return NULL;\r
-        }\r
-        int nCount = 0;\r
-        FX_FLOAT on_leftover = 0, off_leftover = 0;\r
-        for (int i = 0; i < pGraphState->m_DashCount; i += 2) {\r
-            FX_FLOAT on_phase = pGraphState->m_DashArray[i];\r
-            FX_FLOAT off_phase;\r
-            if (i == pGraphState->m_DashCount - 1) {\r
-                off_phase = on_phase;\r
-            } else {\r
-                off_phase = pGraphState->m_DashArray[i + 1];\r
-            }\r
-            on_phase /= width;\r
-            off_phase /= width;\r
-            if (on_phase + off_phase <= 0.00002f) {\r
-                on_phase = 1.0f / 10;\r
-                off_phase = 1.0f / 10;\r
-            }\r
-            if (bDashExtend) {\r
-                if (off_phase < 1) {\r
-                    off_phase = 0;\r
-                } else {\r
-                    off_phase -= 1;\r
-                }\r
-                on_phase += 1;\r
-            }\r
-            if (on_phase == 0 || off_phase == 0) {\r
-                if (nCount == 0) {\r
-                    on_leftover += on_phase;\r
-                    off_leftover += off_phase;\r
-                } else {\r
-                    pDashArray[nCount - 2] += on_phase;\r
-                    pDashArray[nCount - 1] += off_phase;\r
-                }\r
-            } else {\r
-                pDashArray[nCount++] = on_phase + on_leftover;\r
-                on_leftover = 0;\r
-                pDashArray[nCount++] = off_phase + off_leftover;\r
-                off_leftover = 0;\r
-            }\r
-        }\r
-        CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);\r
-        FX_FLOAT phase = pGraphState->m_DashPhase;\r
-        if (bDashExtend)\r
-            if (phase < 0.5f) {\r
-                phase = 0;\r
-            } else {\r
-                phase -= 0.5f;\r
-            }\r
-        CallFunc(GdipSetPenDashOffset)(pPen, phase);\r
-        FX_Free(pDashArray);\r
-        pDashArray = NULL;\r
-    }\r
-    CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);\r
-    return pPen;\r
-}\r
-static BOOL IsSmallTriangle(PointF* points, const CFX_AffineMatrix* pMatrix, int& v1, int& v2)\r
-{\r
-    int pairs[] = {1, 2, 0, 2, 0, 1};\r
-    for (int i = 0; i < 3; i ++) {\r
-        int pair1 = pairs[i * 2];\r
-        int pair2 = pairs[i * 2 + 1];\r
-        FX_FLOAT x1 = points[pair1].X, x2 = points[pair2].X;\r
-        FX_FLOAT y1 = points[pair1].Y, y2 = points[pair2].Y;\r
-        if (pMatrix) {\r
-            pMatrix->Transform(x1, y1);\r
-            pMatrix->Transform(x2, y2);\r
-        }\r
-        FX_FLOAT dx = x1 - x2;\r
-        FX_FLOAT dy = y1 - y2;\r
-        FX_FLOAT distance_square = FXSYS_Mul(dx, dx) + FXSYS_Mul(dy, dy);\r
-        if (distance_square < (1.0f * 2 + 1.0f / 4)) {\r
-            v1 = i;\r
-            v2 = pair1;\r
-            return TRUE;\r
-        }\r
-    }\r
-    return FALSE;\r
-}\r
-BOOL CGdiplusExt::DrawPath(HDC hDC, const CFX_PathData* pPathData,\r
-                           const CFX_AffineMatrix* pObject2Device,\r
-                           const CFX_GraphStateData* pGraphState,\r
-                           FX_DWORD fill_argb,\r
-                           FX_DWORD stroke_argb,\r
-                           int fill_mode\r
-                          )\r
-{\r
-    int nPoints = pPathData->GetPointCount();\r
-    if (nPoints == 0) {\r
-        return TRUE;\r
-    }\r
-    FX_PATHPOINT* pPoints = pPathData->GetPoints();\r
-    GpGraphics* pGraphics = NULL;\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);\r
-    CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);\r
-    CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);\r
-    GpMatrix* pMatrix = NULL;\r
-    if (pObject2Device) {\r
-        CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b, pObject2Device->c, pObject2Device->d, pObject2Device->e, pObject2Device->f, &pMatrix);\r
-        CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);\r
-    }\r
-    PointF *points = FX_Alloc(PointF, nPoints);\r
-    if (!points) {\r
-        return FALSE;\r
-    }\r
-    BYTE * types  = FX_Alloc(BYTE, nPoints);\r
-    if (!types) {\r
-        FX_Free(points);\r
-        return FALSE;\r
-    }\r
-    int nSubPathes = 0;\r
-    FX_BOOL bSubClose = FALSE;\r
-    int pos_subclose = 0;\r
-    FX_BOOL bSmooth = FALSE;\r
-    int startpoint = 0;\r
-    for(int i = 0; i < nPoints; i++) {\r
-        points[i].X = pPoints[i].m_PointX;\r
-        points[i].Y = pPoints[i].m_PointY;\r
-        FX_FLOAT x, y;\r
-        if (pObject2Device) {\r
-            pObject2Device->Transform(pPoints[i].m_PointX, pPoints[i].m_PointY, x, y);\r
-        } else {\r
-            x = pPoints[i].m_PointX;\r
-            y = pPoints[i].m_PointY;\r
-        }\r
-        if (x > 50000 * 1.0f) {\r
-            points[i].X = 50000 * 1.0f;\r
-        }\r
-        if (x < -50000 * 1.0f) {\r
-            points[i].X = -50000 * 1.0f;\r
-        }\r
-        if (y > 50000 * 1.0f) {\r
-            points[i].Y = 50000 * 1.0f;\r
-        }\r
-        if (y < -50000 * 1.0f) {\r
-            points[i].Y = -50000 * 1.0f;\r
-        }\r
-        int point_type = pPoints[i].m_Flag & FXPT_TYPE;\r
-        if(point_type == FXPT_MOVETO) {\r
-            types[i] = PathPointTypeStart;\r
-            nSubPathes ++;\r
-            bSubClose = FALSE;\r
-            startpoint = i;\r
-        } else if (point_type == FXPT_LINETO) {\r
-            types[i] = PathPointTypeLine;\r
-            if (pPoints[i - 1].m_Flag == FXPT_MOVETO && (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&\r
-                    points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {\r
-                points[i].X += 0.01f;\r
-                continue;\r
-            }\r
-            if (!bSmooth && points[i].X != points[i - 1].X && points[i].Y != points[i - 1].Y) {\r
-                bSmooth = TRUE;\r
-            }\r
-        } else if (point_type == FXPT_BEZIERTO)        {\r
-            types[i] = PathPointTypeBezier;\r
-            bSmooth = TRUE;\r
-        }\r
-        if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {\r
-            if (bSubClose) {\r
-                types[pos_subclose] &= ~PathPointTypeCloseSubpath;\r
-            } else {\r
-                bSubClose = TRUE;\r
-            }\r
-            pos_subclose = i;\r
-            types[i] |= PathPointTypeCloseSubpath;\r
-            if (!bSmooth && points[i].X != points[startpoint].X && points[i].Y != points[startpoint].Y) {\r
-                bSmooth = TRUE;\r
-            }\r
-        }\r
-    }\r
-    if (fill_mode & FXFILL_NOPATHSMOOTH) {\r
-        bSmooth = FALSE;\r
-        CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);\r
-    } else if (!(fill_mode & FXFILL_FULLCOVER)) {\r
-        if (!bSmooth && (fill_mode & 3)) {\r
-            bSmooth = TRUE;\r
-        }\r
-        if (bSmooth || pGraphState && pGraphState->m_LineWidth > 2) {\r
-            CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);\r
-        }\r
-    }\r
-    int new_fill_mode = fill_mode & 3;\r
-    if (nPoints == 4 && pGraphState == NULL) {\r
-        int v1, v2;\r
-        if (IsSmallTriangle(points, pObject2Device, v1, v2)) {\r
-            GpPen* pPen = NULL;\r
-            CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);\r
-            CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),\r
-                                    FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));\r
-            CallFunc(GdipDeletePen)(pPen);\r
-            return TRUE;\r
-        }\r
-    }\r
-    GpPath* pGpPath = NULL;\r
-    CallFunc(GdipCreatePath2)(points, types, nPoints, GdiFillType2Gdip(new_fill_mode), &pGpPath);\r
-    if (!pGpPath) {\r
-        if (pMatrix) {\r
-            CallFunc(GdipDeleteMatrix)(pMatrix);\r
-        }\r
-        FX_Free(points);\r
-        FX_Free(types);\r
-        CallFunc(GdipDeleteGraphics)(pGraphics);\r
-        return FALSE;\r
-    }\r
-    if (new_fill_mode) {\r
-        GpBrush* pBrush = _GdipCreateBrush(fill_argb);\r
-        CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));\r
-        CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);\r
-        CallFunc(GdipDeleteBrush)(pBrush);\r
-    }\r
-    if (pGraphState && stroke_argb) {\r
-        GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb, fill_mode & FX_STROKE_TEXT_MODE);\r
-        if (nSubPathes == 1) {\r
-            CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);\r
-        } else {\r
-            int iStart = 0;\r
-            for (int i = 0; i < nPoints; i ++) {\r
-                if (i == nPoints - 1 || types[i + 1] == PathPointTypeStart) {\r
-                    GpPath* pSubPath;\r
-                    CallFunc(GdipCreatePath2)(points + iStart, types + iStart, i - iStart + 1, GdiFillType2Gdip(new_fill_mode), &pSubPath);\r
-                    iStart = i + 1;\r
-                    CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);\r
-                    CallFunc(GdipDeletePath)(pSubPath);\r
-                }\r
-            }\r
-        }\r
-        CallFunc(GdipDeletePen)(pPen);\r
-    }\r
-    if (pMatrix) {\r
-        CallFunc(GdipDeleteMatrix)(pMatrix);\r
-    }\r
-    FX_Free(points);\r
-    FX_Free(types);\r
-    CallFunc(GdipDeletePath)(pGpPath);\r
-    CallFunc(GdipDeleteGraphics)(pGraphics);\r
-    return TRUE;\r
-}\r
-class GpStream : public IStream, public CFX_Object\r
-{\r
-    LONG       m_RefCount;\r
-    int     m_ReadPos;\r
-    CFX_ByteTextBuf    m_InterStream;\r
-public:\r
-    GpStream()\r
-    {\r
-        m_RefCount = 1;\r
-        m_ReadPos = 0;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE\r
-    QueryInterface(REFIID iid, void ** ppvObject)\r
-    {\r
-        if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||\r
-                iid == __uuidof(ISequentialStream))    {\r
-            *ppvObject = static_cast<IStream*>(this);\r
-            AddRef();\r
-            return S_OK;\r
-        } else {\r
-            return E_NOINTERFACE;\r
-        }\r
-    }\r
-    virtual ULONG STDMETHODCALLTYPE AddRef(void)\r
-    {\r
-        return (ULONG)InterlockedIncrement(&m_RefCount);\r
-    }\r
-    virtual ULONG STDMETHODCALLTYPE Release(void)\r
-    {\r
-        ULONG res = (ULONG) InterlockedDecrement(&m_RefCount);\r
-        if (res == 0) {\r
-            delete this;\r
-        }\r
-        return res;\r
-    }\r
-public:\r
-    virtual HRESULT STDMETHODCALLTYPE Read(void* Output, ULONG cb, ULONG* pcbRead)\r
-    {\r
-        size_t bytes_left;\r
-        size_t bytes_out;\r
-        if (pcbRead != NULL) {\r
-            *pcbRead = 0;\r
-        }\r
-        if (m_ReadPos == m_InterStream.GetLength()) {\r
-            return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);\r
-        }\r
-        bytes_left = m_InterStream.GetLength() - m_ReadPos;\r
-        bytes_out = FX_MIN(cb, bytes_left);\r
-        FXSYS_memcpy32(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);\r
-        m_ReadPos += (FX_INT32)bytes_out;\r
-        if (pcbRead != NULL) {\r
-            *pcbRead = (ULONG)bytes_out;\r
-        }\r
-        return S_OK;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE Write(void const* Input, ULONG cb, ULONG* pcbWritten)\r
-    {\r
-        if (cb <= 0) {\r
-            if (pcbWritten != NULL) {\r
-                *pcbWritten = 0;\r
-            }\r
-            return S_OK;\r
-        }\r
-        m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);\r
-        if (pcbWritten != NULL) {\r
-            *pcbWritten = cb;\r
-        }\r
-        return S_OK;\r
-    }\r
-public:\r
-    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE Revert(void)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE Clone(IStream **)\r
-    {\r
-        return E_NOTIMPL;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, DWORD dwOrigin, ULARGE_INTEGER* lpNewFilePointer)\r
-    {\r
-        long   start = 0;\r
-        long   new_read_position;\r
-        switch(dwOrigin) {\r
-            case STREAM_SEEK_SET:\r
-                start = 0;\r
-                break;\r
-            case STREAM_SEEK_CUR:\r
-                start = m_ReadPos;\r
-                break;\r
-            case STREAM_SEEK_END:\r
-                start = m_InterStream.GetLength();\r
-                break;\r
-            default:\r
-                return STG_E_INVALIDFUNCTION;\r
-                break;\r
-        }\r
-        new_read_position = start + (long)liDistanceToMove.QuadPart;\r
-        if (new_read_position < 0 || new_read_position > m_InterStream.GetLength()) {\r
-            return STG_E_SEEKERROR;\r
-        }\r
-        m_ReadPos = new_read_position;\r
-        if (lpNewFilePointer != NULL) {\r
-            lpNewFilePointer->QuadPart = m_ReadPos;\r
-        }\r
-        return S_OK;\r
-    }\r
-    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag)\r
-    {\r
-        if (pStatstg == NULL) {\r
-            return STG_E_INVALIDFUNCTION;\r
-        }\r
-        ZeroMemory(pStatstg, sizeof(STATSTG));\r
-        pStatstg->cbSize.QuadPart = m_InterStream.GetLength();\r
-        return S_OK;\r
-    }\r
-};\r
-typedef struct {\r
-    BITMAPINFO*                pbmi;\r
-    int                                Stride;\r
-    LPBYTE                     pScan0;\r
-    GpBitmap*          pBitmap;\r
-    BitmapData*                pBitmapData;\r
-    GpStream*       pStream;\r
-} PREVIEW3_DIBITMAP;\r
-static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args)\r
-{\r
-    GpBitmap* pBitmap;\r
-    GpStream* pStream = NULL;\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    Status status = Ok;\r
-    if (args.flags == WINDIB_OPEN_PATHNAME) {\r
-        status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name, &pBitmap);\r
-    } else {\r
-        if (args.memory_size == 0 || !args.memory_base) {\r
-            return NULL;\r
-        }\r
-        pStream = FX_NEW GpStream;\r
-        if (!pStream) {\r
-            return NULL;\r
-        }\r
-        pStream->Write(args.memory_base, (ULONG)args.memory_size, NULL);\r
-        status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);\r
-    }\r
-    if (status != Ok) {\r
-        if (pStream) {\r
-            pStream->Release();\r
-        }\r
-        return NULL;\r
-    }\r
-    UINT height, width;\r
-    CallFunc(GdipGetImageHeight)(pBitmap, &height);\r
-    CallFunc(GdipGetImageWidth)(pBitmap, &width);\r
-    PixelFormat pixel_format;\r
-    CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);\r
-    int info_size = sizeof(BITMAPINFOHEADER);\r
-    int bpp = 24;\r
-    int dest_pixel_format = PixelFormat24bppRGB;\r
-    if (pixel_format == PixelFormat1bppIndexed) {\r
-        info_size += 8;\r
-        bpp = 1;\r
-        dest_pixel_format = PixelFormat1bppIndexed;\r
-    } else if (pixel_format == PixelFormat8bppIndexed) {\r
-        info_size += 1024;\r
-        bpp = 8;\r
-        dest_pixel_format = PixelFormat8bppIndexed;\r
-    } else if (pixel_format == PixelFormat32bppARGB) {\r
-        bpp = 32;\r
-        dest_pixel_format = PixelFormat32bppARGB;\r
-    }\r
-    LPBYTE buf = FX_Alloc(BYTE, info_size);\r
-    if (!buf) {\r
-        if (pStream) {\r
-            pStream->Release();\r
-        }\r
-        return NULL;\r
-    }\r
-    BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;\r
-    FXSYS_memset32(buf, 0, info_size);\r
-    pbmih->biBitCount = bpp;\r
-    pbmih->biCompression = BI_RGB;\r
-    pbmih->biHeight = -(int)height;\r
-    pbmih->biPlanes = 1;\r
-    pbmih->biWidth = width;\r
-    Rect rect(0, 0, width, height);\r
-    BitmapData* pBitmapData = FX_Alloc(BitmapData, 1);\r
-    if (!pBitmapData) {\r
-        if (pStream) {\r
-            pStream->Release();\r
-        }\r
-        return NULL;\r
-    }\r
-    CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,\r
-                                 dest_pixel_format, pBitmapData);\r
-    if (pixel_format == PixelFormat1bppIndexed || pixel_format == PixelFormat8bppIndexed) {\r
-        DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));\r
-        struct {\r
-            UINT flags;\r
-            UINT Count;\r
-            DWORD Entries[256];\r
-        } pal;\r
-        int size = 0;\r
-        CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);\r
-        CallFunc(GdipGetImagePalette)(pBitmap, (ColorPalette*)&pal, size);\r
-        int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;\r
-        for (int i = 0; i < entries; i ++) {\r
-            ppal[i] = pal.Entries[i] & 0x00ffffff;\r
-        }\r
-    }\r
-    PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);\r
-    if (!pInfo) {\r
-        if (pStream) {\r
-            pStream->Release();\r
-        }\r
-        return NULL;\r
-    }\r
-    pInfo->pbmi = (BITMAPINFO*)buf;\r
-    pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;\r
-    pInfo->Stride = pBitmapData->Stride;\r
-    pInfo->pBitmap = pBitmap;\r
-    pInfo->pBitmapData = pBitmapData;\r
-    pInfo->pStream = pStream;\r
-    return pInfo;\r
-}\r
-static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo)\r
-{\r
-    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;\r
-    CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);\r
-    CallFunc(GdipDisposeImage)(pInfo->pBitmap);\r
-    FX_Free(pInfo->pBitmapData);\r
-    FX_Free((LPBYTE)pInfo->pbmi);\r
-    if (pInfo->pStream) {\r
-        pInfo->pStream->Release();\r
-    }\r
-    FX_Free(pInfo);\r
-}\r
-CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, LPVOID pData, FX_BOOL bAlpha);\r
-CFX_DIBitmap* CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args)\r
-{\r
-    PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);\r
-    if (pInfo == NULL) {\r
-        return NULL;\r
-    }\r
-    int height = abs(pInfo->pbmi->bmiHeader.biHeight);\r
-    int width = pInfo->pbmi->bmiHeader.biWidth;\r
-    int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;\r
-    LPBYTE pData = FX_Alloc(BYTE, dest_pitch * height);\r
-    if (pData == NULL) {\r
-        FreeDIBitmap(pInfo);\r
-        return NULL;\r
-    }\r
-    if (dest_pitch == pInfo->Stride) {\r
-        FXSYS_memcpy32(pData, pInfo->pScan0, dest_pitch * height);\r
-    } else for (int i = 0; i < height; i ++) {\r
-            FXSYS_memcpy32(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i, dest_pitch);\r
-        }\r
-    CFX_DIBitmap* pDIBitmap = _FX_WindowsDIB_LoadFromBuf(pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);\r
-    FX_Free(pData);\r
-    FreeDIBitmap(pInfo);\r
-    return pDIBitmap;\r
-}\r
-#endif\r
+// Copyright 2014 PDFium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
+
+#include "../../../include/fxge/fx_ge.h"
+#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN64_
+#include <windows.h>
+#include <algorithm>
+namespace Gdiplus {
+  using std::min;
+  using std::max;
+}  // namespace Gdiplus
+#include <gdiplus.h>
+#include "../../../include/fxge/fx_ge_win32.h"
+#include "win32_int.h"
+using namespace Gdiplus;
+using namespace Gdiplus::DllExports;
+#define GdiFillType2Gdip(fill_type) (fill_type == ALTERNATE ? FillModeAlternate : FillModeWinding)
+static CombineMode GdiCombineMode2Gdip(int mode)
+{
+    switch (mode) {
+        case RGN_AND:
+            return CombineModeIntersect;
+    }
+    return CombineModeIntersect;
+}
+enum {
+    FuncId_GdipCreatePath2,
+    FuncId_GdipSetPenDashStyle,
+    FuncId_GdipSetPenDashArray,
+    FuncId_GdipSetPenDashCap197819,
+    FuncId_GdipSetPenLineJoin,
+    FuncId_GdipSetPenWidth,
+    FuncId_GdipCreateFromHDC,
+    FuncId_GdipSetPageUnit,
+    FuncId_GdipSetSmoothingMode,
+    FuncId_GdipCreateSolidFill,
+    FuncId_GdipFillPath,
+    FuncId_GdipDeleteBrush,
+    FuncId_GdipCreatePen1,
+    FuncId_GdipSetPenMiterLimit,
+    FuncId_GdipDrawPath,
+    FuncId_GdipDeletePen,
+    FuncId_GdipDeletePath,
+    FuncId_GdipDeleteGraphics,
+    FuncId_GdipCreateBitmapFromFileICM,
+    FuncId_GdipCreateBitmapFromStreamICM,
+    FuncId_GdipGetImageHeight,
+    FuncId_GdipGetImageWidth,
+    FuncId_GdipGetImagePixelFormat,
+    FuncId_GdipBitmapLockBits,
+    FuncId_GdipGetImagePaletteSize,
+    FuncId_GdipGetImagePalette,
+    FuncId_GdipBitmapUnlockBits,
+    FuncId_GdipDisposeImage,
+    FuncId_GdipFillRectangle,
+    FuncId_GdipCreateBitmapFromScan0,
+    FuncId_GdipSetImagePalette,
+    FuncId_GdipSetInterpolationMode,
+    FuncId_GdipDrawImagePointsI,
+    FuncId_GdipCreateBitmapFromGdiDib,
+    FuncId_GdiplusStartup,
+    FuncId_GdipDrawLineI,
+    FuncId_GdipResetClip,
+    FuncId_GdipCreatePath,
+    FuncId_GdipAddPathPath,
+    FuncId_GdipSetPathFillMode,
+    FuncId_GdipSetClipPath,
+    FuncId_GdipGetClip,
+    FuncId_GdipCreateRegion,
+    FuncId_GdipGetClipBoundsI,
+    FuncId_GdipSetClipRegion,
+    FuncId_GdipWidenPath,
+    FuncId_GdipAddPathLine,
+    FuncId_GdipAddPathRectangle,
+    FuncId_GdipDeleteRegion,
+    FuncId_GdipGetDC,
+    FuncId_GdipReleaseDC,
+    FuncId_GdipSetPenLineCap197819,
+    FuncId_GdipSetPenDashOffset,
+    FuncId_GdipResetPath,
+    FuncId_GdipCreateRegionPath,
+    FuncId_GdipCreateFont,
+    FuncId_GdipGetFontSize,
+    FuncId_GdipCreateFontFamilyFromName,
+    FuncId_GdipSetTextRenderingHint,
+    FuncId_GdipDrawDriverString,
+    FuncId_GdipCreateMatrix2,
+    FuncId_GdipDeleteMatrix,
+    FuncId_GdipSetWorldTransform,
+    FuncId_GdipResetWorldTransform,
+    FuncId_GdipDeleteFontFamily,
+    FuncId_GdipDeleteFont,
+    FuncId_GdipNewPrivateFontCollection,
+    FuncId_GdipDeletePrivateFontCollection,
+    FuncId_GdipPrivateAddMemoryFont,
+    FuncId_GdipGetFontCollectionFamilyList,
+    FuncId_GdipGetFontCollectionFamilyCount,
+    FuncId_GdipSetTextContrast,
+    FuncId_GdipSetPixelOffsetMode,
+    FuncId_GdipGetImageGraphicsContext,
+    FuncId_GdipDrawImageI,
+    FuncId_GdipDrawImageRectI,
+    FuncId_GdipDrawString,
+    FuncId_GdipSetPenTransform,
+};
+static LPCSTR g_GdipFuncNames[] = {
+    "GdipCreatePath2",
+    "GdipSetPenDashStyle",
+    "GdipSetPenDashArray",
+    "GdipSetPenDashCap197819",
+    "GdipSetPenLineJoin",
+    "GdipSetPenWidth",
+    "GdipCreateFromHDC",
+    "GdipSetPageUnit",
+    "GdipSetSmoothingMode",
+    "GdipCreateSolidFill",
+    "GdipFillPath",
+    "GdipDeleteBrush",
+    "GdipCreatePen1",
+    "GdipSetPenMiterLimit",
+    "GdipDrawPath",
+    "GdipDeletePen",
+    "GdipDeletePath",
+    "GdipDeleteGraphics",
+    "GdipCreateBitmapFromFileICM",
+    "GdipCreateBitmapFromStreamICM",
+    "GdipGetImageHeight",
+    "GdipGetImageWidth",
+    "GdipGetImagePixelFormat",
+    "GdipBitmapLockBits",
+    "GdipGetImagePaletteSize",
+    "GdipGetImagePalette",
+    "GdipBitmapUnlockBits",
+    "GdipDisposeImage",
+    "GdipFillRectangle",
+    "GdipCreateBitmapFromScan0",
+    "GdipSetImagePalette",
+    "GdipSetInterpolationMode",
+    "GdipDrawImagePointsI",
+    "GdipCreateBitmapFromGdiDib",
+    "GdiplusStartup",
+    "GdipDrawLineI",
+    "GdipResetClip",
+    "GdipCreatePath",
+    "GdipAddPathPath",
+    "GdipSetPathFillMode",
+    "GdipSetClipPath",
+    "GdipGetClip",
+    "GdipCreateRegion",
+    "GdipGetClipBoundsI",
+    "GdipSetClipRegion",
+    "GdipWidenPath",
+    "GdipAddPathLine",
+    "GdipAddPathRectangle",
+    "GdipDeleteRegion",
+    "GdipGetDC",
+    "GdipReleaseDC",
+    "GdipSetPenLineCap197819",
+    "GdipSetPenDashOffset",
+    "GdipResetPath",
+    "GdipCreateRegionPath",
+    "GdipCreateFont",
+    "GdipGetFontSize",
+    "GdipCreateFontFamilyFromName",
+    "GdipSetTextRenderingHint",
+    "GdipDrawDriverString",
+    "GdipCreateMatrix2",
+    "GdipDeleteMatrix",
+    "GdipSetWorldTransform",
+    "GdipResetWorldTransform",
+    "GdipDeleteFontFamily",
+    "GdipDeleteFont",
+    "GdipNewPrivateFontCollection",
+    "GdipDeletePrivateFontCollection",
+    "GdipPrivateAddMemoryFont",
+    "GdipGetFontCollectionFamilyList",
+    "GdipGetFontCollectionFamilyCount",
+    "GdipSetTextContrast",
+    "GdipSetPixelOffsetMode",
+    "GdipGetImageGraphicsContext",
+    "GdipDrawImageI",
+    "GdipDrawImageRectI",
+    "GdipDrawString",
+    "GdipSetPenTransform",
+};
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreatePath2)(GDIPCONST GpPointF*, GDIPCONST BYTE*, INT, GpFillMode, GpPath **path);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashStyle)(GpPen *pen, GpDashStyle dashstyle);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashArray)(GpPen *pen, GDIPCONST REAL *dash, INT count);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashCap197819)(GpPen *pen, GpDashCap dashCap);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenLineJoin)(GpPen *pen, GpLineJoin lineJoin);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenWidth)(GpPen *pen, REAL width);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateFromHDC)(HDC hdc, GpGraphics **graphics);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPageUnit)(GpGraphics *graphics, GpUnit unit);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetSmoothingMode)(GpGraphics *graphics, SmoothingMode smoothingMode);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateSolidFill)(ARGB color, GpSolidFill **brush);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipFillPath)(GpGraphics *graphics, GpBrush *brush, GpPath *path);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteBrush)(GpBrush *brush);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreatePen1)(ARGB color, REAL width, GpUnit unit, GpPen **pen);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenMiterLimit)(GpPen *pen, REAL miterLimit);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawPath)(GpGraphics *graphics, GpPen *pen, GpPath *path);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeletePen)(GpPen *pen);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeletePath)(GpPath* path);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteGraphics)(GpGraphics *graphics);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromFileICM)(GDIPCONST WCHAR* filename, GpBitmap **bitmap);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromStreamICM)(IStream* stream, GpBitmap **bitmap);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImageWidth)(GpImage *image, UINT *width);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImageHeight)(GpImage *image, UINT *height);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImagePixelFormat)(GpImage *image, PixelFormat *format);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipBitmapLockBits)(GpBitmap* bitmap, GDIPCONST GpRect* rect, UINT flags, PixelFormat format, BitmapData* lockedBitmapData);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImagePalette)(GpImage *image, ColorPalette *palette, INT size);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImagePaletteSize)(GpImage *image, INT *size);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipBitmapUnlockBits)(GpBitmap* bitmap, BitmapData* lockedBitmapData);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDisposeImage)(GpImage *image);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipFillRectangle)(GpGraphics *graphics, GpBrush *brush, REAL x, REAL y, REAL width, REAL height);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromScan0)(INT width, INT height, INT stride, PixelFormat format, BYTE* scan0, GpBitmap** bitmap);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetImagePalette)(GpImage *image, GDIPCONST ColorPalette *palette);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetInterpolationMode)(GpGraphics *graphics, InterpolationMode interpolationMode);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawImagePointsI)(GpGraphics *graphics, GpImage *image, GDIPCONST GpPoint *dstpoints, INT count);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateBitmapFromGdiDib)(GDIPCONST BITMAPINFO* gdiBitmapInfo, VOID* gdiBitmapData, GpBitmap** bitmap);
+typedef Status (WINAPI *FuncType_GdiplusStartup)(OUT FX_UINTPTR *token, const GdiplusStartupInput *input, OUT GdiplusStartupOutput *output);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawLineI)(GpGraphics *graphics, GpPen *pen, int x1, int y1, int x2, int y2);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipResetClip)(GpGraphics *graphics);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreatePath)(GpFillMode brushMode, GpPath **path);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipAddPathPath)(GpPath *path, GDIPCONST GpPath* addingPath, BOOL connect);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPathFillMode)(GpPath *path, GpFillMode fillmode);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetClipPath)(GpGraphics *graphics, GpPath *path, CombineMode combineMode);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetClip)(GpGraphics *graphics, GpRegion *region);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateRegion)(GpRegion **region);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetClipBoundsI)(GpGraphics *graphics, GpRect *rect);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetClipRegion)(GpGraphics *graphics, GpRegion *region, CombineMode combineMode);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipWidenPath)(GpPath *nativePath, GpPen *pen, GpMatrix *matrix, REAL flatness);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipAddPathLine)(GpPath *path, REAL x1, REAL y1, REAL x2, REAL y2);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipAddPathRectangle)(GpPath *path, REAL x, REAL y, REAL width, REAL height);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteRegion)(GpRegion *region);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetDC)(GpGraphics* graphics, HDC * hdc);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipReleaseDC)(GpGraphics* graphics, HDC hdc);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenLineCap197819)(GpPen *pen, GpLineCap startCap, GpLineCap endCap, GpDashCap dashCap);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenDashOffset)(GpPen *pen, REAL offset);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipResetPath)(GpPath *path);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateRegionPath)(GpPath *path, GpRegion **region);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateFont)(GDIPCONST GpFontFamily *fontFamily, REAL emSize, INT style, Unit unit, GpFont **font);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetFontSize)(GpFont *font, REAL *size);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateFontFamilyFromName)(GDIPCONST WCHAR *name, GpFontCollection *fontCollection, GpFontFamily **FontFamily);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetTextRenderingHint)(GpGraphics *graphics, TextRenderingHint mode);
+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);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipCreateMatrix2)(REAL m11, REAL m12, REAL m21, REAL m22, REAL dx, REAL dy, GpMatrix **matrix);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteMatrix)(GpMatrix *matrix);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetWorldTransform)(GpGraphics *graphics, GpMatrix *matrix);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipResetWorldTransform)(GpGraphics *graphics);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteFontFamily)(GpFontFamily *FontFamily);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeleteFont)(GpFont* font);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipNewPrivateFontCollection)(GpFontCollection** fontCollection);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDeletePrivateFontCollection)(GpFontCollection** fontCollection);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipPrivateAddMemoryFont)(GpFontCollection* fontCollection, GDIPCONST void* memory, INT length);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetFontCollectionFamilyList)(GpFontCollection* fontCollection, INT numSought, GpFontFamily* gpfamilies[], INT* numFound);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetFontCollectionFamilyCount)(GpFontCollection* fontCollection, INT* numFound);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetTextContrast)(GpGraphics *graphics, UINT contrast);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPixelOffsetMode)(GpGraphics* graphics, PixelOffsetMode pixelOffsetMode);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipGetImageGraphicsContext)(GpImage *image, GpGraphics **graphics);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawImageI)(GpGraphics *graphics, GpImage *image, INT x, INT y);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawImageRectI)(GpGraphics *graphics, GpImage *image, INT x, INT y, INT width, INT height);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipDrawString)(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, GDIPCONST GpBrush *brush);
+typedef GpStatus (WINGDIPAPI *FuncType_GdipSetPenTransform)(GpPen *pen, GpMatrix *matrix);
+#define CallFunc(funcname) ((FuncType_##funcname)GdiplusExt.m_Functions[FuncId_##funcname])
+typedef HANDLE   (__stdcall *FuncType_GdiAddFontMemResourceEx)(PVOID pbFont, DWORD cbFont, PVOID pdv, DWORD *pcFonts);
+typedef BOOL     (__stdcall *FuncType_GdiRemoveFontMemResourceEx)(HANDLE handle);
+void* CGdiplusExt::GdiAddFontMemResourceEx(void *pFontdata, FX_DWORD size, void* pdv, FX_DWORD* num_face)
+{
+    if (m_pGdiAddFontMemResourceEx) {
+        return ((FuncType_GdiAddFontMemResourceEx)m_pGdiAddFontMemResourceEx)((PVOID)pFontdata, (DWORD)size, (PVOID)pdv, (DWORD*)num_face);
+    }
+    return NULL;
+}
+FX_BOOL CGdiplusExt::GdiRemoveFontMemResourceEx(void* handle)
+{
+    if (m_pGdiRemoveFontMemResourseEx) {
+        return ((FuncType_GdiRemoveFontMemResourceEx)m_pGdiRemoveFontMemResourseEx)((HANDLE)handle);
+    }
+    return FALSE;
+}
+static GpBrush* _GdipCreateBrush(DWORD argb)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpSolidFill* solidBrush = NULL;
+    CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);
+    return solidBrush;
+}
+static CFX_DIBitmap* _StretchMonoToGray(int dest_width, int dest_height,
+                                        const CFX_DIBitmap* pSource, FX_RECT* pClipRect)
+{
+    FX_BOOL bFlipX = dest_width < 0;
+    if (bFlipX) {
+        dest_width = -dest_width;
+    }
+    FX_BOOL bFlipY = dest_height < 0;
+    if (bFlipY) {
+        dest_height = -dest_height;
+    }
+    int result_width = pClipRect->Width();
+    int result_height = pClipRect->Height();
+    int result_pitch = (result_width + 3) / 4 * 4;
+    CFX_DIBitmap* pStretched = FX_NEW CFX_DIBitmap;
+    if (!pStretched) {
+        return NULL;
+    }
+    if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb)) {
+        delete pStretched;
+        return NULL;
+    }
+    LPBYTE dest_buf = pStretched->GetBuffer();
+    int src_width = pSource->GetWidth();
+    int src_height = pSource->GetHeight();
+    int src_count = src_width * src_height;
+    int dest_count = dest_width * dest_height;
+    int ratio = 255 * dest_count / src_count;
+    int y_unit = src_height / dest_height;
+    int x_unit = src_width / dest_width;
+    int area_unit = y_unit * x_unit;
+    LPBYTE src_buf = pSource->GetBuffer();
+    int src_pitch = pSource->GetPitch();
+    for (int dest_y = 0; dest_y < result_height; dest_y ++) {
+        LPBYTE dest_scan = dest_buf + dest_y * result_pitch;
+        int src_y_start = bFlipY ? (dest_height - 1 - dest_y - pClipRect->top) : (dest_y + pClipRect->top);
+        src_y_start = src_y_start * src_height / dest_height;
+        LPBYTE src_scan = src_buf + src_y_start * src_pitch;
+        for (int dest_x = 0; dest_x < result_width; dest_x ++) {
+            int sum = 0;
+            int src_x_start = bFlipX ? (dest_width - 1 - dest_x - pClipRect->left) : (dest_x + pClipRect->left);
+            src_x_start = src_x_start * src_width / dest_width;
+            int src_x_end = src_x_start + x_unit;
+            LPBYTE src_line = src_scan;
+            for (int src_y = 0; src_y < y_unit; src_y ++) {
+                for (int src_x = src_x_start; src_x < src_x_end; src_x ++) {
+                    if (!(src_line[src_x / 8] & (1 << (7 - src_x % 8)))) {
+                        sum += 255;
+                    }
+                }
+                src_line += src_pitch;
+            }
+            dest_scan[dest_x] = 255 - sum / area_unit;
+        }
+    }
+    return pStretched;
+}
+static void OutputImageMask(GpGraphics* pGraphics, BOOL bMonoDevice, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,
+                            int dest_width, int dest_height, FX_ARGB argb, const FX_RECT* pClipRect)
+{
+    ASSERT(pBitmap->GetBPP() == 1);
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    int src_width = pBitmap->GetWidth(), src_height = pBitmap->GetHeight();
+    int src_pitch = pBitmap->GetPitch();
+    FX_LPBYTE scan0 = pBitmap->GetBuffer();
+    if (src_width == 1 && src_height == 1) {
+        if ((scan0[0] & 0x80) == 0) {
+            return;
+        }
+        GpSolidFill* solidBrush;
+        CallFunc(GdipCreateSolidFill)((ARGB)argb, &solidBrush);
+        if (dest_width < 0) {
+            dest_width = -dest_width;
+            dest_left -= dest_width;
+        }
+        if (dest_height < 0) {
+            dest_height = -dest_height;
+            dest_top -= dest_height;
+        }
+        CallFunc(GdipFillRectangle)(pGraphics, solidBrush, (float)dest_left, (float)dest_top,
+                                    (float)dest_width, (float)dest_height);
+        CallFunc(GdipDeleteBrush)(solidBrush);
+        return;
+    }
+    if (!bMonoDevice && abs(dest_width) < src_width && abs(dest_height) < src_height) {
+        FX_RECT image_rect(dest_left, dest_top, dest_left + dest_width, dest_top + dest_height);
+        image_rect.Normalize();
+        FX_RECT image_clip = image_rect;
+        image_clip.Intersect(*pClipRect);
+        if (image_clip.IsEmpty()) {
+            return;
+        }
+        image_clip.Offset(-image_rect.left, -image_rect.top);
+        CFX_DIBitmap* pStretched = NULL;
+        if (src_width * src_height > 10000) {
+            pStretched = _StretchMonoToGray(dest_width, dest_height, pBitmap, &image_clip);
+        } else {
+            pStretched = pBitmap->StretchTo(dest_width, dest_height, FALSE, &image_clip);
+        }
+        GpBitmap* bitmap;
+        CallFunc(GdipCreateBitmapFromScan0)(image_clip.Width(), image_clip.Height(),
+                                            (image_clip.Width() + 3) / 4 * 4, PixelFormat8bppIndexed, pStretched->GetBuffer(), &bitmap);
+        int a, r, g, b;
+        ArgbDecode(argb, a, r, g, b);
+        UINT pal[258];
+        pal[0] = 0;
+        pal[1] = 256;
+        for (int i = 0; i < 256; i ++) {
+            pal[i + 2] = ArgbEncode(i * a / 255, r, g, b);
+        }
+        CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
+        CallFunc(GdipDrawImageI)(pGraphics, bitmap, image_rect.left + image_clip.left,
+                                 image_rect.top + image_clip.top);
+        CallFunc(GdipDisposeImage)(bitmap);
+        delete pStretched;
+        return;
+    }
+    GpBitmap* bitmap;
+    CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch, PixelFormat1bppIndexed, scan0, &bitmap);
+    UINT palette[4] = { PaletteFlagsHasAlpha, 2, 0, argb };
+    CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)palette);
+    Point destinationPoints[] = {
+        Point(dest_left, dest_top),
+        Point(dest_left + dest_width, dest_top),
+        Point(dest_left, dest_top + dest_height)
+    };
+    CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
+    CallFunc(GdipDisposeImage)(bitmap);
+}
+static void OutputImage(GpGraphics* pGraphics, const CFX_DIBitmap* pBitmap, const FX_RECT* pSrcRect,
+                        int dest_left, int dest_top, int dest_width, int dest_height)
+{
+    int src_width = pSrcRect->Width(), src_height = pSrcRect->Height();
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    if (pBitmap->GetBPP() == 1 && (pSrcRect->left % 8)) {
+        FX_RECT new_rect(0, 0, src_width, src_height);
+        CFX_DIBitmap* pCloned = pBitmap->Clone(pSrcRect);
+        if (!pCloned) {
+            return;
+        }
+        OutputImage(pGraphics, pCloned, &new_rect, dest_left, dest_top, dest_width, dest_height);
+        delete pCloned;
+        return;
+    }
+    int src_pitch = pBitmap->GetPitch();
+    FX_LPBYTE scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch + pBitmap->GetBPP() * pSrcRect->left / 8;
+    GpBitmap* bitmap = NULL;
+    switch (pBitmap->GetFormat()) {
+        case FXDIB_Argb:
+            CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                                PixelFormat32bppARGB, scan0, &bitmap);
+            break;
+        case FXDIB_Rgb32:
+            CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                                PixelFormat32bppRGB, scan0, &bitmap);
+            break;
+        case FXDIB_Rgb:
+            CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                                PixelFormat24bppRGB, scan0, &bitmap);
+            break;
+        case FXDIB_8bppRgb: {
+                CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                                    PixelFormat8bppIndexed, scan0, &bitmap);
+                UINT pal[258];
+                pal[0] = 0;
+                pal[1] = 256;
+                for (int i = 0; i < 256; i ++) {
+                    pal[i + 2] = pBitmap->GetPaletteEntry(i);
+                }
+                CallFunc(GdipSetImagePalette)(bitmap, (ColorPalette*)pal);
+                break;
+            }
+        case FXDIB_1bppRgb: {
+                CallFunc(GdipCreateBitmapFromScan0)(src_width, src_height, src_pitch,
+                                                    PixelFormat1bppIndexed, scan0, &bitmap);
+                break;
+            }
+    }
+    if (dest_height < 0) {
+        dest_height --;
+    }
+    if (dest_width < 0) {
+        dest_width --;
+    }
+    Point destinationPoints[] = {
+        Point(dest_left, dest_top),
+        Point(dest_left + dest_width, dest_top),
+        Point(dest_left, dest_top + dest_height)
+    };
+    CallFunc(GdipDrawImagePointsI)(pGraphics, bitmap, destinationPoints, 3);
+    CallFunc(GdipDisposeImage)(bitmap);
+}
+CGdiplusExt::CGdiplusExt()
+{
+    m_hModule = NULL;
+    m_GdiModule = NULL;
+    for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i ++) {
+        m_Functions[i] = NULL;
+    }
+    m_pGdiAddFontMemResourceEx = NULL;
+    m_pGdiRemoveFontMemResourseEx = NULL;
+}
+void CGdiplusExt::Load()
+{
+    CFX_ByteString strPlusPath = "";
+    FX_CHAR buf[MAX_PATH];
+    GetSystemDirectoryA(buf, MAX_PATH);
+    strPlusPath += buf;
+    strPlusPath += "\\";
+    strPlusPath += "GDIPLUS.DLL";
+    m_hModule = LoadLibraryA(strPlusPath);
+    if (m_hModule == NULL) {
+        return;
+    }
+    for (int i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i ++) {
+        m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
+        if (m_Functions[i] == NULL) {
+            m_hModule = NULL;
+            return;
+        }
+    }
+    FX_UINTPTR gdiplusToken;
+    GdiplusStartupInput gdiplusStartupInput;
+    ((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(&gdiplusToken, &gdiplusStartupInput, NULL);
+    m_GdiModule = LoadLibraryA("GDI32.DLL");
+    if (m_GdiModule == NULL) {
+        return;
+    }
+    m_pGdiAddFontMemResourceEx = GetProcAddress(m_GdiModule, "AddFontMemResourceEx");
+    m_pGdiRemoveFontMemResourseEx = GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx");
+}
+CGdiplusExt::~CGdiplusExt()
+{
+}
+LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, FX_DWORD size)
+{
+    GpFontCollection* pCollection = NULL;
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipNewPrivateFontCollection)(&pCollection);
+    GpStatus status = CallFunc(GdipPrivateAddMemoryFont)(pCollection, pData, size);
+    if (status == Ok) {
+        return pCollection;
+    }
+    CallFunc(GdipDeletePrivateFontCollection)(&pCollection);
+    return NULL;
+}
+void CGdiplusExt::DeleteMemFont(LPVOID pCollection)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDeletePrivateFontCollection)((GpFontCollection**)&pCollection);
+}
+FX_BOOL CGdiplusExt::GdipCreateBitmap(CFX_DIBitmap* pBitmap, void**bitmap)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    PixelFormat format;
+    switch (pBitmap->GetFormat()) {
+        case FXDIB_Rgb:
+            format = PixelFormat24bppRGB;
+            break;
+        case FXDIB_Rgb32:
+            format = PixelFormat32bppRGB;
+            break;
+        case FXDIB_Argb:
+            format = PixelFormat32bppARGB;
+            break;
+        default:
+            return FALSE;
+    }
+    GpStatus status = CallFunc(GdipCreateBitmapFromScan0)(pBitmap->GetWidth(), pBitmap->GetHeight(),
+                      pBitmap->GetPitch(), format, pBitmap->GetBuffer(), (GpBitmap**)bitmap);
+    if (status == Ok) {
+        return TRUE;
+    }
+    return FALSE;
+}
+FX_BOOL CGdiplusExt::GdipCreateFromImage(void* bitmap, void** graphics)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpStatus status = CallFunc(GdipGetImageGraphicsContext)((GpBitmap*)bitmap, (GpGraphics**)graphics);
+    if (status == Ok) {
+        return TRUE;
+    }
+    return FALSE;
+}
+FX_BOOL CGdiplusExt::GdipCreateFontFamilyFromName(FX_LPCWSTR name, void* pFontCollection, void**pFamily)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpStatus status = CallFunc(GdipCreateFontFamilyFromName)((GDIPCONST WCHAR *)name, (GpFontCollection*)pFontCollection, (GpFontFamily**)pFamily);
+    if (status == Ok) {
+        return TRUE;
+    }
+    return FALSE;
+}
+FX_BOOL CGdiplusExt::GdipCreateFontFromFamily(void* pFamily, FX_FLOAT font_size, int fontstyle, int flag, void** pFont)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpStatus status = CallFunc(GdipCreateFont)((GpFontFamily*)pFamily, font_size, fontstyle, Unit(flag), (GpFont**)pFont);
+    if (status == Ok) {
+        return TRUE;
+    }
+    return FALSE;
+}
+void CGdiplusExt::GdipGetFontSize(void *pFont, FX_FLOAT *size)
+{
+    REAL get_size;
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpStatus status = CallFunc(GdipGetFontSize)((GpFont *)pFont, (REAL*)&get_size);
+    if (status == Ok) {
+        *size = (FX_FLOAT)get_size;
+    } else {
+        *size = 0;
+    }
+}
+void CGdiplusExt::GdipSetTextRenderingHint(void* graphics, int mode)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipSetTextRenderingHint)((GpGraphics*)graphics, (TextRenderingHint)mode);
+}
+void CGdiplusExt::GdipSetPageUnit(void* graphics, FX_DWORD unit)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipSetPageUnit)((GpGraphics*)graphics, (GpUnit)unit);
+}
+FX_BOOL CGdiplusExt::GdipDrawDriverString(void *graphics,  unsigned short *text, int length,
+        void *font, void* brush, void *positions, int flags, const void *matrix)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpStatus status = CallFunc(GdipDrawDriverString)((GpGraphics*)graphics, (GDIPCONST UINT16 *)text, (INT)length, (GDIPCONST GpFont *)font, (GDIPCONST GpBrush*)brush,
+                      (GDIPCONST PointF *)positions, (INT)flags, (GDIPCONST GpMatrix *)matrix);
+    if (status == Ok) {
+        return TRUE;
+    }
+    return FALSE;
+}
+void CGdiplusExt::GdipCreateBrush(FX_DWORD fill_argb, void** pBrush)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipCreateSolidFill)((ARGB)fill_argb, (GpSolidFill**)pBrush);
+}
+void CGdiplusExt::GdipDeleteBrush(void* pBrush)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDeleteBrush)((GpSolidFill*)pBrush);
+}
+void* CGdiplusExt::GdipCreateFontFromCollection(void* pFontCollection, FX_FLOAT font_size, int fontstyle)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    int numFamilies = 0;
+    GpStatus status = CallFunc(GdipGetFontCollectionFamilyCount)((GpFontCollection*)pFontCollection, &numFamilies);
+    if (status != Ok) {
+        return NULL;
+    }
+    GpFontFamily* family_list[1];
+    status = CallFunc(GdipGetFontCollectionFamilyList)((GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
+    if (status != Ok) {
+        return NULL;
+    }
+    GpFont* pFont = NULL;
+    status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle, UnitPixel, &pFont);
+    if (status != Ok) {
+        return NULL;
+    }
+    return pFont;
+}
+void CGdiplusExt::GdipCreateMatrix(FX_FLOAT a, FX_FLOAT b, FX_FLOAT c, FX_FLOAT d, FX_FLOAT e, FX_FLOAT f, void** matrix)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipCreateMatrix2)(a, b, c, d, e, f, (GpMatrix**)matrix);
+}
+void CGdiplusExt::GdipDeleteMatrix(void* matrix)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDeleteMatrix)((GpMatrix*)matrix);
+}
+void CGdiplusExt::GdipDeleteFontFamily(void* pFamily)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDeleteFontFamily)((GpFontFamily*)pFamily);
+}
+void CGdiplusExt::GdipDeleteFont(void* pFont)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDeleteFont)((GpFont*)pFont);
+}
+void CGdiplusExt::GdipSetWorldTransform(void* graphics, void* pMatrix)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipSetWorldTransform)((GpGraphics*)graphics, (GpMatrix*)pMatrix);
+}
+void CGdiplusExt::GdipDisposeImage(void* bitmap)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDisposeImage)((GpBitmap*)bitmap);
+}
+void CGdiplusExt::GdipDeleteGraphics(void* graphics)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipDeleteGraphics)((GpGraphics*)graphics);
+}
+FX_BOOL CGdiplusExt::StretchBitMask(HDC hDC, BOOL bMonoDevice, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,
+                                    int dest_width, int dest_height, FX_DWORD argb, const FX_RECT* pClipRect, int flags)
+{
+    ASSERT(pBitmap->GetBPP() == 1);
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    GpGraphics* pGraphics = NULL;
+    CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+    CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
+    if (flags & FXDIB_NOSMOOTH) {
+        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeNearestNeighbor);
+    } else {
+        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
+    }
+    OutputImageMask(pGraphics, bMonoDevice, pBitmap, dest_left, dest_top, dest_width, dest_height, argb, pClipRect);
+    CallFunc(GdipDeleteGraphics)(pGraphics);
+    return TRUE;
+}
+FX_BOOL CGdiplusExt::StretchDIBits(HDC hDC, const CFX_DIBitmap* pBitmap, int dest_left, int dest_top,
+                                   int dest_width, int dest_height, const FX_RECT* pClipRect, int flags)
+{
+    GpGraphics* pGraphics;
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+    CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
+    if (flags & FXDIB_NOSMOOTH) {
+        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeNearestNeighbor);
+    } else if (pBitmap->GetWidth() > abs(dest_width) / 2 || pBitmap->GetHeight() > abs(dest_height) / 2) {
+        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeHighQuality);
+    } else {
+        CallFunc(GdipSetInterpolationMode)(pGraphics, InterpolationModeBilinear);
+    }
+    FX_RECT src_rect(0, 0, pBitmap->GetWidth(), pBitmap->GetHeight());
+    OutputImage(pGraphics, pBitmap, &src_rect, dest_left, dest_top, dest_width, dest_height);
+    CallFunc(GdipDeleteGraphics)(pGraphics);
+    CallFunc(GdipDeleteGraphics)(pGraphics);
+    return TRUE;
+}
+static GpPen* _GdipCreatePen(const CFX_GraphStateData* pGraphState, const CFX_AffineMatrix* pMatrix, DWORD argb, FX_BOOL bTextMode = FALSE)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    FX_FLOAT width = pGraphState ? pGraphState->m_LineWidth : 1.0f;
+    if (!bTextMode) {
+        FX_FLOAT unit = pMatrix == NULL ? 1.0f : FXSYS_Div(1.0f, (pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2);
+        if (width < unit) {
+            width = unit;
+        }
+    }
+    GpPen* pPen = NULL;
+    CallFunc(GdipCreatePen1)((ARGB)argb, width, UnitWorld, &pPen);
+    LineCap lineCap;
+    DashCap dashCap = DashCapFlat;
+    FX_BOOL bDashExtend = FALSE;
+    switch(pGraphState->m_LineCap) {
+        case CFX_GraphStateData::LineCapButt:
+            lineCap = LineCapFlat;
+            break;
+        case CFX_GraphStateData::LineCapRound:
+            lineCap = LineCapRound;
+            dashCap = DashCapRound;
+            bDashExtend = TRUE;
+            break;
+        case CFX_GraphStateData::LineCapSquare:
+            lineCap = LineCapSquare;
+            bDashExtend = TRUE;
+            break;
+    }
+    CallFunc(GdipSetPenLineCap197819)(pPen, lineCap, lineCap, dashCap);
+    LineJoin lineJoin;
+    switch(pGraphState->m_LineJoin) {
+        case CFX_GraphStateData::LineJoinMiter:
+            lineJoin = LineJoinMiterClipped;
+            break;
+        case CFX_GraphStateData::LineJoinRound:
+            lineJoin = LineJoinRound;
+            break;
+        case CFX_GraphStateData::LineJoinBevel:
+            lineJoin = LineJoinBevel;
+            break;
+    }
+    CallFunc(GdipSetPenLineJoin)(pPen, lineJoin);
+    if(pGraphState->m_DashCount) {
+        FX_FLOAT* pDashArray = FX_Alloc(FX_FLOAT, pGraphState->m_DashCount + pGraphState->m_DashCount % 2);
+        if (!pDashArray) {
+            return NULL;
+        }
+        int nCount = 0;
+        FX_FLOAT on_leftover = 0, off_leftover = 0;
+        for (int i = 0; i < pGraphState->m_DashCount; i += 2) {
+            FX_FLOAT on_phase = pGraphState->m_DashArray[i];
+            FX_FLOAT off_phase;
+            if (i == pGraphState->m_DashCount - 1) {
+                off_phase = on_phase;
+            } else {
+                off_phase = pGraphState->m_DashArray[i + 1];
+            }
+            on_phase /= width;
+            off_phase /= width;
+            if (on_phase + off_phase <= 0.00002f) {
+                on_phase = 1.0f / 10;
+                off_phase = 1.0f / 10;
+            }
+            if (bDashExtend) {
+                if (off_phase < 1) {
+                    off_phase = 0;
+                } else {
+                    off_phase -= 1;
+                }
+                on_phase += 1;
+            }
+            if (on_phase == 0 || off_phase == 0) {
+                if (nCount == 0) {
+                    on_leftover += on_phase;
+                    off_leftover += off_phase;
+                } else {
+                    pDashArray[nCount - 2] += on_phase;
+                    pDashArray[nCount - 1] += off_phase;
+                }
+            } else {
+                pDashArray[nCount++] = on_phase + on_leftover;
+                on_leftover = 0;
+                pDashArray[nCount++] = off_phase + off_leftover;
+                off_leftover = 0;
+            }
+        }
+        CallFunc(GdipSetPenDashArray)(pPen, pDashArray, nCount);
+        FX_FLOAT phase = pGraphState->m_DashPhase;
+        if (bDashExtend)
+            if (phase < 0.5f) {
+                phase = 0;
+            } else {
+                phase -= 0.5f;
+            }
+        CallFunc(GdipSetPenDashOffset)(pPen, phase);
+        FX_Free(pDashArray);
+        pDashArray = NULL;
+    }
+    CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
+    return pPen;
+}
+static BOOL IsSmallTriangle(PointF* points, const CFX_AffineMatrix* pMatrix, int& v1, int& v2)
+{
+    int pairs[] = {1, 2, 0, 2, 0, 1};
+    for (int i = 0; i < 3; i ++) {
+        int pair1 = pairs[i * 2];
+        int pair2 = pairs[i * 2 + 1];
+        FX_FLOAT x1 = points[pair1].X, x2 = points[pair2].X;
+        FX_FLOAT y1 = points[pair1].Y, y2 = points[pair2].Y;
+        if (pMatrix) {
+            pMatrix->Transform(x1, y1);
+            pMatrix->Transform(x2, y2);
+        }
+        FX_FLOAT dx = x1 - x2;
+        FX_FLOAT dy = y1 - y2;
+        FX_FLOAT distance_square = FXSYS_Mul(dx, dx) + FXSYS_Mul(dy, dy);
+        if (distance_square < (1.0f * 2 + 1.0f / 4)) {
+            v1 = i;
+            v2 = pair1;
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+BOOL CGdiplusExt::DrawPath(HDC hDC, const CFX_PathData* pPathData,
+                           const CFX_AffineMatrix* pObject2Device,
+                           const CFX_GraphStateData* pGraphState,
+                           FX_DWORD fill_argb,
+                           FX_DWORD stroke_argb,
+                           int fill_mode
+                          )
+{
+    int nPoints = pPathData->GetPointCount();
+    if (nPoints == 0) {
+        return TRUE;
+    }
+    FX_PATHPOINT* pPoints = pPathData->GetPoints();
+    GpGraphics* pGraphics = NULL;
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
+    CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
+    CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
+    GpMatrix* pMatrix = NULL;
+    if (pObject2Device) {
+        CallFunc(GdipCreateMatrix2)(pObject2Device->a, pObject2Device->b, pObject2Device->c, pObject2Device->d, pObject2Device->e, pObject2Device->f, &pMatrix);
+        CallFunc(GdipSetWorldTransform)(pGraphics, pMatrix);
+    }
+    PointF *points = FX_Alloc(PointF, nPoints);
+    if (!points) {
+        return FALSE;
+    }
+    BYTE * types  = FX_Alloc(BYTE, nPoints);
+    if (!types) {
+        FX_Free(points);
+        return FALSE;
+    }
+    int nSubPathes = 0;
+    FX_BOOL bSubClose = FALSE;
+    int pos_subclose = 0;
+    FX_BOOL bSmooth = FALSE;
+    int startpoint = 0;
+    for(int i = 0; i < nPoints; i++) {
+        points[i].X = pPoints[i].m_PointX;
+        points[i].Y = pPoints[i].m_PointY;
+        FX_FLOAT x, y;
+        if (pObject2Device) {
+            pObject2Device->Transform(pPoints[i].m_PointX, pPoints[i].m_PointY, x, y);
+        } else {
+            x = pPoints[i].m_PointX;
+            y = pPoints[i].m_PointY;
+        }
+        if (x > 50000 * 1.0f) {
+            points[i].X = 50000 * 1.0f;
+        }
+        if (x < -50000 * 1.0f) {
+            points[i].X = -50000 * 1.0f;
+        }
+        if (y > 50000 * 1.0f) {
+            points[i].Y = 50000 * 1.0f;
+        }
+        if (y < -50000 * 1.0f) {
+            points[i].Y = -50000 * 1.0f;
+        }
+        int point_type = pPoints[i].m_Flag & FXPT_TYPE;
+        if(point_type == FXPT_MOVETO) {
+            types[i] = PathPointTypeStart;
+            nSubPathes ++;
+            bSubClose = FALSE;
+            startpoint = i;
+        } else if (point_type == FXPT_LINETO) {
+            types[i] = PathPointTypeLine;
+            if (pPoints[i - 1].m_Flag == FXPT_MOVETO && (i == nPoints - 1 || pPoints[i + 1].m_Flag == FXPT_MOVETO) &&
+                    points[i].Y == points[i - 1].Y && points[i].X == points[i - 1].X) {
+                points[i].X += 0.01f;
+                continue;
+            }
+            if (!bSmooth && points[i].X != points[i - 1].X && points[i].Y != points[i - 1].Y) {
+                bSmooth = TRUE;
+            }
+        } else if (point_type == FXPT_BEZIERTO)        {
+            types[i] = PathPointTypeBezier;
+            bSmooth = TRUE;
+        }
+        if (pPoints[i].m_Flag & FXPT_CLOSEFIGURE) {
+            if (bSubClose) {
+                types[pos_subclose] &= ~PathPointTypeCloseSubpath;
+            } else {
+                bSubClose = TRUE;
+            }
+            pos_subclose = i;
+            types[i] |= PathPointTypeCloseSubpath;
+            if (!bSmooth && points[i].X != points[startpoint].X && points[i].Y != points[startpoint].Y) {
+                bSmooth = TRUE;
+            }
+        }
+    }
+    if (fill_mode & FXFILL_NOPATHSMOOTH) {
+        bSmooth = FALSE;
+        CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeNone);
+    } else if (!(fill_mode & FXFILL_FULLCOVER)) {
+        if (!bSmooth && (fill_mode & 3)) {
+            bSmooth = TRUE;
+        }
+        if (bSmooth || pGraphState && pGraphState->m_LineWidth > 2) {
+            CallFunc(GdipSetSmoothingMode)(pGraphics, SmoothingModeAntiAlias);
+        }
+    }
+    int new_fill_mode = fill_mode & 3;
+    if (nPoints == 4 && pGraphState == NULL) {
+        int v1, v2;
+        if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
+            GpPen* pPen = NULL;
+            CallFunc(GdipCreatePen1)(fill_argb, 1.0f, UnitPixel, &pPen);
+            CallFunc(GdipDrawLineI)(pGraphics, pPen, FXSYS_round(points[v1].X), FXSYS_round(points[v1].Y),
+                                    FXSYS_round(points[v2].X), FXSYS_round(points[v2].Y));
+            CallFunc(GdipDeletePen)(pPen);
+            return TRUE;
+        }
+    }
+    GpPath* pGpPath = NULL;
+    CallFunc(GdipCreatePath2)(points, types, nPoints, GdiFillType2Gdip(new_fill_mode), &pGpPath);
+    if (!pGpPath) {
+        if (pMatrix) {
+            CallFunc(GdipDeleteMatrix)(pMatrix);
+        }
+        FX_Free(points);
+        FX_Free(types);
+        CallFunc(GdipDeleteGraphics)(pGraphics);
+        return FALSE;
+    }
+    if (new_fill_mode) {
+        GpBrush* pBrush = _GdipCreateBrush(fill_argb);
+        CallFunc(GdipSetPathFillMode)(pGpPath, GdiFillType2Gdip(new_fill_mode));
+        CallFunc(GdipFillPath)(pGraphics, pBrush, pGpPath);
+        CallFunc(GdipDeleteBrush)(pBrush);
+    }
+    if (pGraphState && stroke_argb) {
+        GpPen* pPen = _GdipCreatePen(pGraphState, pObject2Device, stroke_argb, fill_mode & FX_STROKE_TEXT_MODE);
+        if (nSubPathes == 1) {
+            CallFunc(GdipDrawPath)(pGraphics, pPen, pGpPath);
+        } else {
+            int iStart = 0;
+            for (int i = 0; i < nPoints; i ++) {
+                if (i == nPoints - 1 || types[i + 1] == PathPointTypeStart) {
+                    GpPath* pSubPath;
+                    CallFunc(GdipCreatePath2)(points + iStart, types + iStart, i - iStart + 1, GdiFillType2Gdip(new_fill_mode), &pSubPath);
+                    iStart = i + 1;
+                    CallFunc(GdipDrawPath)(pGraphics, pPen, pSubPath);
+                    CallFunc(GdipDeletePath)(pSubPath);
+                }
+            }
+        }
+        CallFunc(GdipDeletePen)(pPen);
+    }
+    if (pMatrix) {
+        CallFunc(GdipDeleteMatrix)(pMatrix);
+    }
+    FX_Free(points);
+    FX_Free(types);
+    CallFunc(GdipDeletePath)(pGpPath);
+    CallFunc(GdipDeleteGraphics)(pGraphics);
+    return TRUE;
+}
+class GpStream FX_FINAL : public IStream, public CFX_Object
+{
+    LONG       m_RefCount;
+    int     m_ReadPos;
+    CFX_ByteTextBuf    m_InterStream;
+public:
+    GpStream()
+    {
+        m_RefCount = 1;
+        m_ReadPos = 0;
+    }
+    virtual HRESULT STDMETHODCALLTYPE
+    QueryInterface(REFIID iid, void ** ppvObject)
+    {
+        if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
+                iid == __uuidof(ISequentialStream))    {
+            *ppvObject = static_cast<IStream*>(this);
+            AddRef();
+            return S_OK;
+        } else {
+            return E_NOINTERFACE;
+        }
+    }
+    virtual ULONG STDMETHODCALLTYPE AddRef(void)
+    {
+        return (ULONG)InterlockedIncrement(&m_RefCount);
+    }
+    virtual ULONG STDMETHODCALLTYPE Release(void)
+    {
+        ULONG res = (ULONG) InterlockedDecrement(&m_RefCount);
+        if (res == 0) {
+            delete this;
+        }
+        return res;
+    }
+public:
+    virtual HRESULT STDMETHODCALLTYPE Read(void* Output, ULONG cb, ULONG* pcbRead)
+    {
+        size_t bytes_left;
+        size_t bytes_out;
+        if (pcbRead != NULL) {
+            *pcbRead = 0;
+        }
+        if (m_ReadPos == m_InterStream.GetLength()) {
+            return HRESULT_FROM_WIN32(ERROR_END_OF_MEDIA);
+        }
+        bytes_left = m_InterStream.GetLength() - m_ReadPos;
+        bytes_out = FX_MIN(cb, bytes_left);
+        FXSYS_memcpy32(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);
+        m_ReadPos += (FX_INT32)bytes_out;
+        if (pcbRead != NULL) {
+            *pcbRead = (ULONG)bytes_out;
+        }
+        return S_OK;
+    }
+    virtual HRESULT STDMETHODCALLTYPE Write(void const* Input, ULONG cb, ULONG* pcbWritten)
+    {
+        if (cb <= 0) {
+            if (pcbWritten != NULL) {
+                *pcbWritten = 0;
+            }
+            return S_OK;
+        }
+        m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);
+        if (pcbWritten != NULL) {
+            *pcbWritten = cb;
+        }
+        return S_OK;
+    }
+public:
+    virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*, ULARGE_INTEGER, ULARGE_INTEGER*, ULARGE_INTEGER*)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE Commit(DWORD)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE Revert(void)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER, ULARGE_INTEGER, DWORD)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE Clone(IStream **)
+    {
+        return E_NOTIMPL;
+    }
+    virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove, DWORD dwOrigin, ULARGE_INTEGER* lpNewFilePointer)
+    {
+        long   start = 0;
+        long   new_read_position;
+        switch(dwOrigin) {
+            case STREAM_SEEK_SET:
+                start = 0;
+                break;
+            case STREAM_SEEK_CUR:
+                start = m_ReadPos;
+                break;
+            case STREAM_SEEK_END:
+                start = m_InterStream.GetLength();
+                break;
+            default:
+                return STG_E_INVALIDFUNCTION;
+                break;
+        }
+        new_read_position = start + (long)liDistanceToMove.QuadPart;
+        if (new_read_position < 0 || new_read_position > m_InterStream.GetLength()) {
+            return STG_E_SEEKERROR;
+        }
+        m_ReadPos = new_read_position;
+        if (lpNewFilePointer != NULL) {
+            lpNewFilePointer->QuadPart = m_ReadPos;
+        }
+        return S_OK;
+    }
+    virtual HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg, DWORD grfStatFlag)
+    {
+        if (pStatstg == NULL) {
+            return STG_E_INVALIDFUNCTION;
+        }
+        ZeroMemory(pStatstg, sizeof(STATSTG));
+        pStatstg->cbSize.QuadPart = m_InterStream.GetLength();
+        return S_OK;
+    }
+};
+typedef struct {
+    BITMAPINFO*                pbmi;
+    int                                Stride;
+    LPBYTE                     pScan0;
+    GpBitmap*          pBitmap;
+    BitmapData*                pBitmapData;
+    GpStream*       pStream;
+} PREVIEW3_DIBITMAP;
+static PREVIEW3_DIBITMAP* LoadDIBitmap(WINDIB_Open_Args_ args)
+{
+    GpBitmap* pBitmap;
+    GpStream* pStream = NULL;
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    Status status = Ok;
+    if (args.flags == WINDIB_OPEN_PATHNAME) {
+        status = CallFunc(GdipCreateBitmapFromFileICM)((wchar_t*)args.path_name, &pBitmap);
+    } else {
+        if (args.memory_size == 0 || !args.memory_base) {
+            return NULL;
+        }
+        pStream = FX_NEW GpStream;
+        if (!pStream) {
+            return NULL;
+        }
+        pStream->Write(args.memory_base, (ULONG)args.memory_size, NULL);
+        status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
+    }
+    if (status != Ok) {
+        if (pStream) {
+            pStream->Release();
+        }
+        return NULL;
+    }
+    UINT height, width;
+    CallFunc(GdipGetImageHeight)(pBitmap, &height);
+    CallFunc(GdipGetImageWidth)(pBitmap, &width);
+    PixelFormat pixel_format;
+    CallFunc(GdipGetImagePixelFormat)(pBitmap, &pixel_format);
+    int info_size = sizeof(BITMAPINFOHEADER);
+    int bpp = 24;
+    int dest_pixel_format = PixelFormat24bppRGB;
+    if (pixel_format == PixelFormat1bppIndexed) {
+        info_size += 8;
+        bpp = 1;
+        dest_pixel_format = PixelFormat1bppIndexed;
+    } else if (pixel_format == PixelFormat8bppIndexed) {
+        info_size += 1024;
+        bpp = 8;
+        dest_pixel_format = PixelFormat8bppIndexed;
+    } else if (pixel_format == PixelFormat32bppARGB) {
+        bpp = 32;
+        dest_pixel_format = PixelFormat32bppARGB;
+    }
+    LPBYTE buf = FX_Alloc(BYTE, info_size);
+    if (!buf) {
+        if (pStream) {
+            pStream->Release();
+        }
+        return NULL;
+    }
+    BITMAPINFOHEADER* pbmih = (BITMAPINFOHEADER*)buf;
+    pbmih->biBitCount = bpp;
+    pbmih->biCompression = BI_RGB;
+    pbmih->biHeight = -(int)height;
+    pbmih->biPlanes = 1;
+    pbmih->biWidth = width;
+    Rect rect(0, 0, width, height);
+    BitmapData* pBitmapData = FX_Alloc(BitmapData, 1);
+    if (!pBitmapData) {
+        if (pStream) {
+            pStream->Release();
+        }
+        return NULL;
+    }
+    CallFunc(GdipBitmapLockBits)(pBitmap, &rect, ImageLockModeRead,
+                                 dest_pixel_format, pBitmapData);
+    if (pixel_format == PixelFormat1bppIndexed || pixel_format == PixelFormat8bppIndexed) {
+        DWORD* ppal = (DWORD*)(buf + sizeof(BITMAPINFOHEADER));
+        struct {
+            UINT flags;
+            UINT Count;
+            DWORD Entries[256];
+        } pal;
+        int size = 0;
+        CallFunc(GdipGetImagePaletteSize)(pBitmap, &size);
+        CallFunc(GdipGetImagePalette)(pBitmap, (ColorPalette*)&pal, size);
+        int entries = pixel_format == PixelFormat1bppIndexed ? 2 : 256;
+        for (int i = 0; i < entries; i ++) {
+            ppal[i] = pal.Entries[i] & 0x00ffffff;
+        }
+    }
+    PREVIEW3_DIBITMAP* pInfo = FX_Alloc(PREVIEW3_DIBITMAP, 1);
+    if (!pInfo) {
+        if (pStream) {
+            pStream->Release();
+        }
+        return NULL;
+    }
+    pInfo->pbmi = (BITMAPINFO*)buf;
+    pInfo->pScan0 = (LPBYTE)pBitmapData->Scan0;
+    pInfo->Stride = pBitmapData->Stride;
+    pInfo->pBitmap = pBitmap;
+    pInfo->pBitmapData = pBitmapData;
+    pInfo->pStream = pStream;
+    return pInfo;
+}
+static void FreeDIBitmap(PREVIEW3_DIBITMAP* pInfo)
+{
+    CGdiplusExt& GdiplusExt = ((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
+    CallFunc(GdipBitmapUnlockBits)(pInfo->pBitmap, pInfo->pBitmapData);
+    CallFunc(GdipDisposeImage)(pInfo->pBitmap);
+    FX_Free(pInfo->pBitmapData);
+    FX_Free((LPBYTE)pInfo->pbmi);
+    if (pInfo->pStream) {
+        pInfo->pStream->Release();
+    }
+    FX_Free(pInfo);
+}
+CFX_DIBitmap* _FX_WindowsDIB_LoadFromBuf(BITMAPINFO* pbmi, LPVOID pData, FX_BOOL bAlpha);
+CFX_DIBitmap* CGdiplusExt::LoadDIBitmap(WINDIB_Open_Args_ args)
+{
+    PREVIEW3_DIBITMAP* pInfo = ::LoadDIBitmap(args);
+    if (pInfo == NULL) {
+        return NULL;
+    }
+    int height = abs(pInfo->pbmi->bmiHeader.biHeight);
+    int width = pInfo->pbmi->bmiHeader.biWidth;
+    int dest_pitch = (width * pInfo->pbmi->bmiHeader.biBitCount + 31) / 32 * 4;
+    LPBYTE pData = FX_Alloc(BYTE, dest_pitch * height);
+    if (pData == NULL) {
+        FreeDIBitmap(pInfo);
+        return NULL;
+    }
+    if (dest_pitch == pInfo->Stride) {
+        FXSYS_memcpy32(pData, pInfo->pScan0, dest_pitch * height);
+    } else for (int i = 0; i < height; i ++) {
+            FXSYS_memcpy32(pData + dest_pitch * i, pInfo->pScan0 + pInfo->Stride * i, dest_pitch);
+        }
+    CFX_DIBitmap* pDIBitmap = _FX_WindowsDIB_LoadFromBuf(pInfo->pbmi, pData, pInfo->pbmi->bmiHeader.biBitCount == 32);
+    FX_Free(pData);
+    FreeDIBitmap(pInfo);
+    return pDIBitmap;
+}
+#endif