/* vi:set ts=8 sts=4 sw=4 noet: *//** Author: MURAOKA Taro <koron.kaoriya@gmail.com>** Contributors:* - Ken Takata** Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>* THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.*/#define WIN32_LEAN_AND_MEAN#ifndef DYNAMIC_DIRECTX# if WINVER < 0x0600# error WINVER must be 0x0600 or above to use DirectWrite(DirectX)# endif#endif#include <windows.h>#include <crtdbg.h>#include <assert.h>#include <math.h>#include <d2d1.h>#include <d2d1helper.h>#include <dwrite.h>#include "gui_dwrite.h"#ifdef __MINGW32__# define __maybenull SAL__maybenull# define __in SAL__in# define __out SAL__out#endif#if (defined(_MSC_VER) && (_MSC_VER >= 1700)) || (__cplusplus >= 201103L)# define FINAL final#else# define FINAL#endif#ifdef DYNAMIC_DIRECTXextern "C" HINSTANCE vimLoadLib(char *name);typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int);typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE,REFIID, const D2D1_FACTORY_OPTIONS *, void **);typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE,REFIID, IUnknown **);static HINSTANCE hD2D1DLL = NULL;static HINSTANCE hDWriteDLL = NULL;static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL;static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL;static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL;#define GetUserDefaultLocaleName (*pGetUserDefaultLocaleName)#define D2D1CreateFactory (*pD2D1CreateFactory)#define DWriteCreateFactory (*pDWriteCreateFactory)static voidunload(HINSTANCE &hinst){if (hinst != NULL){FreeLibrary(hinst);hinst = NULL;}}#endif // DYNAMIC_DIRECTXtemplate <class T> inline void SafeRelease(T **ppT){if (*ppT){(*ppT)->Release();*ppT = NULL;}}struct GdiTextRendererContext{// const fields.COLORREF color;FLOAT cellWidth;// working fields.FLOAT offsetX;};static DWRITE_PIXEL_GEOMETRYToPixelGeometry(int value){switch (value){default:case 0:return DWRITE_PIXEL_GEOMETRY_FLAT;case 1:return DWRITE_PIXEL_GEOMETRY_RGB;case 2:return DWRITE_PIXEL_GEOMETRY_BGR;}}static intToInt(DWRITE_PIXEL_GEOMETRY value){switch (value){case DWRITE_PIXEL_GEOMETRY_FLAT:return 0;case DWRITE_PIXEL_GEOMETRY_RGB:return 1;case DWRITE_PIXEL_GEOMETRY_BGR:return 2;default:return -1;}}static DWRITE_RENDERING_MODEToRenderingMode(int value){switch (value){default:case 0:return DWRITE_RENDERING_MODE_DEFAULT;case 1:return DWRITE_RENDERING_MODE_ALIASED;case 2:return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;case 3:return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL;case 4:return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;case 5:return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;case 6:return DWRITE_RENDERING_MODE_OUTLINE;}}static D2D1_TEXT_ANTIALIAS_MODEToTextAntialiasMode(int value){switch (value){default:case 0:return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;case 1:return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;case 2:return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;case 3:return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;}}static intToInt(DWRITE_RENDERING_MODE value){switch (value){case DWRITE_RENDERING_MODE_DEFAULT:return 0;case DWRITE_RENDERING_MODE_ALIASED:return 1;case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:return 2;case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:return 3;case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:return 4;case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:return 5;case DWRITE_RENDERING_MODE_OUTLINE:return 6;default:return -1;}}class AdjustedGlyphRun : public DWRITE_GLYPH_RUN{private:FLOAT mDelta;FLOAT *mAdjustedAdvances;public:AdjustedGlyphRun(const DWRITE_GLYPH_RUN *glyphRun,FLOAT cellWidth) :DWRITE_GLYPH_RUN(*glyphRun),mDelta(0.0f),mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]){assert(cellWidth != 0.0f);for (UINT32 i = 0; i < glyphRun->glyphCount; ++i){FLOAT orig = glyphRun->glyphAdvances[i];FLOAT adjusted = adjustToCell(orig, cellWidth);mAdjustedAdvances[i] = adjusted;mDelta += adjusted - orig;}glyphAdvances = mAdjustedAdvances;}~AdjustedGlyphRun(void){delete[] mAdjustedAdvances;}FLOAT getDelta(void) const{return mDelta;}static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth){int cellCount = (int)floor(value / cellWidth + 0.5f);if (cellCount < 1)cellCount = 1;return cellCount * cellWidth;}};class GdiTextRenderer FINAL : public IDWriteTextRenderer{public:GdiTextRenderer(IDWriteBitmapRenderTarget* bitmapRenderTarget,IDWriteRenderingParams* renderingParams) :cRefCount_(0),pRenderTarget_(bitmapRenderTarget),pRenderingParams_(renderingParams){pRenderTarget_->AddRef();pRenderingParams_->AddRef();AddRef();}// add "virtual" to avoid a compiler warningvirtual ~GdiTextRenderer(){SafeRelease(&pRenderTarget_);SafeRelease(&pRenderingParams_);}IFACEMETHOD(IsPixelSnappingDisabled)(__maybenull void* clientDrawingContext,__out BOOL* isDisabled){*isDisabled = FALSE;return S_OK;}IFACEMETHOD(GetCurrentTransform)(__maybenull void* clientDrawingContext,__out DWRITE_MATRIX* transform){// forward the render target's transformpRenderTarget_->GetCurrentTransform(transform);return S_OK;}IFACEMETHOD(GetPixelsPerDip)(__maybenull void* clientDrawingContext,__out FLOAT* pixelsPerDip){*pixelsPerDip = pRenderTarget_->GetPixelsPerDip();return S_OK;}IFACEMETHOD(DrawGlyphRun)(__maybenull void* clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_MEASURING_MODE measuringMode,__in DWRITE_GLYPH_RUN const* glyphRun,__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,IUnknown* clientDrawingEffect){HRESULT hr = S_OK;GdiTextRendererContext *context =reinterpret_cast<GdiTextRendererContext*>(clientDrawingContext);AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth);// Pass on the drawing call to the render target to do the real work.RECT dirtyRect = {0};hr = pRenderTarget_->DrawGlyphRun(baselineOriginX + context->offsetX,baselineOriginY,measuringMode,&adjustedGlyphRun,pRenderingParams_,context->color,&dirtyRect);context->offsetX += adjustedGlyphRun.getDelta();return hr;}IFACEMETHOD(DrawUnderline)(__maybenull void* clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,__in DWRITE_UNDERLINE const* underline,IUnknown* clientDrawingEffect){return E_NOTIMPL;}IFACEMETHOD(DrawStrikethrough)(__maybenull void* clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,__in DWRITE_STRIKETHROUGH const* strikethrough,IUnknown* clientDrawingEffect){return E_NOTIMPL;}IFACEMETHOD(DrawInlineObject)(__maybenull void* clientDrawingContext,FLOAT originX,FLOAT originY,IDWriteInlineObject* inlineObject,BOOL isSideways,BOOL isRightToLeft,IUnknown* clientDrawingEffect){return E_NOTIMPL;}public:IFACEMETHOD_(unsigned long, AddRef) (){return InterlockedIncrement(&cRefCount_);}IFACEMETHOD_(unsigned long, Release) (){long newCount = InterlockedDecrement(&cRefCount_);if (newCount == 0){delete this;return 0;}return newCount;}IFACEMETHOD(QueryInterface)(IID const& riid,void** ppvObject){if (__uuidof(IDWriteTextRenderer) == riid){*ppvObject = this;}else if (__uuidof(IDWritePixelSnapping) == riid){*ppvObject = this;}else if (__uuidof(IUnknown) == riid){*ppvObject = this;}else{*ppvObject = NULL;return E_FAIL;}return S_OK;}private:long cRefCount_;IDWriteBitmapRenderTarget* pRenderTarget_;IDWriteRenderingParams* pRenderingParams_;};struct DWriteContext {FLOAT mDpiScaleX;FLOAT mDpiScaleY;bool mDrawing;ID2D1Factory *mD2D1Factory;ID2D1DCRenderTarget *mRT;ID2D1SolidColorBrush *mBrush;IDWriteFactory *mDWriteFactory;IDWriteGdiInterop *mGdiInterop;IDWriteRenderingParams *mRenderingParams;IDWriteTextFormat *mTextFormat;HFONT mLastHFont;DWRITE_FONT_WEIGHT mFontWeight;DWRITE_FONT_STYLE mFontStyle;D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;// METHODSDWriteContext();virtual ~DWriteContext();HRESULT SetLOGFONT(const LOGFONTW &logFont, float fontSize);void SetFont(HFONT hFont);void SetFont(const LOGFONTW &logFont);void DrawText(HDC hdc, const WCHAR* text, int len,int x, int y, int w, int h, int cellWidth, COLORREF color);float PixelsToDipsX(int x);float PixelsToDipsY(int y);void SetRenderingParams(const DWriteRenderingParams *params);DWriteRenderingParams *GetRenderingParams(DWriteRenderingParams *params);};DWriteContext::DWriteContext() :mDpiScaleX(1.f),mDpiScaleY(1.f),mDrawing(false),mD2D1Factory(NULL),mRT(NULL),mBrush(NULL),mDWriteFactory(NULL),mGdiInterop(NULL),mRenderingParams(NULL),mTextFormat(NULL),mLastHFont(NULL),mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),mFontStyle(DWRITE_FONT_STYLE_NORMAL),mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT){HRESULT hr;HDC screen = ::GetDC(0);mDpiScaleX = ::GetDeviceCaps(screen, LOGPIXELSX) / 96.0f;mDpiScaleY = ::GetDeviceCaps(screen, LOGPIXELSY) / 96.0f;::ReleaseDC(0, screen);hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,__uuidof(ID2D1Factory), NULL,reinterpret_cast<void**>(&mD2D1Factory));_RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);if (SUCCEEDED(hr)){D2D1_RENDER_TARGET_PROPERTIES props = {D2D1_RENDER_TARGET_TYPE_DEFAULT,{ DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },0, 0,D2D1_RENDER_TARGET_USAGE_NONE,D2D1_FEATURE_LEVEL_DEFAULT};hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);_RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);}if (SUCCEEDED(hr)){hr = mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black),&mBrush);_RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);}if (SUCCEEDED(hr)){hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED,__uuidof(IDWriteFactory),reinterpret_cast<IUnknown**>(&mDWriteFactory));_RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,mDWriteFactory);}if (SUCCEEDED(hr)){hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);_RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);}if (SUCCEEDED(hr)){hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);_RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,mRenderingParams);}}DWriteContext::~DWriteContext(){SafeRelease(&mTextFormat);SafeRelease(&mRenderingParams);SafeRelease(&mGdiInterop);SafeRelease(&mDWriteFactory);SafeRelease(&mBrush);SafeRelease(&mRT);SafeRelease(&mD2D1Factory);}HRESULTDWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize){// Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspxHRESULT hr = S_OK;IDWriteFont *font = NULL;IDWriteFontFamily *fontFamily = NULL;IDWriteLocalizedStrings *localizedFamilyNames = NULL;if (SUCCEEDED(hr)){hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);}// Get the font family to which this font belongs.if (SUCCEEDED(hr)){hr = font->GetFontFamily(&fontFamily);}// Get the family names. This returns an object that encapsulates one or// more names with the same meaning but in different languages.if (SUCCEEDED(hr)){hr = fontFamily->GetFamilyNames(&localizedFamilyNames);}// Get the family name at index zero. If we were going to display the name// we'd want to try to find one that matched the use locale, but for// purposes of creating a text format object any language will do.wchar_t familyName[100];if (SUCCEEDED(hr)){hr = localizedFamilyNames->GetString(0, familyName,ARRAYSIZE(familyName));}if (SUCCEEDED(hr)){// If no font size was passed in use the lfHeight of the LOGFONT.if (fontSize == 0){// Convert from pixels to DIPs.fontSize = PixelsToDipsY(logFont.lfHeight);if (fontSize < 0){// Negative lfHeight represents the size of the em unit.fontSize = -fontSize;}else{// Positive lfHeight represents the cell height (ascent +// descent).DWRITE_FONT_METRICS fontMetrics;font->GetMetrics(&fontMetrics);// Convert the cell height (ascent + descent) from design units// to ems.float cellHeight = static_cast<float>(fontMetrics.ascent + fontMetrics.descent)/ fontMetrics.designUnitsPerEm;// Divide the font size by the cell height to get the font em// size.fontSize /= cellHeight;}}}// The text format includes a locale name. Ideally, this would be the// language of the text, which may or may not be the same as the primary// language of the user. However, for our purposes the user locale will do.wchar_t localeName[LOCALE_NAME_MAX_LENGTH];if (SUCCEEDED(hr)){if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)hr = HRESULT_FROM_WIN32(GetLastError());}if (SUCCEEDED(hr)){// Create the text format object.hr = mDWriteFactory->CreateTextFormat(familyName,NULL, // no custom font collectionfont->GetWeight(),font->GetStyle(),font->GetStretch(),fontSize,localeName,&mTextFormat);}if (SUCCEEDED(hr)){mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC: DWRITE_FONT_STYLE_NORMAL;}SafeRelease(&localizedFamilyNames);SafeRelease(&fontFamily);SafeRelease(&font);return hr;}voidDWriteContext::SetFont(HFONT hFont){if (mLastHFont != hFont){LOGFONTW lf;if (GetObjectW(hFont, sizeof(lf), &lf)){SetFont(lf);mLastHFont = hFont;}}}voidDWriteContext::SetFont(const LOGFONTW &logFont){SafeRelease(&mTextFormat);mLastHFont = NULL;HRESULT hr = SetLOGFONT(logFont, 0.f);if (SUCCEEDED(hr))hr = mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);if (SUCCEEDED(hr))hr = mTextFormat->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);if (SUCCEEDED(hr))hr = mTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);}voidDWriteContext::DrawText(HDC hdc, const WCHAR* text, int len,int x, int y, int w, int h, int cellWidth, COLORREF color){HRESULT hr = S_OK;IDWriteBitmapRenderTarget *bmpRT = NULL;// Skip when any fonts are not set.if (mTextFormat == NULL)return;// Check possibility of zero divided error.if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f)return;if (SUCCEEDED(hr))hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT);if (SUCCEEDED(hr)){IDWriteTextLayout *textLayout = NULL;HDC memdc = bmpRT->GetMemoryDC();BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY);hr = mDWriteFactory->CreateGdiCompatibleTextLayout(text, len, mTextFormat, PixelsToDipsX(w),PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout);if (SUCCEEDED(hr)){DWRITE_TEXT_RANGE textRange = { 0, (UINT32)len };textLayout->SetFontWeight(mFontWeight, textRange);textLayout->SetFontStyle(mFontStyle, textRange);}if (SUCCEEDED(hr)){GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT,mRenderingParams);GdiTextRendererContext data = {color,PixelsToDipsX(cellWidth),0.0f};textLayout->Draw(&data, renderer, 0, 0);SafeRelease(&renderer);}BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY);SafeRelease(&textLayout);}SafeRelease(&bmpRT);}floatDWriteContext::PixelsToDipsX(int x){return x / mDpiScaleX;}floatDWriteContext::PixelsToDipsY(int y){return y / mDpiScaleY;}voidDWriteContext::SetRenderingParams(const DWriteRenderingParams *params){if (mDWriteFactory == NULL)return;IDWriteRenderingParams *renderingParams = NULL;D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;HRESULT hr;if (params != NULL){hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,params->enhancedContrast, params->clearTypeLevel,ToPixelGeometry(params->pixelGeometry),ToRenderingMode(params->renderingMode), &renderingParams);textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);}elsehr = mDWriteFactory->CreateRenderingParams(&renderingParams);if (SUCCEEDED(hr) && renderingParams != NULL){SafeRelease(&mRenderingParams);mRenderingParams = renderingParams;mTextAntialiasMode = textAntialiasMode;}}DWriteRenderingParams *DWriteContext::GetRenderingParams(DWriteRenderingParams *params){if (params != NULL && mRenderingParams != NULL){params->gamma = mRenderingParams->GetGamma();params->enhancedContrast = mRenderingParams->GetEnhancedContrast();params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());params->textAntialiasMode = mTextAntialiasMode;}return params;}////////////////////////////////////////////////////////////////////////////// PUBLIC C INTERFACESvoidDWrite_Init(void){#ifdef DYNAMIC_DIRECTX// Load libraries.hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll"));hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll"));if (hD2D1DLL == NULL || hDWriteDLL == NULL){DWrite_Final();return;}// Get address of procedures.pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,"D2D1CreateFactory");pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,"DWriteCreateFactory");#endif}voidDWrite_Final(void){#ifdef DYNAMIC_DIRECTXpGetUserDefaultLocaleName = NULL;pD2D1CreateFactory = NULL;pDWriteCreateFactory = NULL;unload(hDWriteDLL);unload(hD2D1DLL);#endif}DWriteContext *DWriteContext_Open(void){#ifdef DYNAMIC_DIRECTXif (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL|| pDWriteCreateFactory == NULL)return NULL;#endifreturn new DWriteContext();}voidDWriteContext_BeginDraw(DWriteContext *ctx){if (ctx != NULL && ctx->mRT != NULL){ctx->mRT->BeginDraw();ctx->mRT->SetTransform(D2D1::IdentityMatrix());ctx->mDrawing = true;}}voidDWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect){if (ctx != NULL && ctx->mRT != NULL){ctx->mRT->BindDC(hdc, rect);ctx->mRT->SetTextAntialiasMode(ctx->mTextAntialiasMode);}}voidDWriteContext_SetFont(DWriteContext *ctx, HFONT hFont){if (ctx != NULL){ctx->SetFont(hFont);}}voidDWriteContext_DrawText(DWriteContext *ctx,HDC hdc,const WCHAR* text,int len,int x,int y,int w,int h,int cellWidth,COLORREF color){if (ctx != NULL)ctx->DrawText(hdc, text, len, x, y, w, h, cellWidth, color);}voidDWriteContext_EndDraw(DWriteContext *ctx){if (ctx != NULL && ctx->mRT != NULL){ctx->mRT->EndDraw();ctx->mDrawing = false;}}voidDWriteContext_Close(DWriteContext *ctx){delete ctx;}voidDWriteContext_SetRenderingParams(DWriteContext *ctx,const DWriteRenderingParams *params){if (ctx != NULL)ctx->SetRenderingParams(params);}DWriteRenderingParams *DWriteContext_GetRenderingParams(DWriteContext *ctx,DWriteRenderingParams *params){if (ctx != NULL)return ctx->GetRenderingParams(params);elsereturn NULL;}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。