# HG changeset patch # User Christian Brabandt # Date 1511703004 -3600 # Node ID 2ebc3df65ca2cae20a632c3bd151565d7edeed80 # Parent 9eeaf892510cb9ca9e6a06a1f3523a2fd2934efc patch 8.0.1343: MS-Windows: does not show colored emojis commit https://github.com/vim/vim/commit/d7ccc4d81dbcfa3ac0352bacea6e294fc9e33fda Author: Bram Moolenaar Date: Sun Nov 26 14:29:32 2017 +0100 patch 8.0.1343: MS-Windows: does not show colored emojis Problem: MS-Windows: does not show colored emojis. Solution: Implement colored emojis. Improve drawing speed. Make 'taamode' work. (Taro Muraoka, Yasuhiro Matsumoto, Ken Takata, close #2375) diff --git a/appveyor.yml b/appveyor.yml --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ matrix: before_build: - '"C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\SetEnv.cmd" /x64 /release' + - 'set INCLUDE=%INCLUDE%C:\Program Files (x86)\Windows Kits\8.1\Include\um' build_script: - src/appveyor.bat diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -6154,11 +6154,34 @@ A jump table for the options with a shor Example: > set encoding=utf-8 - set gfn=Ricty_Diminished:h12:cSHIFTJIS + set gfn=Ricty_Diminished:h12 set rop=type:directx < - If select a raster font (Courier, Terminal or FixedSys) to - 'guifont', it fallbacks to be drawn by GDI automatically. + If select a raster font (Courier, Terminal or FixedSys which + have ".fon" extension in file name) to 'guifont', it will be + drawn by GDI as a fallback. This fallback will cause + significant slow down on drawing. + + NOTE: It is known that some fonts and options combination + causes trouble on drawing glyphs. + + - 'rendmode:5' and 'renmode:6' will not work with some + special made fonts (True-Type fonts which includes only + bitmap glyphs). + - 'taamode:3' will not work with some vector fonts. + + NOTE: With this option, you can display colored emoji + (emoticon) in Windows 8.1 or later. To display colored emoji, + there are some conditions which you should notice. + + - If your font includes non-colored emoji already, it will + be used. + - If your font doesn't have emoji, the system chooses an + alternative symbol font. On Windows 10, "Segoe UI Emoji" + will be used. + - When this alternative font didn't have fixed width glyph, + emoji might be rendered beyond the bounding box of drawing + cell. Other render types are currently not supported. diff --git a/src/gui_dwrite.cpp b/src/gui_dwrite.cpp --- a/src/gui_dwrite.cpp +++ b/src/gui_dwrite.cpp @@ -4,6 +4,7 @@ * * Contributors: * - Ken Takata + * - Yasuhiro Matsumoto * * Copyright (C) 2013 MURAOKA Taro * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. @@ -23,7 +24,21 @@ #include #include #include -#include + +// Disable these macros to compile with old VC and newer SDK (V8.1 or later). +#if defined(_MSC_VER) && (_MSC_VER < 1700) +# define _COM_Outptr_ __out +# define _In_reads_(s) +# define _In_reads_opt_(s) +# define _Maybenull_ +# define _Out_writes_(s) +# define _Out_writes_opt_(s) +# define _Out_writes_to_(x, y) +# define _Out_writes_to_opt_(x, y) +# define _Outptr_ +#endif + +#include #include "gui_dwrite.h" @@ -79,16 +94,6 @@ template inline void SafeRelea } } -struct GdiTextRendererContext -{ - // const fields. - COLORREF color; - FLOAT cellWidth; - - // working fields. - FLOAT offsetX; -}; - static DWRITE_PIXEL_GEOMETRY ToPixelGeometry(int value) { @@ -184,17 +189,151 @@ ToInt(DWRITE_RENDERING_MODE value) } } +class FontCache { +public: + struct Item { + HFONT hFont; + IDWriteTextFormat* pTextFormat; + DWRITE_FONT_WEIGHT fontWeight; + DWRITE_FONT_STYLE fontStyle; + Item() : hFont(NULL), pTextFormat(NULL) {} + }; + +private: + int mSize; + Item *mItems; + +public: + FontCache(int size = 2) : + mSize(size), + mItems(new Item[size]) + { + } + + ~FontCache() + { + for (int i = 0; i < mSize; ++i) + SafeRelease(&mItems[i].pTextFormat); + delete[] mItems; + } + + bool get(HFONT hFont, Item &item) + { + int n = find(hFont); + if (n < 0) + return false; + item = mItems[n]; + slide(n); + return true; + } + + void put(const Item& item) + { + int n = find(item.hFont); + if (n < 0) + n = mSize - 1; + if (mItems[n].pTextFormat != item.pTextFormat) + { + SafeRelease(&mItems[n].pTextFormat); + item.pTextFormat->AddRef(); + } + mItems[n] = item; + slide(n); + } + +private: + int find(HFONT hFont) + { + for (int i = 0; i < mSize; ++i) + { + if (mItems[i].hFont == hFont) + return i; + } + return -1; + } + + void slide(int nextTop) + { + if (nextTop == 0) + return; + Item tmp = mItems[nextTop]; + for (int i = nextTop - 1; i >= 0; --i) + mItems[i + 1] = mItems[i]; + mItems[0] = tmp; + } +}; + +struct DWriteContext { + HDC mHDC; + bool mDrawing; + bool mFallbackDC; + + ID2D1Factory *mD2D1Factory; + + ID2D1DCRenderTarget *mRT; + ID2D1SolidColorBrush *mBrush; + + IDWriteFactory *mDWriteFactory; + IDWriteFactory2 *mDWriteFactory2; + + IDWriteGdiInterop *mGdiInterop; + IDWriteRenderingParams *mRenderingParams; + + FontCache mFontCache; + IDWriteTextFormat *mTextFormat; + DWRITE_FONT_WEIGHT mFontWeight; + DWRITE_FONT_STYLE mFontStyle; + + D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode; + + // METHODS + + DWriteContext(); + + virtual ~DWriteContext(); + + HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, + IDWriteTextFormat **ppTextFormat); + + HRESULT SetFontByLOGFONT(const LOGFONTW &logFont); + + void SetFont(HFONT hFont); + + void BindDC(HDC hdc, RECT *rect); + + void AssureDrawing(); + + ID2D1Brush* SolidBrush(COLORREF color); + + void DrawText(const WCHAR* text, int len, + int x, int y, int w, int h, int cellWidth, COLORREF color, + UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx); + + void FillRect(RECT *rc, COLORREF color); + + void Flush(); + + void SetRenderingParams( + const DWriteRenderingParams *params); + + DWriteRenderingParams *GetRenderingParams( + DWriteRenderingParams *params); +}; + class AdjustedGlyphRun : public DWRITE_GLYPH_RUN { private: + FLOAT &mAccum; FLOAT mDelta; FLOAT *mAdjustedAdvances; public: AdjustedGlyphRun( const DWRITE_GLYPH_RUN *glyphRun, - FLOAT cellWidth) : + FLOAT cellWidth, + FLOAT &accum) : DWRITE_GLYPH_RUN(*glyphRun), + mAccum(accum), mDelta(0.0f), mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]) { @@ -209,45 +348,44 @@ public: glyphAdvances = mAdjustedAdvances; } - ~AdjustedGlyphRun(void) + ~AdjustedGlyphRun() { + mAccum += mDelta; delete[] mAdjustedAdvances; } - FLOAT getDelta(void) const - { - return mDelta; - } - static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth) { - int cellCount = (int)floor(value / cellWidth + 0.5f); + int cellCount = int(floor(value / cellWidth + 0.5f)); if (cellCount < 1) cellCount = 1; return cellCount * cellWidth; } }; -class GdiTextRenderer FINAL : public IDWriteTextRenderer +struct TextRendererContext { + // const fields. + COLORREF color; + FLOAT cellWidth; + + // working fields. + FLOAT offsetX; +}; + +class TextRenderer FINAL : public IDWriteTextRenderer { public: - GdiTextRenderer( - IDWriteBitmapRenderTarget* bitmapRenderTarget, - IDWriteRenderingParams* renderingParams) : + TextRenderer( + DWriteContext* pDWC) : cRefCount_(0), - pRenderTarget_(bitmapRenderTarget), - pRenderingParams_(renderingParams) + pDWC_(pDWC) { - pRenderTarget_->AddRef(); - pRenderingParams_->AddRef(); AddRef(); } // add "virtual" to avoid a compiler warning - virtual ~GdiTextRenderer() + virtual ~TextRenderer() { - SafeRelease(&pRenderTarget_); - SafeRelease(&pRenderingParams_); } IFACEMETHOD(IsPixelSnappingDisabled)( @@ -263,7 +401,8 @@ public: __out DWRITE_MATRIX* transform) { // forward the render target's transform - pRenderTarget_->GetCurrentTransform(transform); + pDWC_->mRT->GetTransform( + reinterpret_cast(transform)); return S_OK; } @@ -271,43 +410,12 @@ public: __maybenull void* clientDrawingContext, __out FLOAT* pixelsPerDip) { - *pixelsPerDip = pRenderTarget_->GetPixelsPerDip(); + float dpiX, unused; + pDWC_->mRT->GetDpi(&dpiX, &unused); + *pixelsPerDip = dpiX / 96.0f; 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(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, @@ -340,6 +448,69 @@ public: return E_NOTIMPL; } + 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) + { + TextRendererContext *context = + reinterpret_cast(clientDrawingContext); + + AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth, + context->offsetX); + + if (pDWC_->mDWriteFactory2 != NULL) + { + IDWriteColorGlyphRunEnumerator *enumerator = NULL; + HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun( + baselineOriginX + context->offsetX, + baselineOriginY, + &adjustedGlyphRun, + NULL, + DWRITE_MEASURING_MODE_GDI_NATURAL, + NULL, + 0, + &enumerator); + if (SUCCEEDED(hr)) + { + // Draw by IDWriteFactory2 for color emoji + BOOL hasRun = TRUE; + enumerator->MoveNext(&hasRun); + while (hasRun) + { + const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun; + enumerator->GetCurrentRun(&colorGlyphRun); + + pDWC_->mBrush->SetColor(colorGlyphRun->runColor); + pDWC_->mRT->DrawGlyphRun( + D2D1::Point2F( + colorGlyphRun->baselineOriginX, + colorGlyphRun->baselineOriginY), + &colorGlyphRun->glyphRun, + pDWC_->mBrush, + DWRITE_MEASURING_MODE_NATURAL); + enumerator->MoveNext(&hasRun); + } + SafeRelease(&enumerator); + return S_OK; + } + } + + // Draw by IDWriteFactory (without color emoji) + pDWC_->mRT->DrawGlyphRun( + D2D1::Point2F( + baselineOriginX + context->offsetX, + baselineOriginY), + &adjustedGlyphRun, + pDWC_->SolidBrush(context->color), + DWRITE_MEASURING_MODE_NATURAL); + return S_OK; + } + public: IFACEMETHOD_(unsigned long, AddRef) () { @@ -385,80 +556,28 @@ public: 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* pDWC_; }; DWriteContext::DWriteContext() : - mDpiScaleX(1.f), - mDpiScaleY(1.f), + mHDC(NULL), mDrawing(false), + mFallbackDC(false), mD2D1Factory(NULL), mRT(NULL), mBrush(NULL), mDWriteFactory(NULL), + mDWriteFactory2(NULL), mGdiInterop(NULL), mRenderingParams(NULL), + mFontCache(8), 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(&mD2D1Factory)); @@ -497,6 +616,15 @@ DWriteContext::DWriteContext() : if (SUCCEEDED(hr)) { + DWriteCreateFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory2), + reinterpret_cast(&mDWriteFactory2)); + _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available"); + } + + if (SUCCEEDED(hr)) + { hr = mDWriteFactory->GetGdiInterop(&mGdiInterop); _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop); } @@ -515,20 +643,24 @@ DWriteContext::~DWriteContext() SafeRelease(&mRenderingParams); SafeRelease(&mGdiInterop); SafeRelease(&mDWriteFactory); + SafeRelease(&mDWriteFactory2); SafeRelease(&mBrush); SafeRelease(&mRT); SafeRelease(&mD2D1Factory); } HRESULT -DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize) +DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont, + IDWriteTextFormat **ppTextFormat) { - // Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspx + // Most of this function is copied from: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/RenderTest/TextHelpers.cpp HRESULT hr = S_OK; + IDWriteTextFormat *pTextFormat = NULL; IDWriteFont *font = NULL; IDWriteFontFamily *fontFamily = NULL; IDWriteLocalizedStrings *localizedFamilyNames = NULL; + float fontSize = 0; if (SUCCEEDED(hr)) { @@ -561,33 +693,30 @@ DWriteContext::SetLOGFONT(const LOGFONTW if (SUCCEEDED(hr)) { - // If no font size was passed in use the lfHeight of the LOGFONT. - if (fontSize == 0) + // Use lfHeight of the LOGFONT as font size. + fontSize = float(logFont.lfHeight); + + if (fontSize < 0) + { + // Negative lfHeight represents the size of the em unit. + fontSize = -fontSize; + } + else { - // 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); + // 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( - fontMetrics.ascent + fontMetrics.descent) - / fontMetrics.designUnitsPerEm; + // Convert the cell height (ascent + descent) from design units + // to ems. + float cellHeight = static_cast( + fontMetrics.ascent + fontMetrics.descent) + / fontMetrics.designUnitsPerEm; - // Divide the font size by the cell height to get the font em - // size. - fontSize /= cellHeight; - } + // Divide the font size by the cell height to get the font em + // size. + fontSize /= cellHeight; } } @@ -612,123 +741,165 @@ DWriteContext::SetLOGFONT(const LOGFONTW font->GetStretch(), fontSize, localeName, - &mTextFormat); + &pTextFormat); } if (SUCCEEDED(hr)) + hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); + + if (SUCCEEDED(hr)) + hr = pTextFormat->SetParagraphAlignment( + DWRITE_PARAGRAPH_ALIGNMENT_CENTER); + + if (SUCCEEDED(hr)) + hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP); + + SafeRelease(&localizedFamilyNames); + SafeRelease(&fontFamily); + SafeRelease(&font); + + if (SUCCEEDED(hr)) + *ppTextFormat = pTextFormat; + else + SafeRelease(&pTextFormat); + + return hr; +} + + HRESULT +DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont) +{ + HRESULT hr = S_OK; + IDWriteTextFormat *pTextFormat = NULL; + + hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat); + + if (SUCCEEDED(hr)) { + SafeRelease(&mTextFormat); + mTextFormat = pTextFormat; mFontWeight = static_cast(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) + FontCache::Item item; + if (mFontCache.get(hFont, item)) { - LOGFONTW lf; - if (GetObjectW(hFont, sizeof(lf), &lf)) + if (item.pTextFormat != NULL) { - SetFont(lf); - mLastHFont = hFont; + item.pTextFormat->AddRef(); + SafeRelease(&mTextFormat); + mTextFormat = item.pTextFormat; + mFontWeight = item.fontWeight; + mFontStyle = item.fontStyle; + mFallbackDC = false; } + else + mFallbackDC = true; + return; } + + HRESULT hr = E_FAIL; + LOGFONTW lf; + if (GetObjectW(hFont, sizeof(lf), &lf)) + hr = SetFontByLOGFONT(lf); + + item.hFont = hFont; + if (SUCCEEDED(hr)) + { + item.pTextFormat = mTextFormat; + item.fontWeight = mFontWeight; + item.fontStyle = mFontStyle; + } + mFontCache.put(item); } void -DWriteContext::SetFont(const LOGFONTW &logFont) +DWriteContext::BindDC(HDC hdc, RECT *rect) { - 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); + Flush(); + mRT->BindDC(hdc, rect); + mRT->SetTransform(D2D1::IdentityMatrix()); + mHDC = hdc; } void -DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len, - int x, int y, int w, int h, int cellWidth, COLORREF color) +DWriteContext::AssureDrawing() { - HRESULT hr = S_OK; - IDWriteBitmapRenderTarget *bmpRT = NULL; + if (mDrawing == false) + { + mRT->BeginDraw(); + mDrawing = true; + } +} + + ID2D1Brush* +DWriteContext::SolidBrush(COLORREF color) +{ + mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 | + UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color)))); + return mBrush; +} - // Skip when any fonts are not set. - if (mTextFormat == NULL) + void +DWriteContext::DrawText(const WCHAR* text, int len, + int x, int y, int w, int h, int cellWidth, COLORREF color, + UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx) +{ + if (mFallbackDC) + { + Flush(); + ExtTextOutW(mHDC, x, y, fuOptions, lprc, text, len, lpDx); return; + } - // Check possibility of zero divided error. - if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f) - return; + AssureDrawing(); - if (SUCCEEDED(hr)) - hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT); + HRESULT hr; + IDWriteTextLayout *textLayout = NULL; + + hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat, + FLOAT(w), FLOAT(h), &textLayout); 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); - } + 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); + TextRenderer renderer(this); + TextRendererContext context = { color, FLOAT(cellWidth), 0.0f }; + textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y)); } - SafeRelease(&bmpRT); + SafeRelease(&textLayout); } - float -DWriteContext::PixelsToDipsX(int x) + void +DWriteContext::FillRect(RECT *rc, COLORREF color) { - return x / mDpiScaleX; + AssureDrawing(); + mRT->FillRectangle( + D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top), + FLOAT(rc->right), FLOAT(rc->bottom)), + SolidBrush(color)); } - float -DWriteContext::PixelsToDipsY(int y) + void +DWriteContext::Flush() { - return y / mDpiScaleY; + if (mDrawing) + { + mRT->EndDraw(); + mDrawing = false; + } } void @@ -757,6 +928,10 @@ DWriteContext::SetRenderingParams( SafeRelease(&mRenderingParams); mRenderingParams = renderingParams; mTextAntialiasMode = textAntialiasMode; + + Flush(); + mRT->SetTextRenderingParams(mRenderingParams); + mRT->SetTextAntialiasMode(mTextAntialiasMode); } } @@ -825,39 +1000,22 @@ DWriteContext_Open(void) } 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); - } + if (ctx != NULL) + ctx->BindDC(hdc, rect); } 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, @@ -865,20 +1023,28 @@ DWriteContext_DrawText( int w, int h, int cellWidth, - COLORREF color) + COLORREF color, + UINT fuOptions, + CONST RECT *lprc, + CONST INT * lpDx) { if (ctx != NULL) - ctx->DrawText(hdc, text, len, x, y, w, h, cellWidth, color); + ctx->DrawText(text, len, x, y, w, h, cellWidth, color, + fuOptions, lprc, lpDx); } void -DWriteContext_EndDraw(DWriteContext *ctx) +DWriteContext_FillRect(DWriteContext *ctx, RECT *rc, COLORREF color) { - if (ctx != NULL && ctx->mRT != NULL) - { - ctx->mRT->EndDraw(); - ctx->mDrawing = false; - } + if (ctx != NULL) + ctx->FillRect(rc, color); +} + + void +DWriteContext_Flush(DWriteContext *ctx) +{ + if (ctx != NULL) + ctx->Flush(); } void diff --git a/src/gui_dwrite.h b/src/gui_dwrite.h --- a/src/gui_dwrite.h +++ b/src/gui_dwrite.h @@ -4,6 +4,7 @@ * * Contributors: * - Ken Takata + * - Yasuhiro Matsumoto * * Copyright (C) 2013 MURAOKA Taro * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. @@ -54,12 +55,10 @@ void DWrite_Init(void); void DWrite_Final(void); DWriteContext *DWriteContext_Open(void); -void DWriteContext_BeginDraw(DWriteContext *ctx); void DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect); void DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont); void DWriteContext_DrawText( DWriteContext *ctx, - HDC hdc, const WCHAR* text, int len, int x, @@ -67,8 +66,12 @@ void DWriteContext_DrawText( int w, int h, int cellWidth, - COLORREF color); -void DWriteContext_EndDraw(DWriteContext *ctx); + COLORREF color, + UINT fuOptions, + CONST RECT *lprc, + CONST INT * lpDx); +void DWriteContext_FillRect(DWriteContext *ctx, RECT *rc, COLORREF color); +void DWriteContext_Flush(DWriteContext *ctx); void DWriteContext_Close(DWriteContext *ctx); void DWriteContext_SetRenderingParams( diff --git a/src/gui_w32.c b/src/gui_w32.c --- a/src/gui_w32.c +++ b/src/gui_w32.c @@ -34,28 +34,14 @@ static DWriteContext *s_dwc = NULL; static int s_directx_enabled = 0; static int s_directx_load_attempted = 0; # define IS_ENABLE_DIRECTX() (s_directx_enabled && s_dwc != NULL) +static int directx_enabled(void); +static void directx_binddc(void); #endif #ifdef FEAT_MENU static int gui_mswin_get_menu_height(int fix_window); #endif -#if defined(FEAT_DIRECTX) || defined(PROTO) - int -directx_enabled(void) -{ - if (s_dwc != NULL) - return 1; - else if (s_directx_load_attempted) - return 0; - /* load DirectX */ - DWrite_Init(); - s_directx_load_attempted = 1; - s_dwc = DWriteContext_Open(); - return s_dwc != NULL ? 1 : 0; -} -#endif - #if defined(FEAT_RENDER_OPTIONS) || defined(PROTO) int gui_mch_set_rendering_options(char_u *s) @@ -369,6 +355,34 @@ static int allow_scrollbar = FALSE; # define MyTranslateMessage(x) TranslateMessage(x) #endif +#if defined(FEAT_DIRECTX) + static int +directx_enabled(void) +{ + if (s_dwc != NULL) + return 1; + else if (s_directx_load_attempted) + return 0; + /* load DirectX */ + DWrite_Init(); + s_directx_load_attempted = 1; + s_dwc = DWriteContext_Open(); + directx_binddc(); + return s_dwc != NULL ? 1 : 0; +} + + static void +directx_binddc(void) +{ + if (s_textArea != NULL) + { + RECT rect; + GetClientRect(s_textArea, &rect); + DWriteContext_BindDC(s_dwc, s_hdc, &rect); + } +} +#endif + #if defined(FEAT_MBYTE) || defined(GLOBAL_IME) /* use of WindowProc depends on wide_WindowProc */ # define MyWindowProc vim_WindowProc @@ -589,6 +603,10 @@ gui_mch_set_blinking(long wait, long on, blink_timer = (UINT) SetTimer(NULL, 0, (UINT)blink_ontime, (TIMERPROC)_OnBlinkTimer); } +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif } static void @@ -1000,6 +1018,19 @@ char_to_string(int ch, char_u *string, i _OnMouseEvent(button, x, y, FALSE, keyFlags); } + static void +_OnSizeTextArea( + HWND hwnd UNUSED, + UINT state UNUSED, + int cx UNUSED, + int cy UNUSED) +{ +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + directx_binddc(); +#endif +} + #ifdef FEAT_MENU /* * Find the vimmenu_T with the given id @@ -1234,6 +1265,7 @@ HandleMouseHide(UINT uMsg, LPARAM lParam HANDLE_MSG(hwnd, WM_XBUTTONDBLCLK,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_XBUTTONDOWN,_OnMouseButtonDown); HANDLE_MSG(hwnd, WM_XBUTTONUP, _OnMouseMoveOrRelease); + HANDLE_MSG(hwnd, WM_SIZE, _OnSizeTextArea); #ifdef FEAT_BEVAL_GUI case WM_NOTIFY: Handle_WM_Notify(hwnd, (LPNMHDR)lParam); @@ -1633,6 +1665,11 @@ gui_mch_invert_rectangle( { RECT rc; +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + /* * Note: InvertRect() excludes right and bottom of rectangle. */ @@ -1661,6 +1698,11 @@ gui_mch_draw_hollow_cursor(guicolor_T co HBRUSH hbr; RECT rc; +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + /* * Note: FrameRect() excludes right and bottom of rectangle. */ @@ -1701,6 +1743,12 @@ gui_mch_draw_part_cursor( rc.top = FILL_Y(gui.row) + gui.char_height - h; rc.right = rc.left + w; rc.bottom = rc.top + h; + +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + hbr = CreateSolidBrush(color); FillRect(s_hdc, &rc, hbr); DeleteBrush(hbr); @@ -2856,10 +2904,6 @@ gui_mch_show_popupmenu_at(vimmenu_T *men out_flush(); /* make sure all output has been processed */ (void)BeginPaint(hwnd, &ps); -#if defined(FEAT_DIRECTX) - if (IS_ENABLE_DIRECTX()) - DWriteContext_BeginDraw(s_dwc); -#endif #ifdef FEAT_MBYTE /* prevent multi-byte characters from misprinting on an invalid @@ -2876,19 +2920,11 @@ gui_mch_show_popupmenu_at(vimmenu_T *men if (!IsRectEmpty(&ps.rcPaint)) { -#if defined(FEAT_DIRECTX) - if (IS_ENABLE_DIRECTX()) - DWriteContext_BindDC(s_dwc, s_hdc, &ps.rcPaint); -#endif gui_redraw(ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right - ps.rcPaint.left + 1, ps.rcPaint.bottom - ps.rcPaint.top + 1); } -#if defined(FEAT_DIRECTX) - if (IS_ENABLE_DIRECTX()) - DWriteContext_EndDraw(s_dwc); -#endif EndPaint(hwnd, &ps); } } @@ -3010,6 +3046,11 @@ gui_mch_flash(int msec) { RECT rc; +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + /* * Note: InvertRect() excludes right and bottom of rectangle. */ @@ -3082,6 +3123,12 @@ gui_mch_delete_lines( intel_gpu_workaround(); +#if defined(FEAT_DIRECTX) + // Commit drawing queue before ScrollWindowEx. + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + rc.left = FILL_X(gui.scroll_region_left); rc.right = FILL_X(gui.scroll_region_right + 1); rc.top = FILL_Y(row); @@ -3115,6 +3162,12 @@ gui_mch_insert_lines( intel_gpu_workaround(); +#if defined(FEAT_DIRECTX) + // Commit drawing queue before ScrollWindowEx. + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + rc.left = FILL_X(gui.scroll_region_left); rc.right = FILL_X(gui.scroll_region_right + 1); rc.top = FILL_Y(row); @@ -6145,9 +6198,6 @@ gui_mch_draw_string( #endif HPEN hpen, old_pen; int y; -#ifdef FEAT_DIRECTX - int font_is_ttf_or_vector = 0; -#endif /* * Italic and bold text seems to have an extra row of pixels at the bottom @@ -6208,6 +6258,11 @@ gui_mch_draw_string( hbr = hbr_cache[brush_lru]; brush_lru = !brush_lru; } + +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_FillRect(s_dwc, &rc, gui.currBgColor); +#endif FillRect(s_hdc, &rc, hbr); SetBkMode(s_hdc, TRANSPARENT); @@ -6227,16 +6282,7 @@ gui_mch_draw_string( #ifdef FEAT_DIRECTX if (IS_ENABLE_DIRECTX()) - { - TEXTMETRIC tm; - - GetTextMetrics(s_hdc, &tm); - if (tm.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_VECTOR)) - { - font_is_ttf_or_vector = 1; - DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont); - } - } + DWriteContext_SetFont(s_dwc, (HFONT)gui.currFont); #endif if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) @@ -6347,12 +6393,13 @@ gui_mch_draw_string( ++clen; } #if defined(FEAT_DIRECTX) - if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector) + if (IS_ENABLE_DIRECTX()) { /* Add one to "cells" for italics. */ - DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen, + DWriteContext_DrawText(s_dwc, unicodebuf, wlen, TEXT_X(col), TEXT_Y(row), FILL_X(cells + 1), FILL_Y(1), - gui.char_width, gui.currFgColor); + gui.char_width, gui.currFgColor, + foptions, pcliprect, unicodepdy); } else #endif @@ -6411,6 +6458,12 @@ gui_mch_draw_string( foptions, pcliprect, (char *)text, len, padding); } +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX() && + (flags & (DRAW_UNDERL | DRAW_STRIKE | DRAW_UNDERC | DRAW_CURSOR))) + DWriteContext_Flush(s_dwc); +#endif + /* Underline */ if (flags & DRAW_UNDERL) { @@ -6473,6 +6526,11 @@ gui_mch_flush(void) BOOL __stdcall GdiFlush(void); # endif +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + GdiFlush(); } @@ -6481,6 +6539,14 @@ clear_rect(RECT *rcp) { HBRUSH hbr; +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + { + DWriteContext_FillRect(s_dwc, rcp, gui.back_pixel); + return; + } +#endif + hbr = CreateSolidBrush(gui.back_pixel); FillRect(s_hdc, rcp, hbr); DeleteBrush(hbr); @@ -8386,6 +8452,11 @@ gui_mch_drawsign(int row, int col, int t if (!gui.in_use || (sign = (signicon_t *)sign_get_image(typenr)) == NULL) return; +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + x = TEXT_X(col); y = TEXT_Y(row); w = gui.char_width * 2; @@ -8865,6 +8936,11 @@ netbeans_draw_multisign_indicator(int ro x = 0; y = TEXT_Y(row); +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX()) + DWriteContext_Flush(s_dwc); +#endif + for (i = 0; i < gui.char_height - 3; i++) SetPixel(s_hdc, x+2, y++, gui.currFgColor); diff --git a/src/proto/gui_w32.pro b/src/proto/gui_w32.pro --- a/src/proto/gui_w32.pro +++ b/src/proto/gui_w32.pro @@ -1,5 +1,4 @@ /* gui_w32.c */ -int directx_enabled(void); int gui_mch_set_rendering_options(char_u *s); int gui_mch_is_blinking(void); int gui_mch_is_blink_off(void); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -772,6 +772,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1343, +/**/ 1342, /**/ 1341,