Skip to content

Commit

Permalink
perf: rewrite UI redrawing
Browse files Browse the repository at this point in the history
  • Loading branch information
fxliang committed Nov 30, 2023
1 parent 936f0a5 commit d0c4da2
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 112 deletions.
27 changes: 13 additions & 14 deletions WeaselUI/DirectWriteResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ DirectWriteResources::~DirectWriteResources()
pD2d1Factory.Reset();
}

HRESULT DirectWriteResources::InitResources(std::wstring label_font_face, int label_font_point,
std::wstring font_face, int font_point,
std::wstring comment_font_face, int comment_font_point, bool vertical_text)
HRESULT DirectWriteResources::InitResources(const std::wstring& label_font_face, const int& label_font_point,
const std::wstring& font_face, const int& font_point,
const std::wstring& comment_font_face, const int& comment_font_point, const bool& vertical_text)
{
// prepare d2d1 resources
pPreeditTextFormat.Reset();
Expand Down Expand Up @@ -131,14 +131,13 @@ HRESULT DirectWriteResources::InitResources(std::wstring label_font_face, int la
pTextFormat->SetWordWrapping(wrapping);
_SetFontFallback(pTextFormat, fontFaceStrVector);
}
fontFaceStrVector.swap(std::vector<std::wstring>());
decltype(fontFaceStrVector)().swap(fontFaceStrVector);

fontFaceStrVector = ws_split(font_face, L",");
//_ParseFontFace(fontFaceStrVector[0], fontWeight, fontStyle);
fontFaceStrVector[0] = std::regex_replace(fontFaceStrVector[0], std::wregex(STYLEORWEIGHT, std::wregex::icase), L"");
hResult = pDWFactory->CreateTextFormat(_mainFontFace.c_str(), NULL,
fontWeight, fontStyle, DWRITE_FONT_STRETCH_NORMAL,
//font_point * dpiScaleX_, L"", reinterpret_cast<IDWriteTextFormat**>(&pPreeditTextFormat));
font_point * dpiScaleX_, L"", reinterpret_cast<IDWriteTextFormat**>(pPreeditTextFormat.GetAddressOf()));
if( pPreeditTextFormat != NULL)
{
Expand All @@ -154,7 +153,7 @@ HRESULT DirectWriteResources::InitResources(std::wstring label_font_face, int la
pPreeditTextFormat->SetWordWrapping(wrapping);
_SetFontFallback(pPreeditTextFormat, fontFaceStrVector);
}
fontFaceStrVector.swap(std::vector<std::wstring>());
decltype(fontFaceStrVector)().swap(fontFaceStrVector);

// label font text format set up
fontFaceStrVector = ws_split(label_font_face, L",");
Expand All @@ -178,7 +177,7 @@ HRESULT DirectWriteResources::InitResources(std::wstring label_font_face, int la
pLabelTextFormat->SetWordWrapping(wrapping);
_SetFontFallback(pLabelTextFormat, fontFaceStrVector);
}
fontFaceStrVector.swap(std::vector<std::wstring>());
decltype(fontFaceStrVector)().swap(fontFaceStrVector);

// comment font text format set up
fontFaceStrVector = ws_split(comment_font_face, L",");
Expand All @@ -202,11 +201,11 @@ HRESULT DirectWriteResources::InitResources(std::wstring label_font_face, int la
pCommentTextFormat->SetWordWrapping(wrapping);
_SetFontFallback(pCommentTextFormat, fontFaceStrVector);
}
fontFaceStrVector.swap(std::vector<std::wstring>());
decltype(fontFaceStrVector)().swap(fontFaceStrVector);
return hResult;
}

HRESULT DirectWriteResources::InitResources(UIStyle& style, UINT dpi = 96)
HRESULT DirectWriteResources::InitResources(const UIStyle& style, const UINT& dpi = 96)
{
_style = style;
if(dpi)
Expand All @@ -218,7 +217,7 @@ HRESULT DirectWriteResources::InitResources(UIStyle& style, UINT dpi = 96)
style.comment_font_face, style.comment_font_point, style.layout_type==UIStyle::LAYOUT_VERTICAL_TEXT);
}

