comparison src/gui_dwrite.cpp @ 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 0af716a4f5d2
children 18e6f4addce9
comparison
equal deleted inserted replaced
12933:9eeaf892510c 12934:2ebc3df65ca2
2 /* 2 /*
3 * Author: MURAOKA Taro <koron.kaoriya@gmail.com> 3 * Author: MURAOKA Taro <koron.kaoriya@gmail.com>
4 * 4 *
5 * Contributors: 5 * Contributors:
6 * - Ken Takata 6 * - Ken Takata
7 * - Yasuhiro Matsumoto
7 * 8 *
8 * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com> 9 * Copyright (C) 2013 MURAOKA Taro <koron.kaoriya@gmail.com>
9 * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE. 10 * THIS FILE IS DISTRIBUTED UNDER THE VIM LICENSE.
10 */ 11 */
11 12
21 #include <crtdbg.h> 22 #include <crtdbg.h>
22 #include <assert.h> 23 #include <assert.h>
23 #include <math.h> 24 #include <math.h>
24 #include <d2d1.h> 25 #include <d2d1.h>
25 #include <d2d1helper.h> 26 #include <d2d1helper.h>
26 #include <dwrite.h> 27
28 // Disable these macros to compile with old VC and newer SDK (V8.1 or later).
29 #if defined(_MSC_VER) && (_MSC_VER < 1700)
30 # define _COM_Outptr_ __out
31 # define _In_reads_(s)
32 # define _In_reads_opt_(s)
33 # define _Maybenull_
34 # define _Out_writes_(s)
35 # define _Out_writes_opt_(s)
36 # define _Out_writes_to_(x, y)
37 # define _Out_writes_to_opt_(x, y)
38 # define _Outptr_
39 #endif
40
41 #include <dwrite_2.h>
27 42
28 #include "gui_dwrite.h" 43 #include "gui_dwrite.h"
29 44
30 #ifdef __MINGW32__ 45 #ifdef __MINGW32__
31 # define __maybenull SAL__maybenull 46 # define __maybenull SAL__maybenull
76 { 91 {
77 (*ppT)->Release(); 92 (*ppT)->Release();
78 *ppT = NULL; 93 *ppT = NULL;
79 } 94 }
80 } 95 }
81
82 struct GdiTextRendererContext
83 {
84 // const fields.
85 COLORREF color;
86 FLOAT cellWidth;
87
88 // working fields.
89 FLOAT offsetX;
90 };
91 96
92 static DWRITE_PIXEL_GEOMETRY 97 static DWRITE_PIXEL_GEOMETRY
93 ToPixelGeometry(int value) 98 ToPixelGeometry(int value)
94 { 99 {
95 switch (value) 100 switch (value)
182 default: 187 default:
183 return -1; 188 return -1;
184 } 189 }
185 } 190 }
186 191
192 class FontCache {
193 public:
194 struct Item {
195 HFONT hFont;
196 IDWriteTextFormat* pTextFormat;
197 DWRITE_FONT_WEIGHT fontWeight;
198 DWRITE_FONT_STYLE fontStyle;
199 Item() : hFont(NULL), pTextFormat(NULL) {}
200 };
201
202 private:
203 int mSize;
204 Item *mItems;
205
206 public:
207 FontCache(int size = 2) :
208 mSize(size),
209 mItems(new Item[size])
210 {
211 }
212
213 ~FontCache()
214 {
215 for (int i = 0; i < mSize; ++i)
216 SafeRelease(&mItems[i].pTextFormat);
217 delete[] mItems;
218 }
219
220 bool get(HFONT hFont, Item &item)
221 {
222 int n = find(hFont);
223 if (n < 0)
224 return false;
225 item = mItems[n];
226 slide(n);
227 return true;
228 }
229
230 void put(const Item& item)
231 {
232 int n = find(item.hFont);
233 if (n < 0)
234 n = mSize - 1;
235 if (mItems[n].pTextFormat != item.pTextFormat)
236 {
237 SafeRelease(&mItems[n].pTextFormat);
238 item.pTextFormat->AddRef();
239 }
240 mItems[n] = item;
241 slide(n);
242 }
243
244 private:
245 int find(HFONT hFont)
246 {
247 for (int i = 0; i < mSize; ++i)
248 {
249 if (mItems[i].hFont == hFont)
250 return i;
251 }
252 return -1;
253 }
254
255 void slide(int nextTop)
256 {
257 if (nextTop == 0)
258 return;
259 Item tmp = mItems[nextTop];
260 for (int i = nextTop - 1; i >= 0; --i)
261 mItems[i + 1] = mItems[i];
262 mItems[0] = tmp;
263 }
264 };
265
266 struct DWriteContext {
267 HDC mHDC;
268 bool mDrawing;
269 bool mFallbackDC;
270
271 ID2D1Factory *mD2D1Factory;
272
273 ID2D1DCRenderTarget *mRT;
274 ID2D1SolidColorBrush *mBrush;
275
276 IDWriteFactory *mDWriteFactory;
277 IDWriteFactory2 *mDWriteFactory2;
278
279 IDWriteGdiInterop *mGdiInterop;
280 IDWriteRenderingParams *mRenderingParams;
281
282 FontCache mFontCache;
283 IDWriteTextFormat *mTextFormat;
284 DWRITE_FONT_WEIGHT mFontWeight;
285 DWRITE_FONT_STYLE mFontStyle;
286
287 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
288
289 // METHODS
290
291 DWriteContext();
292
293 virtual ~DWriteContext();
294
295 HRESULT CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
296 IDWriteTextFormat **ppTextFormat);
297
298 HRESULT SetFontByLOGFONT(const LOGFONTW &logFont);
299
300 void SetFont(HFONT hFont);
301
302 void BindDC(HDC hdc, RECT *rect);
303
304 void AssureDrawing();
305
306 ID2D1Brush* SolidBrush(COLORREF color);
307
308 void DrawText(const WCHAR* text, int len,
309 int x, int y, int w, int h, int cellWidth, COLORREF color,
310 UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx);
311
312 void FillRect(RECT *rc, COLORREF color);
313
314 void Flush();
315
316 void SetRenderingParams(
317 const DWriteRenderingParams *params);
318
319 DWriteRenderingParams *GetRenderingParams(
320 DWriteRenderingParams *params);
321 };
322
187 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN 323 class AdjustedGlyphRun : public DWRITE_GLYPH_RUN
188 { 324 {
189 private: 325 private:
326 FLOAT &mAccum;
190 FLOAT mDelta; 327 FLOAT mDelta;
191 FLOAT *mAdjustedAdvances; 328 FLOAT *mAdjustedAdvances;
192 329
193 public: 330 public:
194 AdjustedGlyphRun( 331 AdjustedGlyphRun(
195 const DWRITE_GLYPH_RUN *glyphRun, 332 const DWRITE_GLYPH_RUN *glyphRun,
196 FLOAT cellWidth) : 333 FLOAT cellWidth,
334 FLOAT &accum) :
197 DWRITE_GLYPH_RUN(*glyphRun), 335 DWRITE_GLYPH_RUN(*glyphRun),
336 mAccum(accum),
198 mDelta(0.0f), 337 mDelta(0.0f),
199 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount]) 338 mAdjustedAdvances(new FLOAT[glyphRun->glyphCount])
200 { 339 {
201 assert(cellWidth != 0.0f); 340 assert(cellWidth != 0.0f);
202 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i) 341 for (UINT32 i = 0; i < glyphRun->glyphCount; ++i)
207 mDelta += adjusted - orig; 346 mDelta += adjusted - orig;
208 } 347 }
209 glyphAdvances = mAdjustedAdvances; 348 glyphAdvances = mAdjustedAdvances;
210 } 349 }
211 350
212 ~AdjustedGlyphRun(void) 351 ~AdjustedGlyphRun()
213 { 352 {
353 mAccum += mDelta;
214 delete[] mAdjustedAdvances; 354 delete[] mAdjustedAdvances;
215 } 355 }
216 356
217 FLOAT getDelta(void) const
218 {
219 return mDelta;
220 }
221
222 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth) 357 static FLOAT adjustToCell(FLOAT value, FLOAT cellWidth)
223 { 358 {
224 int cellCount = (int)floor(value / cellWidth + 0.5f); 359 int cellCount = int(floor(value / cellWidth + 0.5f));
225 if (cellCount < 1) 360 if (cellCount < 1)
226 cellCount = 1; 361 cellCount = 1;
227 return cellCount * cellWidth; 362 return cellCount * cellWidth;
228 } 363 }
229 }; 364 };
230 365
231 class GdiTextRenderer FINAL : public IDWriteTextRenderer 366 struct TextRendererContext {
367 // const fields.
368 COLORREF color;
369 FLOAT cellWidth;
370
371 // working fields.
372 FLOAT offsetX;
373 };
374
375 class TextRenderer FINAL : public IDWriteTextRenderer
232 { 376 {
233 public: 377 public:
234 GdiTextRenderer( 378 TextRenderer(
235 IDWriteBitmapRenderTarget* bitmapRenderTarget, 379 DWriteContext* pDWC) :
236 IDWriteRenderingParams* renderingParams) :
237 cRefCount_(0), 380 cRefCount_(0),
238 pRenderTarget_(bitmapRenderTarget), 381 pDWC_(pDWC)
239 pRenderingParams_(renderingParams) 382 {
240 {
241 pRenderTarget_->AddRef();
242 pRenderingParams_->AddRef();
243 AddRef(); 383 AddRef();
244 } 384 }
245 385
246 // add "virtual" to avoid a compiler warning 386 // add "virtual" to avoid a compiler warning
247 virtual ~GdiTextRenderer() 387 virtual ~TextRenderer()
248 { 388 {
249 SafeRelease(&pRenderTarget_);
250 SafeRelease(&pRenderingParams_);
251 } 389 }
252 390
253 IFACEMETHOD(IsPixelSnappingDisabled)( 391 IFACEMETHOD(IsPixelSnappingDisabled)(
254 __maybenull void* clientDrawingContext, 392 __maybenull void* clientDrawingContext,
255 __out BOOL* isDisabled) 393 __out BOOL* isDisabled)
261 IFACEMETHOD(GetCurrentTransform)( 399 IFACEMETHOD(GetCurrentTransform)(
262 __maybenull void* clientDrawingContext, 400 __maybenull void* clientDrawingContext,
263 __out DWRITE_MATRIX* transform) 401 __out DWRITE_MATRIX* transform)
264 { 402 {
265 // forward the render target's transform 403 // forward the render target's transform
266 pRenderTarget_->GetCurrentTransform(transform); 404 pDWC_->mRT->GetTransform(
405 reinterpret_cast<D2D1_MATRIX_3X2_F*>(transform));
267 return S_OK; 406 return S_OK;
268 } 407 }
269 408
270 IFACEMETHOD(GetPixelsPerDip)( 409 IFACEMETHOD(GetPixelsPerDip)(
271 __maybenull void* clientDrawingContext, 410 __maybenull void* clientDrawingContext,
272 __out FLOAT* pixelsPerDip) 411 __out FLOAT* pixelsPerDip)
273 { 412 {
274 *pixelsPerDip = pRenderTarget_->GetPixelsPerDip(); 413 float dpiX, unused;
414 pDWC_->mRT->GetDpi(&dpiX, &unused);
415 *pixelsPerDip = dpiX / 96.0f;
275 return S_OK; 416 return S_OK;
417 }
418
419 IFACEMETHOD(DrawUnderline)(
420 __maybenull void* clientDrawingContext,
421 FLOAT baselineOriginX,
422 FLOAT baselineOriginY,
423 __in DWRITE_UNDERLINE const* underline,
424 IUnknown* clientDrawingEffect)
425 {
426 return E_NOTIMPL;
427 }
428
429 IFACEMETHOD(DrawStrikethrough)(
430 __maybenull void* clientDrawingContext,
431 FLOAT baselineOriginX,
432 FLOAT baselineOriginY,
433 __in DWRITE_STRIKETHROUGH const* strikethrough,
434 IUnknown* clientDrawingEffect)
435 {
436 return E_NOTIMPL;
437 }
438
439 IFACEMETHOD(DrawInlineObject)(
440 __maybenull void* clientDrawingContext,
441 FLOAT originX,
442 FLOAT originY,
443 IDWriteInlineObject* inlineObject,
444 BOOL isSideways,
445 BOOL isRightToLeft,
446 IUnknown* clientDrawingEffect)
447 {
448 return E_NOTIMPL;
276 } 449 }
277 450
278 IFACEMETHOD(DrawGlyphRun)( 451 IFACEMETHOD(DrawGlyphRun)(
279 __maybenull void* clientDrawingContext, 452 __maybenull void* clientDrawingContext,
280 FLOAT baselineOriginX, 453 FLOAT baselineOriginX,
282 DWRITE_MEASURING_MODE measuringMode, 455 DWRITE_MEASURING_MODE measuringMode,
283 __in DWRITE_GLYPH_RUN const* glyphRun, 456 __in DWRITE_GLYPH_RUN const* glyphRun,
284 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription, 457 __in DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
285 IUnknown* clientDrawingEffect) 458 IUnknown* clientDrawingEffect)
286 { 459 {
287 HRESULT hr = S_OK; 460 TextRendererContext *context =
288 461 reinterpret_cast<TextRendererContext*>(clientDrawingContext);
289 GdiTextRendererContext *context = 462
290 reinterpret_cast<GdiTextRendererContext*>(clientDrawingContext); 463 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth,
291 464 context->offsetX);
292 AdjustedGlyphRun adjustedGlyphRun(glyphRun, context->cellWidth); 465
293 466 if (pDWC_->mDWriteFactory2 != NULL)
294 // Pass on the drawing call to the render target to do the real work. 467 {
295 RECT dirtyRect = {0}; 468 IDWriteColorGlyphRunEnumerator *enumerator = NULL;
296 469 HRESULT hr = pDWC_->mDWriteFactory2->TranslateColorGlyphRun(
297 hr = pRenderTarget_->DrawGlyphRun(
298 baselineOriginX + context->offsetX, 470 baselineOriginX + context->offsetX,
299 baselineOriginY, 471 baselineOriginY,
300 measuringMode,
301 &adjustedGlyphRun, 472 &adjustedGlyphRun,
302 pRenderingParams_, 473 NULL,
303 context->color, 474 DWRITE_MEASURING_MODE_GDI_NATURAL,
304 &dirtyRect); 475 NULL,
305 476 0,
306 context->offsetX += adjustedGlyphRun.getDelta(); 477 &enumerator);
307 478 if (SUCCEEDED(hr))
308 return hr; 479 {
309 } 480 // Draw by IDWriteFactory2 for color emoji
310 481 BOOL hasRun = TRUE;
311 IFACEMETHOD(DrawUnderline)( 482 enumerator->MoveNext(&hasRun);
312 __maybenull void* clientDrawingContext, 483 while (hasRun)
313 FLOAT baselineOriginX, 484 {
314 FLOAT baselineOriginY, 485 const DWRITE_COLOR_GLYPH_RUN* colorGlyphRun;
315 __in DWRITE_UNDERLINE const* underline, 486 enumerator->GetCurrentRun(&colorGlyphRun);
316 IUnknown* clientDrawingEffect) 487
317 { 488 pDWC_->mBrush->SetColor(colorGlyphRun->runColor);
318 return E_NOTIMPL; 489 pDWC_->mRT->DrawGlyphRun(
319 } 490 D2D1::Point2F(
320 491 colorGlyphRun->baselineOriginX,
321 IFACEMETHOD(DrawStrikethrough)( 492 colorGlyphRun->baselineOriginY),
322 __maybenull void* clientDrawingContext, 493 &colorGlyphRun->glyphRun,
323 FLOAT baselineOriginX, 494 pDWC_->mBrush,
324 FLOAT baselineOriginY, 495 DWRITE_MEASURING_MODE_NATURAL);
325 __in DWRITE_STRIKETHROUGH const* strikethrough, 496 enumerator->MoveNext(&hasRun);
326 IUnknown* clientDrawingEffect) 497 }
327 { 498 SafeRelease(&enumerator);
328 return E_NOTIMPL; 499 return S_OK;
329 } 500 }
330 501 }
331 IFACEMETHOD(DrawInlineObject)( 502
332 __maybenull void* clientDrawingContext, 503 // Draw by IDWriteFactory (without color emoji)
333 FLOAT originX, 504 pDWC_->mRT->DrawGlyphRun(
334 FLOAT originY, 505 D2D1::Point2F(
335 IDWriteInlineObject* inlineObject, 506 baselineOriginX + context->offsetX,
336 BOOL isSideways, 507 baselineOriginY),
337 BOOL isRightToLeft, 508 &adjustedGlyphRun,
338 IUnknown* clientDrawingEffect) 509 pDWC_->SolidBrush(context->color),
339 { 510 DWRITE_MEASURING_MODE_NATURAL);
340 return E_NOTIMPL; 511 return S_OK;
341 } 512 }
342 513
343 public: 514 public:
344 IFACEMETHOD_(unsigned long, AddRef) () 515 IFACEMETHOD_(unsigned long, AddRef) ()
345 { 516 {
383 return S_OK; 554 return S_OK;
384 } 555 }
385 556
386 private: 557 private:
387 long cRefCount_; 558 long cRefCount_;
388 IDWriteBitmapRenderTarget* pRenderTarget_; 559 DWriteContext* pDWC_;
389 IDWriteRenderingParams* pRenderingParams_;
390 }; 560 };
391 561
392 struct DWriteContext {
393 FLOAT mDpiScaleX;
394 FLOAT mDpiScaleY;
395 bool mDrawing;
396
397 ID2D1Factory *mD2D1Factory;
398
399 ID2D1DCRenderTarget *mRT;
400 ID2D1SolidColorBrush *mBrush;
401
402 IDWriteFactory *mDWriteFactory;
403 IDWriteGdiInterop *mGdiInterop;
404 IDWriteRenderingParams *mRenderingParams;
405 IDWriteTextFormat *mTextFormat;
406
407 HFONT mLastHFont;
408 DWRITE_FONT_WEIGHT mFontWeight;
409 DWRITE_FONT_STYLE mFontStyle;
410
411 D2D1_TEXT_ANTIALIAS_MODE mTextAntialiasMode;
412
413 // METHODS
414
415 DWriteContext();
416
417 virtual ~DWriteContext();
418
419 HRESULT SetLOGFONT(const LOGFONTW &logFont, float fontSize);
420
421 void SetFont(HFONT hFont);
422
423 void SetFont(const LOGFONTW &logFont);
424
425 void DrawText(HDC hdc, const WCHAR* text, int len,
426 int x, int y, int w, int h, int cellWidth, COLORREF color);
427
428 float PixelsToDipsX(int x);
429
430 float PixelsToDipsY(int y);
431
432 void SetRenderingParams(
433 const DWriteRenderingParams *params);
434
435 DWriteRenderingParams *GetRenderingParams(
436 DWriteRenderingParams *params);
437 };
438
439 DWriteContext::DWriteContext() : 562 DWriteContext::DWriteContext() :
440 mDpiScaleX(1.f), 563 mHDC(NULL),
441 mDpiScaleY(1.f),
442 mDrawing(false), 564 mDrawing(false),
565 mFallbackDC(false),
443 mD2D1Factory(NULL), 566 mD2D1Factory(NULL),
444 mRT(NULL), 567 mRT(NULL),
445 mBrush(NULL), 568 mBrush(NULL),
446 mDWriteFactory(NULL), 569 mDWriteFactory(NULL),
570 mDWriteFactory2(NULL),
447 mGdiInterop(NULL), 571 mGdiInterop(NULL),
448 mRenderingParams(NULL), 572 mRenderingParams(NULL),
573 mFontCache(8),
449 mTextFormat(NULL), 574 mTextFormat(NULL),
450 mLastHFont(NULL),
451 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL), 575 mFontWeight(DWRITE_FONT_WEIGHT_NORMAL),
452 mFontStyle(DWRITE_FONT_STYLE_NORMAL), 576 mFontStyle(DWRITE_FONT_STYLE_NORMAL),
453 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT) 577 mTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_DEFAULT)
454 { 578 {
455 HRESULT hr; 579 HRESULT hr;
456
457 HDC screen = ::GetDC(0);
458 mDpiScaleX = ::GetDeviceCaps(screen, LOGPIXELSX) / 96.0f;
459 mDpiScaleY = ::GetDeviceCaps(screen, LOGPIXELSY) / 96.0f;
460 ::ReleaseDC(0, screen);
461 580
462 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, 581 hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
463 __uuidof(ID2D1Factory), NULL, 582 __uuidof(ID2D1Factory), NULL,
464 reinterpret_cast<void**>(&mD2D1Factory)); 583 reinterpret_cast<void**>(&mD2D1Factory));
465 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory); 584 _RPT2(_CRT_WARN, "D2D1CreateFactory: hr=%p p=%p\n", hr, mD2D1Factory);
495 mDWriteFactory); 614 mDWriteFactory);
496 } 615 }
497 616
498 if (SUCCEEDED(hr)) 617 if (SUCCEEDED(hr))
499 { 618 {
619 DWriteCreateFactory(
620 DWRITE_FACTORY_TYPE_SHARED,
621 __uuidof(IDWriteFactory2),
622 reinterpret_cast<IUnknown**>(&mDWriteFactory2));
623 _RPT1(_CRT_WARN, "IDWriteFactory2: %s\n", SUCCEEDED(hr) ? "available" : "not available");
624 }
625
626 if (SUCCEEDED(hr))
627 {
500 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop); 628 hr = mDWriteFactory->GetGdiInterop(&mGdiInterop);
501 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop); 629 _RPT2(_CRT_WARN, "GetGdiInterop: hr=%p p=%p\n", hr, mGdiInterop);
502 } 630 }
503 631
504 if (SUCCEEDED(hr)) 632 if (SUCCEEDED(hr))
513 { 641 {
514 SafeRelease(&mTextFormat); 642 SafeRelease(&mTextFormat);
515 SafeRelease(&mRenderingParams); 643 SafeRelease(&mRenderingParams);
516 SafeRelease(&mGdiInterop); 644 SafeRelease(&mGdiInterop);
517 SafeRelease(&mDWriteFactory); 645 SafeRelease(&mDWriteFactory);
646 SafeRelease(&mDWriteFactory2);
518 SafeRelease(&mBrush); 647 SafeRelease(&mBrush);
519 SafeRelease(&mRT); 648 SafeRelease(&mRT);
520 SafeRelease(&mD2D1Factory); 649 SafeRelease(&mD2D1Factory);
521 } 650 }
522 651
523 HRESULT 652 HRESULT
524 DWriteContext::SetLOGFONT(const LOGFONTW &logFont, float fontSize) 653 DWriteContext::CreateTextFormatFromLOGFONT(const LOGFONTW &logFont,
525 { 654 IDWriteTextFormat **ppTextFormat)
526 // Most of this function is copy from: http://msdn.microsoft.com/en-us/library/windows/desktop/dd941783(v=vs.85).aspx 655 {
656 // Most of this function is copied from: https://github.com/Microsoft/Windows-classic-samples/blob/master/Samples/Win7Samples/multimedia/DirectWrite/RenderTest/TextHelpers.cpp
527 HRESULT hr = S_OK; 657 HRESULT hr = S_OK;
658 IDWriteTextFormat *pTextFormat = NULL;
528 659
529 IDWriteFont *font = NULL; 660 IDWriteFont *font = NULL;
530 IDWriteFontFamily *fontFamily = NULL; 661 IDWriteFontFamily *fontFamily = NULL;
531 IDWriteLocalizedStrings *localizedFamilyNames = NULL; 662 IDWriteLocalizedStrings *localizedFamilyNames = NULL;
663 float fontSize = 0;
532 664
533 if (SUCCEEDED(hr)) 665 if (SUCCEEDED(hr))
534 { 666 {
535 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font); 667 hr = mGdiInterop->CreateFontFromLOGFONT(&logFont, &font);
536 } 668 }
559 ARRAYSIZE(familyName)); 691 ARRAYSIZE(familyName));
560 } 692 }
561 693
562 if (SUCCEEDED(hr)) 694 if (SUCCEEDED(hr))
563 { 695 {
564 // If no font size was passed in use the lfHeight of the LOGFONT. 696 // Use lfHeight of the LOGFONT as font size.
565 if (fontSize == 0) 697 fontSize = float(logFont.lfHeight);
566 { 698
567 // Convert from pixels to DIPs. 699 if (fontSize < 0)
568 fontSize = PixelsToDipsY(logFont.lfHeight); 700 {
569 if (fontSize < 0) 701 // Negative lfHeight represents the size of the em unit.
570 { 702 fontSize = -fontSize;
571 // Negative lfHeight represents the size of the em unit. 703 }
572 fontSize = -fontSize; 704 else
573 } 705 {
574 else 706 // Positive lfHeight represents the cell height (ascent +
575 { 707 // descent).
576 // Positive lfHeight represents the cell height (ascent + 708 DWRITE_FONT_METRICS fontMetrics;
577 // descent). 709 font->GetMetrics(&fontMetrics);
578 DWRITE_FONT_METRICS fontMetrics; 710
579 font->GetMetrics(&fontMetrics); 711 // Convert the cell height (ascent + descent) from design units
580 712 // to ems.
581 // Convert the cell height (ascent + descent) from design units 713 float cellHeight = static_cast<float>(
582 // to ems. 714 fontMetrics.ascent + fontMetrics.descent)
583 float cellHeight = static_cast<float>( 715 / fontMetrics.designUnitsPerEm;
584 fontMetrics.ascent + fontMetrics.descent) 716
585 / fontMetrics.designUnitsPerEm; 717 // Divide the font size by the cell height to get the font em
586 718 // size.
587 // Divide the font size by the cell height to get the font em 719 fontSize /= cellHeight;
588 // size.
589 fontSize /= cellHeight;
590 }
591 } 720 }
592 } 721 }
593 722
594 // The text format includes a locale name. Ideally, this would be the 723 // The text format includes a locale name. Ideally, this would be the
595 // language of the text, which may or may not be the same as the primary 724 // language of the text, which may or may not be the same as the primary
610 font->GetWeight(), 739 font->GetWeight(),
611 font->GetStyle(), 740 font->GetStyle(),
612 font->GetStretch(), 741 font->GetStretch(),
613 fontSize, 742 fontSize,
614 localeName, 743 localeName,
615 &mTextFormat); 744 &pTextFormat);
616 } 745 }
617 746
618 if (SUCCEEDED(hr)) 747 if (SUCCEEDED(hr))
619 { 748 hr = pTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
749
750 if (SUCCEEDED(hr))
751 hr = pTextFormat->SetParagraphAlignment(
752 DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
753
754 if (SUCCEEDED(hr))
755 hr = pTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
756
757 SafeRelease(&localizedFamilyNames);
758 SafeRelease(&fontFamily);
759 SafeRelease(&font);
760
761 if (SUCCEEDED(hr))
762 *ppTextFormat = pTextFormat;
763 else
764 SafeRelease(&pTextFormat);
765
766 return hr;
767 }
768
769 HRESULT
770 DWriteContext::SetFontByLOGFONT(const LOGFONTW &logFont)
771 {
772 HRESULT hr = S_OK;
773 IDWriteTextFormat *pTextFormat = NULL;
774
775 hr = CreateTextFormatFromLOGFONT(logFont, &pTextFormat);
776
777 if (SUCCEEDED(hr))
778 {
779 SafeRelease(&mTextFormat);
780 mTextFormat = pTextFormat;
620 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight); 781 mFontWeight = static_cast<DWRITE_FONT_WEIGHT>(logFont.lfWeight);
621 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC 782 mFontStyle = logFont.lfItalic ? DWRITE_FONT_STYLE_ITALIC
622 : DWRITE_FONT_STYLE_NORMAL; 783 : DWRITE_FONT_STYLE_NORMAL;
623 } 784 }
624 785
625 SafeRelease(&localizedFamilyNames);
626 SafeRelease(&fontFamily);
627 SafeRelease(&font);
628
629 return hr; 786 return hr;
630 } 787 }
631 788
632 void 789 void
633 DWriteContext::SetFont(HFONT hFont) 790 DWriteContext::SetFont(HFONT hFont)
634 { 791 {
635 if (mLastHFont != hFont) 792 FontCache::Item item;
636 { 793 if (mFontCache.get(hFont, item))
637 LOGFONTW lf; 794 {
638 if (GetObjectW(hFont, sizeof(lf), &lf)) 795 if (item.pTextFormat != NULL)
639 { 796 {
640 SetFont(lf); 797 item.pTextFormat->AddRef();
641 mLastHFont = hFont; 798 SafeRelease(&mTextFormat);
642 } 799 mTextFormat = item.pTextFormat;
643 } 800 mFontWeight = item.fontWeight;
644 } 801 mFontStyle = item.fontStyle;
645 802 mFallbackDC = false;
646 void 803 }
647 DWriteContext::SetFont(const LOGFONTW &logFont) 804 else
648 { 805 mFallbackDC = true;
649 SafeRelease(&mTextFormat);
650 mLastHFont = NULL;
651
652 HRESULT hr = SetLOGFONT(logFont, 0.f);
653
654 if (SUCCEEDED(hr))
655 hr = mTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
656
657 if (SUCCEEDED(hr))
658 hr = mTextFormat->SetParagraphAlignment(
659 DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
660
661 if (SUCCEEDED(hr))
662 hr = mTextFormat->SetWordWrapping(DWRITE_WORD_WRAPPING_NO_WRAP);
663 }
664
665 void
666 DWriteContext::DrawText(HDC hdc, const WCHAR* text, int len,
667 int x, int y, int w, int h, int cellWidth, COLORREF color)
668 {
669 HRESULT hr = S_OK;
670 IDWriteBitmapRenderTarget *bmpRT = NULL;
671
672 // Skip when any fonts are not set.
673 if (mTextFormat == NULL)
674 return; 806 return;
675 807 }
676 // Check possibility of zero divided error. 808
677 if (cellWidth == 0 || mDpiScaleX == 0.0f || mDpiScaleY == 0.0f) 809 HRESULT hr = E_FAIL;
810 LOGFONTW lf;
811 if (GetObjectW(hFont, sizeof(lf), &lf))
812 hr = SetFontByLOGFONT(lf);
813
814 item.hFont = hFont;
815 if (SUCCEEDED(hr))
816 {
817 item.pTextFormat = mTextFormat;
818 item.fontWeight = mFontWeight;
819 item.fontStyle = mFontStyle;
820 }
821 mFontCache.put(item);
822 }
823
824 void
825 DWriteContext::BindDC(HDC hdc, RECT *rect)
826 {
827 Flush();
828 mRT->BindDC(hdc, rect);
829 mRT->SetTransform(D2D1::IdentityMatrix());
830 mHDC = hdc;
831 }
832
833 void
834 DWriteContext::AssureDrawing()
835 {
836 if (mDrawing == false)
837 {
838 mRT->BeginDraw();
839 mDrawing = true;
840 }
841 }
842
843 ID2D1Brush*
844 DWriteContext::SolidBrush(COLORREF color)
845 {
846 mBrush->SetColor(D2D1::ColorF(UINT32(GetRValue(color)) << 16 |
847 UINT32(GetGValue(color)) << 8 | UINT32(GetBValue(color))));
848 return mBrush;
849 }
850
851 void
852 DWriteContext::DrawText(const WCHAR* text, int len,
853 int x, int y, int w, int h, int cellWidth, COLORREF color,
854 UINT fuOptions, CONST RECT *lprc, CONST INT * lpDx)
855 {
856 if (mFallbackDC)
857 {
858 Flush();
859 ExtTextOutW(mHDC, x, y, fuOptions, lprc, text, len, lpDx);
678 return; 860 return;
679 861 }
680 if (SUCCEEDED(hr)) 862
681 hr = mGdiInterop->CreateBitmapRenderTarget(hdc, w, h, &bmpRT); 863 AssureDrawing();
682 864
683 if (SUCCEEDED(hr)) 865 HRESULT hr;
684 { 866 IDWriteTextLayout *textLayout = NULL;
685 IDWriteTextLayout *textLayout = NULL; 867
686 868 hr = mDWriteFactory->CreateTextLayout(text, len, mTextFormat,
687 HDC memdc = bmpRT->GetMemoryDC(); 869 FLOAT(w), FLOAT(h), &textLayout);
688 BitBlt(memdc, 0, 0, w, h, hdc, x, y, SRCCOPY); 870
689 871 if (SUCCEEDED(hr))
690 hr = mDWriteFactory->CreateGdiCompatibleTextLayout( 872 {
691 text, len, mTextFormat, PixelsToDipsX(w), 873 DWRITE_TEXT_RANGE textRange = { 0, UINT32(len) };
692 PixelsToDipsY(h), mDpiScaleX, NULL, TRUE, &textLayout); 874 textLayout->SetFontWeight(mFontWeight, textRange);
693 875 textLayout->SetFontStyle(mFontStyle, textRange);
694 if (SUCCEEDED(hr)) 876
695 { 877 TextRenderer renderer(this);
696 DWRITE_TEXT_RANGE textRange = { 0, (UINT32)len }; 878 TextRendererContext context = { color, FLOAT(cellWidth), 0.0f };
697 textLayout->SetFontWeight(mFontWeight, textRange); 879 textLayout->Draw(&context, &renderer, FLOAT(x), FLOAT(y));
698 textLayout->SetFontStyle(mFontStyle, textRange); 880 }
699 } 881
700 882 SafeRelease(&textLayout);
701 if (SUCCEEDED(hr)) 883 }
702 { 884
703 GdiTextRenderer *renderer = new GdiTextRenderer(bmpRT, 885 void
704 mRenderingParams); 886 DWriteContext::FillRect(RECT *rc, COLORREF color)
705 GdiTextRendererContext data = { 887 {
706 color, 888 AssureDrawing();
707 PixelsToDipsX(cellWidth), 889 mRT->FillRectangle(
708 0.0f 890 D2D1::RectF(FLOAT(rc->left), FLOAT(rc->top),
709 }; 891 FLOAT(rc->right), FLOAT(rc->bottom)),
710 textLayout->Draw(&data, renderer, 0, 0); 892 SolidBrush(color));
711 SafeRelease(&renderer); 893 }
712 } 894
713 895 void
714 BitBlt(hdc, x, y, w, h, memdc, 0, 0, SRCCOPY); 896 DWriteContext::Flush()
715 897 {
716 SafeRelease(&textLayout); 898 if (mDrawing)
717 } 899 {
718 900 mRT->EndDraw();
719 SafeRelease(&bmpRT); 901 mDrawing = false;
720 } 902 }
721
722 float
723 DWriteContext::PixelsToDipsX(int x)
724 {
725 return x / mDpiScaleX;
726 }
727
728 float
729 DWriteContext::PixelsToDipsY(int y)
730 {
731 return y / mDpiScaleY;
732 } 903 }
733 904
734 void 905 void
735 DWriteContext::SetRenderingParams( 906 DWriteContext::SetRenderingParams(
736 const DWriteRenderingParams *params) 907 const DWriteRenderingParams *params)
755 if (SUCCEEDED(hr) && renderingParams != NULL) 926 if (SUCCEEDED(hr) && renderingParams != NULL)
756 { 927 {
757 SafeRelease(&mRenderingParams); 928 SafeRelease(&mRenderingParams);
758 mRenderingParams = renderingParams; 929 mRenderingParams = renderingParams;
759 mTextAntialiasMode = textAntialiasMode; 930 mTextAntialiasMode = textAntialiasMode;
931
932 Flush();
933 mRT->SetTextRenderingParams(mRenderingParams);
934 mRT->SetTextAntialiasMode(mTextAntialiasMode);
760 } 935 }
761 } 936 }
762 937
763 DWriteRenderingParams * 938 DWriteRenderingParams *
764 DWriteContext::GetRenderingParams( 939 DWriteContext::GetRenderingParams(
823 #endif 998 #endif
824 return new DWriteContext(); 999 return new DWriteContext();
825 } 1000 }
826 1001
827 void 1002 void
828 DWriteContext_BeginDraw(DWriteContext *ctx)
829 {
830 if (ctx != NULL && ctx->mRT != NULL)
831 {
832 ctx->mRT->BeginDraw();
833 ctx->mRT->SetTransform(D2D1::IdentityMatrix());
834 ctx->mDrawing = true;
835 }
836 }
837
838 void
839 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect) 1003 DWriteContext_BindDC(DWriteContext *ctx, HDC hdc, RECT *rect)
840 { 1004 {
841 if (ctx != NULL && ctx->mRT != NULL) 1005 if (ctx != NULL)
842 { 1006 ctx->BindDC(hdc, rect);
843 ctx->mRT->BindDC(hdc, rect);
844 ctx->mRT->SetTextAntialiasMode(ctx->mTextAntialiasMode);
845 }
846 } 1007 }
847 1008
848 void 1009 void
849 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont) 1010 DWriteContext_SetFont(DWriteContext *ctx, HFONT hFont)
850 { 1011 {
851 if (ctx != NULL) 1012 if (ctx != NULL)
852 {
853 ctx->SetFont(hFont); 1013 ctx->SetFont(hFont);
854 }
855 } 1014 }
856 1015
857 void 1016 void
858 DWriteContext_DrawText( 1017 DWriteContext_DrawText(
859 DWriteContext *ctx, 1018 DWriteContext *ctx,
860 HDC hdc,
861 const WCHAR* text, 1019 const WCHAR* text,
862 int len, 1020 int len,
863 int x, 1021 int x,
864 int y, 1022 int y,
865 int w, 1023 int w,
866 int h, 1024 int h,
867 int cellWidth, 1025 int cellWidth,
868 COLORREF color) 1026 COLORREF color,
1027 UINT fuOptions,
1028 CONST RECT *lprc,
1029 CONST INT * lpDx)
869 { 1030 {
870 if (ctx != NULL) 1031 if (ctx != NULL)
871 ctx->DrawText(hdc, text, len, x, y, w, h, cellWidth, color); 1032 ctx->DrawText(text, len, x, y, w, h, cellWidth, color,
872 } 1033 fuOptions, lprc, lpDx);
873 1034 }
874 void 1035
875 DWriteContext_EndDraw(DWriteContext *ctx) 1036 void
876 { 1037 DWriteContext_FillRect(DWriteContext *ctx, RECT *rc, COLORREF color)
877 if (ctx != NULL && ctx->mRT != NULL) 1038 {
878 { 1039 if (ctx != NULL)
879 ctx->mRT->EndDraw(); 1040 ctx->FillRect(rc, color);
880 ctx->mDrawing = false; 1041 }
881 } 1042
1043 void
1044 DWriteContext_Flush(DWriteContext *ctx)
1045 {
1046 if (ctx != NULL)
1047 ctx->Flush();
882 } 1048 }
883 1049
884 void 1050 void
885 DWriteContext_Close(DWriteContext *ctx) 1051 DWriteContext_Close(DWriteContext *ctx)
886 { 1052 {