// 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 "core/fxge/include/fx_ge.h"
|
|
#if _FX_OS_ == _FX_WIN32_DESKTOP_ || _FX_OS_ == _FX_WIN64_DESKTOP_
|
#include <windows.h>
|
#include <algorithm>
|
|
namespace Gdiplus {
|
using std::min;
|
using std::max;
|
} // namespace Gdiplus
|
|
#include <gdiplus.h>
|
|
#include "core/fxge/include/fx_ge_win32.h"
|
#include "core/fxge/win32/win32_int.h"
|
|
using namespace Gdiplus; // NOLINT
|
using namespace Gdiplus::DllExports; // NOLINT
|
|
#define GdiFillType2Gdip(fill_type) \
|
(fill_type == ALTERNATE ? FillModeAlternate : FillModeWinding)
|
|
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 uintptr_t* 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* str,
|
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,
|
uint32_t size,
|
void* pdv,
|
uint32_t* num_face) {
|
if (m_pGdiAddFontMemResourceEx) {
|
return ((FuncType_GdiAddFontMemResourceEx)m_pGdiAddFontMemResourceEx)(
|
(PVOID)pFontdata, (DWORD)size, (PVOID)pdv, (DWORD*)num_face);
|
}
|
return nullptr;
|
}
|
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 = nullptr;
|
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 = new CFX_DIBitmap;
|
if (!pStretched->Create(result_width, result_height, FXDIB_8bppRgb)) {
|
delete pStretched;
|
return nullptr;
|
}
|
LPBYTE dest_buf = pStretched->GetBuffer();
|
int src_width = pSource->GetWidth();
|
int src_height = pSource->GetHeight();
|
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();
|
uint8_t* 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 = nullptr;
|
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();
|
uint8_t* scan0 = pBitmap->GetBuffer() + pSrcRect->top * src_pitch +
|
pBitmap->GetBPP() * pSrcRect->left / 8;
|
GpBitmap* bitmap = nullptr;
|
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 = nullptr;
|
m_GdiModule = nullptr;
|
for (size_t i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i++) {
|
m_Functions[i] = nullptr;
|
}
|
m_pGdiAddFontMemResourceEx = nullptr;
|
m_pGdiRemoveFontMemResourseEx = nullptr;
|
}
|
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.c_str());
|
if (!m_hModule) {
|
return;
|
}
|
for (size_t i = 0; i < sizeof g_GdipFuncNames / sizeof(LPCSTR); i++) {
|
m_Functions[i] = GetProcAddress(m_hModule, g_GdipFuncNames[i]);
|
if (!m_Functions[i]) {
|
m_hModule = nullptr;
|
return;
|
}
|
}
|
uintptr_t gdiplusToken;
|
GdiplusStartupInput gdiplusStartupInput;
|
((FuncType_GdiplusStartup)m_Functions[FuncId_GdiplusStartup])(
|
&gdiplusToken, &gdiplusStartupInput, nullptr);
|
m_GdiModule = LoadLibraryA("GDI32.DLL");
|
if (!m_GdiModule) {
|
return;
|
}
|
m_pGdiAddFontMemResourceEx =
|
GetProcAddress(m_GdiModule, "AddFontMemResourceEx");
|
m_pGdiRemoveFontMemResourseEx =
|
GetProcAddress(m_GdiModule, "RemoveFontMemResourceEx");
|
}
|
CGdiplusExt::~CGdiplusExt() {}
|
LPVOID CGdiplusExt::LoadMemFont(LPBYTE pData, uint32_t size) {
|
GpFontCollection* pCollection = nullptr;
|
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 nullptr;
|
}
|
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(const FX_WCHAR* 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, uint32_t 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(uint32_t 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 nullptr;
|
}
|
GpFontFamily* family_list[1];
|
status = CallFunc(GdipGetFontCollectionFamilyList)(
|
(GpFontCollection*)pFontCollection, 1, family_list, &numFamilies);
|
if (status != Ok) {
|
return nullptr;
|
}
|
GpFont* pFont = nullptr;
|
status = CallFunc(GdipCreateFont)(family_list[0], font_size, fontstyle,
|
UnitPixel, &pFont);
|
if (status != Ok) {
|
return nullptr;
|
}
|
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,
|
uint32_t argb,
|
const FX_RECT* pClipRect,
|
int flags) {
|
ASSERT(pBitmap->GetBPP() == 1);
|
CGdiplusExt& GdiplusExt =
|
((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
|
GpGraphics* pGraphics = nullptr;
|
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_Matrix* 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 ? 1.0f / ((pMatrix->GetXUnit() + pMatrix->GetYUnit()) / 2)
|
: 1.0f;
|
if (width < unit) {
|
width = unit;
|
}
|
}
|
GpPen* pPen = nullptr;
|
CallFunc(GdipCreatePen1)((ARGB)argb, width, UnitWorld, &pPen);
|
LineCap lineCap = LineCapFlat;
|
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 = LineJoinMiterClipped;
|
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);
|
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 = nullptr;
|
}
|
CallFunc(GdipSetPenMiterLimit)(pPen, pGraphState->m_MiterLimit);
|
return pPen;
|
}
|
static FX_BOOL IsSmallTriangle(PointF* points,
|
const CFX_Matrix* 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 = (dx * dx) + (dy * dy);
|
if (distance_square < (1.0f * 2 + 1.0f / 4)) {
|
v1 = i;
|
v2 = pair1;
|
return TRUE;
|
}
|
}
|
return FALSE;
|
}
|
FX_BOOL CGdiplusExt::DrawPath(HDC hDC,
|
const CFX_PathData* pPathData,
|
const CFX_Matrix* pObject2Device,
|
const CFX_GraphStateData* pGraphState,
|
uint32_t fill_argb,
|
uint32_t stroke_argb,
|
int fill_mode) {
|
int nPoints = pPathData->GetPointCount();
|
if (nPoints == 0) {
|
return TRUE;
|
}
|
FX_PATHPOINT* pPoints = pPathData->GetPoints();
|
GpGraphics* pGraphics = nullptr;
|
CGdiplusExt& GdiplusExt =
|
((CWin32Platform*)CFX_GEModule::Get()->GetPlatformData())->m_GdiplusExt;
|
CallFunc(GdipCreateFromHDC)(hDC, &pGraphics);
|
CallFunc(GdipSetPageUnit)(pGraphics, UnitPixel);
|
CallFunc(GdipSetPixelOffsetMode)(pGraphics, PixelOffsetModeHalf);
|
GpMatrix* pMatrix = nullptr;
|
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);
|
BYTE* types = FX_Alloc(BYTE, nPoints);
|
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) {
|
int v1, v2;
|
if (IsSmallTriangle(points, pObject2Device, v1, v2)) {
|
GpPen* pPen = nullptr;
|
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 = nullptr;
|
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 final : public IStream {
|
public:
|
GpStream() : m_RefCount(1), m_ReadPos(0) {}
|
|
// IUnknown
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
|
void** ppvObject) override {
|
if (iid == __uuidof(IUnknown) || iid == __uuidof(IStream) ||
|
iid == __uuidof(ISequentialStream)) {
|
*ppvObject = static_cast<IStream*>(this);
|
AddRef();
|
return S_OK;
|
}
|
return E_NOINTERFACE;
|
}
|
ULONG STDMETHODCALLTYPE AddRef() override {
|
return (ULONG)InterlockedIncrement(&m_RefCount);
|
}
|
ULONG STDMETHODCALLTYPE Release() override {
|
ULONG res = (ULONG)InterlockedDecrement(&m_RefCount);
|
if (res == 0) {
|
delete this;
|
}
|
return res;
|
}
|
|
// ISequentialStream
|
HRESULT STDMETHODCALLTYPE Read(void* Output,
|
ULONG cb,
|
ULONG* pcbRead) override {
|
size_t bytes_left;
|
size_t bytes_out;
|
if (pcbRead) {
|
*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 = std::min(pdfium::base::checked_cast<size_t>(cb), bytes_left);
|
FXSYS_memcpy(Output, m_InterStream.GetBuffer() + m_ReadPos, bytes_out);
|
m_ReadPos += (int32_t)bytes_out;
|
if (pcbRead) {
|
*pcbRead = (ULONG)bytes_out;
|
}
|
return S_OK;
|
}
|
HRESULT STDMETHODCALLTYPE Write(void const* Input,
|
ULONG cb,
|
ULONG* pcbWritten) override {
|
if (cb <= 0) {
|
if (pcbWritten) {
|
*pcbWritten = 0;
|
}
|
return S_OK;
|
}
|
m_InterStream.InsertBlock(m_InterStream.GetLength(), Input, cb);
|
if (pcbWritten) {
|
*pcbWritten = cb;
|
}
|
return S_OK;
|
}
|
|
// IStream
|
HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER) override {
|
return E_NOTIMPL;
|
}
|
HRESULT STDMETHODCALLTYPE CopyTo(IStream*,
|
ULARGE_INTEGER,
|
ULARGE_INTEGER*,
|
ULARGE_INTEGER*) override {
|
return E_NOTIMPL;
|
}
|
HRESULT STDMETHODCALLTYPE Commit(DWORD) override { return E_NOTIMPL; }
|
HRESULT STDMETHODCALLTYPE Revert() override { return E_NOTIMPL; }
|
HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER,
|
ULARGE_INTEGER,
|
DWORD) override {
|
return E_NOTIMPL;
|
}
|
HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER,
|
ULARGE_INTEGER,
|
DWORD) override {
|
return E_NOTIMPL;
|
}
|
HRESULT STDMETHODCALLTYPE Clone(IStream** stream) override {
|
return E_NOTIMPL;
|
}
|
HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove,
|
DWORD dwOrigin,
|
ULARGE_INTEGER* lpNewFilePointer) override {
|
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) {
|
lpNewFilePointer->QuadPart = m_ReadPos;
|
}
|
return S_OK;
|
}
|
HRESULT STDMETHODCALLTYPE Stat(STATSTG* pStatstg,
|
DWORD grfStatFlag) override {
|
if (!pStatstg) {
|
return STG_E_INVALIDFUNCTION;
|
}
|
ZeroMemory(pStatstg, sizeof(STATSTG));
|
pStatstg->cbSize.QuadPart = m_InterStream.GetLength();
|
return S_OK;
|
}
|
|
private:
|
LONG m_RefCount;
|
int m_ReadPos;
|
CFX_ByteTextBuf m_InterStream;
|
};
|
|
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 = nullptr;
|
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 nullptr;
|
}
|
pStream = new GpStream;
|
pStream->Write(args.memory_base, (ULONG)args.memory_size, nullptr);
|
status = CallFunc(GdipCreateBitmapFromStreamICM)(pStream, &pBitmap);
|
}
|
if (status != Ok) {
|
if (pStream) {
|
pStream->Release();
|
}
|
return nullptr;
|
}
|
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);
|
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);
|
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);
|
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) {
|
return nullptr;
|
}
|
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_Alloc2D(BYTE, dest_pitch, height);
|
if (dest_pitch == pInfo->Stride) {
|
FXSYS_memcpy(pData, pInfo->pScan0, dest_pitch * height);
|
} else {
|
for (int i = 0; i < height; i++) {
|
FXSYS_memcpy(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
|