xref: /MusicPlayer2/MusicPlayer2/DrawCommon.cpp (revision bdab36f8b39bc8fbd10db7970e6ea1bfffbbdfed)
1 #include "stdafx.h"
2 #include "DrawCommon.h"
3 #include "GdiPlusTool.h"
4 
5 
Reset()6 void CDrawCommon::ScrollInfo::Reset()
7 {
8     shift_cnt = 0;
9     shift_dir = false;
10     freez = 20;
11     dir_changed = false;
12 }
13 
14 /////////////////////////////////////////////////////////////////////////
15 /////////////////////////////////////////////////////////////////////////
CDrawCommon()16 CDrawCommon::CDrawCommon()
17 {
18 }
19 
~CDrawCommon()20 CDrawCommon::~CDrawCommon()
21 {
22     if (m_auto_destory_graphics)
23         SAFE_DELETE(m_pGraphics);
24 }
25 
Create(CDC * pDC,CFont * pFont)26 void CDrawCommon::Create(CDC* pDC, CFont* pFont)
27 {
28     m_pDC = pDC;
29     m_pfont = pFont;
30     if (pDC != nullptr)
31     {
32         m_pGraphics = new Gdiplus::Graphics(pDC->GetSafeHdc());
33         m_auto_destory_graphics = true;
34     }
35 }
36 
Create(CDC * pDC,Gdiplus::Graphics * pGraphics,CFont * pFont)37 void CDrawCommon::Create(CDC* pDC, Gdiplus::Graphics* pGraphics, CFont* pFont)
38 {
39     m_pDC = pDC;
40     m_pfont = pFont;
41     m_pGraphics = pGraphics;
42     m_auto_destory_graphics = false;
43 }
44 
45 //void CDrawCommon::SetBackColor(COLORREF back_color)
46 //{
47 //  m_backColor = back_color;
48 //}
49 
SetFont(CFont * pFont)50 CFont* CDrawCommon::SetFont(CFont* pFont)
51 {
52     CFont* pOldFont = m_pfont;
53     m_pfont = pFont;
54     return pOldFont;
55 }
56 
SetDC(CDC * pDC)57 void CDrawCommon::SetDC(CDC* pDC)
58 {
59     m_pDC = pDC;
60     if (m_auto_destory_graphics)
61         SAFE_DELETE(m_pGraphics);
62     m_pGraphics = new Gdiplus::Graphics(pDC->GetSafeHdc());
63     m_auto_destory_graphics = true;
64 }
65 
DrawWindowText(CRect rect,LPCTSTR lpszString,COLORREF color,Alignment align,bool no_clip_area,bool multi_line,bool default_right_align)66 void CDrawCommon::DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color, Alignment align, bool no_clip_area, bool multi_line, bool default_right_align)
67 {
68     if (m_pDC->GetSafeHdc() == NULL)
69         return;
70     ASSERT(align != Alignment::AUTO);
71     m_pDC->SetTextColor(color);
72     m_pDC->SetBkMode(TRANSPARENT);
73     ASSERT(m_pfont != nullptr); // 请先设置字体
74     if (m_pfont != nullptr)
75         m_pDC->SelectObject(m_pfont);
76     //设置绘图的剪辑区域
77     DrawAreaGuard guard(this, rect, true, !no_clip_area);
78     CSize text_size = m_pDC->GetTextExtent(lpszString);
79     //用背景色填充矩形区域
80     //m_pDC->FillSolidRect(rect, m_backColor);
81     UINT format{};
82     switch (align)
83     {
84     case Alignment::RIGHT: format = DT_RIGHT; break;
85     case Alignment::CENTER: format = DT_CENTER; break;
86     }
87     if (multi_line)
88     {
89         CRect text_rect{ rect };
90         int height = m_pDC->DrawText(lpszString, text_rect, DT_CALCRECT | DT_CENTER | DT_EDITCONTROL | DT_WORDBREAK);
91         if (height < rect.Height())
92             rect.top += (rect.Height() - height) / 2;
93         rect.bottom = rect.top + height;
94         m_pDC->DrawText(lpszString, rect, format | DT_EDITCONTROL | DT_WORDBREAK | DT_NOPREFIX);
95     }
96     else
97     {
98         if (text_size.cx > rect.Width())        //如果文本宽度超过了矩形区域的宽度,设置了居中时左对齐
99         {
100             format = (default_right_align ? DT_RIGHT : 0);
101         }
102         m_pDC->DrawText(lpszString, rect, format | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX | DT_END_ELLIPSIS);
103     }
104 }
105 
DrawWindowText(CRect rect,LPCTSTR lpszString,COLORREF color1,COLORREF color2,int split,Alignment align,bool no_clip_area)106 void CDrawCommon::DrawWindowText(CRect rect, LPCTSTR lpszString, COLORREF color1, COLORREF color2, int split, Alignment align, bool no_clip_area)
107 {
108     if (m_pDC->GetSafeHdc() == NULL)
109         return;
110     ASSERT(align != Alignment::AUTO);
111     if (split < 0) split = 0;
112     if (split > 1000) split = 1000;
113     m_pDC->SetBkMode(TRANSPARENT);
114     ASSERT(m_pfont != nullptr); // 请先设置字体
115     if (m_pfont != nullptr)
116         m_pDC->SelectObject(m_pfont);
117     CSize text_size;    //文本的大小
118     int text_top, text_left;        //输出文本的top和left位置
119     //设置绘图的剪辑区域,防止文字输出超出控件区域
120     DrawAreaGuard guard(this, rect, true, !no_clip_area);
121     //获取文字的宽度和高度
122     text_size = m_pDC->GetTextExtent(lpszString);
123     //计算文字的起始坐标
124     text_top = rect.top + (rect.Height() - text_size.cy) / 2;
125     if (align == Alignment::CENTER)
126         text_left = rect.left + (rect.Width() - text_size.cx) / 2;
127     else if (align == Alignment::RIGHT)
128         text_left = rect.left + (rect.Width() - text_size.cx);
129     else
130         text_left = rect.left;
131     //计算背景文字和覆盖文字的矩形区域
132     CRect text_rect{ CPoint{ text_left, text_top }, text_size };        //背景文字的区域
133     CRect text_f_rect{ CPoint{ text_left, text_top }, CSize{ text_size.cx * split / 1000, text_size.cy } };     //覆盖文字的区域
134                                                                                                                 //如果文本宽度大于控件宽度,就要根据分割的位置滚动文本
135     if (text_size.cx > rect.Width())
136     {
137         //如果分割的位置(歌词进度)剩下的宽度已经小于控件宽度的一半,此时使文本右侧和控件右侧对齐
138         if (text_rect.Width() - text_f_rect.Width() < rect.Width() / 2)
139         {
140             text_rect.MoveToX(rect.left - (text_rect.Width() - rect.Width()));
141             text_f_rect.MoveToX(text_rect.left);
142         }
143         //分割位置剩下的宽度还没有到小于控件宽度的一半,但是分割位置的宽度已经大于控件宽度的一半时,需要移动文本使分割位置正好在控件的中间
144         else if (text_f_rect.Width() > rect.Width() / 2)
145         {
146             text_rect.MoveToX(rect.left - (text_f_rect.Width() - rect.Width() / 2));
147             text_f_rect.MoveToX(text_rect.left);
148         }
149         //分割位置还不到控件宽度的一半时,使文本左侧和控件左侧对齐
150         else
151         {
152             text_rect.MoveToX(rect.left);
153             text_f_rect.MoveToX(rect.left);
154         }
155     }
156 
157     //用背景色填充矩形区域
158     //m_pDC->FillSolidRect(rect, m_backColor);
159     //输出文本
160     m_pDC->SetTextColor(color2);
161     m_pDC->DrawText(lpszString, text_rect, DT_SINGLELINE | DT_NOPREFIX);        //绘制背景文字
162     if (color1 != color2 && split != 1000)  // 进度1000表示当前歌词“已完成”不进行高亮
163     {
164         m_pDC->SetTextColor(color1);
165         m_pDC->DrawText(lpszString, text_f_rect, DT_SINGLELINE | DT_NOPREFIX);      //绘制覆盖文字
166     }
167 }
168 
DrawScrollText(CRect rect,LPCTSTR lpszString,COLORREF color,double pixel,bool center,ScrollInfo & scroll_info,bool reset,bool no_clip_area)169 void CDrawCommon::DrawScrollText(CRect rect, LPCTSTR lpszString, COLORREF color, double pixel, bool center, ScrollInfo& scroll_info, bool reset, bool no_clip_area)
170 {
171     if (m_pDC->GetSafeHdc() == NULL)
172         return;
173     //static int shift_cnt;     //移动的次数
174     //static bool shift_dir;        //移动的方向,右移为false,左移为true
175     //static int freez;         //当该变量大于0时,文本不滚动,直到小于等于0为止
176     //static bool dir_changed{ false }; //如果方向发生了变化,则为true
177 
178     if (scroll_info.last_string.GetLength() != CString(lpszString).GetLength())      //当显示文本长度发生变化时,重置滚动位置
179     {
180         reset = true;
181         scroll_info.last_string = lpszString;
182     }
183 
184     if (reset)
185     {
186         scroll_info.Reset();
187     }
188     m_pDC->SetTextColor(color);
189     m_pDC->SetBkMode(TRANSPARENT);
190     ASSERT(m_pfont != nullptr); // 请先设置字体
191     if (m_pfont != nullptr)
192         m_pDC->SelectObject(m_pfont);
193     CSize text_size;    //文本的大小
194     int text_top, text_left;        //输出文本的top和left位置
195     //设置绘图的剪辑区域,防止文字输出超出控件区域
196     DrawAreaGuard guard(this, rect, true, !no_clip_area);
197     //获取文字的宽度和高度
198     text_size = m_pDC->GetTextExtent(lpszString);
199     //计算文字的起始坐标
200     text_top = rect.top + (rect.Height() - text_size.cy) / 2;
201     if (center)
202         text_left = rect.left + (rect.Width() - text_size.cx) / 2;
203     else
204         text_left = rect.left;
205     //计算文字的矩形区域
206     CRect text_rect{ CPoint{ text_left, text_top }, text_size };
207     //如果文本宽度大于控件宽度,就滚动文本
208     if (text_size.cx > rect.Width())
209     {
210         text_rect.MoveToX(static_cast<int>((rect.left - scroll_info.shift_cnt * pixel)));
211         if ((text_rect.right < rect.right || text_rect.left > rect.left))       //移动到边界时换方向
212         {
213             if (!scroll_info.dir_changed)
214             {
215                 scroll_info.shift_dir = !scroll_info.shift_dir;
216                 scroll_info.freez = 20;     //变换方向时稍微暂停滚动一段时间
217             }
218             scroll_info.dir_changed = true;
219         }
220         else
221         {
222             scroll_info.dir_changed = false;
223         }
224     }
225     //用背景色填充矩形区域
226     //m_pDC->FillSolidRect(rect, m_backColor);
227     //输出文本
228     m_pDC->DrawText(lpszString, text_rect, DT_SINGLELINE | DT_NOPREFIX);
229     if (scroll_info.freez <= 0)     //当freez为0的时候才滚动
230     {
231         if (scroll_info.shift_dir)
232             scroll_info.shift_cnt--;
233         else
234             scroll_info.shift_cnt++;
235     }
236     else
237     {
238         scroll_info.freez--;
239     }
240     if ((rect & text_rect).IsRectEmpty())       //如果文本矩形区域已经超出了绘图区域,则重置滚动
241     {
242         reset = true;
243         scroll_info.shift_cnt = 0;
244         scroll_info.freez = 20;
245     }
246 }
247 
DrawScrollText2(CRect rect,LPCTSTR lpszString,COLORREF color,double pixel,bool center,ScrollInfo & scroll_info,bool reset)248 void CDrawCommon::DrawScrollText2(CRect rect, LPCTSTR lpszString, COLORREF color, double pixel, bool center, ScrollInfo& scroll_info, bool reset)
249 {
250     if (m_pDC->GetSafeHdc() == NULL)
251         return;
252     if (scroll_info.last_string.GetLength() != CString(lpszString).GetLength())      //当显示文本长度发生变化时,重置滚动位置
253     {
254         reset = true;
255         scroll_info.last_string = lpszString;
256     }
257 
258     if (reset)
259     {
260         scroll_info.shift_cnt = 0;
261         scroll_info.freez = 20;
262     }
263     m_pDC->SetTextColor(color);
264     m_pDC->SetBkMode(TRANSPARENT);
265     ASSERT(m_pfont != nullptr); // 请先设置字体
266     if (m_pfont != nullptr)
267         m_pDC->SelectObject(m_pfont);
268     CSize text_size;    //文本的大小
269     int text_top, text_left;        //输出文本的top和left位置
270     //设置绘图的剪辑区域,防止文字输出超出控件区域
271     DrawAreaGuard guard(this, rect, true);
272     //获取文字的宽度和高度
273     text_size = m_pDC->GetTextExtent(lpszString);
274     //计算文字的起始坐标
275     text_top = rect.top + (rect.Height() - text_size.cy) / 2;
276     if (center)
277         text_left = rect.left + (rect.Width() - text_size.cx) / 2;
278     else
279         text_left = rect.left;
280     //计算文字的矩形区域
281     CRect text_rect{ CPoint{ text_left, text_top }, text_size };
282     //如果文本宽度大于控件宽度,就滚动文本
283     if (text_size.cx > rect.Width())
284     {
285         text_rect.MoveToX(static_cast<int>(rect.left - scroll_info.shift_cnt * pixel));
286         if ((text_rect.right < rect.right || text_rect.left > rect.left))       //移动超出边界时暂停滚动,freez从20开始递减
287         {
288             scroll_info.shift_cnt--;    //让文本往回移动一次,防止反复判断为超出边界
289             text_rect.MoveToX(static_cast<int>(rect.left - scroll_info.shift_cnt * pixel));
290             scroll_info.freez = 20;     //变换方向时稍微暂停滚动一段时间
291         }
292     }
293     //用背景色填充矩形区域
294     //m_pDC->FillSolidRect(rect, m_backColor);
295     //输出文本
296     m_pDC->DrawText(lpszString, text_rect, DT_SINGLELINE | DT_NOPREFIX);
297     if (scroll_info.freez <= 0)     //当freez为0的时候才滚动
298     {
299         scroll_info.shift_cnt++;
300     }
301     else
302     {
303         scroll_info.freez--;
304         if (scroll_info.freez == 10)        //当freez递减到一半时将文本复位
305             scroll_info.shift_cnt = 0;
306     }
307     if ((rect & text_rect).IsRectEmpty())       //如果文本矩形区域已经超出了绘图区域,则重置滚动
308     {
309         reset = true;
310         scroll_info.shift_cnt = 0;
311         scroll_info.freez = 20;
312     }
313 }
314 
315 //void CDrawCommon::FillRect(CDC * pDC, CRect rect, COLORREF color)
316 //{
317 //  CBrush BGBrush, *pOldBrush;
318 //  BGBrush.CreateSolidBrush(color);
319 //  pOldBrush = pDC->SelectObject(&BGBrush);
320 //  pDC->FillRect(&rect, &BGBrush);
321 //  pDC->SelectObject(pOldBrush);
322 //  BGBrush.DeleteObject();
323 //}
324 
DrawBitmap(CBitmap & bitmap,CPoint start_point,CSize size,StretchMode stretch_mode,bool no_clip_area)325 void CDrawCommon::DrawBitmap(CBitmap& bitmap, CPoint start_point, CSize size, StretchMode stretch_mode, bool no_clip_area)
326 {
327     if (m_pDC->GetSafeHdc() == NULL)
328         return;
329     CDC memDC;
330 
331     //获取图像实际大小
332     BITMAP bm;
333     GetObject(bitmap, sizeof(BITMAP), &bm);
334 
335     memDC.CreateCompatibleDC(m_pDC);
336     memDC.SelectObject(&bitmap);
337     // 以下两行避免图片失真
338     m_pDC->SetStretchBltMode(HALFTONE);
339     m_pDC->SetBrushOrg(0, 0);
340     //CSize draw_size;
341     DrawAreaGuard guard(this, CRect(start_point, size), true, !no_clip_area);
342     ImageDrawAreaConvert(CSize(bm.bmWidth, bm.bmHeight), start_point, size, stretch_mode);
343     m_pDC->StretchBlt(start_point.x, start_point.y, size.cx, size.cy, &memDC, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
344     memDC.DeleteDC();
345 }
346 
DrawBitmap(UINT bitmap_id,CPoint start_point,CSize size,StretchMode stretch_mode,bool no_clip_area)347 void CDrawCommon::DrawBitmap(UINT bitmap_id, CPoint start_point, CSize size, StretchMode stretch_mode, bool no_clip_area)
348 {
349     CBitmap bitmap;
350     bitmap.LoadBitmap(bitmap_id);
351     DrawBitmap(bitmap, start_point, size, stretch_mode, no_clip_area);
352 }
353 
DrawBitmap(HBITMAP hbitmap,CPoint start_point,CSize size,StretchMode stretch_mode,bool no_clip_area)354 void CDrawCommon::DrawBitmap(HBITMAP hbitmap, CPoint start_point, CSize size, StretchMode stretch_mode, bool no_clip_area)
355 {
356     CBitmap bitmap;
357     if (!bitmap.Attach(hbitmap))
358         return;
359     DrawBitmap(bitmap, start_point, size, stretch_mode, no_clip_area);
360     bitmap.Detach();
361 }
362 
DrawImage(const CImage & image,CPoint start_point,CSize size,StretchMode stretch_mode,bool no_clip_area)363 void CDrawCommon::DrawImage(const CImage& image, CPoint start_point, CSize size, StretchMode stretch_mode, bool no_clip_area)
364 {
365     if (m_pDC->GetSafeHdc() == NULL)
366         return;
367     m_pGraphics->SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQuality);
368     DrawAreaGuard guard(this, CRect(start_point, size), false, !no_clip_area);
369     ImageDrawAreaConvert(CSize(image.GetWidth(), image.GetHeight()), start_point, size, stretch_mode);
370     Gdiplus::Bitmap bm(image, NULL);
371     m_pGraphics->DrawImage(&bm, INT(start_point.x), INT(start_point.y), INT(size.cx), INT(size.cy));
372 }
373 
DrawImage(Gdiplus::Image * pImage,CPoint start_point,CSize size,StretchMode stretch_mode,bool no_clip_area)374 void CDrawCommon::DrawImage(Gdiplus::Image* pImage, CPoint start_point, CSize size, StretchMode stretch_mode, bool no_clip_area)
375 {
376     m_pGraphics->SetInterpolationMode(Gdiplus::InterpolationMode::InterpolationModeHighQuality);
377     DrawAreaGuard guard(this, CRect(start_point, size), false, !no_clip_area);
378     ImageDrawAreaConvert(CSize(pImage->GetWidth(), pImage->GetHeight()), start_point, size, stretch_mode);
379     m_pGraphics->DrawImage(pImage, INT(start_point.x), INT(start_point.y), INT(size.cx), INT(size.cy));
380 }
381 
DrawIcon(HICON hIcon,CPoint start_point,CSize size)382 void CDrawCommon::DrawIcon(HICON hIcon, CPoint start_point, CSize size)
383 {
384     if (m_pDC->GetSafeHdc() == NULL)
385         return;
386     if (size.cx == 0 || size.cy == 0)
387         ::DrawIconEx(m_pDC->GetSafeHdc(), start_point.x, start_point.y, hIcon, 0, 0, 0, NULL, DI_NORMAL | DI_DEFAULTSIZE);
388     else
389         ::DrawIconEx(m_pDC->GetSafeHdc(), start_point.x, start_point.y, hIcon, size.cx, size.cy, 0, NULL, DI_NORMAL);
390 }
391 
DrawIcon(HICON hIcon,CRect rect)392 void CDrawCommon::DrawIcon(HICON hIcon, CRect rect)
393 {
394     DrawIcon(hIcon, rect.TopLeft(), rect.Size());
395 }
396 
DrawIcon(HICON hIcon,CRect rect,int icon_size)397 void CDrawCommon::DrawIcon(HICON hIcon, CRect rect, int icon_size)
398 {
399     CRect rc_icon{ rect };
400     //根据指定的图标大小使图标在矩形中居中
401     rc_icon.left += (rect.Width() - icon_size) / 2;
402     rc_icon.top += (rect.Height() - icon_size) / 2;
403     rc_icon.right = rc_icon.left + icon_size;
404     rc_icon.bottom = rc_icon.top + icon_size;
405     DrawIcon(hIcon, rc_icon);
406 }
407 
FillRect(CRect rect,COLORREF color,bool no_clip_area)408 void CDrawCommon::FillRect(CRect rect, COLORREF color, bool no_clip_area)
409 {
410     if (m_pDC->GetSafeHdc() == NULL)
411         return;
412     DrawAreaGuard guard(this, rect, true, !no_clip_area);
413     m_pDC->FillSolidRect(rect, color);
414 }
415 
FillAlphaRect(CRect rect,COLORREF color,BYTE alpha,bool no_clip_area)416 void CDrawCommon::FillAlphaRect(CRect rect, COLORREF color, BYTE alpha, bool no_clip_area)
417 {
418     if (m_pDC->GetSafeHdc() == NULL)
419         return;
420     if (alpha == 0)
421         return;
422     DrawAreaGuard guard(this, rect, true, !no_clip_area);
423     if (alpha == 255)
424     {
425         FillRect(rect, color, no_clip_area);
426         return;
427     }
428     CDC cdc;
429     if (!cdc.CreateCompatibleDC(m_pDC))
430         return;
431 
432     CBitmap bitmap, * pOldBitmap;
433     bitmap.CreateCompatibleBitmap(m_pDC, rect.Width(), rect.Height());
434     CRect src(rect);
435     src.MoveToXY(0, 0);
436     pOldBitmap = cdc.SelectObject(&bitmap);
437     cdc.FillSolidRect(src, color); //透明色
438 
439     if (::AlphaBlend == 0)
440     {
441         m_pDC->BitBlt(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, &cdc, src.left, src.top, SRCINVERT);
442     }
443     else
444     {
445         BLENDFUNCTION bf;
446         memset(&bf, 0, sizeof(bf));
447         bf.SourceConstantAlpha = alpha; //透明程度//值越大越不透明
448         bf.BlendOp = AC_SRC_OVER;
449         ::AlphaBlend(m_pDC->GetSafeHdc(), rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
450             cdc.GetSafeHdc(), src.left, src.top, src.right - src.left, src.bottom - src.top, bf);
451     }
452     cdc.SelectObject(pOldBitmap);
453 }
454 
DrawRectTopFrame(CRect rect,COLORREF color,int pilex)455 void CDrawCommon::DrawRectTopFrame(CRect rect, COLORREF color, int pilex)
456 {
457     if (m_pDC->GetSafeHdc() == NULL)
458         return;
459     DrawAreaGuard guard(this, rect, true);
460     CPen aPen, * pOldPen;
461     aPen.CreatePen(PS_SOLID, pilex, color);
462     pOldPen = m_pDC->SelectObject(&aPen);
463 
464     m_pDC->MoveTo(rect.TopLeft());
465     m_pDC->LineTo(rect.right, rect.top);
466 
467     m_pDC->SelectObject(pOldPen);
468     aPen.DeleteObject();
469 }
470 
DrawRectOutLine(CRect rect,COLORREF color,int width,bool dot_line)471 void CDrawCommon::DrawRectOutLine(CRect rect, COLORREF color, int width, bool dot_line)
472 {
473     if (m_pDC->GetSafeHdc() == NULL)
474         return;
475     CPen aPen, * pOldPen;
476     aPen.CreatePen((dot_line ? PS_DOT : PS_SOLID), width, color);
477     pOldPen = m_pDC->SelectObject(&aPen);
478     CBrush* pOldBrush{ dynamic_cast<CBrush*>(m_pDC->SelectStockObject(NULL_BRUSH)) };
479 
480     rect.DeflateRect(width / 2, width / 2);
481     m_pDC->Rectangle(rect);
482     m_pDC->SelectObject(pOldPen);
483     m_pDC->SelectObject(pOldBrush);       // Restore the old brush
484     aPen.DeleteObject();
485 }
486 
DrawRectFrame(CRect rect,COLORREF color,int width,BYTE alpha)487 void CDrawCommon::DrawRectFrame(CRect rect, COLORREF color, int width, BYTE alpha /*= 255*/)
488 {
489     if (width < 1)
490         width = 1;
491     if (width > min(rect.Width(), rect.Height()) / 2)       //如果边框宽度超过矩形短边的一半,则直接填充两个矩形
492     {
493         FillAlphaRect(rect, color, alpha, true);
494     }
495     else        //通过绘制4个矩形来绘制矩形边框
496     {
497         CRect rect_top{ rect };
498         rect_top.bottom = rect_top.top + width;
499         FillAlphaRect(rect_top, color, alpha, true);
500 
501         CRect rect_bottom{ rect };
502         rect_bottom.top = rect_bottom.bottom - width;
503         FillAlphaRect(rect_bottom, color, alpha, true);
504 
505         CRect rect_left{ rect };
506         rect_left.right = rect_left.left + width;
507         rect_left.top += width;
508         rect_left.bottom -= width;
509         FillAlphaRect(rect_left, color, alpha, true);
510 
511         CRect rect_right{ rect };
512         rect_right.left = rect_right.right - width;
513         rect_right.top += width;
514         rect_right.bottom -= width;
515         FillAlphaRect(rect_right, color, alpha, true);
516     }
517 }
518 
DrawLine(CPoint point1,CPoint point2,COLORREF color,int width,bool dot_line)519 void CDrawCommon::DrawLine(CPoint point1, CPoint point2, COLORREF color, int width, bool dot_line)
520 {
521     if (m_pDC->GetSafeHdc() == NULL)
522         return;
523     CPen aPen, * pOldPen;
524     aPen.CreatePen((dot_line ? PS_DOT : PS_SOLID), width, color);
525     pOldPen = m_pDC->SelectObject(&aPen);
526     CBrush* pOldBrush{ dynamic_cast<CBrush*>(m_pDC->SelectStockObject(NULL_BRUSH)) };
527 
528     m_pDC->MoveTo(point1);
529     m_pDC->LineTo(point2);
530     m_pDC->SelectObject(pOldPen);
531     m_pDC->SelectObject(pOldBrush);       // Restore the old brush
532     aPen.DeleteObject();
533 }
534 
DrawRoundRect(CRect rect,COLORREF color,int radius,BYTE alpha)535 void CDrawCommon::DrawRoundRect(CRect rect, COLORREF color, int radius, BYTE alpha /*= 255*/)
536 {
537     DrawRoundRect(CGdiPlusTool::CRectToGdiplusRect(rect), CGdiPlusTool::COLORREFToGdiplusColor(color, alpha), radius);
538 }
539 
DrawRoundRect(Gdiplus::Rect rect,Gdiplus::Color color,int radius)540 void CDrawCommon::DrawRoundRect(Gdiplus::Rect rect, Gdiplus::Color color, int radius)
541 {
542     int max_radius{ (std::min)(rect.Width, rect.Height) / 2 };
543     if (radius > max_radius)
544         radius = max_radius;
545     CRect rc{ CGdiPlusTool::GdiplusRectToCRect(rect) };
546     rc.right--;
547     rc.bottom--;
548     //生成圆角矩形路径
549     Gdiplus::GraphicsPath round_rect_path;
550     CGdiPlusTool::CreateRoundRectPath(round_rect_path, rc, radius);
551 
552     m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias);      //设置抗锯齿
553     Gdiplus::SolidBrush brush(color);
554     m_pGraphics->FillPath(&brush, &round_rect_path);          //填充路径
555     m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
556 }
557 
DrawEllipse(CRect rect,COLORREF color,BYTE alpha)558 void CDrawCommon::DrawEllipse(CRect rect, COLORREF color, BYTE alpha /*= 255*/)
559 {
560     DrawEllipse(CGdiPlusTool::CRectToGdiplusRect(rect), CGdiPlusTool::COLORREFToGdiplusColor(color, alpha));
561 }
562 
DrawEllipse(Gdiplus::Rect rect,Gdiplus::Color color)563 void CDrawCommon::DrawEllipse(Gdiplus::Rect rect, Gdiplus::Color color)
564 {
565     //生成椭圆路径
566     Gdiplus::GraphicsPath ellipse_path;
567     ellipse_path.AddEllipse(rect);
568     m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeAntiAlias);      //设置抗锯齿
569     Gdiplus::SolidBrush brush(color);
570     m_pGraphics->FillPath(&brush, &ellipse_path);                  //填充路径
571     m_pGraphics->SetSmoothingMode(Gdiplus::SmoothingMode::SmoothingModeNone);
572 }
573 
GetTextExtent(LPCTSTR str)574 CSize CDrawCommon::GetTextExtent(LPCTSTR str)
575 {
576     if (m_pDC->GetSafeHdc() == NULL)
577         return CSize();
578     ASSERT(m_pfont != nullptr); // 请先设置字体
579     if (m_pfont != nullptr)
580         m_pDC->SelectObject(m_pfont);
581     return m_pDC->GetTextExtent(str);
582 }
583 
584 //bool CDrawCommon::BitmapStretch(CImage * pImage, CImage * ResultImage, CSize size)
585 //{
586 //  if (pImage->IsDIBSection())
587 //  {
588 //      // 取得 pImage 的 DC
589 //      CDC* pImageDC1 = CDC::FromHandle(pImage->GetDC()); // Image 因為有自己的 DC, 所以必須使用 FromHandle 取得對應的 DC
590 //
591 //      CBitmap *bitmap1 = pImageDC1->GetCurrentBitmap();
592 //      BITMAP bmpInfo;
593 //      bitmap1->GetBitmap(&bmpInfo);
594 //
595 //      // 建立新的 CImage
596 //      //ResultImage->Create(size.cx, size.cy, bmpInfo.bmBitsPixel);
597 //      ResultImage->Create(size.cx, size.cy, 24);      //总是将目标图片转换成24位图
598 //      CDC* ResultImageDC = CDC::FromHandle(ResultImage->GetDC());
599 //
600 //      // 當 Destination 比較小的時候, 會根據 Destination DC 上的 Stretch Blt mode 決定是否要保留被刪除點的資訊
601 //      ResultImageDC->SetStretchBltMode(HALFTONE); // 使用最高品質的方式
602 //      ::SetBrushOrgEx(ResultImageDC->m_hDC, 0, 0, NULL); // 調整 Brush 的起點
603 //
604 //      // 把 pImage 畫到 ResultImage 上面
605 //      BOOL rtn = StretchBlt(*ResultImageDC, 0, 0, size.cx, size.cy, *pImageDC1, 0, 0, pImage->GetWidth(), pImage->GetHeight(), SRCCOPY);
606 //      // pImage->Draw(*ResultImageDC,0,0,StretchWidth,StretchHeight,0,0,pImage->GetWidth(),pImage->GetHeight());
607 //
608 //      pImage->ReleaseDC();
609 //      ResultImage->ReleaseDC();
610 //  }
611 //  return true;
612 //}
613 
ImageResize(const CImage & img_src,CImage & img_dest,CSize size)614 void CDrawCommon::ImageResize(const CImage& img_src, CImage& img_dest, CSize size)
615 {
616     img_dest.Destroy();
617     int bpp = img_src.GetBPP();
618     if (bpp < 24)
619         bpp = 24;       //总是将目标图片转换成24位图
620     img_dest.Create(size.cx, size.cy, bpp);
621     //使用GDI+更改图片大小时,左侧和上面会有一像素的灰边,因此在这里将其裁剪掉
622     size.cx++;
623     size.cy++;
624     //使用GDI+高质量绘图
625     img_src.Draw(img_dest.GetDC(), CRect(CPoint(-1, -1), size), Gdiplus::InterpolationMode::InterpolationModeHighQuality);
626     img_dest.ReleaseDC();
627 }
628 
ImageResize(const CImage & img_src,const wstring & path_dest,int size,ImageType type)629 void CDrawCommon::ImageResize(const CImage& img_src, const wstring& path_dest, int size, ImageType type)
630 {
631     CImage imDest;
632 
633     //计算输出图片的大小
634     CSize size_src{ img_src.GetWidth(), img_src.GetHeight() };
635     CSize size_dest{ size_src };
636     CCommon::SizeZoom(size_dest, size);
637 
638     ImageResize(img_src, imDest, size_dest);
639     //输出为指定格式
640     switch (type)
641     {
642     case IT_JPG:
643         imDest.Save(path_dest.c_str(), Gdiplus::ImageFormatJPEG);
644         break;
645     case IT_PNG:
646         imDest.Save(path_dest.c_str(), Gdiplus::ImageFormatPNG);
647         break;
648     case IT_GIF:
649         imDest.Save(path_dest.c_str(), Gdiplus::ImageFormatGIF);
650         break;
651     default:
652         imDest.Save(path_dest.c_str(), Gdiplus::ImageFormatBMP);
653         break;
654     }
655 }
656 
ImageResize(const wstring & path_src,const wstring & path_dest,int size,ImageType type)657 void CDrawCommon::ImageResize(const wstring& path_src, const wstring& path_dest, int size, ImageType type)
658 {
659     CImage imSrc;
660     //读入原始图片
661     imSrc.Load(path_src.c_str());
662     ImageResize(imSrc, path_dest, size, type);
663 }
664 
CopyBitmap(HBITMAP hSourceHbitmap)665 HBITMAP CDrawCommon::CopyBitmap(HBITMAP hSourceHbitmap)
666 {
667     CDC sourceDC;
668     CDC destDC;
669     sourceDC.CreateCompatibleDC(NULL);
670     destDC.CreateCompatibleDC(NULL);
671     //The bitmap information.
672     BITMAP bm = { 0 };
673     //Get the bitmap information.
674     ::GetObject(hSourceHbitmap, sizeof(bm), &bm);
675     // Create a bitmap to hold the result
676     //HBITMAP hbmResult = ::CreateCompatibleBitmap(CClientDC(NULL), bm.bmWidth, bm.bmHeight);
677     HBITMAP hbmResult = ::CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel, NULL);
678 
679     HBITMAP hbmOldSource = (HBITMAP)::SelectObject(sourceDC.m_hDC, hSourceHbitmap);
680     HBITMAP hbmOldDest = (HBITMAP)::SelectObject(destDC.m_hDC, hbmResult);
681     destDC.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, SRCCOPY);
682     destDC.FillSolidRect(5, 5, 5, 5, RGB(78, 176, 255));
683 
684     // Restore DCs
685     ::SelectObject(sourceDC.m_hDC, hbmOldSource);
686     ::SelectObject(destDC.m_hDC, hbmOldDest);
687     ::DeleteObject(sourceDC.m_hDC);
688     ::DeleteObject(destDC.m_hDC);
689 
690     return hbmResult;
691 }
692 
CopyBitmap(CBitmap & dest,CBitmap & src)693 void CDrawCommon::CopyBitmap(CBitmap& dest, CBitmap& src)
694 {
695 #if 1
696     CDC sourceDC;
697     CDC destDC;
698     sourceDC.CreateCompatibleDC(NULL);
699     destDC.CreateCompatibleDC(NULL);
700     //The bitmap information.
701     BITMAP bm = { 0 };
702     //Get the bitmap information.
703     ::GetObject(src.GetSafeHandle(), sizeof(bm), &bm);
704 
705     dest.DeleteObject();
706     //dest.CreateCompatibleBitmap(&destDC, bm.bmWidth, bm.bmHeight);
707     //这里如果使用上面一行注释掉的代码(CreateCompatibleBitmap)会导致BitBlt等到的图像为黑色,因为src和dest图像的位深度不一样
708     dest.CreateBitmap(bm.bmWidth, bm.bmHeight, bm.bmPlanes, bm.bmBitsPixel, NULL);
709 
710     BITMAP bm_dest{};
711     ::GetObject(dest.GetSafeHandle(), sizeof(bm_dest), &bm_dest);
712 
713     sourceDC.SelectObject(&src);
714     destDC.SelectObject(&dest);
715     int rtn = destDC.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &sourceDC, 0, 0, SRCCOPY);
716     ::GetObject(dest.GetSafeHandle(), sizeof(bm_dest), &bm_dest);
717 
718     destDC.FillSolidRect(5, 5, 5, 5, RGB(78, 176, 255));
719     ::GetObject(dest.GetSafeHandle(), sizeof(bm_dest), &bm_dest);
720 #else
721 
722     HBITMAP HBM = (HBITMAP)src.m_hObject;
723 
724     //HBITMAP HBM=(HBITMAP)hSourceBitmap->Detach();//如果希望清除掉原图资源
725     //hDescBitmap = new CBitmap;
726     dest.Attach(HBM);
727 #endif
728 }
729 
SaveBitmap(HBITMAP bitmap,LPCTSTR path)730 void CDrawCommon::SaveBitmap(HBITMAP bitmap, LPCTSTR path)
731 {
732     CImage img_tmp;
733     img_tmp.Attach(bitmap);
734     img_tmp.Save(path);
735     img_tmp.Detach();
736 }
737 
ImageDrawAreaConvert(CSize image_size,CPoint & start_point,CSize & size,StretchMode stretch_mode)738 void CDrawCommon::ImageDrawAreaConvert(CSize image_size, CPoint& start_point, CSize& size, StretchMode stretch_mode)
739 {
740     if (size.cx == 0 || size.cy == 0)       //如果指定的size为0,则使用位图的实际大小绘制
741     {
742         size = CSize(image_size.cx, image_size.cy);
743     }
744     else
745     {
746         if (stretch_mode == StretchMode::FILL)
747         {
748             float w_h_ratio, w_h_ratio_draw;        //图像的宽高比、绘制大小的宽高比
749             w_h_ratio = static_cast<float>(image_size.cx) / image_size.cy;
750             w_h_ratio_draw = static_cast<float>(size.cx) / size.cy;
751             if (w_h_ratio > w_h_ratio_draw)     //如果图像的宽高比大于绘制区域的宽高比,则需要裁剪两边的图像
752             {
753                 int image_width;        //按比例缩放后的宽度
754                 image_width = image_size.cx * size.cy / image_size.cy;
755                 start_point.x -= ((image_width - size.cx) / 2);
756                 size.cx = image_width;
757             }
758             else
759             {
760                 int image_height;       //按比例缩放后的高度
761                 image_height = image_size.cy * size.cx / image_size.cx;
762                 start_point.y -= ((image_height - size.cy) / 2);
763                 size.cy = image_height;
764             }
765         }
766         else if (stretch_mode == StretchMode::FIT)
767         {
768             CSize draw_size = image_size;
769             float w_h_ratio, w_h_ratio_draw;        //图像的宽高比、绘制大小的宽高比
770             w_h_ratio = static_cast<float>(image_size.cx) / image_size.cy;
771             w_h_ratio_draw = static_cast<float>(size.cx) / size.cy;
772             if (w_h_ratio > w_h_ratio_draw)     //如果图像的宽高比大于绘制区域的宽高比
773             {
774                 draw_size.cy = draw_size.cy * size.cx / draw_size.cx;
775                 draw_size.cx = size.cx;
776                 start_point.y += ((size.cy - draw_size.cy) / 2);
777             }
778             else
779             {
780                 draw_size.cx = draw_size.cx * size.cy / draw_size.cy;
781                 draw_size.cy = size.cy;
782                 start_point.x += ((size.cx - draw_size.cx) / 2);
783             }
784             size = draw_size;
785         }
786     }
787 }
788 
CalculateCenterIconRect(CRect rect,int icon_size)789 CRect CDrawCommon::CalculateCenterIconRect(CRect rect, int icon_size)
790 {
791     CRect rc_icon;
792     rc_icon.left = rect.left + (rect.Width() - icon_size) / 2;
793     rc_icon.top = rect.top + (rect.Height() - icon_size) / 2;
794     rc_icon.right = rc_icon.left + icon_size;
795     rc_icon.bottom = rc_icon.top + icon_size;
796     return rc_icon;
797 }
798 
799 ///////////////////////////////////////////////////////////////////////////////////////////////////
800 ///////////////////////////////////////////////////////////////////////////////////////////////////
SetDrawArea(CRect rect,bool gdi_only)801 CRect DrawAreaGuard::SetDrawArea(CRect rect, bool gdi_only)
802 {
803     //如果设置的绘图区域为空,则清除绘图区域
804     if (rect.IsRectEmpty())
805         ResetDrawArea(gdi_only);
806 
807     CRect old_rect{};
808     //设置GDI绘图区域
809     if (m_drawer->m_pDC->GetSafeHdc() != NULL)
810     {
811         m_drawer->m_pDC->GetClipBox(&old_rect);     //获取上次的绘图区域
812 
813         CRgn rgn;
814         rgn.CreateRectRgnIndirect(rect);
815         m_drawer->m_pDC->SelectClipRgn(&rgn);
816     }
817 
818     //设置GDI+绘图区域
819     if (!gdi_only && m_drawer->m_pGraphics != nullptr)
820         m_drawer->m_pGraphics->SetClip(CGdiPlusTool::CRectToGdiplusRect(rect));
821 
822     return old_rect;
823 }
824 
ResetDrawArea(bool gdi_only)825 void DrawAreaGuard::ResetDrawArea(bool gdi_only)
826 {
827     if (m_drawer->m_pDC->GetSafeHdc() != NULL)
828         m_drawer->m_pDC->SelectClipRgn(nullptr);
829 
830     if (!gdi_only && m_drawer->m_pGraphics != nullptr)
831         m_drawer->m_pGraphics->ResetClip();
832 }
833