changeset 12934:2ebc3df65ca2 v8.0.1343

patch 8.0.1343: MS-Windows: does not show colored emojis commit https://github.com/vim/vim/commit/d7ccc4d81dbcfa3ac0352bacea6e294fc9e33fda Author: Bram Moolenaar <Bram@vim.org> 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)
author Christian Brabandt <cb@256bit.org>
date Sun, 26 Nov 2017 14:30:04 +0100
parents 9eeaf892510c
children e952feb15951
files appveyor.yml runtime/doc/options.txt src/gui_dwrite.cpp src/gui_dwrite.h src/gui_w32.c src/proto/gui_w32.pro src/version.c
diffstat 7 files changed, 571 insertions(+), 301 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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.
 
--- a/src/gui_dwrite.cpp
+++ b/src/gui_dwrite.cpp
@@ -4,6 +4,7 @@
  *
  * Contributors:
  *  - Ken Takata
+ *  - Yasuhiro Matsumoto
  *
  * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
  * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
@@ -23,7 +24,21 @@
 #include <math.h>
 #include <d2d1.h>
 #include <d2d1helper.h>
-#include <dwrite.h>
+
+// 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 <dwrite_2.h>
 
 #include "gui_dwrite.h"
 
@@ -79,16 +94,6 @@ template <class T> 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<D2D1_MATRIX_3X2_F*>(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<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,
@@ -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<TextRendererContext*>(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<void**>(&mD2D1Factory));
@@ -497,6 +616,15 @@ DWriteContext::DWriteContext() :
 
     if (SUCCEEDED(hr))
     {
+	DWriteCreateFactory(
+		DWRITE_FACTORY_TYPE_SHARED,
+		__uuidof(IDWriteFactory2),
+		reinterpret_cast<IUnknown**>(&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<float>(
-			fontMetrics.ascent + fontMetrics.descent)
-					       / fontMetrics.designUnitsPerEm;
+	    // 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;
-	    }
+	    // 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<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)
+    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
--- a/src/gui_dwrite.h
+++ b/src/gui_dwrite.h
@@ -4,6 +4,7 @@
  *
  * Contributors:
  *  - Ken Takata
+ *  - Yasuhiro Matsumoto
  *
  * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
  * 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(
--- 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);
 
--- 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);
--- 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,