# HG changeset patch # User Bram Moolenaar # Date 1407329550 -7200 # Node ID 1bff71d202621d97acc4ef21ce9f47b78f68bda3 # Parent e21d20c8500f092324a948244f4664cbe8a324ab updated for version 7.4.393 Problem: Text drawing on newer MS-Windows systems is suboptimal. Some multi-byte characters are not displayed, even though the same font in Notepad can display them. (Srinath Avadhanula) Solution: Add the 'renderoptions' option to enable Direct-X drawing. (Taro Muraoka) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -6622,6 +6622,7 @@ dialog_con Compiled with console dialog dialog_gui Compiled with GUI dialog support. diff Compiled with |vimdiff| and 'diff' support. digraphs Compiled with support for digraphs. +directx Compiled with support for Direct-X and 'renderoptions'. dnd Compiled with support for the "~ register |quote_~|. dos16 16 bits DOS version of Vim. dos32 32 bits DOS (DJGPP) version of Vim. diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -5650,6 +5650,77 @@ A jump table for the options with a shor this option at the default "on". Only switch it off when working with old Vi scripts. + *'renderoptions'* *'rop'* +'renderoptions' 'rop' string (default: empty) + global + {not in Vi} + {only available when compiled with GUI and DIRECTX on + MS-Windows} + Select a text renderer and set its options. The options depend on the + renderer. + + Syntax: > + set rop=type:{renderer}(,{name}:{value})* +< + Currently, only one optional renderer is available. + + render behavior ~ + directx Vim will draw text using DirectX (DirectWrite). It makes + drawn glyphs more beautiful than default GDI. + It requires 'encoding' is "utf-8", and only works on + MS-Windows Vista or newer version. + + Options: + name meaning type value ~ + gamma gamma float 1.0 - 2.2 (maybe) + contrast enhancedContrast float (unknown) + level clearTypeLevel float (unknown) + geom pixelGeometry int 0 - 2 (see below) + renmode renderingMode int 0 - 6 (see below) + taamode textAntialiasMode int 0 - 3 (see below) + + See this URL for detail: + http://msdn.microsoft.com/en-us/library/dd368190.aspx + + For geom: structure of a device pixel. + 0 - DWRITE_PIXEL_GEOMETRY_FLAT + 1 - DWRITE_PIXEL_GEOMETRY_RGB + 2 - DWRITE_PIXEL_GEOMETRY_BGR + + See this URL for detail: + http://msdn.microsoft.com/en-us/library/dd368114.aspx + + For renmode: method of rendering glyphs. + 0 - DWRITE_RENDERING_MODE_DEFAULT + 1 - DWRITE_RENDERING_MODE_ALIASED + 2 - DWRITE_RENDERING_MODE_GDI_CLASSIC + 3 - DWRITE_RENDERING_MODE_GDI_NATURAL + 4 - DWRITE_RENDERING_MODE_NATURAL + 5 - DWRITE_RENDERING_MODE_NATURAL_SYMMETRIC + 6 - DWRITE_RENDERING_MODE_OUTLINE + + See this URL for detail: + http://msdn.microsoft.com/en-us/library/dd368118.aspx + + For taamode: antialiasing mode used for drawing text. + 0 - D2D1_TEXT_ANTIALIAS_MODE_DEFAULT + 1 - D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE + 2 - D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE + 3 - D2D1_TEXT_ANTIALIAS_MODE_ALIASED + + See this URL for detail: + http://msdn.microsoft.com/en-us/library/dd368170.aspx + + Example: > + set encoding=utf-8 + set gfn=Ricty_Diminished:h12:cSHIFTJIS + set rop=type:directx +< + If select a raster font (Courier, Terminal or FixedSys) to + 'guifont', it fallbacks to be drawn by GDI automatically. + + Other render types are currently not supported. + *'report'* 'report' number (default 2) global diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -337,6 +337,7 @@ N *+dialog_con* Support for |:confirm| N *+dialog_con_gui* Support for |:confirm| with GUI and console dialog. N *+diff* |vimdiff| and 'diff' N *+digraphs* |digraphs| *E196* +m *+directx* Win32 GUI only: DirectX and |'renderoptions'| *+dnd* Support for DnD into the "~ register |quote_~|. B *+emacs_tags* |emacs-tags| files N *+eval* expression evaluation |eval.txt| diff --git a/src/Make_cyg.mak b/src/Make_cyg.mak --- a/src/Make_cyg.mak +++ b/src/Make_cyg.mak @@ -8,6 +8,7 @@ # Cygwin application use the Makefile (just like on Unix). # # GUI no or yes: set to yes if you want the GUI version (yes) +# DIRECTX no or yes: set to yes if you want use DirectWrite (no) # PERL define to path to Perl dir to get Perl support (not defined) # PERL_VER define to version of Perl being used (56) # DYNAMIC_PERL no or yes: set to yes to load the Perl DLL dynamically (yes) @@ -88,6 +89,10 @@ ifndef ARCH ARCH = i386 endif +ifndef DIRECTX +DIRECTX = no +endif + ifndef WINVER WINVER = 0x0500 endif @@ -470,6 +475,15 @@ endif endif ############################## +ifeq (yes, $(DIRECTX)) +# Only allow DIRECTX for a GUI build. +DEFINES += -DFEAT_DIRECTX -DDYNAMIC_DIRECTX +EXTRA_OBJS += $(OUTDIR)/gui_dwrite.o +EXTRA_LIBS += -ld2d1 -ldwrite +USE_STDCPLUS = yes +endif + +############################## ifdef XPM # Only allow XPM for a GUI build. DEFINES += -DFEAT_XPM_W32 @@ -495,11 +509,7 @@ ifeq (yes, $(OLE)) DEFINES += -DFEAT_OLE EXTRA_OBJS += $(OUTDIR)/if_ole.o EXTRA_LIBS += -loleaut32 -ifeq (yes, $(STATIC_STDCPLUS)) -EXTRA_LIBS += -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic -else -EXTRA_LIBS += -lstdc++ -endif +USE_STDCPLUS = yes endif ############################## @@ -513,6 +523,15 @@ MKDIR = mkdir DIRSLASH = \\ endif +############################## +ifeq (yes, $(USE_STDCPLUS)) +ifeq (yes, $(STATIC_STDCPLUS)) +EXTRA_LIBS += -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic +else +EXTRA_LIBS += -lstdc++ +endif +endif + #>>>>> end of choices ########################################################################### @@ -643,6 +662,9 @@ distclean: clean $(OUTDIR)/gui_w32.o: gui_w32.c gui_w48.c $(INCL) $(CC) -c $(CFLAGS) gui_w32.c -o $(OUTDIR)/gui_w32.o +$(OUTDIR)/gui_dwrite.o: gui_dwrite.cpp $(INCL) gui_dwrite.h + $(CC) -c $(CFLAGS) gui_dwrite.cpp -o $(OUTDIR)/gui_dwrite.o + $(OUTDIR)/if_cscope.o: if_cscope.c $(INCL) if_cscope.h $(CC) -c $(CFLAGS) if_cscope.c -o $(OUTDIR)/if_cscope.o diff --git a/src/Make_ming.mak b/src/Make_ming.mak --- a/src/Make_ming.mak +++ b/src/Make_ming.mak @@ -31,6 +31,8 @@ DEBUG=no OPTIMIZE=MAXSPEED # set to yes to make gvim, no for vim GUI=yes +# set to yes if you want to use DirectWrite (DirectX) +DIRECTX=no # FEATURES=[TINY | SMALL | NORMAL | BIG | HUGE] # Set to TINY to make minimal version (few features). FEATURES=BIG @@ -456,6 +458,14 @@ endif endif endif +# DirectWrite (DirectX) +ifeq ($(DIRECTX),yes) +# Only allow DirectWrite for a GUI build. +ifeq (yes, $(GUI)) +DEFINES += -DFEAT_DIRECTX -DDYNAMIC_DIRECTX +endif +endif + # Only allow XPM for a GUI build. ifeq (yes, $(GUI)) @@ -593,6 +603,14 @@ OBJ += $(OUTDIR)/netbeans.o LIB += -lwsock32 endif endif +ifeq ($(DIRECTX),yes) +# Only allow DIRECTX for a GUI build. +ifeq (yes, $(GUI)) +OBJ += $(OUTDIR)/gui_dwrite.o +LIB += -ld2d1 -ldwrite +USE_STDCPLUS = yes +endif +endif ifdef XPM # Only allow XPM for a GUI build. ifeq (yes, $(GUI)) @@ -650,11 +668,7 @@ endif ifeq (yes, $(OLE)) LIB += -loleaut32 OBJ += $(OUTDIR)/if_ole.o -ifeq (yes, $(STATIC_STDCPLUS)) -LIB += -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic -else -LIB += -lstdc++ -endif +USE_STDCPLUS = yes endif ifeq (yes, $(MBYTE)) @@ -678,6 +692,14 @@ endif DEFINES+=-DDYNAMIC_ICONV endif +ifeq (yes, $(USE_STDCPLUS)) +ifeq (yes, $(STATIC_STDCPLUS)) +LIB += -Wl,-Bstatic -lstdc++ -Wl,-Bdynamic +else +LIB += -lstdc++ +endif +endif + all: $(TARGET) vimrun.exe xxd/xxd.exe install.exe uninstal.exe GvimExt/gvimext.dll vimrun.exe: vimrun.c @@ -751,6 +773,9 @@ INCL = vim.h feature.h os_win32.h os_dos $(OUTDIR)/gui_w32.o: gui_w32.c gui_w48.c $(INCL) $(CC) -c $(CFLAGS) gui_w32.c -o $(OUTDIR)/gui_w32.o +$(OUTDIR)/gui_dwrite.o: gui_dwrite.cpp $(INCL) gui_dwrite.h + $(CC) -c $(CFLAGS) gui_dwrite.cpp -o $(OUTDIR)/gui_dwrite.o + $(OUTDIR)/if_cscope.o: if_cscope.c $(INCL) if_cscope.h $(CC) -c $(CFLAGS) if_cscope.c -o $(OUTDIR)/if_cscope.o diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -24,6 +24,9 @@ # # GUI interface: GUI=yes (default is no) # +# GUI with DirectWrite(DirectX): DIRECTX=yes +# (default is no, requires GUI=yes) +# # OLE interface: OLE=yes (usually with GUI=yes) # # Multibyte support: MBYTE=yes (default is no) @@ -168,6 +171,9 @@ OBJDIR = .\ObjG !else OBJDIR = .\ObjC !endif +!if "$(DIRECTX)" == "yes" +OBJDIR = $(OBJDIR)X +!endif !if "$(OLE)" == "yes" OBJDIR = $(OBJDIR)O !endif @@ -292,6 +298,13 @@ NBDEBUG_SRC = nbdebug.c NETBEANS_LIB = WSock32.lib !endif +# DirectWrite(DirectX) +!if "$(DIRECTX)" == "yes" +DIRECTX_DEFS = -DFEAT_DIRECTX -DDYNAMIC_DIRECTX +DIRECTX_INCL = gui_dwrite.h +DIRECTX_OBJ = $(OUTDIR)\gui_dwrite.obj +!endif + !ifndef XPM # XPM is not set, use the included xpm files, depending on the architecture. !if "$(CPU)" == "AMD64" @@ -642,6 +655,12 @@ GUI_LIB = \ SUBSYSTEM = console !endif +!if "$(GUI)" == "yes" && "$(DIRECTX)" == "yes" +CFLAGS = $(CFLAGS) $(DIRECTX_DEFS) +GUI_INCL = $(GUI_INCL) $(DIRECTX_INCL) +GUI_OBJ = $(GUI_OBJ) $(DIRECTX_OBJ) +!endif + # iconv.dll library (dynamically loaded) !ifndef ICONV ICONV = yes @@ -1107,6 +1126,8 @@ testclean: $(OUTDIR)/gui_w32.obj: $(OUTDIR) gui_w32.c gui_w48.c $(INCL) $(GUI_INCL) +$(OUTDIR)/gui_dwrite.obj: $(OUTDIR) gui_dwrite.cpp $(INCL) $(GUI_INCL) + $(OUTDIR)/if_cscope.obj: $(OUTDIR) if_cscope.c $(INCL) $(OUTDIR)/if_lua.obj: $(OUTDIR) if_lua.c $(INCL) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -12464,6 +12464,9 @@ f_has(argvars, rettv) #ifdef FEAT_DIGRAPHS "digraphs", #endif +#ifdef FEAT_DIRECTX + "directx", +#endif #ifdef FEAT_DND "dnd", #endif diff --git a/src/gui_dwrite.cpp b/src/gui_dwrite.cpp new file mode 100644 --- /dev/null +++ b/src/gui_dwrite.cpp @@ -0,0 +1,901 @@ +/* vi:set ts=8 sts=4 sw=4 noet: */ +/* + * Author: MURAOKA Taro + * + * Contributors: + * - Ken Takata + * + * Copyright (C) 2013 MURAOKA Taro + * 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 +#include +#include +#include +#include +#include +#include + +#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 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(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: + unsigned 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(&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(&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( + 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(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, 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("d2d1.dll")); + hDWriteDLL = vimLoadLib(const_cast("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; +} diff --git a/src/gui_dwrite.h b/src/gui_dwrite.h new file mode 100644 --- /dev/null +++ b/src/gui_dwrite.h @@ -0,0 +1,85 @@ +/* vi:set ts=8 sts=4 sw=4 noet: */ +/* + * Author: MURAOKA Taro + * + * Contributors: + * - Ken Takata + * + * Copyright (C) 2013 MURAOKA Taro + * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. + */ + +#ifndef GUI_DWRITE_H +#define GUI_DWRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DWriteContext DWriteContext; + +typedef struct DWriteRenderingParams { + float gamma; + float enhancedContrast; + float clearTypeLevel; + /* + * pixelGeometry: + * 0 - DWRITE_PIXEL_GEOMETRY_FLAT + * 1 - DWRITE_PIXEL_GEOMETRY_RGB + * 2 - DWRITE_PIXEL_GEOMETRY_BGR + */ + int pixelGeometry; + /* + * renderingMode: + * 0 - DWRITE_RENDERING_MODE_DEFAULT + * 1 - DWRITE_RENDERING_MODE_ALIASED + * 2 - DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC + * 3 - DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL + * 4 - DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL + * 5 - DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC + * 6 - DWRITE_RENDERING_MODE_OUTLINE + */ + int renderingMode; + /* + * antialiasMode: + * 0 - D2D1_TEXT_ANTIALIAS_MODE_DEFAULT + * 1 - D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE + * 2 - D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE + * 3 - D2D1_TEXT_ANTIALIAS_MODE_ALIASED + */ + int textAntialiasMode; +} DWriteRenderingParams; + +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, + int y, + int w, + int h, + int cellWidth, + COLORREF color); +void DWriteContext_EndDraw(DWriteContext *ctx); +void DWriteContext_Close(DWriteContext *ctx); + +void DWriteContext_SetRenderingParams( + DWriteContext *ctx, + const DWriteRenderingParams *params); + +DWriteRenderingParams *DWriteContext_GetRenderingParams( + DWriteContext *ctx, + DWriteRenderingParams *params); + +#ifdef __cplusplus +} +#endif +#endif/*GUI_DWRITE_H*/ diff --git a/src/gui_w32.c b/src/gui_w32.c --- a/src/gui_w32.c +++ b/src/gui_w32.c @@ -25,6 +25,145 @@ #include "vim.h" +#if defined(FEAT_DIRECTX) +# include "gui_dwrite.h" +#endif + +#if defined(FEAT_DIRECTX) || defined(PROTO) +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) + + 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) +{ +#ifdef FEAT_DIRECTX + int retval = FAIL; + char_u *p, *q; + + int dx_enable = 0; + int dx_flags = 0; + float dx_gamma = 0.0f; + float dx_contrast = 0.0f; + float dx_level = 0.0f; + int dx_geom = 0; + int dx_renmode = 0; + int dx_taamode = 0; + + /* parse string as rendering options. */ + for (p = s; p != NULL && *p != NUL; ) + { + char_u item[256]; + char_u name[128]; + char_u value[128]; + + copy_option_part(&p, item, sizeof(item), ","); + if (p == NULL) + break; + q = &item[0]; + copy_option_part(&q, name, sizeof(name), ":"); + if (q == NULL) + return FAIL; + copy_option_part(&q, value, sizeof(value), ":"); + + if (STRCMP(name, "type") == 0) + { + if (STRCMP(value, "directx") == 0) + dx_enable = 1; + else + return FAIL; + } + else if (STRCMP(name, "gamma") == 0) + { + dx_flags |= 1 << 0; + dx_gamma = (float)atof(value); + } + else if (STRCMP(name, "contrast") == 0) + { + dx_flags |= 1 << 1; + dx_contrast = (float)atof(value); + } + else if (STRCMP(name, "level") == 0) + { + dx_flags |= 1 << 2; + dx_level = (float)atof(value); + } + else if (STRCMP(name, "geom") == 0) + { + dx_flags |= 1 << 3; + dx_geom = atoi(value); + if (dx_geom < 0 || dx_geom > 2) + return FAIL; + } + else if (STRCMP(name, "renmode") == 0) + { + dx_flags |= 1 << 4; + dx_renmode = atoi(value); + if (dx_renmode < 0 || dx_renmode > 6) + return FAIL; + } + else if (STRCMP(name, "taamode") == 0) + { + dx_flags |= 1 << 5; + dx_taamode = atoi(value); + if (dx_taamode < 0 || dx_taamode > 3) + return FAIL; + } + else + return FAIL; + } + + /* Enable DirectX/DirectWrite */ + if (dx_enable) + { + if (!directx_enabled()) + return FAIL; + DWriteContext_SetRenderingParams(s_dwc, NULL); + if (dx_flags) + { + DWriteRenderingParams param; + DWriteContext_GetRenderingParams(s_dwc, ¶m); + if (dx_flags & (1 << 0)) + param.gamma = dx_gamma; + if (dx_flags & (1 << 1)) + param.enhancedContrast = dx_contrast; + if (dx_flags & (1 << 2)) + param.clearTypeLevel = dx_level; + if (dx_flags & (1 << 3)) + param.pixelGeometry = dx_geom; + if (dx_flags & (1 << 4)) + param.renderingMode = dx_renmode; + if (dx_flags & (1 << 5)) + param.textAntialiasMode = dx_taamode; + DWriteContext_SetRenderingParams(s_dwc, ¶m); + } + } + s_directx_enabled = dx_enable; + + return OK; +#else + return FAIL; +#endif +} +#endif + /* * These are new in Windows ME/XP, only defined in recent compilers. */ @@ -1624,6 +1763,11 @@ gui_mch_init(void) set_vim_var_nr(VV_WINDOWID, HandleToLong(s_hwnd)); #endif +#ifdef FEAT_RENDER_OPTIONS + if (p_rop) + (void)gui_mch_set_rendering_options(p_rop); +#endif + theend: /* Display any pending error messages */ display_errors(); @@ -1695,9 +1839,9 @@ gui_mch_set_shellsize(int width, int hei /* compute the size of the outside of the window */ win_width = width + (GetSystemMetrics(SM_CXFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; + GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; win_height = height + (GetSystemMetrics(SM_CYFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 + GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 + GetSystemMetrics(SM_CYCAPTION) #ifdef FEAT_MENU + gui_mswin_get_menu_height(FALSE) @@ -2239,6 +2383,9 @@ gui_mch_draw_string( #endif HPEN hpen, old_pen; int y; +#ifdef FEAT_DIRECTX + int font_is_ttf_or_vector = 0; +#endif #ifndef MSWIN16_FASTTEXT /* @@ -2326,6 +2473,20 @@ gui_mch_draw_string( SetTextColor(s_hdc, gui.currFgColor); SelectFont(s_hdc, gui.currFont); +#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); + } + } +#endif + if (pad_size != Columns || padding == NULL || padding[0] != gui.char_width) { vim_free(padding); @@ -2360,6 +2521,14 @@ gui_mch_draw_string( if (text[n] >= 0x80) break; +#if defined(FEAT_DIRECTX) + /* Quick hack to enable DirectWrite. To use DirectWrite (antialias), it is + * required that unicode drawing routine, currently. So this forces it + * enabled. */ + if (enc_utf8 && IS_ENABLE_DIRECTX()) + n = 0; /* Keep n < len, to enter block for unicode. */ +#endif + /* Check if the Unicode buffer exists and is big enough. Create it * with the same length as the multi-byte string, the number of wide * characters is always equal or smaller. */ @@ -2418,8 +2587,17 @@ gui_mch_draw_string( i += utfc_ptr2len_len(text + i, len - i); ++clen; } - ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row), - foptions, pcliprect, unicodebuf, wlen, unicodepdy); +#if defined(FEAT_DIRECTX) + if (IS_ENABLE_DIRECTX() && font_is_ttf_or_vector) + { + DWriteContext_DrawText(s_dwc, s_hdc, unicodebuf, wlen, + TEXT_X(col), TEXT_Y(row), FILL_X(cells), FILL_Y(1), + gui.char_width, gui.currFgColor); + } + else +#endif + ExtTextOutW(s_hdc, TEXT_X(col), TEXT_Y(row), + foptions, pcliprect, unicodebuf, wlen, unicodepdy); len = cells; /* used for underlining */ } else if ((enc_codepage > 0 && (int)GetACP() != enc_codepage) || enc_latin9) @@ -2549,14 +2727,14 @@ gui_mch_get_screen_dimensions(int *scree *screen_w = workarea_rect.right - workarea_rect.left - (GetSystemMetrics(SM_CXFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; + GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; /* FIXME: dirty trick: Because the gui_get_base_height() doesn't include * the menubar for MSwin, we subtract it from the screen height, so that * the window size can be made to fit on the screen. */ *screen_h = workarea_rect.bottom - workarea_rect.top - (GetSystemMetrics(SM_CYFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 + GetSystemMetrics(SM_CXPADDEDBORDER)) * 2 - GetSystemMetrics(SM_CYCAPTION) #ifdef FEAT_MENU - gui_mswin_get_menu_height(FALSE) @@ -3188,13 +3366,13 @@ gui_mch_dialog( GetWindowRect(s_hwnd, &rect); maxDialogWidth = rect.right - rect.left - (GetSystemMetrics(SM_CXFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; + GetSystemMetrics(SM_CXPADDEDBORDER)) * 2; if (maxDialogWidth < DLG_MIN_MAX_WIDTH) maxDialogWidth = DLG_MIN_MAX_WIDTH; maxDialogHeight = rect.bottom - rect.top - (GetSystemMetrics(SM_CYFRAME) + - GetSystemMetrics(SM_CXPADDEDBORDER)) * 4 + GetSystemMetrics(SM_CXPADDEDBORDER)) * 4 - GetSystemMetrics(SM_CYCAPTION); if (maxDialogHeight < DLG_MIN_MAX_HEIGHT) maxDialogHeight = DLG_MIN_MAX_HEIGHT; @@ -3351,11 +3529,11 @@ gui_mch_dialog( /* Restrict the size to a maximum. Causes a scrollbar to show up. */ if (dlgheight > maxDialogHeight) { - msgheight = msgheight - (dlgheight - maxDialogHeight); - dlgheight = maxDialogHeight; - scroll_flag = WS_VSCROLL; - /* Make sure scrollbar doesn't appear in the middle of the dialog */ - messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX; + msgheight = msgheight - (dlgheight - maxDialogHeight); + dlgheight = maxDialogHeight; + scroll_flag = WS_VSCROLL; + /* Make sure scrollbar doesn't appear in the middle of the dialog */ + messageWidth = dlgwidth - DLG_ICON_WIDTH - 3 * dlgPaddingX; } add_word(PixelToDialogY(dlgheight)); diff --git a/src/gui_w48.c b/src/gui_w48.c --- a/src/gui_w48.c +++ b/src/gui_w48.c @@ -2785,6 +2785,10 @@ 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 @@ -2800,9 +2804,20 @@ gui_mch_show_popupmenu_at(vimmenu_T *men #endif 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); } } @@ -3043,6 +3058,12 @@ gui_mch_insert_lines( void gui_mch_exit(int rc) { +#if defined(FEAT_DIRECTX) + DWriteContext_Close(s_dwc); + DWrite_Final(); + s_dwc = NULL; +#endif + ReleaseDC(s_textArea, s_hdc); DeleteObject(s_brush); diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -2124,6 +2124,15 @@ static struct vimoption {"remap", NULL, P_BOOL|P_VI_DEF, (char_u *)&p_remap, PV_NONE, {(char_u *)TRUE, (char_u *)0L} SCRIPTID_INIT}, + {"renderoptions", "rop", P_STRING|P_COMMA|P_RCLR|P_VI_DEF, +#ifdef FEAT_RENDER_OPTIONS + (char_u *)&p_rop, PV_NONE, + {(char_u *)"", (char_u *)0L} +#else + (char_u *)NULL, PV_NONE, + {(char_u *)NULL, (char_u *)0L} +#endif + SCRIPTID_INIT}, {"report", NULL, P_NUM|P_VI_DEF, (char_u *)&p_report, PV_NONE, {(char_u *)2L, (char_u *)0L} SCRIPTID_INIT}, @@ -6999,6 +7008,14 @@ did_set_string_option(opt_idx, varp, new } #endif +#if defined(FEAT_RENDER_OPTIONS) + else if (varp == &p_rop && gui.in_use) + { + if (!gui_mch_set_rendering_options(p_rop)) + errmsg = e_invarg; + } +#endif + /* Options that are a list of flags. */ else { diff --git a/src/option.h b/src/option.h --- a/src/option.h +++ b/src/option.h @@ -655,6 +655,9 @@ EXTERN long p_rdt; /* 'redrawtime' */ #endif EXTERN int p_remap; /* 'remap' */ EXTERN long p_re; /* 'regexpengine' */ +#ifdef FEAT_RENDER_OPTIONS +EXTERN char_u *p_rop; /* 'renderoptions' */ +#endif EXTERN long p_report; /* 'report' */ #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX) EXTERN long p_pvh; /* 'previewheight' */ 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,4 +1,6 @@ /* gui_w32.c */ +int directx_enabled __ARGS((void)); +int gui_mch_set_rendering_options __ARGS((char_u *s)); void gui_mch_set_blinking __ARGS((long wait, long on, long off)); void gui_mch_stop_blink __ARGS((void)); void gui_mch_start_blink __ARGS((void)); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -189,6 +189,13 @@ static char *(features[]) = #else "-digraphs", #endif +#ifdef FEAT_GUI_W32 +# ifdef FEAT_DIRECTX + "+directx", +# else + "-directx", +# endif +#endif #ifdef FEAT_DND "+dnd", #else @@ -735,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 393, +/**/ 392, /**/ 391, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -134,6 +134,13 @@ # endif #endif +/* Check support for rendering options */ +#ifdef FEAT_GUI +# if defined(FEAT_DIRECTX) +# define FEAT_RENDER_OPTIONS +# endif +#endif + /* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */ #if _MSC_VER >= 1400 # define _CRT_SECURE_NO_DEPRECATE