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