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