mFC 橡皮线

一般都用GDI实现:


void CXiangpijinView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

CView::OnMouseMove(nFlags, point);
if (Lbrndown)
{

CClientDC dc(this);
int oldmode=dc.SetROP2(R2_NOTXORPEN);
CPen pen(PS_SOLID, 2.0, RGB(255,0,0)), *oldpen;
oldpen = dc.SelectObject(&pen);

dc.MoveTo(m_ptOrigin);
dc.LineTo(m_ptEnd);

m_ptEnd=point;

dc.MoveTo(m_ptOrigin);
dc.LineTo(m_ptEnd);

dc.SelectObject(oldpen);
dc.SetROP2(oldmode);
ReleaseDC(&dc);

}

}

void CXiangpijinView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

CView::OnLButtonDown(nFlags, point);
Lbrndown=true;
m_ptOrigin = m_ptEnd = point;
}

主要得益于GDI支持SetROP2,绘图模式。

nDrawMode Specifies the new drawing mode. It can be any of the following
values:

R2_BLACK   Pixel is always black.

R2_WHITE   Pixel is always white.

R2_NOP   Pixel remains unchanged.

R2_NOT   Pixel is the inverse of the screen color.

R2_COPYPEN   Pixel is the pen color.

R2_NOTCOPYPEN   Pixel is the inverse of the pen color.

R2_MERGEPENNOT   Pixel is a combination of the pen color and the
inverse of the screen color (final pixel = (NOT screen pixel) OR pen).

R2_MASKPENNOT   Pixel is a combination of the colors common to both
the pen and the inverse of the screen (final pixel = (NOT screen pixel) AND
pen).

R2_MERGENOTPEN   Pixel is a combination of the screen color and the
inverse of the pen color (final pixel = (NOT pen) OR screen pixel).

R2_MASKNOTPEN   Pixel is a combination of the colors common to both
the screen and the inverse of the pen (final pixel = (NOT pen) AND screen
pixel).

R2_MERGEPEN   Pixel is a combination of the pen color and the
screen color (final pixel = pen OR screen pixel).

R2_NOTMERGEPEN   Pixel is the inverse of the R2_MERGEPEN color
(final pixel = NOT(pen OR screen pixel)).

R2_MASKPEN   Pixel is a combination of the colors common to both
the pen and the screen (final pixel = pen AND screen pixel).

R2_NOTMASKPEN   Pixel is the inverse of the R2_MASKPEN color (final
pixel = NOT(pen AND screen pixel)).

R2_XORPEN   Pixel is a combination of the colors that are in the
pen or in the screen, but not in both (final pixel = pen XOR screen pixel).

R2_NOTXORPEN   Pixel is the inverse of the R2_XORPEN color (final
pixel = NOT(pen XOR screen pixel)).

原理:R2_NOTXORPEN画出来的颜色与R2_XORPEN相反,R2_XORPEN是屏幕颜色和画笔颜色的异或。OnMouseMove第一次被调用时,还没画线,所以屏幕的颜色是白色的,R2_XORPEN是当前画笔的颜色取反,那么R2_NOTXORPEN就是当前画笔颜色了。就是说第一次画的线是画笔的颜色。

第二次调用OnMouseMove时,m_ptOrigin和m_ptEnd两个点还没变,所以可以用这两个点再画线,将第一次画的线覆盖掉,变成画布的颜色,然后在新的point点和m_ptOrigin之间重新画线,颜色为画笔颜色。在旧的直线上面画线,因为线本来有颜色,所以R2_XORPEN(屏幕的颜色==画笔颜色)就会变成黑色(1
xor 1=0,0 xor
0=0),取反,即R2_NOTXORPEN为白色,就是画布的颜色,看起来就像消失了一样,其实只不过是线变成白色了(如果画笔不是白色,比如使用你系统设置了护眼配色,客户区变成不伤眼的浅绿色,这样显示出来的颜色还是白色,而不是客户区的颜色)。旧的直线删除了,就可以在新的点point上再次画线了。

注意:如果背景色是白色,那么画出来的线就是画笔的颜色,如果不是白色,线的颜色为笔色与屏幕颜色异或再取反的颜色。

同理只需替换绘图函数就可可以实现矩形、椭圆的橡皮筋效果。

