分享

Visual C MFC 中使用 GDI

 山峰云绕 2023-07-23 发布于贵州

https://m.toutiao.com/is/ipqLJmE/ 


前言:作为Windows图形系统的GDI存在诸多不足:Alpha通道支持较弱、不支持高级图像功能、抗锯齿能力差等。为了解决这些问题,微软推出GDI+,使用非常方便,满足大多数场合需求。需要指明的是,对于游戏等性能要求高的需求,微软推荐使用Direct2D和Direct3D,它们很好支持抗抗锯齿等高级特性且支持硬件加速。

基于GDI+绘制的Visual C++ MFC软件界面示例图

下面详细说下GDI+ 在Visual C++ MFC常用方法的使用。

1.环境初始化

GDI+使用需要链接对应的链接库,在stdafx头文件中添加如下代码:

//GDI+ 兼容Visual C++ 6.0#ifndef ULONG_PTR#define ULONG_PTR unsigned long*#endif#include 'gdiplus/GdiPlus.h'#pragma comment(lib, 'GdiPlus.lib')using namespace Gdiplus;

2.常见图像绘制

和GDI 中dc对应的是GDI+中的Graphics。但是:

1.GDI是有状态的,GDI+是无状态的,比如GDI中要把当前画笔、画刷等选进dc,然后使用这些GDI图元来工作,GDI+中不一样,画笔、画刷、字体等等都是对象,我们只需要Graphics绘制时将对象作为参数传递即可。

2.GDI画矩形、圆等图形的副作用是会填充区域,要不填充区域必须使用NULL_BRUSH,GDI+中绘制边缘和填充是两个概念,另外GDI+支持的图形种类更多也更灵活

演示如下:

