文章目录

  • 一、开发环境
  • 二、程序功能
  • 三、功能实现
    • 1 铅笔
    • 2 线段
    • 3 特殊形状
    • 4 放大镜
    • 5 橡皮擦
    • 6 添加文本

一、开发环境

Visual Studio,MFC,单文档

二、程序功能

1.实现设置画笔属性,选择画笔粗细的功能
2.实现颜色选择功能
3.实现油漆桶填充功能
4.实现画直线和铅笔画功能
5.实现特殊形状绘制(矩形、三角形、圆、椭圆)功能
6.实现局部放大功能
7.实现插入位图功能
8.实现保存所绘制图形的功能
9.实现橡皮擦功能
10.实现添加文本功能

三、功能实现

我们首先创建了单文档的应用程序MFC_PAINT,然后主要在CMFC_PAINTView.cpp和CMFC_PAINTView.h这两个文件里面写主要的实现代码。
绘图类CMFC_PAINTView,继承自CView,在这个类里面定义了需要用到的成员变量和成员函数。

class CMFC_PAINTView : public CView
{public:int m_PenSize;int m_TextID;COLORREF m_PenColor, m_BrushColor;CPoint m_PointBegin, m_PointEnd;CEdit* m_Edit = nullptr;CPoint m_TextPos = CPoint(0, 0);CPoint m_MagnifyCenter;int m_MagnifyWidth;int m_MagnifyHeight;//我们主要通过改变枚举变量的具体的值来切换相应的功能。enum DrawType {LineSegment, Circle, Rectangle, Ellips, Eraser, Pencil, Magnify, Text, Fill, myLoadImage, Triangle}m_DrawType;
// Generated message map functions
protected://{{AFX_MSG(CMFC_PAINTView)//这些是在Menu栏添加相应的事件处理函数自动生成的成员函数,我们通过该
//事件处理函数改变DrawType的值,然后再在三个消息处理函数中用Switch-case
//语句区分相应的功能,为各个功能添加具体的实现代码。afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnMouseMove(UINT nFlags, CPoint point);afx_msg void OnLButtonUp(UINT nFlags, CPoint point);afx_msg void OnDrawLine();       //线段afx_msg void OnMenueraser();     //橡皮擦afx_msg void OnMenupencil();     //铅笔afx_msg void OnMenumagnify();    //放大afx_msg void OnMenutext();       //添加文字afx_msg void OnMenucircle();     //圆afx_msg void OnMenuellips();     //椭圆afx_msg void OnMenurectangle();  //矩形afx_msg void OnMenusize();       //画笔大小afx_msg void OnMenucolor();      //画笔颜色afx_msg void OnMenufill();       //油漆桶工具afx_msg void OnMenubrushcolor(); //油漆桶填充颜色afx_msg void OnFileSave();  //保存图片afx_msg void OnFileOpen();   //打开位图//}}AFX_MSG
};

接下来我们分别讲解一下各个功能的具体实现:

1 铅笔

首先是铅笔功能,使用铅笔功能用户可以在画板上任意画画,实现该功能的原理是:用m_PointBegin和m_PointEnd两个成员变量记录鼠标移动时的位置用MoveTo和LineTo函数绘制多条首尾顺序相连的极短直线。
具体代码如下:

case DrawType::Pencil:
{m_PointBegin = m_PointEnd;m_PointEnd = point;dc.MoveTo(m_PointBegin);dc.LineTo(m_PointEnd);break;
}

效果如下:

2 线段

绘制线段也是直接用m_PointBegin和m_PointEnd两个成员变量记录鼠标移动时的位置用MoveTo和LineTo函数画直线。
具体代码如下:

case DrawType::LineSegment:
{dc.SetROP2(R2_NOTXORPEN);dc.MoveTo(m_PointBegin);dc.LineTo(m_PointEnd);dc.MoveTo(m_PointBegin);dc.LineTo(point);m_PointEnd = point;break;
}

3 特殊形状

