text: /* vi:set ts=8 sts=4 sw=4 noet: */
  /*
   * Author: MURAOKA Taro <koron.kaoriya@gmail.com>
   *
   * Contributors:
   *  - Ken Takata
   *
   * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
   * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
   */
  
  #define WIN32_LEAN_AND_MEAN
  
  #ifndef DYNAMIC_DIRECTX
  # if WINVER < 0x0600
  #  error WINVER must be 0x0600 or above to use DirectWrite(DirectX)
  # endif
  #endif
  
  #include <windows.h>
  #include <crtdbg.h>
  #include <assert.h>
  #include <math.h>
  #include <d2d1.h>
  #include <d2d1helper.h>
  #include <dwrite.h>
  
  #include "gui_dwrite.h"
  
  #ifdef __MINGW32__
  # define __maybenull	SAL__maybenull
  # define __in		SAL__in
  # define __out		SAL__out
  #endif
  
  #ifdef DYNAMIC_DIRECTX
  extern "C" HINSTANCE vimLoadLib(char *name);
  
  typedef int (WINAPI *PGETUSERDEFAULTLOCALENAME)(LPWSTR, int);
  typedef HRESULT (WINAPI *PD2D1CREATEFACTORY)(D2D1_FACTORY_TYPE,
  	REFIID, const D2D1_FACTORY_OPTIONS *, void **);
  typedef HRESULT (WINAPI *PDWRITECREATEFACTORY)(DWRITE_FACTORY_TYPE,
  	REFIID, IUnknown **);
  
  static HINSTANCE hD2D1DLL = NULL;
  static HINSTANCE hDWriteDLL = NULL;
  
  static PGETUSERDEFAULTLOCALENAME pGetUserDefaultLocaleName = NULL;
  static PD2D1CREATEFACTORY pD2D1CreateFactory = NULL;
  static PDWRITECREATEFACTORY pDWriteCreateFactory = NULL;
  
  #define GetUserDefaultLocaleName	(*pGetUserDefaultLocaleName)
  #define D2D1CreateFactory		(*pD2D1CreateFactory)
  #define DWriteCreateFactory		(*pDWriteCreateFactory)
  
      static void
  unload(HINSTANCE &hinst)
  {
      if (hinst != NULL)
      {
  	FreeLibrary(hinst);
  	hinst = NULL;
      }
  }
  #endif // DYNAMIC_DIRECTX
  
  template <class T> inline void SafeRelease(T **ppT)
  {
      if (*ppT)
      {
  	(*ppT)->Release();
  	*ppT = NULL;
      }
  }
  
  struct GdiTextRendererContext
  {
      // const fields.
      COLORREF color;
      FLOAT cellWidth;
  
      // working fields.
      FLOAT offsetX;
  };
  
      static DWRITE_PIXEL_GEOMETRY
  ToPixelGeometry(int value)
  {
      switch (value)
      {
  	default:
  	case 0:
  	    return DWRITE_PIXEL_GEOMETRY_FLAT;
  	case 1:
  	    return DWRITE_PIXEL_GEOMETRY_RGB;
  	case 2:
  	    return DWRITE_PIXEL_GEOMETRY_BGR;
      }
  }
  
      static int
  ToInt(DWRITE_PIXEL_GEOMETRY value)
  {
      switch (value)
      {
  	case DWRITE_PIXEL_GEOMETRY_FLAT:
  	    return 0;
  	case DWRITE_PIXEL_GEOMETRY_RGB:
  	    return 1;
  	case DWRITE_PIXEL_GEOMETRY_BGR:
  	    return 2;
  	default:
  	    return -1;
      }
  }
  
      static DWRITE_RENDERING_MODE
  ToRenderingMode(int value)
  {
      switch (value)
      {
  	default:
  	case 0:
  	    return DWRITE_RENDERING_MODE_DEFAULT;
  	case 1:
  	    return DWRITE_RENDERING_MODE_ALIASED;
  	case 2:
  	    return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC;
  	case 3:
  	    return DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL;
  	case 4:
  	    return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
  	case 5:
  	    return DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
  	case 6:
  	    return DWRITE_RENDERING_MODE_OUTLINE;
      }
  }
  
      static D2D1_TEXT_ANTIALIAS_MODE
  ToTextAntialiasMode(int value)
  {
      switch (value)
      {
  	default:
  	case 0:
  	    return D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
  	case 1:
  	    return D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
  	case 2:
  	    return D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
  	case 3:
  	    return D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
      }
  }
  
      static int
  ToInt(DWRITE_RENDERING_MODE value)
  {
      switch (value)
      {
  	case DWRITE_RENDERING_MODE_DEFAULT:
  	    return 0;
  	case DWRITE_RENDERING_MODE_ALIASED:
  	    return 1;
  	case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
  	    return 2;
  	case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
  	    return 3;
  	case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL:
  	    return 4;
  	case DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC:
  	    return 5;
  	case DWRITE_RENDERING_MODE_OUTLINE:
  	    return 6;
  	default:
  	    return -1;
      }
  }
  
  class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
  {
  private:
      FLOAT mDelta;
      FLOAT *mAdjustedAdvances;
  
  public:
      AdjustedGlyphRun(
  	    const DWRITE_GLYPH_RUN *glyphRun,
  	    FLOAT cellWidth) :
  	DWRITE_GLYPH_RUN(*glyphRun),
  	mDelta(0.0f),
  	mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
      {
  	assert(cellWidth != 0.0f);
  	for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
  	{
  	    FLOAT orig = glyphRun->glyphAdvances[i];
  	    FLOAT adjusted = adjustToCell(orig, cellWidth);
  	    mAdjustedAdvances[i] = adjusted;
  	    mDelta += adjusted - orig;
  	}
  	glyphAdvances = mAdjustedAdvances;
      }
  
      ~AdjustedGlyphRun(void)
      {
  	delete[] mAdjustedAdvances;
      }
  
      FLOAT getDelta(void) const
      {
  	return mDelta;
      }
  
      static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
      {
  	int cellCount = (int)floor(value / cellWidth + 0.5f);
  	if (cellCount < 1)
  	    cellCount = 1;
  	return cellCount * cellWidth;
      }
  };
  
  class GdiTextRenderer : public IDWriteTextRenderer
  {
  public:
      GdiTextRenderer(
  	    IDWriteBitmapRenderTarget* bitmapRenderTarget,
  	    IDWriteRenderingParams* renderingParams) :
  	cRefCount_(0),
  	pRenderTarget_(bitmapRenderTarget),
  	pRenderingParams_(renderingParams)
      {
  	pRenderTarget_->AddRef();
  	pRenderingParams_->AddRef();
  	AddRef();
      }
  
      ~GdiTextRenderer()
      {
  	SafeRelease(&pRenderTarget_);
  	SafeRelease(&pRenderingParams_);
      }
  
      IFACEMETHOD(IsPixelSnappingDisabled)(
  	__maybenull void* clientDrawingContext,
  	__out BOOL* isDisabled)
      {
  	*isDisabled = FALSE;
  	return S_OK;
      }
  
      IFACEMETHOD(GetCurrentTransform)(
  	__maybenull void* clientDrawingContext,
  	__out DWRITE_MATRIX* transform)
      {
  	//forward the render target's transform
  	pRenderTarget_->GetCurrentTransform(transform);
  	return S_OK;
      }
  
      IFACEMETHOD(GetPixelsPerDip)(
  	__maybenull void* clientDrawingContext,
  	__out FLOAT* pixelsPerDip)
      {
  	*pixelsPerDip = pRenderTarget_->GetPixelsPerDip();
  	return S_OK;
      }
  
      IFACEMETHOD(DrawGlyphRun)(
  	__maybenull void* clientDrawingContext,
  	FLOAT baselineOriginX,
  	FLOAT baselineOriginY,
  	DWRITE_MEASURING_MODE measuringMode,
  	__in DWRITE_GLYPH_RUN const* glyphRun,
  	__in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
  	IUnknown* clientDrawingEffect)
      {
  	HRESULT hr = S_OK;
  
  	GdiTextRendererContext *context =
  	    reinterpret_cast<GdiTextRendererContext*>(clientDrawingContext);
  
  	AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth);
  
  	// Pass on the drawing call to the render target to do the real work.
  	RECT dirtyRect = {0};
  
  	hr = pRenderTarget_->DrawGlyphRun(
  		baselineOriginX + context->offsetX,
  		baselineOriginY,
  		measuringMode,
  		&adjustedGlyphRun,
  		pRenderingParams_,
  		context->color,
  		&dirtyRect);
  
  	context->offsetX += adjustedGlyphRun.getDelta();
  
  	return hr;
      }
  
      IFACEMETHOD(DrawUnderline)(
  	__maybenull void* clientDrawingContext,
  	FLOAT baselineOriginX,
  	FLOAT baselineOriginY,
  	__in DWRITE_UNDERLINE const* underline,
  	IUnknown* clientDrawingEffect)
      {
  	return E_NOTIMPL;
      }
  
      IFACEMETHOD(DrawStrikethrough)(
  	__maybenull void* clientDrawingContext,
  	FLOAT baselineOriginX,
  	FLOAT baselineOriginY,
  	__in DWRITE_STRIKETHROUGH const* strikethrough,
  	IUnknown* clientDrawingEffect)
      {
  	return E_NOTIMPL;
      }
  
      IFACEMETHOD(DrawInlineObject)(
  	__maybenull void* clientDrawingContext,
  	FLOAT originX,
  	FLOAT originY,
  	IDWriteInlineObject* inlineObject,
  	BOOL isSideways,
  	BOOL isRightToLeft,
  	IUnknown* clientDrawingEffect)
      {
  	return E_NOTIMPL;
      }
  
  public:
      IFACEMETHOD_(unsigned long, AddRef) ()
      {
  	return InterlockedIncrement(&cRefCount_);
      }
  
      IFACEMETHOD_(unsigned long, Release) ()
      {
  	long newCount = InterlockedDecrement(&cRefCount_);
  
  	if (newCount == 0)
  	{
  	    delete this;
  	    return 0;
  	}
  	return newCount;
      }
  
      IFACEMETHOD(QueryInterface)(
  	IID const& riid,
  	void** ppvObject)
      {
  	if (__uuidof(IDWriteTextRenderer) == riid)
  	{
  	    *ppvObject = this;
  	}
  	else if (__uuidof(IDWritePixelSnapping) == riid)
  	{
  	    *ppvObject = this;
  	}
  	else if (__uuidof(IUnknown) == riid)
  	{
  	    *ppvObject = this;
  	}
  	else
  	{
  	    *ppvObject = NULL;
  	    return E_FAIL;
  	}
  
  	return S_OK;
      }
  
  private:
      long cRefCount_;
      IDWriteBitmapRenderTarget* pRenderTarget_;
      IDWriteRenderingParams* pRenderingParams_;
  };
  
  struct DWriteContext {
      FLOAT mDpiScaleX;
      FLOAT mDpiScaleY;
      bool mDrawing;
  
      ID2D1Factory *mD2D1Factory;
  
      ID2D1DCRenderTarget *mRT;
      ID2D1SolidColorBrush *mBrush;
  
      IDWriteFactory *mDWriteFactory;
      IDWriteGdiInterop *mGdiInterop;
      IDWriteRenderingParams *mRenderingParams;
      IDWriteTextFormat *mTextFormat;
  
      HFONT mLastHFont;
      DWRITE_FONT_WEIGHT mFontWeight;
      DWRITE_FONT_STYLE mFontStyle;
  
      D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
  
      // METHODS
  
      DWriteContext();
  
      virtual ~DWriteContext();
  
      HRESULT SetLOGFONT(const LOGFONTW &logFont, float fontSize);
  
      void SetFont(HFONT hFont);
  
      void SetFont(const LOGFONTW &logFont);
  
      void DrawText(HDC hdc, const WCHAR* text, int len,
  	int x, int y, int w, int h, int cellWidth, COLORREF color);
  
      float PixelsToDipsX(int x);
  
      float PixelsToDipsY(int y);
  
      void SetRenderingParams(
  	    const DWriteRenderingParams *params);
  
      DWriteRenderingParams *GetRenderingParams(
  	    DWriteRenderingParams *params);
  };
  
  DWriteContext::DWriteContext() :
      mDpiScaleX(1.f),
      mDpiScaleY(1.f),
      mDrawing(false),
      mD2D1Factory(NULL),
      mRT(NULL),
      mBrush(NULL),
      mDWriteFactory(NULL),
      mGdiInterop(NULL),
      mRenderingParams(NULL),
      mTextFormat(NULL),
      mLastHFont(NULL),
      mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
      mFontStyle(DWRITE_FONT_STYLE_NORMAL),
      mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
  {
      HRESULT hr;
  
      HDC screen = ::GetDC(0);
      mDpiScaleX = ::GetDeviceCaps(screen, LOGPIXELSX) / 96.0f;
      mDpiScaleY = ::GetDeviceCaps(screen, LOGPIXELSY) / 96.0f;
      ::ReleaseDC(0, screen);
  
      hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
  	    __uuidof(ID2D1Factory), NULL,
  	    reinterpret_cast<void**>(&mD2D1Factory));
      _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
  
      if (SUCCEEDED(hr))
      {
  	D2D1_RENDER_TARGET_PROPERTIES props = {
  	    D2D1_RENDER_TARGET_TYPE_DEFAULT,
  	    { DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE },
  	    0, 0,
  	    D2D1_RENDER_TARGET_USAGE_NONE,
  	    D2D1_FEATURE_LEVEL_DEFAULT
  	};
  	hr = mD2D1Factory->CreateDCRenderTarget(&props, &mRT);
  	_RPT2(_CRT_WARN, "CreateDCRenderTarget: hr=%p p=%p\n", hr, mRT);
      }
  
      if (SUCCEEDED(hr))
      {
  	hr = mRT->CreateSolidColorBrush(
  		D2D1::ColorF(D2D1::ColorF::Black),
  		&mBrush);
  	_RPT2(_CRT_WARN, "CreateSolidColorBrush: hr=%p p=%p\n", hr, mBrush);
      }
  
      if (SUCCEEDED(hr))
      {
  	hr = DWriteCreateFactory(
  		DWRITE_FACTORY_TYPE_SHARED,
  		__uuidof(IDWriteFactory),
  		reinterpret_cast<IUnknown**>(&mDWriteFactory));
  	_RPT2(_CRT_WARN, "DWriteCreateFactory: hr=%p p=%p\n", hr,
  		mDWriteFactory);
      }
  
      if (SUCCEEDED(hr))
      {
  	hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
  	_RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
      }
  
      if (SUCCEEDED(hr))
      {
  	hr = mDWriteFactory->CreateRenderingParams(&mRenderingParams);
  	_RPT2(_CRT_WARN, "CreateRenderingParams: hr=%p p=%p\n", hr,
  		mRenderingParams);
      }
  }
  
  DWriteContext::~DWriteContext()
  {
      SafeRelease(&mTextFormat);
      SafeRelease(&mRenderingParams);
      SafeRelease(&mGdiInterop);
      SafeRelease(&mDWriteFactory);
      SafeRelease(&mBrush);
      SafeRelease(&mRT);
      SafeRelease(&mD2D1Factory);
  }
  
      HRESULT
  DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize)
  {
      // Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspx
      HRESULT hr = S_OK;
  
      IDWriteFont *font = NULL;
      IDWriteFontFamily *fontFamily = NULL;
      IDWriteLocalizedStrings *localizedFamilyNames = NULL;
  
      if (SUCCEEDED(hr))
      {
  	hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
      }
  
      // Get the font family to which this font belongs.
      if (SUCCEEDED(hr))
      {
  	hr = font->GetFontFamily(&fontFamily);
      }
  
      // Get the family names. This returns an object that encapsulates one or
      // more names with the same meaning but in different languages.
      if (SUCCEEDED(hr))
      {
  	hr = fontFamily->GetFamilyNames(&localizedFamilyNames);
      }
  
      // Get the family name at index zero. If we were going to display the name
      // we'd want to try to find one that matched the use locale, but for
      // purposes of creating a text format object any language will do.
  
      wchar_t familyName[100];
      if (SUCCEEDED(hr))
      {
  	hr = localizedFamilyNames->GetString(0, familyName,
  		ARRAYSIZE(familyName));
      }
  
      if (SUCCEEDED(hr))
      {
  	// If no font size was passed in use the lfHeight of the LOGFONT.
  	if (fontSize == 0)
  	{
  	    // Convert from pixels to DIPs.
  	    fontSize = PixelsToDipsY(logFont.lfHeight);
  	    if (fontSize < 0)
  	    {
  		// Negative lfHeight represents the size of the em unit.
  		fontSize = -fontSize;
  	    }
  	    else
  	    {
  		// Positive lfHeight represents the cell height (ascent +
  		// descent).
  		DWRITE_FONT_METRICS fontMetrics;
  		font->GetMetrics(&fontMetrics);
  
  		// Convert the cell height (ascent + descent) from design units
  		// to ems.
  		float cellHeight = static_cast<float>(
  			fontMetrics.ascent + fontMetrics.descent)
  					       / fontMetrics.designUnitsPerEm;
  
  		// Divide the font size by the cell height to get the font em
  		// size.
  		fontSize /= cellHeight;
  	    }
  	}
      }
  
      // The text format includes a locale name. Ideally, this would be the
      // language of the text, which may or may not be the same as the primary
      // language of the user. However, for our purposes the user locale will do.
      wchar_t localeName[LOCALE_NAME_MAX_LENGTH];
      if (SUCCEEDED(hr))
      {
  	if (GetUserDefaultLocaleName(localeName, LOCALE_NAME_MAX_LENGTH) == 0)
  	    hr = HRESULT_FROM_WIN32(GetLastError());
      }
  
      if (SUCCEEDED(hr))
      {
  	// Create the text format object.
  	hr = mDWriteFactory->CreateTextFormat(
  		familyName,
  		NULL, // no custom font collection
  		font->GetWeight(),
  		font->GetStyle(),
  		font->GetStretch(),
  		fontSize,
  		localeName,
  		&mTextFormat);
      }
  
      if (SUCCEEDED(hr))
      {
  	mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
  	mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
  	    : DWRITE_FONT_STYLE_NORMAL;
      }
  
      SafeRelease(&localizedFamilyNames);
      SafeRelease(&fontFamily);
      SafeRelease(&font);
  
      return hr;
  }
  
      void
  DWriteContext::SetFont(HFONT hFont)
  {
      if (mLastHFont != hFont)
      {
  	LOGFONTW lf;
  	if (GetObjectW(hFont, sizeof(lf), &lf))
  	{
  	    SetFont(lf);
  	    mLastHFont = hFont;
  	}
      }
  }
  
      void
  DWriteContext::SetFont(const LOGFONTW &logFont)
  {
      SafeRelease(&mTextFormat);
      mLastHFont = NULL;
  
      HRESULT hr = SetLOGFONT(logFont, 0.f);
  
      if (SUCCEEDED(hr))
  	hr = mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
  
      if (SUCCEEDED(hr))
  	hr = mTextFormat->SetParagraphAlignment(
  		DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
  
      if (SUCCEEDED(hr))
  	hr = mTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
  }
  
      void
  DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len,
  	int x, int y, int w, int h, int cellWidth, COLORREF color)
  {
      HRESULT hr = S_OK;
      IDWriteBitmapRenderTarget *bmpRT = NULL;
  
      // Skip when any fonts are not set.
      if (mTextFormat == NULL)
  	return;
  
      // Check possibility of zero divided error.
      if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f)
  	return;
  
      if (SUCCEEDED(hr))
  	hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT);
  
      if (SUCCEEDED(hr))
      {
  	IDWriteTextLayout *textLayout = NULL;
  
  	HDC memdc = bmpRT->GetMemoryDC();
  	BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY);
  
  	hr = mDWriteFactory->CreateGdiCompatibleTextLayout(
  		text, len, mTextFormat, PixelsToDipsX(w),
  		PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout);
  
  	if (SUCCEEDED(hr))
  	{
  	    DWRITE_TEXT_RANGE textRange = { 0, len };
  	    textLayout->SetFontWeight(mFontWeight, textRange);
  	    textLayout->SetFontStyle(mFontStyle, textRange);
  	}
  
  	if (SUCCEEDED(hr))
  	{
  	    GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT,
  		    mRenderingParams);
  	    GdiTextRendererContext data = {
  		color,
  		PixelsToDipsX(cellWidth),
  		0.0f
  	    };
  	    textLayout->Draw(&data, renderer, 0, 0);
  	    SafeRelease(&renderer);
  	}
  
  	BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY);
  
  	SafeRelease(&textLayout);
      }
  
      SafeRelease(&bmpRT);
  }
  
      float
  DWriteContext::PixelsToDipsX(int x)
  {
      return x / mDpiScaleX;
  }
  
      float
  DWriteContext::PixelsToDipsY(int y)
  {
      return y / mDpiScaleY;
  }
  
      void
  DWriteContext::SetRenderingParams(
  	const DWriteRenderingParams *params)
  {
      if (mDWriteFactory == NULL)
  	return;
  
      IDWriteRenderingParams *renderingParams = NULL;
      D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode =
  	D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
      HRESULT hr;
      if (params != NULL)
      {
  	hr = mDWriteFactory->CreateCustomRenderingParams(params->gamma,
  		params->enhancedContrast, params->clearTypeLevel,
  		ToPixelGeometry(params->pixelGeometry),
  		ToRenderingMode(params->renderingMode), &renderingParams);
  	textAntialiasMode = ToTextAntialiasMode(params->textAntialiasMode);
      }
      else
  	hr = mDWriteFactory->CreateRenderingParams(&renderingParams);
      if (SUCCEEDED(hr) && renderingParams != NULL)
      {
  	SafeRelease(&mRenderingParams);
  	mRenderingParams = renderingParams;
  	mTextAntialiasMode = textAntialiasMode;
      }
  }
  
      DWriteRenderingParams *
  DWriteContext::GetRenderingParams(
  	DWriteRenderingParams *params)
  {
      if (params != NULL && mRenderingParams != NULL)
      {
  	params->gamma = mRenderingParams->GetGamma();
  	params->enhancedContrast = mRenderingParams->GetEnhancedContrast();
  	params->clearTypeLevel = mRenderingParams->GetClearTypeLevel();
  	params->pixelGeometry = ToInt(mRenderingParams->GetPixelGeometry());
  	params->renderingMode = ToInt(mRenderingParams->GetRenderingMode());
  	params->textAntialiasMode = mTextAntialiasMode;
      }
      return params;
  }
  
  ////////////////////////////////////////////////////////////////////////////
  // PUBLIC C INTERFACES
  
      void
  DWrite_Init(void)
  {
  #ifdef DYNAMIC_DIRECTX
      // Load libraries.
      hD2D1DLL = vimLoadLib(const_cast<char*>("d2d1.dll"));
      hDWriteDLL = vimLoadLib(const_cast<char*>("dwrite.dll"));
      if (hD2D1DLL == NULL || hDWriteDLL == NULL)
      {
  	DWrite_Final();
  	return;
      }
      // Get address of procedures.
      pGetUserDefaultLocaleName = (PGETUSERDEFAULTLOCALENAME)GetProcAddress(
  	    GetModuleHandle("kernel32.dll"), "GetUserDefaultLocaleName");
      pD2D1CreateFactory = (PD2D1CREATEFACTORY)GetProcAddress(hD2D1DLL,
  	    "D2D1CreateFactory");
      pDWriteCreateFactory = (PDWRITECREATEFACTORY)GetProcAddress(hDWriteDLL,
  	    "DWriteCreateFactory");
  #endif
  }
  
      void
  DWrite_Final(void)
  {
  #ifdef DYNAMIC_DIRECTX
      pGetUserDefaultLocaleName = NULL;
      pD2D1CreateFactory = NULL;
      pDWriteCreateFactory = NULL;
      unload(hDWriteDLL);
      unload(hD2D1DLL);
  #endif
  }
  
      DWriteContext *
  DWriteContext_Open(void)
  {
  #ifdef DYNAMIC_DIRECTX
      if (pGetUserDefaultLocaleName == NULL || pD2D1CreateFactory == NULL
  	    || pDWriteCreateFactory == NULL)
  	return NULL;
  #endif
      return new DWriteContext();
  }
  
      void
  DWriteContext_BeginDraw(DWriteContext *ctx)
  {
      if (ctx != NULL && ctx->mRT != NULL)
      {
  	ctx->mRT->BeginDraw();
  	ctx->mRT->SetTransform(D2D1::IdentityMatrix());
  	ctx->mDrawing = true;
      }
  }
  
      void
  DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect)
  {
      if (ctx != NULL && ctx->mRT != NULL)
      {
  	ctx->mRT->BindDC(hdc, rect);
  	ctx->mRT->SetTextAntialiasMode(ctx->mTextAntialiasMode);
      }
  }
  
      void
  DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
  {
      if (ctx != NULL)
      {
  	ctx->SetFont(hFont);
      }
  }
  
      void
  DWriteContext_DrawText(
  	DWriteContext *ctx,
  	HDC hdc,
  	const WCHAR* text,
  	int len,
  	int x,
  	int y,
  	int w,
  	int h,
  	int cellWidth,
  	COLORREF color)
  {
      if (ctx != NULL)
  	ctx->DrawText(hdc, text, len, x, y, w, h, cellWidth, color);
  }
  
      void
  DWriteContext_EndDraw(DWriteContext *ctx)
  {
      if (ctx != NULL && ctx->mRT != NULL)
      {
  	ctx->mRT->EndDraw();
  	ctx->mDrawing = false;
      }
  }
  
      void
  DWriteContext_Close(DWriteContext *ctx)
  {
      delete ctx;
  }
  
      void
  DWriteContext_SetRenderingParams(
  	DWriteContext *ctx,
  	const DWriteRenderingParams *params)
  {
      if (ctx != NULL)
  	ctx->SetRenderingParams(params);
  }
  
      DWriteRenderingParams *
  DWriteContext_GetRenderingParams(
  	DWriteContext *ctx,
  	DWriteRenderingParams *params)
  {
      if (ctx != NULL)
  	return ctx->GetRenderingParams(params);
      else
  	return NULL;
  }
  