| // Copyright 2010-2015, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "renderer/win32/text_renderer.h" |
| |
| #define _ATL_NO_AUTOMATIC_NAMESPACE |
| #define _WTL_NO_AUTOMATIC_NAMESPACE |
| // Workaround against KB813540 |
| #include <atlbase_mozc.h> |
| #include <atlcom.h> |
| #include <objbase.h> |
| #include <d2d1.h> |
| #include <dwrite.h> |
| |
| #include <memory> |
| |
| #include "base/logging.h" |
| #include "base/system_util.h" |
| #include "base/win_util.h" |
| #include "renderer/renderer_style.pb.h" |
| #include "renderer/renderer_style_handler.h" |
| |
| namespace mozc { |
| namespace renderer { |
| namespace win32 { |
| |
| using ATL::CComPtr; |
| using WTL::CDC; |
| using WTL::CDCHandle; |
| using WTL::CFont; |
| using WTL::CFontHandle; |
| using WTL::CLogFont; |
| using WTL::CPoint; |
| using WTL::CRect; |
| using WTL::CSize; |
| using ::mozc::renderer::RendererStyle; |
| using ::mozc::renderer::RendererStyleHandler; |
| |
| namespace { |
| |
| COLORREF GetTextColor(TextRenderer::FONT_TYPE type) { |
| switch (type) { |
| case TextRenderer::FONTSET_SHORTCUT: |
| return RGB(0x61, 0x61, 0x61); |
| case TextRenderer::FONTSET_CANDIDATE: |
| return RGB(0x00, 0x00, 0x00); |
| case TextRenderer::FONTSET_DESCRIPTION: |
| return RGB(0x88, 0x88, 0x88); |
| case TextRenderer::FONTSET_FOOTER_INDEX: |
| return RGB(0x4c, 0x4c, 0x4c); |
| case TextRenderer::FONTSET_FOOTER_LABEL: |
| return RGB(0x4c, 0x4c, 0x4c); |
| case TextRenderer::FONTSET_FOOTER_SUBLABEL: |
| return RGB(0xA7, 0xA7, 0xA7); |
| } |
| |
| // TODO(horo): Not only infolist fonts but also candidate fonts |
| // should be created from RendererStyle |
| RendererStyle style; |
| RendererStyleHandler::GetRendererStyle(&style); |
| const auto &infostyle = style.infolist_style(); |
| switch (type) { |
| case TextRenderer::FONTSET_INFOLIST_CAPTION: |
| return RGB(infostyle.caption_style().foreground_color().r(), |
| infostyle.caption_style().foreground_color().g(), |
| infostyle.caption_style().foreground_color().b()); |
| case TextRenderer::FONTSET_INFOLIST_TITLE: |
| return RGB(infostyle.title_style().foreground_color().r(), |
| infostyle.title_style().foreground_color().g(), |
| infostyle.title_style().foreground_color().b()); |
| case TextRenderer::FONTSET_INFOLIST_DESCRIPTION: |
| return RGB(infostyle.description_style().foreground_color().r(), |
| infostyle.description_style().foreground_color().g(), |
| infostyle.description_style().foreground_color().b()); |
| } |
| |
| LOG(DFATAL) << "Unknown type: " << type; |
| return RGB(0, 0, 0); |
| } |
| |
| CLogFont GetLogFont(TextRenderer::FONT_TYPE type) { |
| switch (type) { |
| case TextRenderer::FONTSET_SHORTCUT: { |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| font.MakeLarger(3); |
| font.lfWeight = FW_BOLD; |
| return font; |
| } |
| case TextRenderer::FONTSET_CANDIDATE: { |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| font.MakeLarger(3); |
| font.lfWeight = FW_NORMAL; |
| return font; |
| } |
| case TextRenderer::FONTSET_DESCRIPTION: |
| case TextRenderer::FONTSET_FOOTER_INDEX: |
| case TextRenderer::FONTSET_FOOTER_LABEL: |
| case TextRenderer::FONTSET_FOOTER_SUBLABEL: { |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| font.lfWeight = FW_NORMAL; |
| return font; |
| } |
| } |
| |
| // TODO(horo): Not only infolist fonts but also candidate fonts |
| // should be created from RendererStyle |
| RendererStyle style; |
| RendererStyleHandler::GetRendererStyle(&style); |
| const auto &infostyle = style.infolist_style(); |
| switch (type) { |
| case TextRenderer::FONTSET_INFOLIST_CAPTION: { |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| font.lfHeight = -infostyle.caption_style().font_size(); |
| return font; |
| } |
| case TextRenderer::FONTSET_INFOLIST_TITLE: { |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| font.lfHeight = -infostyle.title_style().font_size(); |
| return font; |
| } |
| case TextRenderer::FONTSET_INFOLIST_DESCRIPTION: { |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| font.lfHeight = -infostyle.description_style().font_size(); |
| return font; |
| } |
| } |
| |
| LOG(DFATAL) << "Unknown type: " << type; |
| CLogFont font; |
| font.SetMessageBoxFont(); |
| return font; |
| } |
| |
| DWORD GetGdiDrawTextStyle(TextRenderer::FONT_TYPE type) { |
| switch (type) { |
| case TextRenderer::FONTSET_CANDIDATE: |
| return DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_DESCRIPTION: |
| return DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_FOOTER_INDEX: |
| return DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_FOOTER_LABEL: |
| return DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_FOOTER_SUBLABEL: |
| return DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_SHORTCUT: |
| return DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_INFOLIST_CAPTION: |
| return DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX; |
| case TextRenderer::FONTSET_INFOLIST_TITLE: |
| return DT_LEFT | DT_SINGLELINE | DT_WORDBREAK | DT_EDITCONTROL | |
| DT_NOPREFIX; |
| case TextRenderer::FONTSET_INFOLIST_DESCRIPTION: |
| return DT_LEFT | DT_WORDBREAK | DT_EDITCONTROL | DT_NOPREFIX; |
| default: |
| LOG(DFATAL) << "Unknown type: " << type; |
| return 0; |
| } |
| } |
| |
| class GdiTextRenderer : public TextRenderer { |
| public: |
| GdiTextRenderer() |
| : render_info_(new RenderInfo[SIZE_OF_FONT_TYPE]) { |
| mem_dc_.CreateCompatibleDC(); |
| OnThemeChanged(); |
| } |
| |
| virtual ~GdiTextRenderer() { |
| } |
| |
| private: |
| // TextRenderer overrides: |
| virtual void OnThemeChanged() { |
| // delete old fonts |
| for (size_t i = 0; i < SIZE_OF_FONT_TYPE; ++i) { |
| if (!render_info_[i].font.IsNull()) { |
| render_info_[i].font.DeleteObject(); |
| } |
| } |
| |
| for (size_t i = 0; i < SIZE_OF_FONT_TYPE; ++i) { |
| const auto font_type = static_cast<FONT_TYPE>(i); |
| const auto &log_font = GetLogFont(font_type); |
| render_info_[i].style = GetGdiDrawTextStyle(font_type); |
| render_info_[i].font.CreateFontIndirectW(&log_font); |
| render_info_[i].color = GetTextColor(font_type); |
| } |
| } |
| |
| virtual Size MeasureString(FONT_TYPE font_type, const wstring &str) const { |
| const auto previous_font = mem_dc_.SelectFont(render_info_[font_type].font); |
| CRect rect; |
| mem_dc_.DrawTextW(str.c_str(), str.length(), &rect, |
| DT_NOPREFIX | DT_LEFT | DT_SINGLELINE | DT_CALCRECT); |
| mem_dc_.SelectFont(previous_font); |
| return Size(rect.Width(), rect.Height()); |
| } |
| |
| virtual Size MeasureStringMultiLine( |
| FONT_TYPE font_type, const wstring &str, const int width) const { |
| const auto previous_font = mem_dc_.SelectFont(render_info_[font_type].font); |
| CRect rect(0, 0, width, 0); |
| mem_dc_.DrawTextW(str.c_str(), str.length(), &rect, |
| DT_NOPREFIX | DT_LEFT | DT_WORDBREAK | DT_CALCRECT); |
| mem_dc_.SelectFont(previous_font); |
| return Size(rect.Width(), rect.Height()); |
| } |
| |
| virtual void RenderText(CDCHandle dc, |
| const wstring &text, |
| const Rect &rect, |
| FONT_TYPE font_type) const { |
| vector<TextRenderingInfo> infolist; |
| infolist.push_back(TextRenderingInfo(text, rect)); |
| RenderTextList(dc, infolist, font_type); |
| } |
| |
| virtual void RenderTextList(CDCHandle dc, |
| const vector<TextRenderingInfo> &display_list, |
| FONT_TYPE font_type) const { |
| const auto &render_info = render_info_[font_type]; |
| const auto old_font = dc.SelectFont(render_info.font); |
| const auto previous_color = dc.SetTextColor(render_info.color); |
| for (auto it = display_list.begin(); it != display_list.end(); ++it) { |
| const auto &info = *it; |
| CRect rect(info.rect.Left(), info.rect.Top(), |
| info.rect.Right(), info.rect.Bottom()); |
| const auto &text = info.text; |
| dc.DrawTextW(text.data(), text.size(), &rect, render_info.style); |
| } |
| dc.SetTextColor(previous_color); |
| dc.SelectFont(old_font); |
| } |
| |
| struct RenderInfo { |
| RenderInfo() : color(0), style(0) {} |
| COLORREF color; |
| DWORD style; |
| CFont font; |
| }; |
| unique_ptr<RenderInfo[]> render_info_; |
| mutable CDC mem_dc_; |
| |
| DISALLOW_COPY_AND_ASSIGN(GdiTextRenderer); |
| }; |
| |
| const D2D1_DRAW_TEXT_OPTIONS kD2D1DrawTextOptions = |
| static_cast<D2D1_DRAW_TEXT_OPTIONS>(4); |
| |
| class DirectWriteTextRenderer : public TextRenderer { |
| public: |
| static DirectWriteTextRenderer *Create() { |
| const auto d2d1 = WinUtil::LoadSystemLibrary(L"d2d1.dll"); |
| const auto d2d1_create_factory = |
| reinterpret_cast<D2D1CreateFactoryPtr>( |
| ::GetProcAddress(d2d1, "D2D1CreateFactory")); |
| if (d2d1_create_factory == nullptr) { |
| return nullptr; |
| } |
| |
| const auto dwrite = WinUtil::LoadSystemLibrary(L"dwrite.dll"); |
| const auto dwrite_create_factory = |
| reinterpret_cast<DWriteCreateFactoryPtr>( |
| ::GetProcAddress(dwrite, "DWriteCreateFactory")); |
| if (dwrite_create_factory == nullptr) { |
| return nullptr; |
| } |
| |
| HRESULT hr = S_OK; |
| CComPtr<ID2D1Factory> d2d_factory; |
| hr = d2d1_create_factory(D2D1_FACTORY_TYPE_SINGLE_THREADED, |
| __uuidof(ID2D1Factory), |
| nullptr, |
| reinterpret_cast<void **>(&d2d_factory)); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| CComPtr<IDWriteFactory> dwrite_factory; |
| hr = dwrite_create_factory( |
| DWRITE_FACTORY_TYPE_SHARED, |
| __uuidof(IDWriteFactory), |
| reinterpret_cast<IUnknown **>(&dwrite_factory)); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| CComPtr<IDWriteGdiInterop> interop; |
| hr = dwrite_factory->GetGdiInterop(&interop); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| const auto kernel32 = WinUtil::GetSystemModuleHandle(L"kernel32.dll"); |
| const auto get_user_default_locale_name = |
| reinterpret_cast<GetUserDefaultLocaleNamePtr>( |
| ::GetProcAddress(kernel32, "GetUserDefaultLocaleName")); |
| if (get_user_default_locale_name == nullptr) { |
| return nullptr; |
| } |
| return new DirectWriteTextRenderer( |
| d2d_factory, dwrite_factory, interop, get_user_default_locale_name); |
| } |
| virtual ~DirectWriteTextRenderer() { |
| } |
| |
| private: |
| typedef int (WINAPI *GetUserDefaultLocaleNamePtr)(LPWSTR locale_name, |
| int locale_name_buffer_len); |
| |
| typedef HRESULT (WINAPI *D2D1CreateFactoryPtr)( |
| D2D1_FACTORY_TYPE factory_type, |
| const IID &iid, |
| const D2D1_FACTORY_OPTIONS *factory_options, |
| void **factory); |
| |
| typedef HRESULT (WINAPI *DWriteCreateFactoryPtr)( |
| DWRITE_FACTORY_TYPE factory_type, |
| const IID &iid, |
| IUnknown **factory); |
| |
| DirectWriteTextRenderer( |
| ID2D1Factory *d2d2_factory, |
| IDWriteFactory *dwrite_factory, |
| IDWriteGdiInterop *dwrite_interop, |
| GetUserDefaultLocaleNamePtr get_user_default_locale_name) |
| : d2d2_factory_(d2d2_factory), |
| dwrite_factory_(dwrite_factory), |
| dwrite_interop_(dwrite_interop), |
| get_user_default_locale_name_(get_user_default_locale_name) { |
| OnThemeChanged(); |
| } |
| |
| // TextRenderer overrides: |
| virtual void OnThemeChanged() { |
| // delete old fonts |
| render_info_.clear(); |
| render_info_.resize(SIZE_OF_FONT_TYPE); |
| |
| for (size_t i = 0; i < SIZE_OF_FONT_TYPE; ++i) { |
| const auto font_type = static_cast<FONT_TYPE>(i); |
| const auto &log_font = GetLogFont(font_type); |
| render_info_[i].color = GetTextColor(font_type); |
| render_info_[i].format = CreateFormat(log_font); |
| render_info_[i].format_to_render = CreateFormat(log_font); |
| const auto style = GetGdiDrawTextStyle(font_type); |
| const auto render_font = render_info_[i].format_to_render; |
| if ((style & DT_VCENTER) == DT_VCENTER) { |
| render_font->SetParagraphAlignment(DWRITE_PARAGRAPH_ALIGNMENT_CENTER); |
| } |
| if ((style & DT_LEFT) == DT_LEFT) { |
| render_font->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_LEADING); |
| } |
| if ((style & DT_CENTER) == DT_CENTER) { |
| render_font->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER); |
| } |
| if ((style & DT_LEFT) == DT_RIGHT) { |
| render_font->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_TRAILING); |
| } |
| } |
| } |
| |
| // Retrieves the bounding box for a given string. |
| virtual Size MeasureString(FONT_TYPE font_type, const wstring &str) const { |
| return MeasureStringImpl(font_type, str, 0, false); |
| } |
| |
| virtual Size MeasureStringMultiLine(FONT_TYPE font_type, const wstring &str, |
| const int width) const { |
| return MeasureStringImpl(font_type, str, width, true); |
| } |
| |
| virtual void RenderText(CDCHandle dc, const wstring &text, |
| const Rect &rect, FONT_TYPE font_type) const { |
| vector<TextRenderingInfo> infolist; |
| infolist.push_back(TextRenderingInfo(text, rect)); |
| RenderTextList(dc, infolist, font_type); |
| } |
| |
| virtual void RenderTextList(CDCHandle dc, |
| const vector<TextRenderingInfo> &display_list, |
| FONT_TYPE font_type) const { |
| CreateRenderTargetIfNecessary(); |
| if (dc_render_target_ == nullptr) { |
| return; |
| } |
| CRect total_rect; |
| for (size_t i = 0; i < display_list.size(); ++i) { |
| const auto &item = display_list[i]; |
| total_rect.right = max(total_rect.right, item.rect.Right()); |
| total_rect.bottom = max(total_rect.right, item.rect.Bottom()); |
| } |
| HRESULT hr = S_OK; |
| hr = dc_render_target_->BindDC(dc, &total_rect); |
| if (FAILED(hr)) { |
| return; |
| } |
| CComPtr<ID2D1SolidColorBrush> brush; |
| hr = dc_render_target_->CreateSolidColorBrush( |
| ToD2DColor(render_info_[font_type].color), |
| &brush); |
| if (FAILED(hr)) { |
| return; |
| } |
| D2D1_DRAW_TEXT_OPTIONS option = D2D1_DRAW_TEXT_OPTIONS_NONE; |
| if (SystemUtil::IsWindows8_1OrLater()) { |
| option |= kD2D1DrawTextOptions; |
| } |
| dc_render_target_->BeginDraw(); |
| dc_render_target_->SetTransform(D2D1::Matrix3x2F::Identity()); |
| for (size_t i = 0; i < display_list.size(); ++i) { |
| const auto &item = display_list[i]; |
| const D2D1_RECT_F render_rect = { |
| item.rect.Left(), item.rect.Top(), item.rect.Right(), |
| item.rect.Bottom(), |
| }; |
| dc_render_target_->DrawText(item.text.data(), |
| item.text.size(), |
| render_info_[font_type].format_to_render, |
| render_rect, |
| brush, |
| option); |
| } |
| hr = dc_render_target_->EndDraw(); |
| if (hr == D2DERR_RECREATE_TARGET) { |
| dc_render_target_.Release(); |
| } |
| } |
| |
| Size MeasureStringImpl(FONT_TYPE font_type, const wstring &str, |
| const int width, bool use_width) const { |
| HRESULT hr = S_OK; |
| const FLOAT kLayoutLimit = 100000.0f; |
| CComPtr<IDWriteTextLayout> layout; |
| hr = dwrite_factory_->CreateTextLayout(str.data(), |
| str.size(), |
| render_info_[font_type].format, |
| (use_width ? width : kLayoutLimit), |
| kLayoutLimit, |
| &layout); |
| if (FAILED(hr)) { |
| return Size(); |
| } |
| DWRITE_TEXT_METRICS metrix = {}; |
| hr = layout->GetMetrics(&metrix); |
| if (FAILED(hr)) { |
| return Size(); |
| } |
| return Size(ceilf(metrix.widthIncludingTrailingWhitespace), |
| ceilf(metrix.height)); |
| } |
| |
| static D2D1_COLOR_F ToD2DColor(COLORREF color_ref) { |
| D2D1_COLOR_F color; |
| color.a = 1.0; |
| color.r = GetRValue(color_ref) / 255.0f; |
| color.g = GetGValue(color_ref) / 255.0f; |
| color.b = GetBValue(color_ref) / 255.0f; |
| return color; |
| } |
| |
| CComPtr<IDWriteTextFormat> CreateFormat(CLogFont logfont) { |
| HRESULT hr = S_OK; |
| CComPtr<IDWriteFont> font; |
| hr = dwrite_interop_->CreateFontFromLOGFONT(&logfont, &font); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| CComPtr<IDWriteFontFamily> font_family; |
| hr = font->GetFontFamily(&font_family); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| CComPtr<IDWriteLocalizedStrings> localized_family_names; |
| hr = font_family->GetFamilyNames(&localized_family_names); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| UINT32 length = 0; |
| hr = localized_family_names->GetStringLength(0, &length); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| length += 1; // for NUL. |
| unique_ptr<wchar_t[]> family_name(new wchar_t[length]); |
| hr = localized_family_names->GetString(0, family_name.get(), length); |
| if (FAILED(hr)) { |
| return nullptr; |
| } |
| auto font_size = logfont.lfHeight; |
| if (font_size < 0) { |
| font_size = -font_size; |
| } else { |
| DWRITE_FONT_METRICS font_metrix = {}; |
| font->GetMetrics(&font_metrix); |
| const auto cell_height = |
| static_cast<float>(font_metrix.ascent + font_metrix.descent) / |
| font_metrix.designUnitsPerEm; |
| font_size /= cell_height; |
| } |
| |
| wchar_t locale_name[LOCALE_NAME_MAX_LENGTH] = {}; |
| if (get_user_default_locale_name_(locale_name, arraysize(locale_name)) |
| == 0) { |
| return nullptr; |
| } |
| |
| CComPtr<IDWriteTextFormat> format; |
| hr = dwrite_factory_->CreateTextFormat(family_name.get(), |
| nullptr, |
| font->GetWeight(), |
| font->GetStyle(), |
| font->GetStretch(), |
| font_size, |
| locale_name, |
| &format); |
| return format; |
| } |
| |
| void CreateRenderTargetIfNecessary() const { |
| if (dc_render_target_) { |
| return; |
| } |
| const auto property = D2D1::RenderTargetProperties( |
| D2D1_RENDER_TARGET_TYPE_DEFAULT, |
| D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE), |
| 0, |
| 0, |
| D2D1_RENDER_TARGET_USAGE_NONE, |
| D2D1_FEATURE_LEVEL_DEFAULT); |
| d2d2_factory_->CreateDCRenderTarget(&property, &dc_render_target_); |
| } |
| |
| struct RenderInfo { |
| RenderInfo() : color(0) {} |
| COLORREF color; |
| CComPtr<IDWriteTextFormat> format; |
| CComPtr<IDWriteTextFormat> format_to_render; |
| }; |
| |
| CComPtr<ID2D1Factory> d2d2_factory_; |
| CComPtr<IDWriteFactory> dwrite_factory_; |
| mutable CComPtr<ID2D1DCRenderTarget> dc_render_target_; |
| CComPtr<IDWriteGdiInterop> dwrite_interop_; |
| vector<RenderInfo> render_info_; |
| GetUserDefaultLocaleNamePtr get_user_default_locale_name_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DirectWriteTextRenderer); |
| }; |
| |
| } // namespace |
| |
| TextRenderer::~TextRenderer() { |
| } |
| |
| // static |
| TextRenderer *TextRenderer::Create() { |
| auto *dwrite_text_renderer = DirectWriteTextRenderer::Create(); |
| if (dwrite_text_renderer != nullptr) { |
| return dwrite_text_renderer; |
| } |
| return new GdiTextRenderer(); |
| } |
| |
| } // namespace win32 |
| } // namespace renderer |
| } // namespace mozc |