Mercurial > vim
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 { |