| /* 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 |
| |
| #ifdef DYNAMIC_DIRECTX |
| extern "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 void |
| unload(HINSTANCE &hinst) |
| { |
| if (hinst != NULL) |
| { |
| FreeLibrary(hinst); |
| hinst = NULL; |
| } |
| } |
| #endif // DYNAMIC_DIRECTX |
| |
| template <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_GEOMETRY |
| ToPixelGeometry(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 int |
| ToInt(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_MODE |
| ToRenderingMode(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_MODE |
| ToTextAntialiasMode(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 int |
| ToInt(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 : public IDWriteTextRenderer |
| { |
| public: |
| GdiTextRenderer( |
| IDWriteBitmapRenderTarget* bitmapRenderTarget, |
| IDWriteRenderingParams* renderingParams) : |
| cRefCount_(0), |
| pRenderTarget_(bitmapRenderTarget), |
| pRenderingParams_(renderingParams) |
| { |
| pRenderTarget_->AddRef(); |
| pRenderingParams_->AddRef(); |
| AddRef(); |
| } |
| |
| ~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 transform |
| pRenderTarget_->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; |
| |
| // METHODS |
| |
| DWriteContext(); |
| |
| 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); |
| } |
| |
| HRESULT |
| DWriteContext::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).aspx |
| HRESULT 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 collection |
| font->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; |
| } |
| |
| void |
| DWriteContext::SetFont(HFONT hFont) |
| { |
| if (mLastHFont != hFont) |
| { |
| LOGFONTW lf; |
| if (GetObjectW(hFont, sizeof(lf), &lf)) |
| { |
| SetFont(lf); |
| mLastHFont = hFont; |
| } |
| } |
| } |
| |
| void |
| DWriteContext::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); |
| } |
| |
| void |
| DWriteContext::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); |
| } |
| |
| float |
| DWriteContext::PixelsToDipsX(int x) |
| { |
| return x / mDpiScaleX; |
| } |
| |
| float |
| DWriteContext::PixelsToDipsY(int y) |
| { |
| return y / mDpiScaleY; |
| } |
| |
| void |
| DWriteContext::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); |
| } |
| else |
| hr = 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 INTERFACES |
| |
| void |
| DWrite_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 |
| } |
| |
| void |
| DWrite_Final(void) |
| { |
| #ifdef DYNAMIC_DIRECTX |
| pGetUserDefaultLocaleName = NULL; |
| pD2D1CreateFactory = NULL; |
| pDWriteCreateFactory = NULL; |
| unload(hDWriteDLL); |
| unload(hD2D1DLL); |
| #endif |
| } |
| |
| DWriteContext * |
| DWriteContext_Open(void) |
| { |
| #ifdef DYNAMIC_DIRECTX |
| if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL |
| || pDWriteCreateFactory == NULL) |
| return NULL; |
| #endif |
| return new DWriteContext(); |
| } |
| |
| void |
| DWriteContext_BeginDraw(DWriteContext *ctx) |
| { |
| if (ctx != NULL && ctx->mRT != NULL) |
| { |
| ctx->mRT->BeginDraw(); |
| ctx->mRT->SetTransform(D2D1::IdentityMatrix()); |
| ctx->mDrawing = true; |
| } |
| } |
| |
| void |
| DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect) |
| { |
| if (ctx != NULL && ctx->mRT != NULL) |
| { |
| ctx->mRT->BindDC(hdc, rect); |
| ctx->mRT->SetTextAntialiasMode(ctx->mTextAntialiasMode); |
| } |
| } |
| |
| void |
| DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) |
| { |
| if (ctx != NULL) |
| { |
| ctx->SetFont(hFont); |
| } |
| } |
| |
| void |
| DWriteContext_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); |
| } |
| |
| void |
| DWriteContext_EndDraw(DWriteContext *ctx) |
| { |
| if (ctx != NULL && ctx->mRT != NULL) |
| { |
| ctx->mRT->EndDraw(); |
| ctx->mDrawing = false; |
| } |
| } |
| |
| void |
| DWriteContext_Close(DWriteContext *ctx) |
| { |
| delete ctx; |
| } |
| |
| void |
| DWriteContext_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); |
| else |
| return NULL; |
| } |