void DrawLines(HDC &hdc){        Graphics g(hdc);        Pen redP(Color(255,0,0), 2);        g.DrawLine(&redP, 20, 20, 40, 60);        redP.SetDashStyle(DashStyle::DashStyleDashDotDot);//线条样式        g.DrawLine(&redP, 1, 10, 300, 20);        Pen cyanP(Color::DarkCyan);        g.DrawRectangle(&cyanP, 60,30,20,100);        g.DrawEllipse(&cyanP, 60,30,20,100);        Point pts[] = {Point(10,120), Point(60,200), Point(80,200), Point(90,90)};        g.DrawPolygon(&cyanP, pts, 4);        Pen pinkP(Color::HotPink);        PointF pts2[] = {PointF(10.0f,220.0f), PointF(60.0f, 230.0f), PointF(50.0f, 250.0f)};        g.DrawCurve(&pinkP, pts2, 3);        Point pt1(10,300);        Point pt2(50,400);        Point pt3(100,350);        Point pt4(30,250);        g.DrawBezier(&pinkP, pt1, pt2, pt3, pt4);}//填充和绘制分开void FillShape(){        Graphics g(m_hWnd);        SolidBrush redB(Color(255,0,0));        g.FillRectangle(&redB, 100,400,200,250);}

可以看到:

1.这里绘制时我们创建一个Pen对象,设置他的属性,然后Graphics绘制时作为参数

2.填充区域是Fill*函数,绘制图形是Draw*函数

3.路径和区域

在GDI中使用路径,必须包含在BeginPath和EndPath中,使用非常不方便。在GDI+中路径GraphicPath、区域Region和其他图元(如Pen)一样, 都是基本的对象。如下:

void DrawPathAndRegion(HDC &hdc){ PointF data[] = { PointF(40,140), PointF(275,200), PointF(105,255), PointF(50,350), PointF(20,180) }; BYTE typeline[] = { PathPointTypeLine, PathPointTypeLine, PathPointTypeLine, PathPointTypeLine, PathPointTypeLine }; GraphicsPath path1(data, typeline, 5); Graphics g(hdc); g.SetSmoothingMode(SmoothingModeAntiAlias);//绘制时反走样来消除锯齿 g.TranslateTransform(400., 0.); g.FillPath(&SolidBrush(Color::Red), &path1); g.TranslateTransform(300., 0.); GraphicsPath path2; FontFamily fontFamily(L'Arial'); path2.AddString(L'Add String', -1, &fontFamily, FontStyleRegular, 100, PointF(0,0), NULL); Pen pen(Color::Black); g.DrawPath(&pen, &path2); GraphicsState s = g.Save(); g.SetClip(&path2);//path做剪切区域 for (int i=0; i<100; i+=2) { g.DrawLine(&pen, 0, i, 600, i); } Region rgn(&path2); //路径转区域 g.Restore(s); //取消剪切区域 g.TranslateTransform(0., 100.); g.FillRegion(&SolidBrush(Color::Black), &rgn);}

可以看到这里路径path1由指定的点连成的线构成,路径path2由字体输出构成, setclip可以类似GDI设置当前绘图的剪切区域,这里我们用字体做剪切区域,效果就是在字体区域中间填充一系列的横线。

路径和区域可以互转。

另外,这里 g.SetSmoothingMode(SmoothingModeAntiAlias);指定绘制时反走样来消除锯齿,g.Save和g.Restore(s)可以完成Graphics的设置和恢复,g.TranslateTransform指明当前坐标系的平移变换(还支持旋转、缩放等高级变换功能)。

4.文本绘制

GDI中DrawText来绘制文本和测量输出区域大小,GDI+提供了更强大的功能,而且支持字体的抗锯齿输出,如下:

void DrawText(HDC &hdc){        Graphics g(hdc);        Font myFont(L'Arial', 50, FontStyleItalic, UnitPixel);        RectF layoutRect(10.0f, 10.0f, 197.0f, 50.0f);        StringFormat format;        format.SetAlignment(StringAlignmentCenter); //水平对齐        format.SetLineAlignment(StringAlignmentCenter); //垂直对齐        format.SetFormatFlags(StringFormatFlagsNoClip); //不截断        format.SetHotkeyPrefix(HotkeyPrefixShow); //&前导符        SolidBrush blackBrush(Color(255,0,0,0));        LPCWSTR pszText = L'Out&putx Text';        g.DrawString(pszText, -1, &myFont, layoutRect, &format, &blackBrush);        g.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit); //抗锯齿        format.SetTrimming(StringTrimmingEllipsisCharacter); //结尾        layoutRect.Offset(0, 100);        LPCWSTR pszText2 = L'Output Text Output Text Output Text Output Text';        g.DrawString(pszText2, -1, &myFont, layoutRect, &format, &blackBrush);        //测量指定文本大小        RectF boundRect;        g.MeasureString(pszText2, -1, &myFont, PointF(0,0), &boundRect);        CString str;        str.Format(L'W:%d-H:%d', boundRect.Width, boundRect.Height);        OutputDebugString(str);        //测量指定layoutRect宽度内,文本显示的个数、行数和包围框大小        INT codepointsFitted = 0;        INT linesFilled = 0;        RectF layoutRect2(10.0f, 10.0f, 197.0f, 0.f);//高度必须为0才能自适应计算        g.MeasureString(pszText2, -1, &myFont,        layoutRect2, &format,        &boundRect, &codepointsFitted, &linesFilled);}

可以看到类似GDI,这里我们 使用StringFormat控制输出文本的格式。 g.SetTextRenderingHint(
TextRenderingHintAntiAliasGridFit);指明字体抗锯齿输出。

使用MeasureString测量文本输出大小,而且可以看到 指定包围盒高度为0时它可以根据宽度来完成自适应计算,计算出文本显示的个数、行数和包围框大小。

5.图像绘制

GDI中图像功能有很多确定,只支持加载bmp格式,Alpha通道支持有限,图像处理功能较弱。GDI+很好的弥补了这一切。

GDI+支持BMP/TIFF/JPG/PNG/GIF等,且支持透明,GDI的AlphaBlend函数缩放时不支持抗锯齿,GDI+的DrawImage弥补了这些,如下:

void DrawImages(HDC &hdc){ Graphics g(hdc); g.Clear(Color::WhiteSmoke);//清空当前背景 Image img1(L'.\\pop_bk.png'); PointF pt1(0.f, 0.f); g.DrawImage(&img1, pt1); g.SetInterpolationMode(InterpolationModeHighQualityBicubic);//设置插值模式,处理图像缩放 RectF rcDest(400.f,400.f,REAL(img1.GetWidth()), REAL(img1.GetHeight())); g.DrawImage(&img1, rcDest, 20.f,20.f,40.f,40.f, UnitPixel);}

g.SetInterpolationMode(
InterpolationModeHighQualityBicubic)设置图像缩放时的插值方式,保证高质量显示。这里主要演示DrawImage使用,图像的加载和处理下一节再细述。

可在DrawImage时传入Attr参数,指定图像的色彩变换,具体参考API使用。

6.双缓冲绘制

GDI中我们都是通过创建一个兼容DC,一个兼容BMP,兼容BMP选入兼容DC绘制完成后,再将BMP一次性贴到实际DC上。

在GDI+中不用这么麻烦,GDI+将一切表面抽象,如果想在位图上绘制,直接将Graphics的表面选成位图即可,如下:

//双缓冲void DrawImages5(HDC &hdc){        Bitmap bmp1(L'.\\pop_bk.png');        //内存绘图        Bitmap bmpMem(bmp1.GetWidth(), bmp1.GetHeight());        Graphics gMem(&bmpMem);//以内存Bitmap为表面绘图        gMem.DrawImage(&bmp1, 0, 0);        gMem.DrawRectangle(&Pen(Color::Black), 0,0,40,40);        //一次性绘制到DC        Graphics g(hdc);        g.DrawImage(&bmpMem, 0, 0);}

7.GDI+绘制示例程序

8、结束语

Visual C++ / MFC的世界,仍受不少IT专家和程序员的喜爱,话说微软提供的操作系统和开发工具,从兼容性上来说,小编认为是全球公认的第一,不亏IDE拳霸世界这么多年,九十年代发布的VC6.0代码工程可以快速无缝升级到VS2022。建议从事C++编程的老铁们尝试一下,Visual C++ MFC作为曾经的Windows桌面开发王者,在全球服务器省电趋势下,或者仍有全新的开始和保持生命力。

参考:
https://blog.csdn.net/wenzhou1219/article/details/78243682

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多