求多边形填充算法,分不够再加

jingqiang2008 2003-10-15 02:55:36
求多边形填充算法(扫描转换填充算法中的Y-X扫描线算法的具体实现)。急需求多边形填充算法的实现,效率越高越好,身边只有一本书讲到扫描转换填充算法中的Y-X扫描线算法效率较高,如能提供效率更高的算法更好。分不够可再加。请大家帮助。
...全文
159 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
jingqiang2008 2003-10-20
  • 打赏
  • 举报
回复
yndfcd(YNDFCD),POLYGON2D_PTR 定义是什么?请帖出完整的代码好么?我是新手,看起来比较费劲。谢谢。
jingqiang2008 2003-10-17
  • 打赏
  • 举报
回复
感谢回帖,不过看的头都大了:(
jingqiang2008 2003-10-16
  • 打赏
  • 举报
回复
这是我目前用的。
#include <stdlib.h>
#include <malloc.h>
#define WINDOW_HEIGHT 480
#define NULL 0
typedef struct tEdge
{
int ymax;
float x;
float dx;
struct tEdge *next;
}Edge;
typedef struct point
{
int x;
int y;
}POINT;
void InsertEdge(Edge *list,Edge *edge)
{
Edge *p,*q=list;
p=q->next;
while(p)
{
if(edge->x<p->x)
p=NULL;
else
{
q=p;
p=p->next;
}
}
edge->next=q->next;
q->next=edge;
}
int yNext(int k,int cnt,POINT *pts)
{
int j;
if((k+1)>(cnt-1))
j=0;
else
j=k+1;
while(pts[k].y==pts[j].y)
if((j+1)>(cnt-1))
j=0;
else
j++;
return(pts[j].y);
}
void MakeEdgeRec(POINT lower,POINT upper,int yComp,Edge *edge,Edge *edges[])
{
edge->dx=(float)(upper.x-lower.x)/(upper.y-lower.y);
edge->x=(float)lower.x;
if(upper.y<yComp)
edge->ymax=upper.y-1;
else
edge->ymax =upper.y;
InsertEdge(edges[lower.y],edge);
}
void BuildEdgeList(int cnt,POINT *pts,Edge *edges[])
{
Edge *edge;
POINT v1,v2;
int i,yPrev=pts[cnt-2].y;
v1.x=pts[cnt-1].x;
v1.y=pts[cnt-1].y;
for(i=0;i<cnt;i++)
{
v2=pts[i];
if(v1.y!=v2.y)
{
edge=(Edge*)malloc(sizeof(Edge));
if(v1.y<v2.y)
MakeEdgeRec(v1,v2,yNext(i,cnt,pts),edge,edges);
else
MakeEdgeRec(v2,v1,yPrev,edge,edges);
}
yPrev=v1.y;
v1=v2;
}
}
void BuildActiveList(int scan,Edge *active,Edge *edges[])
{
Edge *p,*q;
p=edges[scan]->next ;
while(p)
{
q=p->next ;
InsertEdge(active,p);
p=q;
}
}
void FillScan(int scan,Edge *active,int color)
{
Edge *p1,*p2;
int i;
p1=active->next;
while(p1)
{
p2=p1->next;
for(i=p1->x;i<p2->x;i++)
setpixel((int)i,scan,color);
p1=p2->next ;
}
}
void DeleteAfter(Edge *q)
{
Edge *p=q->next ;
q->next =p->next ;
free(p);
}
void UpdateActiveList(int scan,Edge *active)
{
Edge *q=active,*p=active->next ;
while(p)
if(scan>=p->ymax)
{
p=p->next ;
DeleteAfter(q);
}
else
{
p->x=p->x+p->dx;
q=p;p=p->next;
}
}
void ResortActiveList(Edge *active)
{
Edge *q,*p=active->next;
active->next =NULL;
while(p)
{
q=p->next ;
InsertEdge(active,p);
p=q;
}
}
void ScanFill(int cnt,POINT *pts,int color)
{
Edge *edges[WINDOW_HEIGHT],*active;
int i,scan,scanmax=0,scanmin=WINDOW_HEIGHT;
for(i=0;i<cnt-1;i++)
{
if(scanmax<pts[i].y) scanmax=pts[i].y;
if(scanmin>pts[i].y) scanmin=pts[i].y;
}
for(scan=scanmin;scan<=scanmax;scan++)
{
edges[scan]=(Edge *)malloc(sizeof(Edge));
edges[scan]->next=NULL;
}
BuildEdgeList(cnt,pts,edges);
active=(Edge *)malloc(sizeof(Edge));
active->next =NULL;
for(scan=scanmin;scan<=scanmax;scan++)
{
BuildActiveList(scan,active,edges);
if(active->next )
{
FillScan(scan,active,color);
UpdateActiveList(scan,active);
ResortActiveList(active);
}
}
}
jakeye 2003-10-16
  • 打赏
  • 举报
回复
我不知道,学习学习。
tonybaobao 2003-10-16
  • 打赏
  • 举报
回复
我也只知道种子算法了……
yndfcd 2003-10-16
  • 打赏
  • 举报
回复
// calculate edge 2:

while ((errorterm2 < xdiff2) && (count2 > 0))
{
// finished w/edge 2?
if (count2--)
{
// count down on edge 2
offset2+=xunit2; // increment pixel offset
xstart2+=xunit2;
} // end if

errorterm2+=ydiff2; // increment error term

if (errorterm2 < xdiff2)
{ // if not more than XDIFF
vbuffer[offset2]=(UCHAR)poly->color; // ...plot a pixel
} // end if

} // end while

errorterm2-=xdiff2; // if time to increment X, restore error term

// draw line from edge 1 to edge 2:

length=offset2-offset1; // determine length of horizontal line

if (length < 0)
{ // if negative...
length=-length; // make it positive
start=offset2; // and set START to edge 2
} // end if
else
start=offset1; // else set START to edge 1

for (int index=start; index < start+length+1; index++)
{ // From edge to edge...
vbuffer[index]=(UCHAR)poly->color; // ...draw the line
} // end for index

offset1+=mempitch; // advance edge 1 offset to next line
ystart1++;
offset2+=mempitch; // advance edge 2 offset to next line
ystart2++;

} // end if

} // end if
else
{
// increment edge 1 on X and edge 2 on Y:
count1=xdiff1; // count for X increment on edge 1
count2=ydiff2; // count for Y increment on edge 2

while (count1 && count2)
{ // continue drawing until one edge is done
// calculate edge 1:
while ((errorterm1 < xdiff1) && (count1 > 0))
{ // finished w/edge 1?
if (count1--)
{
// count down on edge 1
offset1+=xunit1; // increment pixel offset
xstart1+=xunit1;
} // end if

errorterm1+=ydiff1; // increment error term

if (errorterm1 < xdiff1)
{ // If not more than XDIFF
vbuffer[offset1]=(UCHAR)poly->color; // ...plot a pixel
} // end if

} // end while

errorterm1-=xdiff1; // If time to increment X, restore error term

// calculate edge 2:
errorterm2+=xdiff2; // increment error term

if (errorterm2 >= ydiff2)
{ // if time to increment Y...
errorterm2-=ydiff2; // ...restore error term
offset2+=xunit2; // ...and advance offset to next pixel
xstart2+=xunit2;
} // end if

count2--;

// draw line from edge 1 to edge 2:

length=offset2-offset1; // determine length of horizontal line

if (length < 0)
{ // if negative...
length=-length; // ...make it positive
start=offset2; // and set START to edge 2
} // end if
else
start=offset1; // else set START to edge 1

for (int index=start; index < start+length+1; index++) // from edge to edge
{
vbuffer[index]=(UCHAR)poly->color; // ...draw the line
} // end for index

offset1+=mempitch; // advance edge 1 offset to next line
ystart1++;
offset2+=mempitch; // advance edge 2 offset to next line
ystart2++;

} // end while
} // end if
} // end if
else
{
if (xdiff2 > ydiff2)
{
// increment edge 1 on Y and edge 2 on X:

count1=ydiff1; // count for Y increment on edge 1
count2=xdiff2; // count for X increment on edge 2

while(count1 && count2)
{ // continue drawing until one edge is done
// calculate edge 1:

errorterm1+=xdiff1; // Increment error term

if (errorterm1 >= ydiff1)
{ // if time to increment Y...
errorterm1-=ydiff1; // ...restore error term
offset1+=xunit1; // ...and advance offset to next pixel
xstart1+=xunit1;
} // end if

count1--;
yndfcd 2003-10-16
  • 打赏
  • 举报
回复
void Draw_Filled_Polygon2D(POLYGON2D_PTR poly, UCHAR *vbuffer, int mempitch)
{
// this function draws a general n sided polygon

int ydiff1, ydiff2, // difference between starting x and ending x
xdiff1, xdiff2, // difference between starting y and ending y
start, // starting offset of line between edges
length, // distance from edge 1 to edge 2
errorterm1, errorterm2, // error terms for edges 1 & 2
offset1, offset2, // offset of current pixel in edges 1 & 2
count1, count2, // increment count for edges 1 & 2
xunit1, xunit2; // unit to advance x offset for edges 1 & 2

// initialize count of number of edges drawn:
int edgecount = poly->num_verts-1;

// determine which vertex is at top of polygon:

int firstvert=0; // start by assuming vertex 0 is at top

int min_y=poly->vlist[0].y; // find y coordinate of vertex 0

for (int index=1; index < poly->num_verts; index++)
{
// Search thru vertices
if ((poly->vlist[index].y) < min_y)
{
// is another vertex higher?
firstvert=index;
min_y=poly->vlist[index].y;
} // end if

} // end for index

// finding starting and ending vertices of first two edges:
int startvert1=firstvert; // get starting vertex of edge 1
int startvert2=firstvert; // get starting vertex of edge 2
int xstart1=poly->vlist[startvert1].x+poly->x0;
int ystart1=poly->vlist[startvert1].y+poly->y0;
int xstart2=poly->vlist[startvert2].x+poly->x0;
int ystart2=poly->vlist[startvert2].y+poly->y0;
int endvert1=startvert1-1; // get ending vertex of edge 1

if (endvert1 < 0)
endvert1=poly->num_verts-1; // check for wrap

int xend1=poly->vlist[endvert1].x+poly->x0; // get x & y coordinates
int yend1=poly->vlist[endvert1].y+poly->y0; // of ending vertices
int endvert2=startvert2+1; // get ending vertex of edge 2

if (endvert2==(poly->num_verts))
endvert2=0; // Check for wrap

int xend2=poly->vlist[endvert2].x+poly->x0; // get x & y coordinates
int yend2=poly->vlist[endvert2].y+poly->y0; // of ending vertices

// draw the polygon:

while (edgecount>0)
{
// continue drawing until all edges drawn
offset1=mempitch*ystart1+xstart1; // offset of edge 1
offset2=mempitch*ystart2+xstart2; // offset of edge 2

// initialize error terms
// for edges 1 & 2
errorterm1=0;
errorterm2=0;

// get absolute value of
if ((ydiff1=yend1-ystart1) < 0)
ydiff1=-ydiff1;

// x & y lengths of edges
if ((ydiff2=yend2-ystart2) < 0)
ydiff2=-ydiff2;

if ((xdiff1=xend1-xstart1) < 0)
{
// get value of length
xunit1=-1; // calculate X increment
xdiff1=-xdiff1;
} // end if
else
{
xunit1=1;
} // end else

if ((xdiff2=xend2-xstart2) < 0)
{
// Get value of length
xunit2=-1; // calculate X increment
xdiff2=-xdiff2;
} // end else
else
{
xunit2=1;
} // end else

// choose which of four routines to use
if (xdiff1 > ydiff1)
{
// if x length of edge 1 is greater than y length
if (xdiff2 > ydiff2)
{
// if X length of edge 2 is greater than y length

// increment edge 1 on X and edge 2 on X:
count1=xdiff1; // count for x increment on edge 1
count2=xdiff2; // count for x increment on edge 2

while (count1 && count2)
{
// continue drawing until one edge is done
// calculate edge 1:
while ((errorterm1 < xdiff1) && (count1 > 0))
{
// finished w/edge 1?
if (count1--)
{
// count down on edge 1
offset1+=xunit1; // increment pixel offset
xstart1+=xunit1;
} // end if

errorterm1+=ydiff1; // increment error term

if (errorterm1 < xdiff1)
{ // if not more than XDIFF
vbuffer[offset1]=(UCHAR)poly->color; // ...plot a pixel
} // end if

} // end while

errorterm1-=xdiff1; // if time to increment X, restore error term
yndfcd 2003-10-16
  • 打赏
  • 举报
回复
二种方法:
1。将多边分成若干个三角形,将每个三角形再分成小的有一边水平的三角形。然后分别推填充这些小三角形。

2。将多边的形的顶点从上到下,从左到右的方法排列,然后计算很每条边的斜率,然后境减相应的值。
jingqiang2008 2003-10-16
  • 打赏
  • 举报
回复
是不是书上讲的算法都能看到填充的过程?库函数用的是么方法呢?大家讨论一下。做这个程序是因为上边安排下来,要在LINUX上画图用的。
jingqiang2008 2003-10-16
  • 打赏
  • 举报
回复
把上面的程序在VC下试了一下,比较慢啊:(,比FillRgn()速度慢了很多,FillRgn是怎么做的啊。哪位高手请帮帮忙啊。
simclock 2003-10-16
  • 打赏
  • 举报
回复
带种子的扫描线填充?
SCUM 2003-10-15
  • 打赏
  • 举报
回复
以前在DOS下写图形库的时候用的是Y桶法
速度一般
别的方法没有真正试过
愿闻道
TianGuangZao 2003-10-15
  • 打赏
  • 举报
回复
学过电视机的一点原理,跟你讲的有点类似。
先给出你的算法,然后才能比较谁的更好。
jingqiang2008 2003-10-15
  • 打赏
  • 举报
回复
在线等待!!!
本系统是在Windows XP系统和MapGis6.7(B20051118)基础上,以Microsoft VC++ 6.0为编程语言,MapGis 6.7 SDK为开发平台进行开发的地质图件制作软件。系统基于MapGis输入编辑子系统强大的图形编辑能力,添加专业的地质图件制作工具,大大提高了地质图件的制作效率,能够很完美的转换CAD数据格式为MapGis格式。地质数据采集系统采用Microsoft Access的MDB格式,自动计算绘制符合行业标准的MapGis格式地质图件。 本软件完全免费使用。在使用过程中,如果是本软件的缺陷造成你的损失,本人不承担任何责任。一旦开始使用,视为你同意。 本软件可以自由复制传播,但不可用于商业用途。 操作演示请切换到“操作视频”帮助页面,网上下载高清有声视频。 现Section版本具有的主要功能模块: 1、绘制剖面图功能; 2、绘制柱状图功能; 3、辅助工具Ⅰ和辅助工具Ⅱ(CAD转MapGis文件); 4、其他功能(暂时未归类功能)。 编辑本段Section快捷键对照表 Ctrl+A 选择相同图元功能 B 选择区 Ctrl +C 复制图元功能 C 选择点 Ctrl +V 粘贴图元功能 N 选择弧段 Ctrl +X 剪切图元功能 V 选择线 Ctrl +Z 后悔操作 X 选择子图 Shift+Z 选多类型图元功能 Z 选择文本 Shift +C 超级拷贝功能 E 扩展工具箱 Shift +V 超级粘贴功能 T 系统工具箱 F2 正交功能 Del 删除选择图元 Tab 全屏功能 空格键 捕捉功能 → 向右移动选择图元 ↑ 向上移动选择图元 ← 向左移动选择图元 ↓ 向下移动选择图元 Y 捕捉点图元 U 捕捉节点及端点 I(i) 捕捉交叉点 O 捕捉垂点 P 捕捉最近点 [ 捕捉中点 鼠标中键 按住可以移动图形 鼠标滚轮 放大缩小功能 双击鼠标中键 复原窗口功能 双击鼠标右键 取消所有操作,回到初始状态 (以上快捷键不区大小写),其它快捷键与mapgis相同。 在编辑视图中,如果状态为准备状态,按住Ctrl右击,将弹出常用图元编辑快捷菜单;按住Shift右击,将弹出扩展功能快捷菜单。 编辑本段安装与卸载 系统要 基础软件:MapGis软件。推荐MapGis 6.7版本Build051118。 系统支持:Section软件支持Microsoft Windows 的2000, XP, Vista和 7系列的操作系统。(32位,64位未测试)。 语言:支持中文,简体。 下载 进入地信网论坛和华夏土地网论坛下载最新版本的Section程序。 安装软件步骤 1)对于Section单个程序:把section.exe和section.chm等放入MapGis67\program目录内即可。 2)对于独立安装包:(相对于单个程序文件,还包含了先前的各种自定义配置文件) 第一步:双击运行安装程序Section2010.exe,打开安装向导页面开始安装Section软件; 第二步:仔细阅读“许可协议”,点击“同意”,继续下一步安装,否则点击“取消”,退出软件安装; 第三步:仔细阅读“信息”,阅读软件重要信息,点击“下一步”,继续下一步安装,否则点击“取消”,退出软件安装; 第四步:选择安装路径:默认情况下,软件安装在系统的C:\MapGis67\program目录下,单击“浏览”可更改路径;注意非默认位置时,目录只要指向program的上级目录,即mapgis67。 第五步:请“选择组件”,有完全安装,简洁安装和自定义安装3种供选择。点击“下一步”,继续下一步安装; 第六步:在开始菜单文件夹中创建程序的文件夹名称。点击“下一步”,继续下一步安装; 第七步:选择附加任务:创建桌面快捷方式和快捷启动栏快捷方式,以及Section关联MapGis文件等。勾选所需选项,点击“下一步”,开始准备安装; 第八步:点击“安装”。直至最后,不想在安装完成后运行Section程序请去勾,点击完成。 卸载 1)对于非安装包:把section.exe程序和section.chm等文件删除即可。 2)对于独立安装包:打开控制面板,进入添加/删除程序(Win7的为程序和功能)找到Section卸载项目,双击“Section卸载”或者右键菜单中的卸载。 FAQ: 1、 双击section.exe后出现 “无法启动此程序,因计算机丢失basroot.dll……”的提示,程序不能使用? 答:没有在mapgis环境下缺少基本组件和动态链接库,运行程序会出现此提示,把section.exe放入MapGis67\program目录内即可。 0327d正式版0612 软件更新日志 1、修正不能删除角度花纹有残留的Bu
8-1 用QPainter绘图(Painting with QPainter) 2011-10-26 19:56:04| 类: 默认类 | 标签: |字号大中小 订阅 8-1 用QPainter绘图(Painting with QPainter) 类: C++ GUI Programming with Qt 4 2007-05-29 21:52 8228人阅读 评论(3) 收藏 举报 要在绘图设备(paint device,一般是一个控件)上开始绘制,我们只要创建一个QPainter,把绘图设备指针传给QPainter对象。例如: oid MyWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); ... } 使用QPainter的draw…()函数我们可以绘制各种图形。图8.1给出了主要的一些。绘制的方式由QPainter的设置决定。设置的一部是从绘图设备得到的,其他是初始化时的默认值。三个主要的设置为:画笔,刷子和字体。 画笔用来绘制直线和图形的边框。包含颜色,宽度,线型,角设置和连接设置。 刷子是填充几何图形的方式。包含颜色,方式设置,也可以是一个位图或者渐变色。 字体用来绘制文本。字体的属性很多,如字体名,字号等。 这些设置随时可以改变,可用QPen,QBrush,QFont对象调用setPen(),setBrush(),setFont()修改。 Figure 8.1. QPainter's most frequently used draw...() functions Figure 8.2. Cap and join styles < XMLNAMESPACE PREFIX ="O" /> < XMLNAMESPACE PREFIX ="V" /> Figure 8.3. Pen styles Figure 8.4. Predefined brush styles 现在来看看具体的例子。下面的代码是绘制图8.5(a)中椭圆的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 12, Qt::DashDotLine, Qt::RoundCap)); painter.setBrush(QBrush(Qt::green, Qt::SolidPattern)); painter.drawEllipse(80, 80, 400, 240); 调用函数setRenderHint(QPainter::Antialiasing,true),使绘制时边缘平滑,使用颜色浓度的变化,把图形的边缘转换为象素时引起的扭曲变形尽可能减少,在支持这一功能的平台或者绘图设备上得到一个平滑的边缘。 Figure 8.5. Geometric shape examples 下面的代码是图8.5(b)中绘制扇形的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 15, Qt::SolidLine, Qt::RoundCap, Qt::MiterJoin)); painter.setBrush(QBrush(Qt::blue, Qt::DiagCrossPattern)); painter.drawPie(80, 80, 400, 240, 60 * 16, 270 * 16); 函数drawPie()的最后两个参数值的单位为一度的十六之一。 下面的代码是图8.5(c)中绘制贝赛尔曲线的代码: QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing, true); QPainterPath path; path.moveTo(80, 320); path.cubicTo(200, 80, 320, 80, 480, 320); painter.setPen(QPen(Qt::black, 8)); painter.drawPath(path); 通过连接基本图形元素,直线,椭圆,多段线,圆弧,二次和三次贝塞尔曲线等,QPainterPath类能确定任何矢量图形。因此,绘图路径(Painter paths)是最基本的绘制元素,任何图形和图形的组合都可以同路径(path)表示。 一个路径能够确定一个轮廓,由这个轮廓确定的区域可以由刷子来填充。在图8.5(c)中我们没有设置刷子,因此只绘制了轮廓。 以上的三个例子都是使用了默认的刷子(Qt::SolidePattern,Qt::DiagCrossPattern,Qt::NoBrush)。在现在的应用程序中,单色填充已经很少使用,渐变色填充开始收到欢迎。渐变是依靠颜色的变化实现两种或者多种颜色之间平滑的过渡。渐变通常用来处理3D效果,如使用Plastique渐变方式来表现QPushButtons。 Qt支持三种类型的渐变:线形渐变,圆锥渐变和圆形渐变(linear, conical, and radial)。下一节的OvenTimer例子就是在一个控件中使用了所有这三种渐变。 线形渐变由两个控制点和直线上的一系列颜色点组成。图8.6由下面的代码得到:在两个控制点之间,在三个不同的位置确定了三个不同的颜色值。位置有0到1的浮点数得到,0为第一个控制点,1为第二个控制点。不同位置点之间的颜色由差值计算得到。 LinearGradient gradient(50, 100, 300, 350); gradient.setColorAt(0.0, Qt::white); gradient.setColorAt(0.2, Qt::green); gradient.setColorAt(1.0, Qt::black); Figure 8.6. QPainter's gradient brushes 圆形渐变由颜色组,圆心(xc,yc),半径r和焦点(xf,yf)定义。圆心和半径定义一个圆,颜色从焦点开始扩散到周围,焦点可以是圆心也可以是圆内的任意一个点。 圆锥渐变由圆心(xc,yc)和一个角度a定义。颜色从圆心开始像表的秒针一样扩散。 我们已经提到了QPainter的画笔,刷子和字体设置。此外,QPainter还有其他一些设置影响图形和文字的绘制: 1. 背景刷子,当背景模式为Qt::OpaqueMode(缺省值为Qt::transparentMode)时,背景刷子用来填充几何图形,文字,和位图的背景(在绘图刷子的下面) 2. 刷子的起点:刷子的起始绘制点,通常为控件的左上角。 3. 剪辑区域,剪辑区域为绘图设备上可以绘制的区域,在剪辑区域意外进行的绘制是无效的。 4. 视口,窗口,世界坐标:这三个决定了QPainter的逻辑坐标映射到物理坐标的方式。通常,逻辑坐标和物理坐标是重合的。坐标系统在下一节介绍。 5. 组合方式:组合方式决定绘制设备上新绘制的象素和已经存在的象素的影响方式。缺省方式为覆盖式(source over),新象素画在已有元素的上面。只是有限一个绘图设备支持组合方式的设置,将在本章后面介绍 在任何时候,我们可以调用save()把QPainter当前的设置保存在一个内部栈里,然后调用restore()进行恢复。我们能够临时改变QPainter的一些设置,然后恢复先前的值 Qt的二位图形引擎是基于 QPainter 类的。它既可以绘制几何形状,也可以绘制像素映射、图像和文字。自定义窗口部件需要重新实现 QWidget::paintEvent() 。 void Widget:: paintEvent( QPaintEvent * /*paintEvent*/ ) { draw(); drawBezier(); } void Widget:: draw() { QPainter painter( this ); painter. setPen( QPen ( Qt :: black, 10 , Qt :: DashDotLine, Qt :: RoundCap)); painter. setBrush( QBrush ( Qt :: green, Qt :: SolidPattern)); painter. drawEllipse( 10 , 10 , 400 , 240 ); painter. drawRect( QRect ( 10 , 300 , 400 , 140 )); } 首先需要创建一个 QPainter ,将需要绘图的设备的指针传递给 QPainter 。 QPainter 有各种 draw…() 可以绘制不同的图形。画笔、画刷、字体是最重要的三种设置。 画笔用来画线和边缘, QPainter ::setPen() 可以对画笔进行设置,包括颜色、宽度、线型、拐点风格等。 画刷用来填充几何形状的图案, QPainter ::setBrush() 可以对画刷进行设置,包括颜色和纹理风格。 绘制文本时需要对字体进行设置, QPainter ::setFont() 对字体进行设置,包括字体族和磅值。 void Widget:: drawBezier() { QPainter painter( this ); painter. setRenderHint( QPainter :: Antialiasing, true ); QPainterPath path; path. moveTo( 10 , 320 ); path. cubicTo( 200 , 80 , 400 , 80 , 480 , 320 ); painter. setPen( QPen ( Qt :: black, 8 )); painter. setBrush( QBrush ( Qt :: green, Qt :: DiagCrossPattern)); painter. drawPath( path); } QPainterPath 可以通过连接基本的图形单元元素来确定任意的矢量形状,包括:直线、椭圆、多边形、弧形、贝赛尔曲线等。 首先使用 QPainter ::moveto() 来确定图形的起点,然后通过 cubicTo() 来确定绘制的线路。前 4 个参数确定了两个控制点,后两个参数是结束点的位置。 修改函数如下: void Widget:: drawBezier() { QPainter painter( this ); painter. setRenderHint( QPainter :: Antialiasing, true ); QPainterPath path; path. moveTo( 10 , 320 ); path. cubicTo( 200 , 80 , 400 , 80 , 480 , 320 ); painter. setPen( QPen ( Qt :: black, 2 )); painter. drawPath( path); painter. setPen( QPen ( Qt :: red, 4 )); painter. drawPoint( 10 , 320 ); painter. drawPoint( 200 , 80 ); painter. drawPoint( 400 , 80 ); painter. drawPoint( 480 , 320 ); } 画笔样式 为了尝试画笔的样式,这里故意使用了一个新的画笔: painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine, Qt::RoundCap)); 我们对照着API去看,第一个参数是画笔颜色,这里设置为黑色;第二个参数是画笔的粗细,这里是5px;第三个是画笔样式,我们使用了 DashDotLine,正如同其名字所示,是一个短线和一个点相间的类型;第四个是RoundCap,也就是圆形笔帽。然后我们使用一个黄色的画刷填充,画了一个椭圆。 后面的一个和前面的十相似,唯一的区别是多了一句 painter.setRenderHint(QPainter::Antialiasing, true); 不过这句也很清楚,就是设置Antialiasing属性为true.如果你学过图形学就会知道,这个长长的单词就是"反走样".经过这句设置,我们就打开了QPainter的反走样功能。还记得我们曾经说过,QPainter是一个状态机,因此,只要这里我们打开了它,之后所有的代码都会是反走样绘制的了。 看到这里你会发现,反走样的效果其实比不走样要好得多,那么,为什么不默认打开反走样呢?这是因为,反走样是一种比较复杂的算法,在一些对图像质量要不高的应用中,是不需要进行反走样的。为了提高效率,一般的图形绘制系统,如Java2D、OpenGL之类都是默认不进行反走样的。 还有一个疑问,既然反走样比不反走样的图像质量高很多,不进行反走样的绘制还有什么作用呢?前面说的是一个方面,也就是,在一些对图像质量要不高的环境下,或者说性能受限的环境下,比如嵌入式和手机环境,是不必须要进行反走样的。另外还有一点,在一些必须精确操作像素的应用中,也是不能进行反走样的。请看下面的图片: 上图是使用Photoshop的铅笔和画笔工具画的1像素的点在放大到3200%视图下截下来的。Photoshop里面的铅笔工具是不进行反走样,而画笔是要进行反走样的。在放大的情况下就会知道,有反走样的情况下是不能进行精确到1像素的操作的。因为反走样很难让你控制到1个像素。这不是 Photoshop画笔工具的缺陷,而是反走样算法的问题。如果你想了解为什么这样,请查阅计算机图形学里面关于反走样的原理部。 反走样是图形学中的重要概念,用以防止“锯齿”现象的出现。很多系统的绘图API里面都会内置了反走样的算法,不过默认一般都是关闭的,Qt也不例外。下面我们来看看代码。这段代码仅仅给出了paintEvent函数,相信你可以很轻松地替换掉前面章节中的相关代码。 void PaintedWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine, Qt::RoundCap)); painter.setBrush(Qt::yellow); painter.drawEllipse(50, 150, 200, 150); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(QPen(Qt::black, 5, Qt::DashDotLine, Qt::RoundCap)); painter.setBrush(Qt::yellow); painter.drawEllipse(300, 150, 200, 150); } 声明:本文原创于yafeilinux的百度博客,http://hi.baidu.com/yafeilinux 转载请注明出处。 前面一节我们讲解了图片的显示,其中很多都用到了坐标的变化,这一节我们简单讲一下Qt的坐标系统,其实也还是主要讲上一节的那几个函数。这里我们先讲解一下Qt的坐标系,然后讲解那几个函数,它们别是: translate()函数,进行平移变换;scale()函数,进行比例变换;rotate()函数,进行旋转变换;shear()函数,进行扭曲变换。 最后介绍两个有用的函数save()和restore(),利用它们来保存和弹出坐标系的状态,从而实现快速利用几个变换来绘图。 一、坐标系简介。 Qt中每一个窗口都有一个坐标系,默认的,窗口左上角为坐标原点,然后水平向右依次增大,水平向左依次减小,垂直向下依次增大,垂直向上依次减小。原点即为(0,0)点,然后以像素为单位增减。 例如: void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::red); painter.drawRect(0,0,100,100); painter.setBrush(Qt::yellow); painter.drawRect(-50,-50,100,100); } 我们先在原点(0,0)绘制了一个长宽都是100像素的红色矩形,又在(-50,-50)点绘制了一个同样大小的黄色矩形。可以看到,我们只能看到黄色矩形的一部。效果如下图。 二、坐标系变换。 坐标系变换是利用变换矩阵来进行的,我们可以利用QTransform类来设置变换矩阵,因为一般我们不需要进行更改,所以这里不在涉及。下面我们只是对坐标系的平移,缩放,旋转,扭曲等应用进行介绍。 1.利用translate()函数进行平移变换。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::yellow); painter.drawRect(0,0,50,50); painter.translate(100,100); //将点(100,100)设为原点 painter.setBrush(Qt::red); painter.drawRect(0,0,50,50); painter.translate(-100,-100); painter.drawLine(0,0,20,20); } 效果如下。 这里将(100,100)点作为了原点,所以此时(100,100)就是(0,0)点,以前的(0,0)点就是 (-100,-100)点。要想使原来的(0,0)点重新成为原点,就是将(-100,-100)设为原点。 2.利用scale()函数进行比例变换,实现缩放效果。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::yellow); painter.drawRect(0,0,100,100); painter.scale(2,2); //放大两倍 painter.setBrush(Qt::red); painter.drawRect(50,50,50,50); } 效果如下。 可以看到,painter.scale(2,2),是将横纵坐标都扩大了两倍,现在的(50,50)点就相当于以前的 (100,100)点。 3.利用shear()函数就行扭曲变换。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setBrush(Qt::yellow); painter.drawRect(0,0,50,50); painter.shear(0,1); //纵向扭曲变形 painter.setBrush(Qt::red); painter.drawRect(50,0,50,50); } 效果如下。 这里,painter.shear(0,1),是对纵向进行扭曲,0表示不扭曲,当将第一个0更改时就会对横行进行扭曲,关于扭曲变换到底是什么效果,你观察一下是很容易发现的。 4.利用rotate()函数进行比例变换,实现缩放效果。 void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawLine(0,0,100,0); painter.rotate(30); //以原点为中心,顺时针旋转30度 painter.drawLine(0,0,100,0); painter.translate(100,100); painter.rotate(30); painter.drawLine(0,0,100,0); } 效果如下。 因为默认的rotate()函数是以原点为中心进行顺时针旋转的,所以我们要想使其以其他点为中心进行旋转,就要先进行原点的变换。这里的painter.translate(100,100)将(100,100)设置为新的原点,想让直线以其为中心进行旋转,可是你已经发现效果并非如此。是什么原因呢?我们添加一条语句,如下: void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.drawLine(0,0,100,0); painter.rotate(30); //以原点为中心,顺时针旋转30度 painter.drawLine(0,0,100,0); painter.rotate(-30); painter.translate(100,100); painter.rotate(30); painter.drawLine(0,0,100,0); } 效果如下。 这时就是我们想要的效果了。我们加的一句代码为painter.rotate(-30),这是因为前面已经将坐标旋转了30度,我们需要将其再旋转回去,才能是以前正常的坐标系统。不光这个函数如此,这里介绍的这几个函数均如此,所以很容易出错。下面我们将利用两个函数来很好的解决这个问题。 三、坐标系状态的保护。 我们可以先利用save()函数来保存坐标系现在的状态,然后进行变换操作,操作完之后,再用restore()函数将以前的坐标系状态恢复,其实就是一个入栈和出栈的操作。 例如: void Dialog::paintEvent(QPaintEvent *) { QPainter painter(this); painter.save(); //保存坐标系状态 painter.translate(100,100); painter.drawLine(0,0,50,50); painter.restore(); //恢复以前的坐标系状态 painter.drawLine(0,0,50,50);
Java开发技术大全 电子版 第1篇Java基础知识入门. 第1章Java的开发运行环境2 1.1Java的运行环境与虚拟机2 1.2Java的开发环境4 1.2.1JDK的安装4 1.2.2如何设置系统环境变量6 1.2.3编译命令的使用8 1.2.4解释执行命令的使用10 1.2.5UltraEdit的使用11 1.3一个简单的Java应用程序14 1.4一个简单的Java小程序16 1.5本章小结18 第2章Java语言基础19 2.1Java语言的特点19 2.2Java程序的构成21 2.3数据类 型23 2.3.1基本数据类型23 2.3.2常量25 2.3.3变量26 2.3.4整型数据27 .2.3.5浮点型数据29 2.3.6字符型数据30 2.3.7布尔型数据32 2.3.8变量赋初值33 2.3.9变量的作用域34 2.3.10数据类型转换34 2.4运算符与表达式37 2.4.1算术运算符和算术表达式38 2.4.2关系运算符和关系表达式43 2.4.3逻辑运算符和逻辑表达式44 2.4.4条件运算符和条件表达式48 2.4.5位运算符和位运算表达式50 2.4.6赋值运算符和赋值表达式53 2.4.7表达式的值顺序55 2.5流程控制语句58 2.5.1三种基本控制结构58 2.5.2表达式语句和空语句59 2.5.3块语句60 2.5.4if~else支语句61 2.5.5多路支switch~case语句69 2.5.6当型循环while语句71 2.5.7直到型循环do~while语句72 2.5.8当型循环for语句74 2.5.9循环的嵌套78 2.5.10跳转语句break80 2.5.11跳转语句continue82 2.6程序文本的风格84 2.6.1注释84 2.6.2程序的格式编排87 2.7基础语法实战演习88 2.7.1判断闰年88 2.7.2最大公约数和最小公倍数89 2.7.3Fibonacci数列90 2.7.4逆向输出数字91 2.7.5水仙花数92 2.7.6输出图形93 2.7.7输出九九口诀表94 2.8本章小结95 第2篇Java面向对象编程 第3章对象和类98 3.1面向对象的基本概念98 3.1.1对象98 3.1.2类99 3.1.3消息101 3.1.4面向对象的4个基本特征101 3.2类与对象104 3.2.1类的基本结构104 3.2.2类的声明104 3.2.3创建类体105 3.2.4对象的生命周期106 3.2.5对象的创建106 3.2.6对象的使用108 3.2.7对象的释放和垃圾收集机制108 3.3成员变量的定义与使用109 3.3.1成员变量的定义109 3.3.2成员变量的访问权限110 3.3.3实例成员变量和静态成员变量114 3.4方法的定义和实现116 3.4.1方法的声明117 3.4.2创建方法体与return语句117 3.4.3局部变量和成员变量的区别119 3.4.4方法的访问权限121 3.5方法的调用121 3.5.1方法调用的形式121 3.5.2方法调用的参数123 3.5.3隐含参数this127 3.6构造方法128 3.6.1无参数构造方法的定义和使用129 3.6.2带参数构造方法的定义和使用131 3.6.3this关键字和构造方法的调用132 3.7静态方法133 3.7.1静态方法的声明和定义134 3.7.2静态方法和实例方法的区别134 3.7.3静态代码块136 3.7.4再论静态成员变量137 3.8main()方法和命令行参数139 3.9结束方法141 3.10本地方法141 3.11本章小结144 第4章继承与多态145 4.1继承的基本原理145 4.2子类对父类的继承146 4.3属性隐藏和方法的覆盖148 4.3.1属性的隐藏148 4.3.2方法的覆盖151 4.4构造方法的继承154 4.5super的使用156 4.5.1用super引用父类的成员156 4.5.2使用super调用父类的构造方法157 4.6继承的内部处理158 4.7多态的基本概念159 4.8重载159 4.8.1普通方法的重载160 4.8.2构造方法的重载161 4.8.3重载的解析163 4.8.4重载与覆盖的区别165 4.9运行时多态165 4.9.1实例方法的运行时多态165 4.9.2成员变量运行时的表现167 4.9.3静态方法运行时

69,369

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