1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "renderer/win32/text_renderer.h"
31 
32 #define _ATL_NO_AUTOMATIC_NAMESPACE
33 #define _WTL_NO_AUTOMATIC_NAMESPACE
34 #include <atlbase.h>
35 #include <atlcom.h>
36 #include <objbase.h>
37 #include <d2d1.h>
38 #include <dwrite.h>
39 
40 #include <memory>
41 
42 #include "base/logging.h"
43 #include "base/system_util.h"
44 #include "protocol/renderer_style.pb.h"
45 #include "renderer/renderer_style_handler.h"
46 
47 namespace mozc {
48 namespace renderer {
49 namespace win32 {
50 
51 using ATL::CComPtr;
52 using WTL::CDC;
53 using WTL::CDCHandle;
54 using WTL::CFont;
55 using WTL::CFontHandle;
56 using WTL::CLogFont;
57 using WTL::CPoint;
58 using WTL::CRect;
59 using WTL::CSize;
60 using ::mozc::renderer::RendererStyle;
61 using ::mozc::renderer::RendererStyleHandler;
62 
63 namespace {
64 
ToCRect(const Rect & rect)65 WTL::CRect ToCRect(const Rect &rect) {
66   return WTL::CRect(rect.Left(), rect.Top(), rect.Right(), rect.Bottom());
67 }
68 
GetTextColor(TextRenderer::FONT_TYPE type)69 COLORREF GetTextColor(TextRenderer::FONT_TYPE type) {
70   switch (type) {
71     case TextRenderer::FONTSET_SHORTCUT:
72       return RGB(0x61, 0x61, 0x61);
73     case TextRenderer::FONTSET_CANDIDATE:
74       return RGB(0x00, 0x00, 0x00);
75     case TextRenderer::FONTSET_DESCRIPTION:
76       return RGB(0x88, 0x88, 0x88);
77     case TextRenderer::FONTSET_FOOTER_INDEX:
78       return RGB(0x4c, 0x4c, 0x4c);
79     case TextRenderer::FONTSET_FOOTER_LABEL:
80       return RGB(0x4c, 0x4c, 0x4c);
81     case TextRenderer::FONTSET_FOOTER_SUBLABEL:
82       return RGB(0xA7, 0xA7, 0xA7);
83   }
84 
85   // TODO(horo): Not only infolist fonts but also candidate fonts
86   //             should be created from RendererStyle
87   RendererStyle style;
88   RendererStyleHandler::GetRendererStyle(&style);
89   const auto &infostyle = style.infolist_style();
90   switch (type) {
91     case TextRenderer::FONTSET_INFOLIST_CAPTION:
92       return RGB(infostyle.caption_style().foreground_color().r(),
93                  infostyle.caption_style().foreground_color().g(),
94                  infostyle.caption_style().foreground_color().b());
95     case TextRenderer::FONTSET_INFOLIST_TITLE:
96       return RGB(infostyle.title_style().foreground_color().r(),
97                  infostyle.title_style().foreground_color().g(),
98                  infostyle.title_style().foreground_color().b());
99     case TextRenderer::FONTSET_INFOLIST_DESCRIPTION:
100       return RGB(infostyle.description_style().foreground_color().r(),
101                  infostyle.description_style().foreground_color().g(),
102                  infostyle.description_style().foreground_color().b());
103   }
104 
105   LOG(DFATAL) << "Unknown type: " << type;
106   return RGB(0, 0, 0);
107 }
108 
GetLogFont(TextRenderer::FONT_TYPE type)109 CLogFont GetLogFont(TextRenderer::FONT_TYPE type) {
110   switch (type) {
111     case TextRenderer::FONTSET_SHORTCUT: {
112       CLogFont font;
113       font.SetMessageBoxFont();
114       font.MakeLarger(3);
115       font.lfWeight = FW_BOLD;
116       return font;
117     }
118     case TextRenderer::FONTSET_CANDIDATE: {
119       CLogFont font;
120       font.SetMessageBoxFont();
121       font.MakeLarger(3);
122       font.lfWeight = FW_NORMAL;
123       return font;
124     }
125     case TextRenderer::FONTSET_DESCRIPTION:
126     case TextRenderer::FONTSET_FOOTER_INDEX:
127     case TextRenderer::FONTSET_FOOTER_LABEL:
128     case TextRenderer::FONTSET_FOOTER_SUBLABEL: {
129       CLogFont font;
130       font.SetMessageBoxFont();
131       font.lfWeight = FW_NORMAL;
132       return font;
133     }
134   }
135 
136   // TODO(horo): Not only infolist fonts but also candidate fonts
137   //             should be created from RendererStyle
138   RendererStyle style;
139   RendererStyleHandler::GetRendererStyle(&style);
140   const auto &infostyle = style.infolist_style();
141   switch (type) {
142     case TextRenderer::FONTSET_INFOLIST_CAPTION: {
143       CLogFont font;
144       font.SetMessageBoxFont();
145       font.lfHeight = -infostyle.caption_style().font_size();
146       return font;
147     }
148     case TextRenderer::FONTSET_INFOLIST_TITLE: {
149       CLogFont font;
150       font.SetMessageBoxFont();
151       font.lfHeight = -infostyle.title_style().font_size();
152       return font;
153     }
154     case TextRenderer::FONTSET_INFOLIST_DESCRIPTION: {
155       CLogFont font;
156       font.SetMessageBoxFont();
157       font.lfHeight = -infostyle.description_style().font_size();
158       return font;
159     }
160   }
161 
162   LOG(DFATAL) << "Unknown type: " << type;
163   CLogFont font;
164   font.SetMessageBoxFont();
165   return font;
166 }
167 
GetGdiDrawTextStyle(TextRenderer::FONT_TYPE type)168 DWORD GetGdiDrawTextStyle(TextRenderer::FONT_TYPE type) {
169   switch (type) {
170     case TextRenderer::FONTSET_CANDIDATE:
171       return DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
172     case TextRenderer::FONTSET_DESCRIPTION:
173       return DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
174     case TextRenderer::FONTSET_FOOTER_INDEX:
175       return DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
176     case TextRenderer::FONTSET_FOOTER_LABEL:
177       return DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
178     case TextRenderer::FONTSET_FOOTER_SUBLABEL:
179       return DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
180     case TextRenderer::FONTSET_SHORTCUT:
181       return DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
182     case TextRenderer::FONTSET_INFOLIST_CAPTION:
183       return DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX;
184     case TextRenderer::FONTSET_INFOLIST_TITLE:
185       return DT_LEFT | DT_SINGLELINE | DT_WORDBREAK | DT_EDITCONTROL |
186           DT_NOPREFIX;
187     case TextRenderer::FONTSET_INFOLIST_DESCRIPTION:
188       return DT_LEFT | DT_WORDBREAK | DT_EDITCONTROL | DT_NOPREFIX;
189     default:
190       LOG(DFATAL) << "Unknown type: " << type;
191       return 0;
192   }
193 }
194 
195 class GdiTextRenderer : public TextRenderer {
196  public:
GdiTextRenderer()197   GdiTextRenderer()
198       : render_info_(new RenderInfo[SIZE_OF_FONT_TYPE]) {
199     mem_dc_.CreateCompatibleDC();
200     OnThemeChanged();
201   }
202 
~GdiTextRenderer()203   virtual ~GdiTextRenderer() {
204   }
205 
206  private:
207   // TextRenderer overrides:
OnThemeChanged()208   virtual void OnThemeChanged() {
209     // delete old fonts
210     for (size_t i = 0; i < SIZE_OF_FONT_TYPE; ++i) {
211       if (!render_info_[i].font.IsNull()) {
212         render_info_[i].font.DeleteObject();
213       }
214     }
215 
216     for (size_t i = 0; i < SIZE_OF_FONT_TYPE; ++i) {
217       const auto font_type = static_cast<FONT_TYPE>(i);
218       const auto &log_font = GetLogFont(font_type);
219       render_info_[i].style = GetGdiDrawTextStyle(font_type);
220       render_info_[i].font.CreateFontIndirectW(&log_font);
221       render_info_[i].color = GetTextColor(font_type);
222     }
223   }
224 
MeasureString(FONT_TYPE font_type,const std::wstring & str) const225   virtual Size MeasureString(FONT_TYPE font_type,
226                              const std::wstring &str) const {
227     const auto previous_font = mem_dc_.SelectFont(render_info_[font_type].font);
228     CRect rect;
229     mem_dc_.DrawTextW(str.c_str(), str.length(), &rect,
230                       DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_CALCRECT);
231     mem_dc_.SelectFont(previous_font);
232     return Size(rect.Width(), rect.Height());
233   }
234 
MeasureStringMultiLine(FONT_TYPE font_type,const std::wstring & str,const int width) const235   virtual Size MeasureStringMultiLine(
236       FONT_TYPE font_type, const std::wstring &str, const int width) const {
237     const auto previous_font = mem_dc_.SelectFont(render_info_[font_type].font);
238     CRect rect(0, 0, width, 0);
239     mem_dc_.DrawTextW(str.c_str(), str.length(), &rect,
240                       DT_NOPREFIX | DT_LEFT | DT_WORDBREAK | DT_CALCRECT);
241     mem_dc_.SelectFont(previous_font);
242     return Size(rect.Width(), rect.Height());
243   }
244 
RenderText(CDCHandle dc,const std::wstring & text,const Rect & rect,FONT_TYPE font_type) const245   virtual void RenderText(CDCHandle dc,
246                           const std::wstring &text,
247                           const Rect &rect,
248                           FONT_TYPE font_type) const {
249     std::vector<TextRenderingInfo> infolist;
250     infolist.push_back(TextRenderingInfo(text, rect));
251     RenderTextList(dc, infolist, font_type);
252   }
253 
RenderTextList(CDCHandle dc,const std::vector<TextRenderingInfo> & display_list,FONT_TYPE font_type) const254   virtual void RenderTextList(
255       CDCHandle dc,
256       const std::vector<TextRenderingInfo> &display_list,
257       FONT_TYPE font_type) const {
258     const auto &render_info = render_info_[font_type];
259     const auto old_font = dc.SelectFont(render_info.font);
260     const auto previous_color = dc.SetTextColor(render_info.color);
261     for (auto it = display_list.begin(); it != display_list.end(); ++it) {
262       const auto &info = *it;
263       CRect rect(info.rect.Left(), info.rect.Top(),
264                  info.rect.Right(), info.rect.Bottom());
265       const auto &text = info.text;
266       dc.DrawTextW(text.data(), text.size(), &rect, render_info.style);
267     }
268     dc.SetTextColor(previous_color);
269     dc.SelectFont(old_font);
270   }
271 
272   struct RenderInfo {
RenderInfomozc::renderer::win32::__anon9e3abba90111::GdiTextRenderer::RenderInfo273     RenderInfo() : color(0), style(0) {}
274     COLORREF color;
275     DWORD style;
276     CFont font;
277   };
278   std::unique_ptr<RenderInfo[]> render_info_;
279   mutable CDC mem_dc_;
280 
281   DISALLOW_COPY_AND_ASSIGN(GdiTextRenderer);
282 };
283 
284 class DirectWriteTextRenderer : public TextRenderer {
285  public:
Create()286   static DirectWriteTextRenderer *Create() {
287     HRESULT hr = S_OK;
288     CComPtr<ID2D1Factory> d2d_factory;
289     hr = ::D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED,
290                              __uuidof(ID2D1Factory),
291                              nullptr,
292                              reinterpret_cast<void **>(&d2d_factory));
293     if (FAILED(hr)) {
294       return nullptr;
295     }
296     CComPtr<IDWriteFactory> dwrite_factory;
297     hr = ::DWriteCreateFactory(
298         DWRITE_FACTORY_TYPE_SHARED,
299         __uuidof(IDWriteFactory),
300         reinterpret_cast<IUnknown **>(&dwrite_factory));
301     if (FAILED(hr)) {
302       return nullptr;
303     }
304     CComPtr<IDWriteGdiInterop> interop;
305     hr = dwrite_factory->GetGdiInterop(&interop);
306     if (FAILED(hr)) {
307       return nullptr;
308     }
309     return new DirectWriteTextRenderer(d2d_factory, dwrite_factory, interop);
310   }
~DirectWriteTextRenderer()311   virtual ~DirectWriteTextRenderer() {
312   }
313 
314  private:
DirectWriteTextRenderer(ID2D1Factory * d2d2_factory,IDWriteFactory * dwrite_factory,IDWriteGdiInterop * dwrite_interop)315   DirectWriteTextRenderer(
316       ID2D1Factory *d2d2_factory,
317       IDWriteFactory *dwrite_factory,
318       IDWriteGdiInterop *dwrite_interop)
319       : d2d2_factory_(d2d2_factory),
320         dwrite_factory_(dwrite_factory),
321         dwrite_interop_(dwrite_interop) {
322     OnThemeChanged();
323   }
324 
325   // TextRenderer overrides:
OnThemeChanged()326   virtual void OnThemeChanged() {
327     // delete old fonts
328     render_info_.clear();
329     render_info_.resize(SIZE_OF_FONT_TYPE);
330 
331     for (size_t i = 0; i < SIZE_OF_FONT_TYPE; ++i) {
332       const auto font_type = static_cast<FONT_TYPE>(i);
333       const auto &log_font = GetLogFont(font_type);
334       render_info_[i].color = GetTextColor(font_type);
335       render_info_[i].format = CreateFormat(log_font);
336       render_info_[i].format_to_render = CreateFormat(log_font);
337       const auto style = GetGdiDrawTextStyle(font_type);
338       const auto render_font = render_info_[i].format_to_render;
339       if ((style & DT_VCENTER) == DT_VCENTER) {
340         render_font->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER);
341       }
342       if ((style & DT_LEFT) == DT_LEFT) {
343         render_font->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING);
344       }
345       if ((style & DT_CENTER) == DT_CENTER) {
346         render_font->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER);
347       }
348       if ((style & DT_LEFT) == DT_RIGHT) {
349         render_font->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING);
350       }
351     }
352   }
353 
354   // Retrieves the bounding box for a given string.
MeasureString(FONT_TYPE font_type,const std::wstring & str) const355   virtual Size MeasureString(FONT_TYPE font_type,
356                              const std::wstring &str) const {
357     return MeasureStringImpl(font_type, str, 0, false);
358   }
359 
MeasureStringMultiLine(FONT_TYPE font_type,const std::wstring & str,const int width) const360   virtual Size MeasureStringMultiLine(FONT_TYPE font_type,
361                                       const std::wstring &str,
362                                       const int width) const {
363     return MeasureStringImpl(font_type, str, width, true);
364   }
365 
RenderText(CDCHandle dc,const std::wstring & text,const Rect & rect,FONT_TYPE font_type) const366   virtual void RenderText(CDCHandle dc, const std::wstring &text,
367                           const Rect &rect, FONT_TYPE font_type) const {
368     std::vector<TextRenderingInfo> infolist;
369     infolist.push_back(TextRenderingInfo(text, rect));
370     RenderTextList(dc, infolist, font_type);
371   }
372 
RenderTextList(CDCHandle dc,const std::vector<TextRenderingInfo> & display_list,FONT_TYPE font_type) const373   virtual void RenderTextList(
374       CDCHandle dc,
375       const std::vector<TextRenderingInfo> &display_list,
376       FONT_TYPE font_type) const {
377     const size_t kMaxTrial = 3;
378     size_t trial = 0;
379     while (true) {
380       CreateRenderTargetIfNecessary();
381       if (dc_render_target_ == nullptr) {
382         // This is not a recoverable error.
383         return;
384       }
385       const HRESULT hr = RenderTextListImpl(dc, display_list, font_type);
386       if (hr == D2DERR_RECREATE_TARGET && trial < kMaxTrial) {
387         // This is a recoverable error just by recreating the render target.
388         dc_render_target_.Release();
389         ++trial;
390         continue;
391       }
392       // For other error codes (including S_OK and S_FALSE), or if we exceed the
393       // maximum number of trials, we simply accept the result here.
394       return;
395     }
396   }
397 
RenderTextListImpl(CDCHandle dc,const std::vector<TextRenderingInfo> & display_list,FONT_TYPE font_type) const398   HRESULT RenderTextListImpl(CDCHandle dc,
399                              const std::vector<TextRenderingInfo> &display_list,
400                              FONT_TYPE font_type) const {
401     CRect total_rect;
402     for (const auto &item : display_list) {
403       const auto &item_rect = ToCRect(item.rect);
404       total_rect.right = std::max(total_rect.right, item_rect.right);
405       total_rect.bottom = std::max(total_rect.bottom, item_rect.bottom);
406     }
407     HRESULT hr = S_OK;
408     hr = dc_render_target_->BindDC(dc, &total_rect);
409     if (FAILED(hr)) {
410       return hr;
411     }
412     CComPtr<ID2D1SolidColorBrush> brush;
413     hr = dc_render_target_->CreateSolidColorBrush(
414         ToD2DColor(render_info_[font_type].color),
415         &brush);
416     if (FAILED(hr)) {
417       return hr;
418     }
419     D2D1_DRAW_TEXT_OPTIONS option = D2D1_DRAW_TEXT_OPTIONS_NONE;
420     if (SystemUtil::IsWindows8_1OrLater()) {
421       option |= D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT;
422     }
423     dc_render_target_->BeginDraw();
424     dc_render_target_->SetTransform(D2D1::Matrix3x2F::Identity());
425     for (size_t i = 0; i < display_list.size(); ++i) {
426       const auto &item = display_list[i];
427       const D2D1_RECT_F render_rect = {
428         static_cast<float>(item.rect.Left()),
429         static_cast<float>(item.rect.Top()),
430         static_cast<float>(item.rect.Right()),
431         static_cast<float>(item.rect.Bottom()),
432       };
433       dc_render_target_->DrawText(item.text.data(),
434                                   item.text.size(),
435                                   render_info_[font_type].format_to_render,
436                                   render_rect,
437                                   brush,
438                                   option);
439     }
440     return dc_render_target_->EndDraw();
441   }
442 
MeasureStringImpl(FONT_TYPE font_type,const std::wstring & str,const int width,bool use_width) const443   Size MeasureStringImpl(FONT_TYPE font_type, const std::wstring &str,
444                          const int width, bool use_width) const {
445     HRESULT hr = S_OK;
446     const FLOAT kLayoutLimit = 100000.0f;
447     CComPtr<IDWriteTextLayout> layout;
448     hr = dwrite_factory_->CreateTextLayout(str.data(),
449                                            str.size(),
450                                            render_info_[font_type].format,
451                                            (use_width ? width : kLayoutLimit),
452                                            kLayoutLimit,
453                                            &layout);
454     if (FAILED(hr)) {
455       return Size();
456     }
457     DWRITE_TEXT_METRICS metrix = {};
458     hr = layout->GetMetrics(&metrix);
459     if (FAILED(hr)) {
460       return Size();
461     }
462     return Size(ceilf(metrix.widthIncludingTrailingWhitespace),
463                 ceilf(metrix.height));
464   }
465 
ToD2DColor(COLORREF color_ref)466   static D2D1_COLOR_F ToD2DColor(COLORREF color_ref) {
467     D2D1_COLOR_F color;
468     color.a = 1.0;
469     color.r = GetRValue(color_ref) / 255.0f;
470     color.g = GetGValue(color_ref) / 255.0f;
471     color.b = GetBValue(color_ref) / 255.0f;
472     return color;
473   }
474 
CreateFormat(CLogFont logfont)475   CComPtr<IDWriteTextFormat> CreateFormat(CLogFont logfont) {
476     HRESULT hr = S_OK;
477     CComPtr<IDWriteFont> font;
478     hr = dwrite_interop_->CreateFontFromLOGFONT(&logfont, &font);
479     if (FAILED(hr)) {
480       return nullptr;
481     }
482     CComPtr<IDWriteFontFamily> font_family;
483     hr = font->GetFontFamily(&font_family);
484     if (FAILED(hr)) {
485       return nullptr;
486     }
487     CComPtr<IDWriteLocalizedStrings> localized_family_names;
488     hr = font_family->GetFamilyNames(&localized_family_names);
489     if (FAILED(hr)) {
490       return nullptr;
491     }
492     UINT32 length = 0;
493     hr = localized_family_names->GetStringLength(0, &length);
494     if (FAILED(hr)) {
495       return nullptr;
496     }
497     length += 1;  // for NUL.
498     std::unique_ptr<wchar_t[]> family_name(new wchar_t[length]);
499     hr = localized_family_names->GetString(0, family_name.get(), length);
500     if (FAILED(hr)) {
501       return nullptr;
502     }
503     auto font_size = logfont.lfHeight;
504     if (font_size < 0) {
505       font_size = -font_size;
506     } else {
507       DWRITE_FONT_METRICS font_metrix = {};
508       font->GetMetrics(&font_metrix);
509       const auto cell_height =
510           static_cast<float>(font_metrix.ascent + font_metrix.descent) /
511           font_metrix.designUnitsPerEm;
512       font_size /= cell_height;
513     }
514 
515     wchar_t locale_name[LOCALE_NAME_MAX_LENGTH] = {};
516     if (::GetUserDefaultLocaleName(locale_name, arraysize(locale_name))
517         == 0) {
518       return nullptr;
519     }
520 
521     CComPtr<IDWriteTextFormat> format;
522     hr = dwrite_factory_->CreateTextFormat(family_name.get(),
523                                            nullptr,
524                                            font->GetWeight(),
525                                            font->GetStyle(),
526                                            font->GetStretch(),
527                                            font_size,
528                                            locale_name,
529                                            &format);
530     return format;
531   }
532 
CreateRenderTargetIfNecessary() const533   void CreateRenderTargetIfNecessary() const {
534     if (dc_render_target_) {
535       return;
536     }
537     const auto property = D2D1::RenderTargetProperties(
538         D2D1_RENDER_TARGET_TYPE_DEFAULT,
539         D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
540         0,
541         0,
542         D2D1_RENDER_TARGET_USAGE_NONE,
543         D2D1_FEATURE_LEVEL_DEFAULT);
544     d2d2_factory_->CreateDCRenderTarget(&property, &dc_render_target_);
545   }
546 
547   struct RenderInfo {
RenderInfomozc::renderer::win32::__anon9e3abba90111::DirectWriteTextRenderer::RenderInfo548     RenderInfo() : color(0) {}
549     COLORREF color;
550     CComPtr<IDWriteTextFormat> format;
551     CComPtr<IDWriteTextFormat> format_to_render;
552   };
553 
554   CComPtr<ID2D1Factory> d2d2_factory_;
555   CComPtr<IDWriteFactory> dwrite_factory_;
556   mutable CComPtr<ID2D1DCRenderTarget> dc_render_target_;
557   CComPtr<IDWriteGdiInterop> dwrite_interop_;
558   std::vector<RenderInfo> render_info_;
559 
560   DISALLOW_COPY_AND_ASSIGN(DirectWriteTextRenderer);
561 };
562 
563 }  // namespace
564 
~TextRenderer()565 TextRenderer::~TextRenderer() {
566 }
567 
568 // static
Create()569 TextRenderer *TextRenderer::Create() {
570   // In some environments, DirectWrite cannot render characters in the
571   // candidate window or even worse may cause crash.  As a workaround,
572   // we try to use DirectWrite only on Windows 8.1 and later.
573   // TODO(yukawa): Reactivate the following code for older OSes when
574   // the root cause of b/23803925 is identified.
575   if (SystemUtil::IsWindows8_1OrLater()) {
576     auto *dwrite_text_renderer = DirectWriteTextRenderer::Create();
577     if (dwrite_text_renderer != nullptr) {
578       return dwrite_text_renderer;
579     }
580   }
581   return new GdiTextRenderer();
582 }
583 
584 }  // namespace win32
585 }  // namespace renderer
586 }  // namespace mozc
587