而GDI+不支持SetRop2,

好像说是因为SetRop2只针对光栅,而GDI+不只是用于光栅,要支持打印机等原因。不懂。

一般来说,新的库应该有方法来取代SetRopt2原来的功能。

这就使得画橡皮条非常困难,网上提供了一些方法以及问题:

1/
使用DllImport,导入SetRop2  : 结合GDI绘图,这违背了GDI+删去SetRop2的本意。

2/
更新从起始点到结束点的整个块状区域 : 我觉得为了一条线而刷新整个区域是很愚蠢的事情,虽然可以使用双缓冲等等技术

3/ 使用一个ErasePen和一个DrawPen,ErasePen和背景同色 : 这个方法很有问题,当和其他线条相交时会擦去原来的线条

不知道为什么要把SetRop2去掉。

我自己实现的使用双缓冲来实现橡皮条


void CXiangpijinView::OnDraw(CDC* pDC)
{
CXiangpijinDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here

GetClientRect(&rc);
Graphics Gp(pDC->m_hDC);
SolidBrush mybrush(Color( 135 ,135,135));
Gp.FillRectangle(&mybrush,0,0,rc.Width(),rc.Height());
Gp.ReleaseHDC(hMemDC);

HBITMAP hBitmap,hOldBitmap;
//建立一个屏幕设备环境句柄
hMemDC=CreateCompatibleDC(pDC->m_hDC);
//建立一个与屏幕设备环境句柄兼容、与鼠标指针所在窗口的区域等大的位图
hBitmap=CreateCompatibleBitmap(pDC->m_hDC,rc.Width(),rc.Height());
//把新位图选到内存设备描述表中
hOldBitmap=(HBITMAP)SelectObject(hMemDC,hBitmap);
//把屏幕设备描述表拷贝到内存设备描述表中
BitBlt(hMemDC,0,0,rc.Width(),rc.Height(),pDC->m_hDC,0,0,SRCCOPY);

}
void CXiangpijinView::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

CView::OnMouseMove(nFlags, point);
if (Lbrndown)
{

BitBlt(GetDC()->m_hDC,0,0,rc.Width(),rc.Height(),hMemDC,0,0,SRCCOPY);
   Graphics Gp3(GetDC()->m_hDC);
        Pen mypen(Color(0,255,0),2.0);
   Gp3.DrawLine(&mypen,m_ptOrigin.x,m_ptOrigin.y,point.x,point.y);  
     Gp3.ReleaseHDC(GetDC()->m_hDC);

}

}

void CXiangpijinView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

CView::OnLButtonDown(nFlags, point);
Lbrndown=true;
m_ptOrigin = m_ptEnd = point;
}

void CXiangpijinView::OnLButtonUp(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default

CView::OnLButtonUp(nFlags, point);
//把新的DC拷贝到hMemDC
BitBlt(hMemDC,0,0,rc.Width(),rc.Height(),GetDC()->m_hDC,0,0,SRCCOPY);

}

主要原理是绘图前把屏幕DC内容拷贝到兼容的内存位图中,绘图时把兼容dC
上的内容拷贝到屏幕,然后在在当前DC上绘图,一次绘制结束再把 DC上拷贝到兼容DC上。

常用绘图操作

m_map = new Bitmap(rc.Width(),rc.Height());

Pen mypen(Color(0,255,0),2.0);

m_Gpp = new Graphics(m_map);

m_Gpp->DrawEllipse(&mypen,po.X,po.Y,8.0,8.0);

Graphics Gp3(GetDC()->m_hDC);

Gp3.DrawImage(m_map,0,0);

Gp3.ReleaseHDC(GetDC()->m_hDC);

mFC 橡皮线

时间: 2025-01-12 23:35:08

mFC 橡皮线的相关文章

《深入浅出MFC》第七章 简单而完整:MFC骨干程序

不二法门:熟记MFC类层次结构.经常使用的主要MFC类:CWinApp, CWnd<-CView, CWnd<-CFrameWnd, CFrameWnd<-CMDIFrameWnd, CFrameWnd<-CMDIChildWnd, CWnd<-CDialog, CWnd<-CControlBar, CControlBar<-CStatusBar, CControlBar<-CToolBar, CCmdTarget<-CDocument, CCmdTa

深入浅出MFC——消息映射与命令传递(六)