void weasel::DirectWriteResources::SetDpi(UINT dpi)
void weasel::DirectWriteResources::SetDpi(const UINT& dpi)
{
dpiScaleX_ = dpi / 72.0f;
dpiScaleY_ = dpi / 72.0f;
Expand Down Expand Up @@ -250,7 +249,7 @@ static std::wstring _MatchWordsOutLowerCaseTrim1st(const std::wstring& wstr, con
return res;
}

void DirectWriteResources::_ParseFontFace(const std::wstring fontFaceStr, DWRITE_FONT_WEIGHT& fontWeight, DWRITE_FONT_STYLE& fontStyle)
void DirectWriteResources::_ParseFontFace(const std::wstring& fontFaceStr, DWRITE_FONT_WEIGHT& fontWeight, DWRITE_FONT_STYLE& fontStyle)
{
const std::wstring patWeight(L"(:thin|:extra_light|:ultra_light|:light|:semi_light|:medium|:demi_bold|:semi_bold|:bold|:extra_bold|:ultra_bold|:black|:heavy|:extra_black|:ultra_black)");
const std::map<std::wstring, DWRITE_FONT_WEIGHT> _mapWeight = {
Expand Down Expand Up @@ -286,7 +285,7 @@ void DirectWriteResources::_ParseFontFace(const std::wstring fontFaceStr, DWRITE
fontStyle = (it2 != _mapStyle.end()) ? it2->second : DWRITE_FONT_STYLE_NORMAL;
}

void DirectWriteResources::_SetFontFallback(ComPtr<IDWriteTextFormat1> textFormat, std::vector<std::wstring> fontVector)
void DirectWriteResources::_SetFontFallback(ComPtr<IDWriteTextFormat1> textFormat, const std::vector<std::wstring>& fontVector)
{
ComPtr<IDWriteFontFallback> pSysFallback;
pDWFactory->GetSystemFontFallback(pSysFallback.GetAddressOf());
Expand Down Expand Up @@ -338,13 +337,13 @@ void DirectWriteResources::_SetFontFallback(ComPtr<IDWriteTextFormat1> textForma
DWRITE_UNICODE_RANGE range = { first, last };
const WCHAR* familys = { _fontFaceWstr.c_str() };
pFontFallbackBuilder->AddMapping(&range, 1, &familys, 1);
fallbackFontsVector.swap(std::vector<std::wstring>());
decltype(fallbackFontsVector)().swap(fallbackFontsVector);
}
// add system defalt font fallback
pFontFallbackBuilder->AddMappings(pSysFallback.Get());
pFontFallbackBuilder->CreateFontFallback(pFontFallback.GetAddressOf());
textFormat->SetFontFallback(pFontFallback.Get());
fallbackFontsVector.swap(std::vector<std::wstring>());
decltype(fallbackFontsVector)().swap(fallbackFontsVector);
pFontFallback.Reset();
pSysFallback.Reset();
pFontFallbackBuilder.Reset();
Expand Down
129 changes: 74 additions & 55 deletions WeaselUI/WeaselPanel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ WeaselPanel::~WeaselPanel()
void WeaselPanel::_ResizeWindow()
{
CDCHandle dc = GetDC();
m_size = m_layout->GetContentSize();
CSize m_size = m_layout->GetContentSize();
SetWindowPos(NULL, 0, 0, m_size.cx, m_size.cy, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW);
ReleaseDC(dc);
}
Expand Down Expand Up @@ -121,16 +121,15 @@ void WeaselPanel::Refresh()
// show schema menu status: schema_id == L".default"
bool show_schema_menu = m_status.schema_id == L".default";
bool margin_negative = (m_style.margin_x < 0 || m_style.margin_y < 0);
bool inline_no_candidates = m_style.inline_preedit && (m_ctx.cinfo.candies.size() == 0) && (!show_tips);
// when to hide_cadidates?
// 1. inline_no_candidates
// or
// 2. margin_negative, and not in show tips mode( ascii switching / half-full switching / simp-trad switching / error tips), and not in schema menu
// 1. margin_negative, and not in show tips mode( ascii switching / half-full switching / simp-trad switching / error tips), and not in schema menu
// 2. inline preedit without candidates
bool inline_no_candidates = (m_style.inline_preedit && m_candidateCount == 0) && !show_tips;
hide_candidates = inline_no_candidates || (margin_negative && !show_tips && !show_schema_menu);

// only RedrawWindow if no need to hide candidates window
if(!hide_candidates)
{
// only RedrawWindow if no need to hide candidates window, or inline_no_candidates
if(!hide_candidates || inline_no_candidates)
{
_InitFontRes();
_CreateLayout();

Expand All @@ -139,19 +138,22 @@ void WeaselPanel::Refresh()
ReleaseDC(dc);
_ResizeWindow();
_RepositionWindow();
RedrawWindow();
if(m_ctx != m_octx) {
m_octx = m_ctx;
RedrawWindow();
}
}
}

void WeaselPanel::_InitFontRes(void)
void WeaselPanel::_InitFontRes(bool forced)
{
HMONITOR hMonitor = MonitorFromRect(m_inputPos, MONITOR_DEFAULTTONEAREST);
UINT dpiX = 96, dpiY = 96;
if (hMonitor)
GetDpiForMonitor(hMonitor, MDT_EFFECTIVE_DPI, &dpiX, &dpiY);
// prepare d2d1 resources
// if style changed, or dpi changed, or pDWR NULL, re-initialize directwrite resources
if ((pDWR == NULL) || (m_ostyle != m_style) || (dpiX != dpi))
if (forced || (pDWR == NULL) || (m_ostyle != m_style) || (dpiX != dpi))
{
pDWR.reset();
pDWR = std::make_shared< DirectWriteResources>(m_style, dpiX);
Expand Down Expand Up @@ -344,7 +346,11 @@ LRESULT WeaselPanel::OnMouseLeave(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&
return 0;
}

void WeaselPanel::_HighlightText(CDCHandle &dc, CRect rc, COLORREF color, COLORREF shadowColor, int radius, BackType type = BackType::TEXT, IsToRoundStruct rd = IsToRoundStruct(), COLORREF bordercolor=TRANS_COLOR)
void WeaselPanel::_HighlightText(CDCHandle &dc, const CRect& rc,
const COLORREF& color, const COLORREF& shadowColor, const int& radius,
const BackType& type = BackType::TEXT,
const IsToRoundStruct& rd = IsToRoundStruct(),
const COLORREF& bordercolor=TRANS_COLOR)
{
// Graphics obj with SmoothingMode
Gdiplus::Graphics g_back(dc);
Expand Down Expand Up @@ -429,7 +435,7 @@ void WeaselPanel::_HighlightText(CDCHandle &dc, CRect rc, COLORREF color, COLORR
}

// draw preedit text, text only
bool WeaselPanel::_DrawPreedit(Text const& text, CDCHandle dc, CRect const& rc)
bool WeaselPanel::_DrawPreedit(const Text& text, CDCHandle dc, const CRect& rc)
{
bool drawn = false;
std::wstring const& t = text.str;
Expand Down Expand Up @@ -516,7 +522,7 @@ bool WeaselPanel::_DrawPreedit(Text const& text, CDCHandle dc, CRect const& rc)
}

// draw hilited back color, back only
bool WeaselPanel::_DrawPreeditBack(Text const& text, CDCHandle dc, CRect const& rc)
bool WeaselPanel::_DrawPreeditBack(const Text& text, CDCHandle dc, const CRect& rc)
{
bool drawn = false;
std::wstring const& t = text.str;
Expand Down Expand Up @@ -573,7 +579,12 @@ bool WeaselPanel::_DrawCandidates(CDCHandle &dc, bool back)
const std::vector<Text> &candidates(m_ctx.cinfo.candies);
const std::vector<Text> &comments(m_ctx.cinfo.comments);
const std::vector<Text> &labels(m_ctx.cinfo.labels);

// prevent all text format nullptr
if(pDWR->pTextFormat.Get() == nullptr
&& pDWR->pLabelTextFormat.Get() == nullptr
&& pDWR->pCommentTextFormat.Get() == nullptr) {
_InitFontRes(true);
}
ComPtr<IDWriteTextFormat1> txtFormat = pDWR->pTextFormat;
ComPtr<IDWriteTextFormat1> labeltxtFormat = pDWR->pLabelTextFormat;
ComPtr<IDWriteTextFormat1> commenttxtFormat = pDWR->pCommentTextFormat;
Expand Down Expand Up @@ -627,23 +638,23 @@ bool WeaselPanel::_DrawCandidates(CDCHandle &dc, bool back)
_HighlightText(dc, rect, m_style.hilited_candidate_back_color, m_style.hilited_candidate_shadow_color, m_style.round_corner, bkType, rd, m_style.hilited_candidate_border_color);
if (m_style.mark_text.empty() && COLORNOTTRANSPARENT(m_style.hilited_mark_color))
{
BYTE r = GetRValue(m_style.hilited_mark_color);
BYTE g = GetGValue(m_style.hilited_mark_color);
BYTE b = GetBValue(m_style.hilited_mark_color);
BYTE alpha = (BYTE)((m_style.hilited_mark_color >> 24) & 255);
int height = min(rect.Height() - m_style.hilite_padding_y * 2, rect.Height() - m_style.round_corner * 2);
int width = min(rect.Width() - m_style.hilite_padding_x * 2, rect.Width() - m_style.round_corner * 2);
Gdiplus::Graphics g_back(dc);
g_back.SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeHighQuality);
Gdiplus::Color mark_color = Gdiplus::Color::MakeARGB(alpha, r, g, b);
Gdiplus::Color mark_color = GDPCOLOR_FROM_COLORREF(m_style.hilited_mark_color);
Gdiplus::SolidBrush mk_brush(mark_color);
if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT)
{
CRect mkrc{ rect.left + m_style.round_corner, rect.top, rect.right - m_style.round_corner, rect.top + m_layout->MARK_HEIGHT / 2 };
int x = rect.left + (rect.Width() - width)/2;
CRect mkrc{ x, rect.top, x + width, rect.top + m_layout->MARK_HEIGHT / 2 };
GraphicsRoundRectPath mk_path(mkrc, 2);
g_back.FillPath(&mk_brush, &mk_path);
}
else
{
CRect mkrc{ rect.left, rect.top + m_style.round_corner, rect.left + m_layout->MARK_WIDTH / 2, rect.bottom - m_style.round_corner };
int y = rect.top + (rect.Height() - height)/2;
CRect mkrc{ rect.left, y, rect.left + m_layout->MARK_WIDTH / 2, y + height };
GraphicsRoundRectPath mk_path(mkrc, 2);
g_back.FillPath(&mk_brush, &mk_path);
}
Expand Down Expand Up @@ -718,6 +729,8 @@ bool WeaselPanel::_DrawCandidates(CDCHandle &dc, bool back)
//draw client area
void WeaselPanel::DoPaint(CDCHandle dc)
{
// turn off WS_EX_TRANSPARENT, for better resp performance
ModifyStyleEx(WS_EX_TRANSPARENT, WS_EX_LAYERED);
GetClientRect(&rcw);
// prepare memDC
CDCHandle hdc = ::GetDC(m_hWnd);
Expand Down Expand Up @@ -763,7 +776,7 @@ void WeaselPanel::DoPaint(CDCHandle dc)
delete[] btmys;
}
// background and candidates back, hilite back drawing start
if (!m_ctx.empty()) {
if ((!m_ctx.empty() && !m_style.inline_preedit) || (m_style.inline_preedit && (m_candidateCount || !m_ctx.aux.empty() ))) {
CRect backrc = m_layout->GetContentRect();
_HighlightText(memDC, backrc, m_style.back_color, m_style.shadow_color, m_style.round_corner_ex, BackType::BACKGROUND, IsToRoundStruct(), m_style.border_color);
}
Expand All @@ -783,8 +796,11 @@ void WeaselPanel::DoPaint(CDCHandle dc)
drawn |= _DrawCandidates(memDC, true);
// background and candidates back, hilite back drawing end

// begin texts drawing
pDWR->pRenderTarget->BindDC(memDC, &rcw);
// begin texts drawing, if pRenderTarget failed, force to reinit directwrite resources
if (FAILED(pDWR->pRenderTarget->BindDC(memDC, &rcw))) {
_InitFontRes(true);
pDWR->pRenderTarget->BindDC(memDC, &rcw);
}
pDWR->pRenderTarget->BeginDraw();
// draw auxiliary string
if (!m_ctx.aux.str.empty())
Expand Down Expand Up @@ -822,8 +838,6 @@ void WeaselPanel::DoPaint(CDCHandle dc)
}
_LayerUpdate(rcw, memDC);

// turn off WS_EX_TRANSPARENT after drawings, for better resp performance
::SetWindowLong(m_hWnd, GWL_EXSTYLE, ::GetWindowLong(m_hWnd, GWL_EXSTYLE) & (~WS_EX_TRANSPARENT));
// clean objs
::DeleteDC(memDC);
::DeleteObject(memBitmap);
Expand All @@ -850,9 +864,8 @@ LRESULT WeaselPanel::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHa
return TRUE;
}

LRESULT WeaselPanel::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
LRESULT WeaselPanel::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
m_osize = {0,0};
delete m_layout;
m_layout = NULL;
return 0;
Expand All @@ -868,36 +881,34 @@ void WeaselPanel::MoveTo(RECT const& rc)
{
if(!m_layout) return; // avoid handling nullptr in _RepositionWindow
// if ascii_tip_follow_cursor set, move tip icon to mouse cursor
if(m_style.ascii_tip_follow_cursor && m_ctx.aux.empty() && (!m_status.composing) && m_layout->ShouldDisplayStatusIcon()) // ascii icon
{
if(m_style.ascii_tip_follow_cursor && m_ctx.empty()
&& (!m_status.composing) && m_layout->ShouldDisplayStatusIcon()) {
// ascii icon follow cursor
POINT p;
::GetCursorPos(&p);
RECT irc{p.x-STATUS_ICON_SIZE, p.y-STATUS_ICON_SIZE, p.x, p.y};
m_inputPos = irc;
m_oinputPos = irc;
_RepositionWindow(true);
RedrawWindow();
} else
if((rc.left != m_oinputPos.left && rc.bottom != m_oinputPos.bottom) // pos changed
|| rc.left != m_oinputPos.left
|| m_size != m_osize
|| m_octx != m_ctx
|| (m_ctx.preedit.str.empty() && (CRect(rc) == m_oinputPos)) // first click old pos
|| !m_ctx.aux.str.empty() // aux not empty, msg
|| (m_ctx.aux.empty() && (m_layout) && m_layout->ShouldDisplayStatusIcon())) // ascii icon
{
m_octx = m_ctx;
m_osize = m_size;
} else if (!(rc.left == m_inputPos.left && rc.bottom != m_inputPos.bottom
&& abs(rc.bottom - m_inputPos.bottom) < 6)) {
// in some apps like word 2021, with inline_preedit set,
// bottom of rc would flicker 1 px or 2, make the candidate flickering
m_inputPos = rc;
m_inputPos.OffsetRect(0, 6);
m_oinputPos = m_inputPos;
// buffer current m_istorepos status
bool m_istorepos_buf = m_istorepos;
// with parameter to avoid vertical flicker
_RepositionWindow(true);
RedrawWindow();
// m_istorepos status changed by _RepositionWindow, or tips to show,
// redrawing is required
if(m_istorepos != m_istorepos_buf || !m_ctx.aux.empty()
|| (m_ctx.empty() && m_layout->ShouldDisplayStatusIcon()))
RedrawWindow();
}
}

void WeaselPanel::_RepositionWindow(bool adj)
void WeaselPanel::_RepositionWindow(const bool& adj)
{
RECT rcWorkArea;
memset(&rcWorkArea, 0, sizeof(rcWorkArea));
Expand All @@ -918,32 +929,40 @@ void WeaselPanel::_RepositionWindow(bool adj)
rcWorkArea.bottom -= height;
int x = m_inputPos.left;
int y = m_inputPos.bottom;
x -= (m_style.shadow_offset_x >= 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetX : (m_layout->offsetX / 2);
if(adj) y -= (m_style.shadow_offset_y > 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetY : (m_layout->offsetY / 2);
if(m_style.shadow_radius)
{
x -= (m_style.shadow_offset_x >= 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetX : (m_layout->offsetX / 2);
if (adj)
y -= (m_style.shadow_offset_y > 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetY : (m_layout->offsetY / 2);
}
// for vertical text layout, flow right to left, make window left side
if(m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT && !m_style.vertical_text_left_to_right)
{
x += m_layout->offsetX - width;
if ( m_style.shadow_offset_x < 0 ) x += m_layout->offsetX;
if (m_style.shadow_offset_x < 0)
x += m_layout->offsetX;
}
if(adj) m_istorepos = false;
if (x > rcWorkArea.right) x = rcWorkArea.right; // over workarea right
if (x < rcWorkArea.left) x = rcWorkArea.left; // over workarea left
// show panel above the input focus if we're around the bottom
if (y > rcWorkArea.bottom) {
y = m_inputPos.top - height - 6; // over workarea bottom
if( !adj && (y + height < m_oinputPos.top) )
y = m_oinputPos.top - height;
if(m_style.shadow_radius && m_style.shadow_offset_y > 0)
y -= m_style.shadow_offset_y;
m_istorepos = (m_style.vertical_auto_reverse && m_style.layout_type == UIStyle::LAYOUT_VERTICAL);
y += (m_style.shadow_offset_y < 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetY : (m_layout->offsetY / 2);
}
if(m_style.shadow_radius > 0)
y += (m_style.shadow_offset_y < 0 || COLORTRANSPARENT(m_style.shadow_color)) ? m_layout->offsetY : (m_layout->offsetY / 2);
}
if (y < rcWorkArea.top) y = rcWorkArea.top; // over workarea top
// memorize adjusted position (to avoid window bouncing on height change)
m_inputPos.bottom = y;
SetWindowPos(HWND_TOPMOST, x, y, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW);
}

void WeaselPanel::_TextOut(CRect const& rc, std::wstring psz, size_t cch, int inColor, IDWriteTextFormat1* pTextFormat)
void WeaselPanel::_TextOut(const CRect& rc, const std::wstring& psz,
const size_t& cch, const int& inColor,
IDWriteTextFormat1* const pTextFormat)
{
if (pTextFormat == NULL) return;
float r = (float)(GetRValue(inColor))/255.0f;
Expand All @@ -960,7 +979,7 @@ void WeaselPanel::_TextOut(CRect const& rc, std::wstring psz, size_t cch, int in
pDWR->SetBrushColor(D2D1::ColorF(r, g, b, alpha));

if (NULL != pDWR->pBrush && NULL != pTextFormat) {
pDWR->CreateTextLayout( psz.c_str(), (UINT32)psz.size(), pTextFormat, (float)rc.Width(), (float)rc.Height());
pDWR->CreateTextLayout( psz.c_str(), (int)cch, pTextFormat, (float)rc.Width(), (float)rc.Height());
if (m_style.layout_type == UIStyle::LAYOUT_VERTICAL_TEXT) {
DWRITE_FLOW_DIRECTION flow = m_style.vertical_text_left_to_right ? DWRITE_FLOW_DIRECTION_LEFT_TO_RIGHT : DWRITE_FLOW_DIRECTION_RIGHT_TO_LEFT;
pDWR->SetLayoutReadingDirection(DWRITE_READING_DIRECTION_TOP_TO_BOTTOM);
Expand Down
Loading

0 comments on commit d0c4da2

Please sign in to comment.