xref: /MusicPlayer2/MusicPlayer2/CUIDrawer.cpp (revision e496400b88f7fbf29a2315234990ff687f065638)
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 (CPlayerUIHelper::IsMidiLyric())
70     {
71         wstring current_lyric{ CPlayer::GetInstance().GetMidiLyric() };
72         DrawWindowText(lyric_area, current_lyric.c_str(), m_colors.color_text, Alignment::CENTER, false, true);
73     }
74     else if (CPlayer::GetInstance().m_Lyrics.IsEmpty())
75     {
76         DrawWindowText(lyric_area, CCommon::LoadText(IDS_NO_LYRIC_INFO), m_colors.color_text_2, Alignment::CENTER);
77     }
78     else
79     {
80         //CRect arect{ lyric_area };		//一行歌词的矩形区域
81         //arect.bottom = arect.top + lyric_height;
82         //vector<CRect> rects(CPlayer::GetInstance().m_Lyrics.GetLyricCount() + 1, arect);
83         //为每一句歌词创建一个矩形,保存在容器里
84         vector<CRect> rects;
85         int lyric_count = CPlayer::GetInstance().m_Lyrics.GetLyricCount() + 1;		//获取歌词数量(由于第一行歌词需要显示标题,所以这里要+1)
86         for (int i{}; i < lyric_count; i++)
87         {
88             CRect arect{ lyric_area };
89             if (!CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.empty() && theApp.m_lyric_setting_data.show_translate)
90                 arect.bottom = arect.top + lyric_height2;
91             else
92                 arect.bottom = arect.top + lyric_height;
93             rects.push_back(arect);
94         }
95         int center_pos = (lyric_area.top + lyric_area.bottom) / 2;		//歌词区域的中心y坐标
96         Time time{ CPlayer::GetInstance().GetCurrentPosition() };		//当前播放时间
97         int lyric_index = CPlayer::GetInstance().m_Lyrics.GetLyricIndex(time) + 1;		//当前歌词的序号(歌词的第一句GetLyricIndex返回的是0,由于显示时第一句歌词要显示标题,所以这里要+1)
98         int progress = CPlayer::GetInstance().m_Lyrics.GetLyricProgress(time);		//当前歌词进度(范围为0~1000)
99         int y_progress;			//当前歌词在y轴上的进度
100         if (!CPlayer::GetInstance().m_Lyrics.GetLyric(lyric_index).translate.empty() && theApp.m_lyric_setting_data.show_translate)
101             y_progress = progress * lyric_height2 / 1000;
102         else
103             y_progress = progress * lyric_height / 1000;
104         //int start_pos = center_pos - y_progress - (lyric_index + 1)*lyric_height;		//第1句歌词的起始y坐标
105         //计算第1句歌词的起始y坐标
106         //由于当前歌词需要显示在歌词区域的中心位置,因此从中心位置开始,减去当前歌词在Y轴上的进度
107         //再依次减去之前每一句歌词的高度,即得到了第一句歌词的起始位置
108         int start_pos;
109         start_pos = center_pos - y_progress;
110         for (int i{ lyric_index - 1 }; i >= 0; i--)
111         {
112             if (theApp.m_lyric_setting_data.show_translate && !CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.empty())
113                 start_pos -= lyric_height2;
114             else
115                 start_pos -= lyric_height;
116         }
117 
118         //依次绘制每一句歌词
119         for (size_t i{}; i < rects.size(); i++)
120         {
121             //计算每一句歌词的位置
122             if (i == 0)
123                 rects[i].MoveToY(start_pos);
124             else
125                 rects[i].MoveToY(rects[i - 1].bottom);
126             //绘制歌词文本
127             if (!(rects[i] & lyric_area).IsRectEmpty())		//只有当一句歌词的矩形区域和歌词区域的矩形有交集时,才绘制歌词
128             {
129                 //设置歌词文本和翻译文本的矩形区域
130                 CRect rect_text{ rects[i] };
131                 CRect rect_translate;
132                 if (!CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.empty() && theApp.m_lyric_setting_data.show_translate)
133                 {
134                     rect_text.MoveToY(rect_text.top + line_space);
135                     rect_text.bottom = rect_text.top + GetLyricTextHeight();
136                     rect_translate = rect_text;
137                     rect_translate.MoveToY(rect_text.bottom + line_space);
138                 }
139 
140                 if (i == lyric_index && progress < 1000)		//绘制正在播放的歌词
141                 {
142                     CLyrics::Lyric lyric = CPlayer::GetInstance().m_Lyrics.GetLyric(i);
143                     //这里实现文本从非高亮缓慢变化到高亮效果
144                     int last_time_span = time - lyric.time;     //当前播放的歌词已持续的时间
145                     int fade_percent = last_time_span / 8;         //计算颜色高亮变化的百分比,除数越大则持续时间越长,10则为1秒
146                     COLORREF text_color = CColorConvert::GetGradientColor(m_colors.color_text_2, m_colors.color_text, fade_percent);
147                     //绘制歌词文本
148                     SetLyricFont();
149                     if (theApp.m_lyric_setting_data.lyric_karaoke_disp)
150                         DrawWindowText(rect_text, lyric.text.c_str(), m_colors.color_text, m_colors.color_text_2, progress, align, true);
151                     else
152                         DrawWindowText(rect_text, lyric.text.c_str(), text_color, text_color, progress, align, true);
153                     //绘制翻译文本
154                     if (!lyric.translate.empty() && theApp.m_lyric_setting_data.show_translate)
155                     {
156                         SetLyricFontTranslated();
157                         DrawWindowText(rect_translate, CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.c_str(), text_color, text_color, progress, align, true);
158                     }
159                 }
160                 else		//绘制非正在播放的歌词
161                 {
162                     SetLyricFont();
163                     COLORREF text_color;
164                     if (i == lyric_index - 1)      //绘制上一句歌词(这里实现上一句歌词颜色从高亮缓慢变化到非高亮效果)
165                     {
166                         CLyrics::Lyric playing_lyric = CPlayer::GetInstance().m_Lyrics.GetLyric(lyric_index);
167                         int last_time_span = time - playing_lyric.time;     //当前播放的歌词已持续的时间
168                         int fade_percent = last_time_span / 20;         //计算颜色高亮变化的百分比,当持续时间为2000毫秒时为100%,即颜色缓慢变化的时间为2秒
169                         text_color = CColorConvert::GetGradientColor(m_colors.color_text, m_colors.color_text_2, fade_percent);
170                     }
171                     else
172                     {
173                         text_color = m_colors.color_text_2;
174                     }
175                     //绘制歌词文本
176                     DrawWindowText(rect_text, CPlayer::GetInstance().m_Lyrics.GetLyric(i).text.c_str(), text_color, align, true);
177                     //绘制翻译文本
178                     if (!CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.empty() && theApp.m_lyric_setting_data.show_translate)
179                     {
180                         SetLyricFontTranslated();
181                         DrawWindowText(rect_translate, CPlayer::GetInstance().m_Lyrics.GetLyric(i).translate.c_str(), text_color, align, true);
182                     }
183                 }
184             }
185         }
186     }
187     SetFont(pOldFont);
188 }
189 
190 void CUIDrawer::DrawLyricTextSingleLine(CRect rect, bool double_line, Alignment align)
191 {
192     CFont* pOldFont = SetLyricFont();
193 
194     if (CPlayerUIHelper::IsMidiLyric())
195     {
196         wstring current_lyric{ CPlayer::GetInstance().GetMidiLyric() };
197         DrawWindowText(rect, current_lyric.c_str(), m_colors.color_text, Alignment::CENTER, false, true);
198     }
199     else if (CPlayer::GetInstance().m_Lyrics.IsEmpty())
200     {
201         DrawWindowText(rect, CCommon::LoadText(IDS_NO_LYRIC_INFO), m_colors.color_text_2, Alignment::CENTER);
202     }
203     else
204     {
205         SetDrawArea(rect);
206         CRect lyric_rect = rect;
207 
208         const bool old_mode{ !theApp.m_lyric_setting_data.lyric_karaoke_disp || !theApp.m_lyric_setting_data.donot_show_blank_lines };
209         const wstring mark{ L"♪♪♪" };
210 
211         auto& now_lyrics{ CPlayer::GetInstance().m_Lyrics };
212         Time time{ CPlayer::GetInstance().GetCurrentPosition() };
213         CLyrics::Lyric current_lyric = now_lyrics.GetLyric(time, 0);
214         bool is_lyric_empty{ current_lyric.text.empty() };
215         int lyric_mode{};       // 0:默认状态 1:有进度符号且正在显示进度符号 2:有进度符号且正在显示歌词
216         int lyric_index = now_lyrics.GetLyricIndex(time);
217         int progress = now_lyrics.GetLyricProgress(time);
218 
219         if (old_mode)  // 设置为独立显示空行
220         {
221             if (is_lyric_empty)
222                 current_lyric.text = CCommon::LoadText(IDS_DEFAULT_LYRIC_TEXT_CORTANA);
223         }
224         else
225         {
226             lyric_index = now_lyrics.GetLyricIndexIgnoreBlank(lyric_index, 0); // 使用忽略空白行后的索引
227             if (is_lyric_empty)                                                // 如果当前time对应歌词为空则试着忽略空白获取歌词
228                 current_lyric = now_lyrics.GetLyricIgnoreBlank(lyric_index);
229             if (current_lyric.text.empty())                                            // 如果忽略空白仍然不能取得歌词说明时间已超过末尾的歌词
230                 current_lyric.text = CCommon::LoadText(IDS_DEFAULT_LYRIC_TEXT);
231             else
232             {
233                 bool blanktimeok{ now_lyrics.GetBlankTimeBeforeLyric(lyric_index) > LYRIC_BLANK_IGNORE_TIME };   // 判断空白时长是否有必要显示符号
234                 if (is_lyric_empty)                                            // 当前time处在空白行中并且正在提前显示下一行歌词
235                 {
236                     if (blanktimeok)
237                     {
238                         progress = now_lyrics.GetBlankLyricProgress(lyric_index, time);      // 获取合并空白行后的进度
239                         lyric_mode = 1;
240                     }
241                     else
242                         progress = 0;
243                 }
244                 else if (blanktimeok)                                          // 当前time对应非空歌词但是上行歌词为空
245                     lyric_mode = 2;
246             }
247         }
248 
249         //双行显示歌词
250         if (double_line && (!CPlayer::GetInstance().m_Lyrics.IsTranslated() || !theApp.m_lyric_setting_data.show_translate) && rect.Height() > static_cast<int>(GetLyricTextHeight() * 1.73))
251         {
252             wstring next_lyric_text;
253             if (old_mode)
254             {
255                 next_lyric_text = now_lyrics.GetLyric(time, 1).text;
256                 if (next_lyric_text.empty())
257                     next_lyric_text = CCommon::LoadText(IDS_DEFAULT_LYRIC_TEXT);
258             }
259             else
260             {
261                 int next_lyric_index{ now_lyrics.GetLyricIndexIgnoreBlank(lyric_index, 1) };
262                 next_lyric_text = now_lyrics.GetLyricIgnoreBlank(next_lyric_index).text;
263                 if (next_lyric_text.empty())
264                     next_lyric_text = CCommon::LoadText(IDS_DEFAULT_LYRIC_TEXT);
265                 else if (now_lyrics.GetBlankTimeBeforeLyric(next_lyric_index) > LYRIC_BLANK_IGNORE_TIME)
266                     next_lyric_text = mark + L" " + next_lyric_text;
267             }
268             //这里实现文本从非高亮缓慢变化到高亮效果
269             int last_time_span = time - current_lyric.time;     //当前播放的歌词已持续的时间
270             int fade_percent = last_time_span / 8;         //计算颜色高亮变化的百分比,除数越大则持续时间越长,10则为1秒
271             DrawLyricDoubleLine(lyric_rect, mark.c_str(), current_lyric.text.c_str(), next_lyric_text.c_str(), progress, lyric_index, lyric_mode, fade_percent);
272         }
273         else
274         {
275             // 单行歌词在这里显示翻译,同时更新歌词区域为单行有翻译时的位置
276             if (theApp.m_lyric_setting_data.show_translate && !current_lyric.translate.empty() && rect.Height() > static_cast<int>(GetLyricTextHeight() * 1.73))
277             {
278                 lyric_rect.bottom = lyric_rect.top + rect.Height() / 2;
279                 CRect translate_rect = lyric_rect;
280                 translate_rect.MoveToY(lyric_rect.bottom);
281 
282                 SetLyricFontTranslated();
283                 DrawWindowText(translate_rect, current_lyric.translate.c_str(), m_colors.color_text, m_colors.color_text, progress, align, true);
284             }
285             // 绘制单行歌词
286             SetLyricFont();
287             if (lyric_mode == 1)
288                 DrawWindowTextForLyric(lyric_rect, mark.c_str(), current_lyric.text.c_str(), m_colors.color_text, theApp.m_lyric_setting_data.lyric_karaoke_disp ? m_colors.color_text_2 : m_colors.color_text, progress, true, align, true);
289             else if (lyric_mode == 2)
290                 DrawWindowTextForLyric(lyric_rect, mark.c_str(), current_lyric.text.c_str(), m_colors.color_text, theApp.m_lyric_setting_data.lyric_karaoke_disp ? m_colors.color_text_2 : m_colors.color_text, progress, false, align, true);
291             else
292                 DrawWindowText(lyric_rect, current_lyric.text.c_str(), m_colors.color_text, theApp.m_lyric_setting_data.lyric_karaoke_disp ? m_colors.color_text_2 : m_colors.color_text, progress, align, true);
293         }
294     }
295 
296     SetFont(pOldFont);
297 }
298 
299 void CUIDrawer::DrawSpectrum(CRect rect, SpectrumCol col, bool draw_reflex /*= false*/, bool low_freq_in_center, bool fixed_width)
300 {
301     int cols;		//要显示的频谱柱形的数量
302     switch (col)
303     {
304     case CUIDrawer::SC_64:
305         cols = 64;
306         break;
307     case CUIDrawer::SC_32:
308         cols = 32;
309         break;
310     case CUIDrawer::SC_16:
311         cols = 16;
312         break;
313     case CUIDrawer::SC_8:
314         cols = 8;
315         break;
316     default:
317         cols = SPECTRUM_COL;
318         break;
319     }
320     int max_width{ rect.Width() };
321     if (fixed_width)
322     {
323         if (col == SC_64)
324         {
325             max_width = DPI(280);
326         }
327     }
328     int gap_width{ max_width * (SPECTRUM_COL / cols) / 168 };		//频谱柱形间隙宽度
329     if (theApp.m_ui_data.full_screen && !m_for_cortana_lyric)
330         gap_width *= CONSTVAL::FULL_SCREEN_ZOOM_FACTOR;
331     int width = (max_width - (cols - 1) * gap_width) / (cols - 1);
332     if (gap_width < 1)
333         gap_width = 1;
334     if (width < 1)
335         width = 1;
336 
337     if (fixed_width)
338         SetDrawArea(rect);
339     DrawSpectrum(rect, width, gap_width, cols, m_colors.color_spectrum, draw_reflex, low_freq_in_center);
340 }
341 
342 void CUIDrawer::DrawSpectrum(CRect rect, int col_width, int gap_width, int cols, COLORREF color, bool draw_reflex /*= false*/, bool low_freq_in_center)
343 {
344     CRect rc_spectrum_top = rect;
345     if (draw_reflex)     //如果要绘制倒影,则倒影占总高度的1/3
346         rc_spectrum_top.bottom = rect.top + (rect.Height() * 2 / 3);
347 
348     CRect rects[SPECTRUM_COL];
349     rects[0] = rc_spectrum_top;
350     rects[0].right = rects[0].left + col_width;
351     for (int i{ 1 }; i < cols; i++)
352     {
353         rects[i] = rects[0];
354         rects[i].left += (i * (col_width + gap_width));
355         rects[i].right += (i * (col_width + gap_width));
356     }
357     for (int i{}; i < cols; i++)
358     {
359         int index;
360         if (low_freq_in_center)
361         {
362             if (i < cols / 2)
363                 index = (-i + cols / 2) * 2 - 1;
364             else
365                 index = (i - cols / 2) * 2;
366         }
367         else
368         {
369             index = i;
370         }
371         if (index >= cols)
372             index = cols;
373         if (index < 0)
374             index = 0;
375         float spetral_data = CPlayer::GetInstance().GetSpectralData()[index * (SPECTRUM_COL / cols)];
376         float peak_data = CPlayer::GetInstance().GetSpectralPeakData()[index * (SPECTRUM_COL / cols)];
377 
378         CRect rect_tmp{ rects[i] };
379         int spetral_height = static_cast<int>(spetral_data * rects[0].Height() / 30 * theApp.m_app_setting_data.sprctrum_height / 100);
380         int peak_height = static_cast<int>(peak_data * rects[0].Height() / 30 * theApp.m_app_setting_data.sprctrum_height / 100);
381         if (spetral_height < 0 || CPlayer::GetInstance().IsError()) spetral_height = 0;		//如果播放出错,不显示频谱
382         if (peak_height < 0 || CPlayer::GetInstance().IsError()) peak_height = 0;
383 
384         int peak_rect_height = max(theApp.DPIRound(1.1), gap_width / 2);        //顶端矩形的高度
385         spetral_height += peak_rect_height;                                     //频谱至少和顶端矩形一样高
386         peak_height += peak_rect_height;
387 
388         rect_tmp.top = rect_tmp.bottom - spetral_height;
389         if (rect_tmp.top < rects[0].top) rect_tmp.top = rects[0].top;
390         FillRect(rect_tmp, color, true);
391         //绘制倒影
392         if (draw_reflex)
393         {
394             CRect rc_invert = rect_tmp;
395             rc_invert.bottom = rect_tmp.top + rect_tmp.Height() * 2 / 3;
396             rc_invert.MoveToY(rect_tmp.bottom + gap_width);
397             FillAlphaRect(rc_invert, color, 96, true);
398         }
399 
400         //绘制顶端
401         CRect rect_peak{ rect_tmp };
402         rect_peak.bottom = rect_tmp.bottom - peak_height - gap_width;
403         rect_peak.top = rect_peak.bottom - peak_rect_height;
404         FillRect(rect_peak, color, true);
405         ////绘制顶端倒影
406         //CRect rc_peak_invert = rect_peak;
407         //rc_peak_invert.MoveToY(rc_invert.top + peak_height + theApp.DPIRound(1.1));
408         //FillAlphaRect(rc_peak_invert, color, 96);
409     }
410 
411 }
412 
413 int CUIDrawer::DPI(int pixel)
414 {
415     if (theApp.m_ui_data.full_screen && !m_for_cortana_lyric)
416         return static_cast<int>(theApp.DPI(pixel * CONSTVAL::FULL_SCREEN_ZOOM_FACTOR));
417     else
418         return theApp.DPI(pixel);
419 }
420 
421 void CUIDrawer::DrawLyricDoubleLine(CRect rect, LPCTSTR beforelyric, LPCTSTR lyric, LPCTSTR next_lyric, int progress, int index, int lyric_mode, int fade_percent)
422 {
423     CFont* pOldFont = SetLyricFont();
424     static bool swap;
425     static int last_index;
426     if (last_index != index)		//如果歌词索引改变,说明歌词切换到了下一句
427     {
428         swap = !swap;
429     }
430     last_index = index;
431 
432     CRect up_rect{ rect }, down_rect{ rect };		//上半部分和下半部分歌词的矩形区域
433     up_rect.bottom = up_rect.top + (up_rect.Height() / 2);
434     down_rect.top = down_rect.bottom - (down_rect.Height() / 2);
435 
436     //根据下一句歌词的文本计算需要的宽度,从而实现下一行歌词右对齐
437     //GetDC()->SelectObject(&theApp.m_font_set.lyric.GetFont(theApp.m_ui_data.full_screen));
438     int width;
439     if (!swap)
440         width = GetTextExtent(next_lyric).cx;
441     else if (lyric_mode != 0)
442     {
443         wstring mark_lyric;
444         mark_lyric.append(beforelyric).append(L" ").append(lyric);
445         width = GetTextExtent(mark_lyric.c_str()).cx;
446     }
447     else
448         width = GetTextExtent(lyric).cx;
449     if (width < rect.Width())
450         down_rect.left = down_rect.right - width;
451 
452     COLORREF color1, color2;
453     if (theApp.m_lyric_setting_data.lyric_karaoke_disp)
454     {
455         color1 = m_colors.color_text;
456         color2 = m_colors.color_text_2;
457     }
458     else
459     {
460         color1 = color2 = CColorConvert::GetGradientColor(m_colors.color_text_2, m_colors.color_text, fade_percent);
461     }
462 
463     // 绘制当前歌词
464     if (lyric_mode == 1)
465         DrawWindowTextForLyric(swap ? down_rect : up_rect, beforelyric, lyric, color1, color2, progress, true);
466     else if (lyric_mode == 2)
467         DrawWindowTextForLyric(swap ? down_rect : up_rect, beforelyric, lyric, color1, color2, progress, false);
468     else
469         DrawWindowText(swap ? down_rect : up_rect, lyric, color1, color2, progress);
470     //DrawWindowText(swap ? down_rect : up_rect, lyric, color1, color2, progress);
471 
472     // 绘制下一句歌词
473     DrawWindowText(swap ? up_rect : down_rect, next_lyric, m_colors.color_text_2);
474     SetFont(pOldFont);
475 }
476 
477 CFont* CUIDrawer::SetLyricFont()
478 {
479     if (!m_for_cortana_lyric)
480         return SetFont(&theApp.m_font_set.lyric.GetFont(theApp.m_ui_data.full_screen));
481     else
482         return SetFont(&theApp.m_font_set.cortana.GetFont());
483 }
484 
485 CFont* CUIDrawer::SetLyricFontTranslated()
486 {
487     if (!m_for_cortana_lyric)
488         return SetFont(&theApp.m_font_set.lyric_translate.GetFont(theApp.m_ui_data.full_screen));
489     else
490         return SetFont(&theApp.m_font_set.cortana_translate.GetFont());
491 }
492