绘制特殊形状,我们实现了直线段、矩形、圆形、椭圆、三角形等多种形状,绘制这些形状其实都是用的一个原理,只要保存下鼠标移动时的起始点和终点,然后用dc自带的函数绘制出相应的形状即可。比如用dc.rectangle画矩形,用dc.ellipse函数画椭圆。
在画圆时需要将矩形转化为正方形,然后在里面用ellipse函数画圆。三角形用矩形重绘,在起点和终点确定的矩形中间确定三角形的三个顶点,顶点在上边的中点底边的两个点就是矩形下半部分的点。由于dc没有绘制三角形的函数,就调用polygon即绘制多边形函数,两两连线。
这里需要说明的是绘制形状时的橡皮筋效果的实现:
R2_NOTXORPEN画出来的颜色与R2_XORPEN相反,R2_XORPEN是屏幕颜色和画笔颜色的异或。
OnMouseMove第一次被调用时,还没画线,所以屏幕的颜色是白色的,R2_XORPEN是当前画笔的颜色,取反,那么R2_NOTXORPEN就是当前画笔颜色了。就是说第一次画的线是画笔的颜色。
第二次调用OnMouseMove时,m_PointBegin和 m_PointEnd两个点还没变,所以可以用这两个点再画线,将第一次画的线覆盖掉,变成画布的颜色。因为是在旧的直线上面画线,所以线本来有颜色,此时屏幕的颜色==画笔颜色,所以R2_XORPEN就会变成黑色,取反,那么R2_NOTXORPEN为白色,就是画布的颜色,这样原来的线看起来就像消失了一样,其实只不过是线变成白色了。
具体代码如下:

case DrawType::Rectangle:
{    dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(PS_NULL);CRect rectP1(m_PointBegin,m_PointEnd);dc.Rectangle(rectP1);CRect rectP2(m_PointBegin,point);dc.Rectangle(rectP2);m_PointEnd=point;break;
}
case DrawType::Circle:
{       dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(PS_NULL);int length_1=m_PointEnd.y-m_PointBegin.y;if(m_PointBegin.x<m_PointEnd.x){m_PointEnd.x=m_PointBegin.x+abs(length_1);}else{m_PointEnd.x=m_PointBegin.x+abs(length_1);}CRect rectP1(m_PointBegin,m_PointEnd);dc.Ellipse(rectP1);int length_2=point.y-m_PointBegin.y;if(point.x<m_PointEnd.x){m_PointEnd.x=m_PointBegin.x+abs(length_2);}else{m_PointEnd.x=m_PointBegin.x+abs(length_2);}m_PointEnd.y=point.y;CRect rectP2(m_PointBegin,m_PointEnd);dc.Ellipse(rectP2);m_PointEnd=point;break;
}
case DrawType::Ellips:
{     dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(PS_NULL);CRect rectP1(m_PointBegin,m_PointEnd);dc.Ellipse(rectP1);CRect rectP2(m_PointBegin,point);dc.Ellipse(rectP2);m_PointEnd=point;break;
}
case DrawType::Triangle:
{                dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(PS_NULL);CPoint p1[3];int a = (m_PointBegin.x - m_PointEnd.x) / 2;p1[0].x = m_PointBegin.x + abs(a);p1[0].y = m_PointBegin.y;p1[1].x = m_PointBegin.x;p1[1].y = m_PointEnd.y;p1[2] = m_PointEnd;dc.Polygon(p1, 3);CPoint p2[3];int b = (m_PointBegin.x - point.x) / 2;p2[0].x = m_PointBegin.x + abs(b);p2[0].y = m_PointBegin.y;p2[1].x = m_PointBegin.x;p2[1].y = point.y;p2[2] = point;dc.Polygon(p2, 3);m_PointEnd = point;break;
}

实现效果如下:

4 放大镜

接下来是放大镜,实现的效果就如这个视频中所示,这个效果的实现主要是应用了StretchBlt函数,这个函数的功能是从源矩形中复制一个位图到目标矩形,必要时按目标设备设置的模式进行图像的拉伸或压缩。我们通过GetClientRect获取到当前屏幕的大小,然后再调用自定义函数GetMagnifyRect函数获取放大框的大小,这样就可以将放大框中的内容放大到屏幕大小。
具体实现代码如下:

