摘 要 根据应用软件的需要,提出了蚂蚁线实现的功能需求,采用GDI++/GDI、多线程技术编程设计实现了蚂蚁线种类、绘制、属性管理类;讨论了GDI++/GDI实现方法的区别;采用线程同步、信号事件等技术实现的蚂蚁线易于扩展、运行稳定、可靠。
关键词 蚂蚁线;流动;GDI/GDI++;线程
一、引言
图形图像处理软件中经常要进行区域、图元的选择,以往都是一些静态的直线选择块,单一而且呆板。实现软件中的流动性选择线(框)可为软件增添不少光彩。流动性选择线(框),也称蚂蚁线,就是类似Photoshop软件中选取区域时候显示的线条,像蚂蚁一样不停地流动,如下图所示,某应用软件中选择对象后,“小蚂蚁”不停地爬动,
图1 应用软件中的蚂蚁线
如下图所示,即黑色线段按照一定方向不停地移动(矩形为一个选择框)。
图2 流动选择框(蚂蚁线)
目前各种媒体上所见到的都是采用Delphi、.NET C#(参见文献2)实现,尚不见Visual C的完整实现,本文通过分析相关商业软件,用Visual C++完全实现了商业软件中蚂蚁线的功能。
二、功能
(1)同时管理多个蚂蚁线;
(2)支持多种蚂蚁线,线、矩形、椭圆、曲线等;
(3动态修改蚂蚁线的属性,如线型、颜色、宽度等;
(4)支持线两端的属性,如圆形、矩形等;
(5)使用方便、易于管理。
本文实现的蚂蚁线在某矢量图形系统中得到应用,实践证明运行稳定、可靠。
三、设计
为了实现以上功能,共设计了两种类:蚂蚁线种类类(CMarchingAntsType)和蚂蚁线管理类(CMarchingAntsManager)。如表1所示。
表1 蚂蚁线类定义
CMarchingAntsType |
蚂蚁线种类的基类 |
CMarchingAntsPolyLine |
折线蚂蚁线类 |
CMarchingAntsPoints |
点蚂蚁线类 |
CMarchingAntsManager |
蚂蚁线管理类 |
为了实现不同种类的蚂蚁线,从CMarchingAntsType继承了两个类:CMarchingAntsPolyLine(折线蚂蚁线类)和CMarchingAntsManager(蚂蚁线管理类)。继承关系如图3所示。
图3 继承关系
四、蚂蚁线种类类
1.蚂蚁线种类的基类(CMarchingAntsType)
为了方便实现不同的蚂蚁线,定义了一个基类CMarchingAntsType,这个基类定义了蚂蚁线的基本属性,绘制方法定义成虚函数virtual void Draw(Graphics *graphics)=0(virtual void Draw(HDC hDC)=0),两个绘制函数分别对应GDI++和GDI的绘制方法。这个类存在虚函数不能直接实例化。主要的属性如表2所示。
表2蚂蚁线种类的基类属性
unsigned int width |
线宽 |
float dash_length |
虚线长度 |
float space_length |
空线长度 |
unsigned int dash_color |
虚线颜色 |
unsigned int space_color |
空线颜色 |
unsigned int LineHeadType |
线端点类型 |
unsigned int LineHeadSize |
线端点矩形尺寸 |
float offst |
偏移量 |
REAL m_DashOffset |
虚线偏移量 |
int m_MaxDistance |
最大距离 |
bool b_DirectionFlag |
移动方向 |
CRITICAL_SECTION cs |
互斥量 |
HDC m_hDC |
绘图句柄 |
2. 折线蚂蚁线类(CMarchingAntsPolyLine)
CMarchingAntsPolyLine类实现了有关折线(由一系列线段组成)的蚂蚁线,存储了折线的坐标串Point *point(//坐标数组)和unsigned int count(//坐标数量),最为关键的是重载了绘制函数Draw(),实现了折线蚂蚁线的实质性绘制。
void Draw(Graphics *graphics)的关键代码如下:
//分别产生虚线和空线画笔、设置画笔属性
Pen m_DashPen(…);
Pen m_SpacePen((…);
m_DashPen.SetWidth(REAL(GetWidth()));
m_SpacePen.SetWidth(REAL(GetWidth()));
m_DashPen.SetDashStyle(DashStyleCustom);
REAL dashVals[4] = {
REAL(GetDashLength()), //虚线长度
REAL(GetSpaceLength()), //空线长度
REAL(GetDashLength()),
REAL(GetSpaceLength())};
//设置虚线和空线长度参数m_DashPen.SetDashPattern(dashVals, 4);
//设置开始到虚线的长度,这是实现蚂蚁
//爬动的关键步骤,通过不断修改这个值
//和重新绘制即可实现蚂蚁不断爬动
m_DashPen.SetDashOffset(m_DashOffset);
//绘制
graphics->DrawLines(&m_SpacePen,point,count);
graphics->DrawLines(&m_DashPen,point,count);
//绘制线的两端,如小矩形和椭圆等,可以进行扩展
Pen myPen(Color(0,0,0));
SolidBrush myBrush(Color(0,255,0));
unsigned int i;
switch(LineHeadType)
…
void Draw(HDC hDC)的关键代码如下:
unsigned int i=0;
int len=0;
//分别对每条线段处理
for(i=0;i<count-1;i++)
{
::LineDDA(…);
len+=(int)sqrt(…);
}
if(len%((int)(GetDashLength()+GetSpaceLength()))==0)
m_DashOffset++;
//绘制线的两端,如小矩形和椭圆等,可以进行扩展
HPEN hPen=CreatePen(PS_SOLID,1,RGB(0,0,0));
HPEN oldPen=(HPEN)SelectObject(hDC,hPen);
HBRUSH hBrush=CreateSolidBrush(RGB(0,255,0));
HBRUSH OldBrush=(HBRUSH)SelectObject(…);
…
GDI++和GDI的实现的区别:是在GDI中没有设置虚线离开始点距离的SetDashOffset方法,需要利用LineDDA实现,效果也与GDI++有所区别。
LineDDA是一个32位的图形设备接口库函数调用,从如下所示的函数原形中可以看出其入口参数是一组线条坐标、一个回调函数的地址以及一个指向应用程序定义数据的指针:
BOOL LineDDA( int nXStart, // 线条起点的X坐标
int nYStart, // 线条起点的Y坐标
int nXEnd, // 线条终点的X坐标
int nYEnd, // 线条终点的Y坐标
LINEDDAPROC lpLineFunc, // 回调函数的指针
LPARAM lpData // 应用程序定义数据的指针
由lpLineFunc指针指向的回调函数将在除终点外的线段的每个点上被调用,显然这里是实现蚂蚁线爬动的最佳地方。该回调函数在CMarchingAntsType类中定义为VOID CALLBACK MovingDots(int X,int Y,LPARAM lpData),主要代码如下:
//移动的量
int count =((int)(pType->m_DashOffset)) %((int)( ……));
COLORREF vColor=RGB(0,0,0);
if (count < pType->GetDashLength())
vColor = pType->GetForeColor();
else if (count < pType->GetDashLength()+pType->GetSpaceLength())
vColor = pType->GetBackColor();
//在每点进行绘制
SetPixel(pType->m_hDC, X, Y, vColor);
//增加偏移量
pType->m_DashOffset+=1;
3.点蚂蚁线类(CMarchingAntsPoints)
绘制点的蚂蚁线,实现与CMarchingAntsPolyLine类似,这里不再赘述。
|