1. 消息分类: 2. 万流归宗——Command Target(CCmdTarget): 3. "消息映射"是MFC内建的一个信息分派机制.通过三个宏(DECLARE_MESSAGE_MAP/BEGIN.../ON.../END...)完成消息网的建构. 4. 注意:CWinThread派生自CCmdTarget,但没有DECLARE_/BEGIN_/END_宏组. 5. 消息映射与虚函数: 6.

MFC控件使用技巧:List Control

1)每列内容过长,显示不完整 只有加载数据的情况下,才会出现水平滚动条 解决方案: 可以添加如下一个空的内容项: m_List.InsertItem(0,NULL);//为了显示进度条 2)不允许点击修改第一列(当然最多能够让我们改动的也只有他了) MFC默认情况下可以修改第一列,其他的不允许修改(需要定制DrawItem) 解决方案: Edit Labels 属性设置为 False 3)报表的形式表示 View 属性设置为 Report 4)注意:不要和列表框控件混淆(英文名: List Bo

【mfc】用对话框分页实现用户登录

所谓的对话框分页就是点击完一个对话框的按钮,切换到另一个对话框, 这样的对话框多用于一些需要用户登录才能够进行操作的软件, 下面就用对话框分页来实现用户登录系统 一.基本目标 有如下的程序,输入用户名与密码,如果用户名为admin,密码为123456,那么则成功登录,切换到一个有"欢迎登录"与"关闭"按钮的对话框 如果用户名或者密码输入错误则弹出提示, 点击关闭能够关闭这个程序,不弹出用户登录的对话框. 二.制作过程 1.首先如同之前的<[mfc]对于对话框程

MFC中给控件添加变量,DoDataExchange中

DoDataExchange函数其实是一项数据动态绑定技术.比如你在写动态按钮过程中须对按钮添加变量时,怎么添加?控件类已经写好了,其变量是已经固定的.你要添加新的变量就要用到DoDataExchange函数. 你要在对话框的构造函数里面初始化一个变量,再用DoDataExchange函数将它绑定到你的动态按扭中,比如:DDX_Check(pDX, IDC_CHECK1, m_Lesson1);这就是将m_Lesson1(这是一个外部变量,其定义在对话框的构造函数里)绑定到IDC_CHECK1中

MFC消息映射的原理:笔记

多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表:两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里面的每个类有很多成员函数(只说消息反映相关的成员函数啊),而且在派生类中,需要改写的也比较少(我用来做练习的程序就是那么一两个,呵呵).那么用c++的虚函数的实现机制会导致什么问题呢?就是大量虚表的建立使得空间浪费掉很多. 嗯-怎么办呢?于是各大c++名库(比如QT,MFC,VCL-)在消息映射的实

MFC-消息分派

前言 由于工作需要,这几天学了一点MFC,在AFX里看到很多熟悉的东西,如类型信息,序列化,窗口封装和消息分派.几乎每个界面库都必须提供这些基础服务,但提供的手法却千差万别.MFC大量地借用了宏,映射表来实现,而VCL则更多的在语言级别上给与支持.这其实是很容易理解的,因为C++是一个标准,不会因某个应用而随便扩展语言:相反Delphi完全由一个公司掌握,因此每支持一项新技术,变化最大的往往是语言本身. 学习MFC的代码,再对照VCL的实现,这真是一个很有意思的过程,其中可以看到两个框架在一些设

MFC中的DC CDC HDC由来由去理解

MFC中的DC CDC HDC由来由去理解 在此非常感谢博客主的究竟钻研,非常详细的参考资料:http://blog.csdn.net/yam_killer/article/details/7661449

(七):处理MFC

(一):简单介绍 为了可以在一个Winelib应用中使用MFC,你须要首先使用Winelib又一次编译MFC. 在理论上,你应该为Windows的MFC编写一个封装(怎样编写在后面介绍).可是,在实践中,对MFC来说.可能不是一个真正的解决方案: 数量巨大的API使得编写封装的工作量非常大 进一步说.MFC包括大量的API.这些API在制作封装的时候处理起来是非常复杂的. 即使你编写了封装,你也须要去改动MFC的头文件以使编译器不会堵塞他们. 在你应用中的非常大一部分MFC代码是以宏的形式展现的