//在OnMouseMove() 中添加如下代码:case DrawType::Magnify: {//CClientDC dc(this);dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(5);dc.Rectangle(rectUndraw);delete rectUndraw;dc.Rectangle(rectDraw);delete rectDraw;m_MagnifyCenter = point;break;}
//在OnLButtonUp() 中添加如下代码:case DrawType::Magnify:  {dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(5);dc.Rectangle(rectUndraw);GetClientRect(&rect);dc.StretchBlt(0, 0, rect.Width(), rect.Height(), pDC, rectUndraw->left, rectUndraw->top, rectUndraw->Width(), rectUndraw->Height(), SRCCOPY);ReleaseDC(pDC);delete rectUndraw;break;}
//建立类向导返回放大框的位置
CRect* CMFC_PAINTView::GetMagnifyRect(CPoint& p_Msg)
{CRect rect;GetClientRect(&rect);CPoint leftTop,rightBottom;leftTop.x=p_Msg.x-m_MagnifyWidth/2;rightBottom.x=p_Msg.x+m_MagnifyWidth/2;leftTop.y=p_Msg.y-m_MagnifyHeight/2;rightBottom.y=p_Msg.y+m_MagnifyHeight/2;if(leftTop.x<0){leftTop.x=0;rightBottom.x=leftTop.x+m_MagnifyWidth;}else if(rightBottom.x>rect.right){rightBottom.x=rect.right;leftTop.x=rightBottom.x-m_MagnifyWidth;}if(leftTop.y<0){leftTop.y=0;rightBottom.y=leftTop.y+m_MagnifyHeight;}else if(rightBottom.y>rect.bottom){rightBottom.y=rect.bottom;leftTop.y=rightBottom.y-m_MagnifyHeight;}CRect*r_Rect = new CRect(leftTop,rightBottom);return r_Rect;
}
//在OnLButtonDown() 中添加如下代码:CRect* rectDraw=GetMagnifyRect(point);CRect* rectUndraw = GetMagnifyRect(m_MagnifyCenter);CPen newPen, * oldPen;SetClassLong(this->GetSafeHwnd(),GCL_HCURSOR , (LONG)LoadCursor(NULL , IDC_CROSS));switch(m_DrawType){case DrawType::Magnify:  //CClientDC dc(this);newPen.CreatePen(PS_SOLID, m_PenSize, m_PenColor);oldPen = dc.SelectObject(&newPen);dc.SetROP2(R2_NOTXORPEN);dc.SelectStockObject(5);dc.Rectangle(rectUndraw);dc.SelectObject(oldPen);delete rectUndraw;SetClassLong(this->GetSafeHwnd(),GCL_HCURSOR , (LONG)LoadCursor(NULL , IDC_ARROW));break;default:break;}

实现效果如下:

5 橡皮擦

橡皮擦功能和铅笔功能类似,就是通过GetBkColor函数获取用户区背景色,然后将铅笔的颜色改为用户区背景色就可以实现橡皮擦的功能了。

//在OnMouseMove() 中添加如下代码:case DrawType::Eraser: {CPen newPen(PS_SOLID, m_PenSize * 10, pColor);dc.SelectObject(&newPen);m_PointBegin = m_PointEnd;m_PointEnd = point;dc.MoveTo(m_PointBegin);dc.LineTo(m_PointEnd);break;}

实现效果如下:

6 添加文本

实现添加文本功能时,先用CEdit的成员函数Create绘制一个文本输入框,在输入框内可以输入文本,当回车退出文本框时,使用虚函数PreTranslateMessage(MSG* pMsg) 截获消息,我们通过重载它来处理键盘和鼠标消息,在这个函数里我们通过GetWindowText获取输入的文字,然后通过TextOut再将其在原位置绘制出来。
实现代码如下:

//在OnMouseMove() 中添加如下代码:case DrawType::Text:{if (nullptr != m_Edit){delete m_Edit;}CEdit* myEdit = new CEdit();myEdit->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(m_PointBegin, point), this, m_TextID);m_Edit = myEdit;myEdit->ShowWindow(SW_SHOW);break;}
//在OnLButtonUp() 中添加如下代码:case DrawType::Text:{CEdit* myEdit = new CEdit();myEdit->Create(WS_CHILD | WS_VISIBLE | WS_BORDER, CRect(m_PointBegin, point), this, m_TextID);myEdit->ShowWindow(SW_SHOW);if (nullptr != m_Edit){delete m_Edit;}m_Edit = myEdit;break;}

实现效果如下:

我们第二篇继续~

