1 #include "stdafx.h" 2 #include "CUIDrawer.h" 3 #include "MusicPlayer2.h" 4 5 6 CUIDrawer::CUIDrawer(UIColors& colors) 7 : m_colors(colors) 8 { 9 } 10 11 12 CUIDrawer::~CUIDrawer() 13 { 14 } 15 16 void CUIDrawer::DrawLryicCommon(CRect rect, Alignment align) 17 { 18 SetDrawArea(rect); 19 20 if (!IsDrawMultiLine(rect.Height())) 21 DrawLyricTextSingleLine(rect, true, align); 22 else 23 DrawLyricTextMultiLine(rect, align); 24 } 25 26 int CUIDrawer::GetLyricTextHeight() const 27 { 28 //计算文本高度 29 if (!m_for_cortana_lyric) 30 m_pDC->SelectObject(&theApp.m_font_set.lyric.GetFont(theApp.m_ui_data.full_screen)); 31 else 32 m_pDC->SelectObject(&theApp.m_font_set.cortana.GetFont()); 33 return m_pDC->GetTextExtent(L"文").cy; //根据当前的字体设置计算文本的高度 34 } 35 36 void CUIDrawer::Create(CDC* pDC, CWnd* pMainWnd) 37 { 38 CDrawCommon::Create(pDC, pMainWnd); 39 } 40 41 bool CUIDrawer::IsDrawMultiLine(int height) const 42 { 43 return height >= static_cast<int>(GetLyricTextHeight() * 3.5); 44 } 45 46 void CUIDrawer::SetForCortanaLyric(bool for_cortana_lyric) 47 { 48 m_for_cortana_lyric = for_cortana_lyric; 49 } 50 51 void CUIDrawer::DrawLyricTextMultiLine(CRect lyric_area, Alignment align) 52 { 53 int line_space{}; 54 if (m_for_cortana_lyric) 55 { 56 line_space = theApp.DPI(4); 57 } 58 else 59 { 60 line_space = theApp.m_lyric_setting_data.lyric_line_space; 61 if (theApp.m_ui_data.full_screen) 62 line_space = static_cast<int>(line_space * CONSTVAL::FULL_SCREEN_ZOOM_FACTOR); 63 } 64 65 int lyric_height = GetLyricTextHeight() + line_space; //文本高度加上行间距 66 int lyric_height2 = lyric_height * 2 + line_space; //包含翻译的歌词高度 67 68 CFont* pOldFont = SetLyricFont(); 69 if (CPlayer::GetInstance().IsPlaylistEmpty()) //当前播放为空时在歌词区域显示播放提示 70 { 71 CFont* font = SetFont(&theApp.m_font_set.font10.GetFont()); 72 CString no_track_tip_str{ CCommon::LoadTextFormat(IDS_NO_TRACKS_TIP_INFO, { theApp.m_accelerator_res.GetShortcutDescriptionById(ID_SHOW_PLAYLIST), theApp.m_accelerator_res.GetShortcutDescriptionById(ID_FILE_OPEN), theApp.m_accelerator_res.GetShortcutDescriptionById(ID_FILE_OPEN_FOLDER), theApp.m_accelerator_res.GetShortcutDescriptionById(ID_SET_PATH)})}; 73 DrawWindowText(lyric_area, no_track_tip_str, m_colors.color_text_2, Alignment::LEFT, false, true); 74 SetFont(font); 75 } 76 else if (CPlayerUIHelper::IsMidiLyric()) 77 { 78 wstring current_lyric{ CPlayer::GetInstance().GetMidiLyric() }; 79 DrawWindowText(lyric_area, current_lyric.c_str(), m_colors.color_text, Alignment::CENTER, false, true); 80 } 81 else if (CPlayer::GetInstance().m_Lyrics.IsEmpty()) 82 { 83 DrawWindowText(lyric_area, CCommon::LoadText(IDS_NO_LYRIC_INFO), m_colors.color_text_2, Alignment::CENTER); 84 } 85 else 86 { 87 //CRect arect{ lyric_area }; //一行歌词的矩形区域 88 //arect.bottom = arect.top + lyric_height; 89 //vector<CRect> rects(CPlayer::GetInstance().m_Lyrics.GetLyricCount() + 1, arect); 90 //为每一句歌词创建一个矩形,保存在容器里 91 vector<CRect> rects; 92 int lyric_count = CPlayer::GetInstance().m_Lyrics.GetLyricCount() + 1; //获取歌词数量(由于第一行歌词需要显示标题,所以这里要+1) 93 for (int i{}; i < lyric_count; i++) 94 { 95 CRect arect{ lyric_area }; 96 if (!CPlayer::GetInstance().m_Lyrics.GetLyric(i - 1).translate.empty() && theApp.m_lyric_setting_data.show_translate) 97 arect.bottom = arect.top + lyric_height2; 98 else 99 arect.bottom = arect.top + lyric_height; 100 rects.push_back(arect); 101 } 102 int center_pos = (lyric_area.top + lyric_area.bottom) / 2; //歌词区域的中心y坐标 103 Time time{ CPlayer::GetInstance().GetCurrentPosition() }; //当前播放时间 104 int lyric_index = CPlayer::GetInstance().m_Lyrics.GetLyricIndex(time); // 当前歌词的序号 105 int progress{ CPlayer::GetInstance().m_Lyrics.GetLyricProgress(time, false, false, [this](const wstring& str) { return GetTextExtent(str.c_str()).cx; }) }; // 当前歌词进度(范围为0~1000),多行歌词使用的进度不含进度符号 106 int y_progress; //当前歌词在y轴上的进度 107 if (!CPlayer::GetInstance().m_Lyrics.GetLyric(lyric_index).translate.empty() && theApp.m_lyric_setting_data.show_translate) 108 y_progress = progress * lyric_height2 / 1000; 109 else 110 y_progress = progress * lyric_height / 1000; 111 //int start_pos = center_pos - y_progress - (lyric_index + 1)*lyric_height; //第1句歌词的起始y坐标 112 //计算第1句歌词的起始y坐标 113 //由于当前歌词需要显示在歌词区域的中心位置,因此从中心位置开始,减去当前歌词在Y轴上的进度 114 //再依次减去之前每一句歌词的高度,即得到了第一句歌词的起始位置 115 int start_pos; 116 start_pos = center_pos - y_progress; 117 for (int i{ lyric_index - 1 }; i >= -1; i--) 118 { 119 if (theApp.m_lyric_setting_data.show_translate && !CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.empty()) 120 start_pos -= lyric_height2; 121 else 122 start_pos -= lyric_height; 123 } 124 125 //依次绘制每一句歌词 126 for (int i{ -1 }; i < static_cast<int>(rects.size()) - 1; i++) 127 { 128 //计算每一句歌词的位置 129 if (i == -1) 130 rects[i + 1].MoveToY(start_pos); 131 else 132 rects[i + 1].MoveToY(rects[i].bottom); 133 //绘制歌词文本 134 if (!(rects[i + 1] & lyric_area).IsRectEmpty()) //只有当一句歌词的矩形区域和歌词区域的矩形有交集时,才绘制歌词 135 { 136 //设置歌词文本和翻译文本的矩形区域 137 CRect rect_text{ rects[i + 1] }; 138 CRect rect_translate; 139 const CLyrics::Lyric& lyric_i = CPlayer::GetInstance().m_Lyrics.GetLyric(i); 140 if (!lyric_i.translate.empty() && theApp.m_lyric_setting_data.show_translate) 141 { 142 rect_text.MoveToY(rect_text.top + line_space); 143 rect_text.bottom = rect_text.top + GetLyricTextHeight(); 144 rect_translate = rect_text; 145 rect_translate.MoveToY(rect_text.bottom + line_space); 146 } 147 148 if (i == lyric_index && progress < 1000) //绘制正在播放的歌词(处于逐渐过度到高亮过程中的歌词) 149 { 150 //这里实现文本从非高亮缓慢变化到高亮效果 151 int last_time_span = time - lyric_i.time_start; //当前播放的歌词已持续的时间 152 int fade_percent = last_time_span / 8; //计算颜色高亮变化的百分比,除数越大则持续时间越长,10则为1秒 153 COLORREF text_color = CColorConvert::GetGradientColor(m_colors.color_text_2, m_colors.color_text, fade_percent); 154 //绘制歌词文本 155 SetLyricFont(); 156 if (theApp.m_lyric_setting_data.lyric_karaoke_disp) 157 DrawWindowText(rect_text, lyric_i.text.c_str(), m_colors.color_text, m_colors.color_text_2, progress, align, true); 158 else 159 DrawWindowText(rect_text, lyric_i.text.c_str(), text_color, text_color, progress, align, true); 160 //绘制翻译文本 161 if (!lyric_i.translate.empty() && theApp.m_lyric_setting_data.show_translate) 162 { 163 SetLyricFontTranslated(); 164 DrawWindowText(rect_translate, lyric_i.translate.c_str(), text_color, text_color, progress, align, true); 165 } 166 } 167 else //绘制非正在播放的歌词 168 { 169 SetLyricFont(); 170 COLORREF text_color; 171 if (i == lyric_index - 1 || (i == lyric_index && progress == 1000)) // 绘制正在取消高亮的歌词(这里实现一句歌词颜色从高亮缓慢变化到非高亮效果),逐字歌词最后一句在此处取消高亮 172 { 173 int last_time_span = time - (lyric_i.time_start + lyric_i.time_span); // 引入逐字歌词后上句歌词结束后时长不等于当前播放的歌词已持续的时间,此处应当使用上句歌词结束时间 174 int fade_percent = last_time_span / 20; //计算颜色高亮变化的百分比,当持续时间为2000毫秒时为100%,即颜色缓慢变化的时间为2秒 175 text_color = CColorConvert::GetGradientColor(m_colors.color_text, m_colors.color_text_2, fade_percent); 176 } 177 else 178 { 179 text_color = m_colors.color_text_2; 180 } 181 //绘制歌词文本 182 DrawWindowText(rect_text, lyric_i.text.c_str(), text_color, align, true); 183 //绘制翻译文本 184 if (!lyric_i.translate.empty() && theApp.m_lyric_setting_data.show_translate) 185 { 186 SetLyricFontTranslated(); 187 DrawWindowText(rect_translate, lyric_i.translate.c_str(), text_color, align, true); 188 } 189 } 190 } 191 } 192 } 193 SetFont(pOldFont); 194 } 195 196 void CUIDrawer::DrawLyricTextSingleLine(CRect rect, bool double_line, Alignment align) 197 { 198 CFont* pOldFont = SetLyricFont(); 199 200 if (CPlayerUIHelper::IsMidiLyric()) 201 { 202 wstring current_lyric{ CPlayer::GetInstance().GetMidiLyric() }; 203 DrawWindowText(rect, current_lyric.c_str(), m_colors.color_text, Alignment::CENTER, false, true); 204 } 205 else if (CPlayer::GetInstance().m_Lyrics.IsEmpty()) 206 { 207 DrawWindowText(rect, CCommon::LoadText(IDS_NO_LYRIC_INFO), m_colors.color_text_2, Alignment::CENTER); 208 } 209 else 210 { 211 SetDrawArea(rect); 212 CRect lyric_rect = rect; 213 214 const bool karaoke{ theApp.m_lyric_setting_data.lyric_karaoke_disp }; 215 const bool ignore_blank{ theApp.m_lyric_setting_data.donot_show_blank_lines}; 216 auto& now_lyrics{ CPlayer::GetInstance().m_Lyrics }; 217 Time time{ CPlayer::GetInstance().GetCurrentPosition() }; 218 CLyrics::Lyric& current_lyric{ now_lyrics.GetLyric(time, false, ignore_blank, karaoke) }; 219 int progress{ now_lyrics.GetLyricProgress(time, ignore_blank, karaoke, [this](const wstring& str) { return GetTextExtent(str.c_str()).cx; }) }; 220 static int last_progress{ -1 }; 221 222 if (current_lyric.text.empty()) 223 current_lyric.text = CCommon::LoadText(IDS_DEFAULT_LYRIC_TEXT); 224 //双行显示歌词 225 if (double_line && (current_lyric.translate.empty() || !theApp.m_lyric_setting_data.show_translate) && rect.Height() > static_cast<int>(GetLyricTextHeight() * 1.73)) 226 { 227 wstring next_lyric_text; 228 next_lyric_text = now_lyrics.GetLyric(time, true, ignore_blank, karaoke).text; 229 if (next_lyric_text.empty()) 230 next_lyric_text = CCommon::LoadText(IDS_DEFAULT_LYRIC_TEXT); 231 //这里实现文本从非高亮缓慢变化到高亮效果 232 int last_time_span = time - current_lyric.time_start; //当前播放的歌词已持续的时间 233 int fade_percent = last_time_span / 8; //计算颜色高亮变化的百分比,除数越大则持续时间越长,10则为1秒 234 if (progress == 1000) fade_percent = 0; // 进度为1000时当前歌词“已完成”不再高亮 235 // 这里的fade_percent当合并空行开启时可能为负,在颜色渐变处规范取值,此处不再处理 236 DrawLyricDoubleLine(lyric_rect, current_lyric.text.c_str(), next_lyric_text.c_str(), progress, last_progress > progress, fade_percent); 237 } 238 else 239 { 240 // 单行歌词在这里显示翻译,同时更新歌词区域为单行有翻译时的位置 241 if (theApp.m_lyric_setting_data.show_translate && !current_lyric.translate.empty() && rect.Height() > static_cast<int>(GetLyricTextHeight() * 1.73)) 242 { 243 lyric_rect.bottom = lyric_rect.top + rect.Height() / 2; 244 CRect translate_rect = lyric_rect; 245 translate_rect.MoveToY(lyric_rect.bottom); 246 247 SetLyricFontTranslated(); 248 DrawWindowText(translate_rect, current_lyric.translate.c_str(), m_colors.color_text, m_colors.color_text, progress, align, true); 249 } 250 // 绘制单行歌词 251 SetLyricFont(); 252 if (theApp.m_lyric_setting_data.lyric_karaoke_disp) 253 DrawWindowText(lyric_rect, current_lyric.text.c_str(), m_colors.color_text, m_colors.color_text_2, progress, align, true); 254 else if (0 < progress && progress < 1000) // 仅高亮“正在进行”的歌词 255 DrawWindowText(lyric_rect, current_lyric.text.c_str(), m_colors.color_text, m_colors.color_text, progress, align, true); 256 else 257 DrawWindowText(lyric_rect, current_lyric.text.c_str(), m_colors.color_text_2, m_colors.color_text_2, progress, align, true); 258 } 259 last_progress = progress; 260 } 261 262 SetFont(pOldFont); 263 } 264 265 void CUIDrawer::DrawSpectrum(CRect rect, SpectrumCol col, bool draw_reflex /*= false*/, bool low_freq_in_center, bool fixed_width, Alignment alignment) 266 { 267 int cols; //要显示的频谱柱形的数量 268 switch (col) 269 { 270 case CUIDrawer::SC_64: 271 cols = 64; 272 break; 273 case CUIDrawer::SC_32: 274 cols = 32; 275 break; 276 case CUIDrawer::SC_16: 277 cols = 16; 278 break; 279 case CUIDrawer::SC_8: 280 cols = 8; 281 break; 282 default: 283 cols = SPECTRUM_COL; 284 break; 285 } 286 int max_width{ rect.Width() }; 287 if (fixed_width) 288 { 289 if (col == SC_64) 290 { 291 max_width = DPI(280); 292 } 293 } 294 int gap_width{ max_width * (SPECTRUM_COL / cols) / 168 }; //频谱柱形间隙宽度 295 if (theApp.m_ui_data.full_screen && !m_for_cortana_lyric) 296 gap_width *= CONSTVAL::FULL_SCREEN_ZOOM_FACTOR; 297 int width = (max_width - (cols - 1) * gap_width) / (cols - 1); 298 if (gap_width < 1) 299 gap_width = 1; 300 if (width < 1) 301 width = 1; 302 303 if (fixed_width) 304 SetDrawArea(rect); 305 DrawSpectrum(rect, width, gap_width, cols, m_colors.color_spectrum, draw_reflex, low_freq_in_center, alignment); 306 } 307 308 void CUIDrawer::DrawSpectrum(CRect rect, int col_width, int gap_width, int cols, COLORREF color, bool draw_reflex /*= false*/, bool low_freq_in_center, Alignment alignment) 309 { 310 CRect rc_spectrum_top = rect; 311 if (draw_reflex) //如果要绘制倒影,则倒影占总高度的1/3 312 rc_spectrum_top.bottom = rect.top + (rect.Height() * 2 / 3); 313 314 CRect rects[SPECTRUM_COL]; 315 rects[0] = rc_spectrum_top; 316 rects[0].right = rects[0].left + col_width; 317 318 //频谱的实际宽度 319 int width_actrual{ col_width * cols + gap_width * (cols - 1) }; 320 //如果频谱的实际宽度小于矩形的宽度,则让根据alignment的值让频谱居中或右对齐显示 321 if ((width_actrual < rect.Width() && alignment == Alignment::CENTER) || (width_actrual >= rect.Width() && theApp.m_app_setting_data.spectrum_low_freq_in_center)) 322 rects[0].MoveToX(rects[0].left + (rect.Width() - width_actrual) / 2); 323 else if (width_actrual < rect.Width() && alignment == Alignment::RIGHT) 324 rects[0].MoveToX(rects[0].left + (rect.Width() - width_actrual)); 325 326 for (int i{ 1 }; i < cols; i++) 327 { 328 rects[i] = rects[0]; 329 rects[i].left += (i * (col_width + gap_width)); 330 rects[i].right += (i * (col_width + gap_width)); 331 } 332 for (int i{}; i < cols; i++) 333 { 334 int index; 335 if (low_freq_in_center) 336 { 337 if (i < cols / 2) 338 index = (-i + cols / 2) * 2 - 1; 339 else 340 index = (i - cols / 2) * 2; 341 } 342 else 343 { 344 index = i; 345 } 346 if (index >= cols) 347 index = cols; 348 if (index < 0) 349 index = 0; 350 float spetral_data = CPlayer::GetInstance().GetSpectralData()[index * (SPECTRUM_COL / cols)]; 351 float peak_data = CPlayer::GetInstance().GetSpectralPeakData()[index * (SPECTRUM_COL / cols)]; 352 353 CRect rect_tmp{ rects[i] }; 354 int spetral_height = static_cast<int>(spetral_data * rects[0].Height() / 30 * theApp.m_app_setting_data.sprctrum_height / 100); 355 int peak_height = static_cast<int>(peak_data * rects[0].Height() / 30 * theApp.m_app_setting_data.sprctrum_height / 100); 356 if (spetral_height < 0 || CPlayer::GetInstance().IsError()) spetral_height = 0; //如果播放出错,不显示频谱 357 if (peak_height < 0 || CPlayer::GetInstance().IsError()) peak_height = 0; 358 359 int peak_rect_height = max(theApp.DPIRound(1.1), gap_width / 2); //顶端矩形的高度 360 spetral_height += peak_rect_height; //频谱至少和顶端矩形一样高 361 peak_height += peak_rect_height; 362 363 rect_tmp.top = rect_tmp.bottom - spetral_height; 364 if (rect_tmp.top < rects[0].top) rect_tmp.top = rects[0].top; 365 FillRect(rect_tmp, color, true); 366 //绘制倒影 367 if (draw_reflex) 368 { 369 CRect rc_invert = rect_tmp; 370 rc_invert.bottom = rect_tmp.top + rect_tmp.Height() * 2 / 3; 371 rc_invert.MoveToY(rect_tmp.bottom + gap_width); 372 FillAlphaRect(rc_invert, color, 96, true); 373 } 374 375 //绘制顶端 376 CRect rect_peak{ rect_tmp }; 377 rect_peak.bottom = rect_tmp.bottom - peak_height - gap_width; 378 rect_peak.top = rect_peak.bottom - peak_rect_height; 379 FillRect(rect_peak, color, true); 380 ////绘制顶端倒影 381 //CRect rc_peak_invert = rect_peak; 382 //rc_peak_invert.MoveToY(rc_invert.top + peak_height + theApp.DPIRound(1.1)); 383 //FillAlphaRect(rc_peak_invert, color, 96); 384 } 385 386 } 387 388 int CUIDrawer::DPI(int pixel) 389 { 390 if (theApp.m_ui_data.full_screen && !m_for_cortana_lyric) 391 return static_cast<int>(theApp.DPI(pixel * CONSTVAL::FULL_SCREEN_ZOOM_FACTOR)); 392 else 393 return theApp.DPI(pixel); 394 } 395 396 void CUIDrawer::DrawLyricDoubleLine(CRect rect, LPCTSTR lyric, LPCTSTR next_lyric, int progress, bool switch_flag, int fade_percent) 397 { 398 CFont* pOldFont = SetLyricFont(); 399 static bool swap{}; 400 swap ^= switch_flag; 401 402 CRect up_rect{ rect }, down_rect{ rect }; //上半部分和下半部分歌词的矩形区域 403 up_rect.bottom = up_rect.top + (up_rect.Height() / 2); 404 down_rect.top = down_rect.bottom - (down_rect.Height() / 2); 405 406 //根据下一句歌词的文本计算需要的宽度,从而实现下一行歌词右对齐 407 //GetDC()->SelectObject(&theApp.m_font_set.lyric.GetFont(theApp.m_ui_data.full_screen)); 408 int width; 409 if (!swap) 410 width = GetTextExtent(next_lyric).cx; 411 else 412 width = GetTextExtent(lyric).cx; 413 if (width < rect.Width()) 414 down_rect.left = down_rect.right - width; 415 416 COLORREF color1, color2; 417 if (theApp.m_lyric_setting_data.lyric_karaoke_disp) 418 { 419 color1 = m_colors.color_text; 420 color2 = m_colors.color_text_2; 421 } 422 else 423 { 424 color1 = color2 = CColorConvert::GetGradientColor(m_colors.color_text_2, m_colors.color_text, fade_percent); 425 } 426 427 // 绘制当前歌词 428 if (!swap) 429 { 430 DrawWindowText(up_rect, lyric, color1, color2, progress); 431 DrawWindowText(down_rect, next_lyric, m_colors.color_text_2); 432 } 433 else 434 { 435 DrawWindowText(up_rect, next_lyric, m_colors.color_text_2); 436 DrawWindowText(down_rect, lyric, color1, color2, progress); 437 } 438 SetFont(pOldFont); 439 } 440 441 CFont* CUIDrawer::SetLyricFont() 442 { 443 if (!m_for_cortana_lyric) 444 return SetFont(&theApp.m_font_set.lyric.GetFont(theApp.m_ui_data.full_screen)); 445 else 446 return SetFont(&theApp.m_font_set.cortana.GetFont()); 447 } 448 449 CFont* CUIDrawer::SetLyricFontTranslated() 450 { 451 if (!m_for_cortana_lyric) 452 return SetFont(&theApp.m_font_set.lyric_translate.GetFont(theApp.m_ui_data.full_screen)); 453 else 454 return SetFont(&theApp.m_font_set.cortana_translate.GetFont()); 455 } 456