MFC制作Windows画图程序(一)相关推荐

  1. MFC制作Windows画图程序(二)

    继<MFC制作Windows画图程序(一)> 文章目录 7 颜色填充功能 8 选择画笔颜色 9 设置画笔粗细 10 将绘制出来的图形保存的实现 11 添加位图 7 颜色填充功能 油漆桶功能 ...

  2. html5画图程序,基于HTML5的Windows画图程序

    Windows 画图程序一直伴随着 Windows 各个版本,这个很简单,几乎没有什么用处的程序拥有最基本的绘图功能,很适合用来涂鸦.HTML5 的 Canvas 对象使在线绘图成为可能,虽然 HTM ...

  3. 用JAVA制作一个画图程序

    在日常生活中画图软件给我们带来了许多便利,往大了说可以用它来制作图标,往小了说也可以当做一个涂鸦板消磨时间. 比如:制作个图标 用来画画: 这个画图程序界面如上,这个程序的功能如下: 1.可更改的画笔 ...

  4. 87岁的老奶奶喜欢用windows画图程序作画,而且画得还贼好!

    来自:安说|搜集有趣的事 信息化时代,谁还不会点新技能呢? 你可能认为老年人很难跟上时代的步伐,比如说你的奶奶不会用智能手机,不会发微信,不会使用手机支付等等.但是事实是,他们可能并不是不会,而是不想 ...

  5. 制作Windows Mobile程序安装包

    使用Visual Studio 2005制作wm上的cab安装包 打开项目,解决方案中添加新项,添加"智能设置CAB项目":或者在空VS中新建一个"智能设置CAB项目&q ...

  6. 怎样使用MFC 调用windows系统程序 windows media player

    背景:我需要一个函数,启动windows系统中的程序 windows media player ,用于播放我所指定的音频文件: 问题:百度网上的大多数用法,都是在mfc中插入 windows medi ...

  7. 使用MFC制作windows悬浮球

    上周入手一个二手windows平板(目的只是为了玩),发现习惯使用手机悬浮球后,再转到winpad上操作极为不便,比如想显示桌面都要小心翼翼点,切换任务也比较麻烦(本人不习惯开启平板模式,使用的是常规 ...

  8. python_制作Windows安装程序包

    一.打包 1.打包.py文件 打包方法请查看另一篇文章: python3_将多个.py文件打包成exe程序并添加图标 二.制作安装包 1.准备已经打包完成的exe文件,如下图 2.下载NSIS VNI ...

  9. 【Java】Java GUI制作Windows桌面程序,利用windowbuilder生成界面,使用exe4j打包成可执行文件,使用Inno Setup打包成安装包,超级详细教程

    目录 1.GUI插件 1.1 下载GUI绘制插件 1.2 eclipse中配置windowbuilder插件 2.绘制GUI界面 2.1 建立一个GUI的项目 3.配置Maven项目 3.1新建一个M ...

最新文章

  1. git 比较两个版本之间的区别
  2. python input与返回值-Python 详解基本语法_函数_返回值
  3. mysql8 安装_mysql 8.x 安装向导
  4. java 语法 冒号_java中生僻的冒号跳转语法
  5. Puzzle 18 - StringCheese - byte storage
  6. 昆西·拉森的净资产是多少?
  7. Java笔记-Java端口扫描功能(含TCP包分析以及原理)
  8. KubeEdge 初测
  9. Java 图片处理解决方案:ImageMagick 快速入门教程
  10. QT-C++ Nesting排料优化,广告,服装,木工排料(支持矩形、异形排版,提高优化效率)
  11. Fall 2020 Berkeley cs61a Projects Ants答案
  12. 全平台视频转GIF软件对比与推荐(iOS/安卓/Windows/Mac)
  13. C语言也能干大事第十二节(如鹏基础)
  14. 静态网页连接mysql数据库_静态网页可以联接sql数据库吗?代码怎么写?
  15. 计算机技术在生物学中的应用题库,2018年第二军医大学基础医学部816计算机在生物医学中的应用之生物化学考研基础五套测试题...
  16. wpf 语音通话_WPF+WCF一步一步打造音频聊天室(四):视频会话
  17. html如何引入lrc文件,lrc文件怎么打开?lrc是什么文件?
  18. IE火狐的代理服务器的设置
  19. 張學友 - Private Corner 新专辑1.29
  20. 在计算机中关闭应用程序,电脑中如何取消点击关机后出现的还需要关闭程序的提示...

热门文章

  1. ffmpeg mkv 转 MP4
  2. 甬矽电子科创板上市:年营收21亿募资11亿 市值122亿
  3. 跳妹儿读绘本:我家孩子爱不释手的经典绘本之套装书
  4. git检出新分支遇到的文件路径过长Filename too long的问题
  5. IDEA括起选中的选中的内容
  6. 山西民生云登录显示服务器异常,山西民生云服务器异常
  7. 萌新改代码系列(一)--VINS+GPS
  8. Discuz X2.0数据字典(数据库表作用解释)
  9. Linux MMC子系统分析(二)——Host分析
  10. ios 移动社交 app 的demo 附:图文展示,客户端+服务器端源码