用AGG实现高质量图形输出(二)
来自http://www.cppprog.com/2009/0821/150.html
上次讲了AGG的显示原理并举了一个简单的例子,这一篇文章开始讲AGG工作流程里的每个环节。为了方便对照,再放一次AGG显示流程 图
另外,上 一篇文章里的例 程也 很重要,后面的例子都将基于这个代码。
下面,我们来考察AGG显示流程中的每个环节。理解每个环节最好的方法是编写实验代码,建议先参照这里建 立一个可以运行的AGG实验环境。
顶点源(Vertex Source)
<!--基本属性-->顶点源(Vertex Source)
顶点源是一种可以产生多边形所需要的“带命令的顶点”的对象。比如三角形顶点源,就应该会产生一个带“MoveTo”命令的点,另外二 个带"LineTo"命令的点和最终闭合的“ClosePoly”命令。
头文件
#include <agg_path_storage.h> //path_storage #include <agg_ellipse.h> // ellipse #include <agg_arc.h> // arc #include <agg_arrowhead.h> // arrowhead #include <agg_curves.h> // curve3, curve4 #include <agg_gsv_text.h> // gsv_text, gsv_text_outline #include <agg_rounded_rect.h> // rounded_rect ...
类型
自定义类 | 所有实现了void rewind(unsigned path_id);和unsigned vertex(double* x, double* y);的类。 |
ellipse | 圆,输入为中心点坐标和XY轴半径,本文所用的例子就 使用了这个顶点源 |
arc | 弧线,输入为中心点坐标和XY轴半径,以及起始和终止角(rad),顺时针/逆时针方向 |
curve3 | 贝塞尔曲线,输入为起点坐标、第一控制点坐标、终点点坐标 |
curve4 | 贝塞尔曲线,输入为起点坐标、第一控制点坐标、第二控制点坐标、终点坐标 |
gsv_text | 使用AGG自带字模的文字输出(只支持ASCII码),使用start_point方法指定文字位置,text方法指定 文字,flip指定是否上下倒转,size指定文字大小,适合与conv_stroke或gsv_text_outline配合。 |
gsv_text_outline<> | 可变换文字,输入为gsv_text和变换矩阵(默认为trans_affine,后文会提到)。width方法设置文 字宽度 |
rounded_rect | 圆角方形,输入为左上角右下角坐标和圆角半径 |
path_storage | 路径存储器,可以用join_path方法加入多个顶点源。而且path_storage本身支持move_to, line_to,curve和arc_to等画线功能 |
arrowhead | 箭头,它是作为标记点来用的 |
其中的arrowhead颇为特殊,它一般作为线段的标记点,具体用法是这样的:
arrowhead ah; ah.head(d1,d2,d3,d4); //定义箭头 ah.tail(d1,d2,d3,d4); //定义箭尾 VertexSource VS; //其它顶点源 // 使用顶点转换器,并指定Markers类型为vcgen_markers_term // 顶点转换器可以是conv_dash、conv_stroke或conv_marker_adaptor,见后文《坐标转换管道》 // vcgen_markers_term:以端点作为标记点 conv_stroke<VertexSource, vcgen_markers_term> csVS(VS); ...draw_term // 用conv_marker指定ah作为线段marker点的标记 conv_marker<vcgen_markers_term, arrowhead> arrow(csVS.markers(), ah); ras.add_path(csVS); ras.add_path(arrow); //marker要紧随其后加入 ...render
ah.head()和ah.tail()方法中的d1,d2,d3,d4参数的意义见下图

例,画一条简单的箭头直线(基于此处代码)
在on_draw()方法最后加上下列代码:
- agg::arrowhead ah;
- ah.head(0,10,5,5);
- ah.tail(10,10,5,5);
- // 用path_storage生成一条直线
- agg::path_storage ps;
- ps.move_to(160,60);
- ps.line_to(100,100);
- // 转换
- agg::conv_stroke<agg::path_storage, agg::vcgen_markers_term> csps(ps);
- agg::conv_marker<agg::vcgen_markers_term, agg::arrowhead>
- arrow(csps.markers(), ah);
- // 画线
- ras.add_path(csps);
- agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba8(0,0,0));
- // 画箭头
- ras.add_path(arrow);
- agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba8(255,0,0));
得到的图形是:

注意要加头文件:
#include "agg_conv_marker.h" #include "agg_arrowhead.h" #include "agg_path_storage.h" #include "agg_vcgen_markers_term.h"
试验代码,自定义一个顶点源(基于此处代码)
为了对顶点源有更深入的了解,我们要自己实现一个顶点源,这个顶点源只是很简单的一个三角形。
前面说过,只要实现了 void rewind(unsigned path_id); 和 unsigned vertex(double* x, double* y); 方法就可以作为顶点源。
rewind方 法指示顶点源回到第一个顶点;vertex方 法取出当前顶点然后把当前顶点下移,返回值是当前顶点所带的命令。所谓的命令是一个enum path_commands_e类 型,定义如下:
- enum path_commands_e
- {
- path_cmd_stop = 0, //----path_cmd_stop
- path_cmd_move_to = 1, //----path_cmd_move_to
- path_cmd_line_to = 2, //----path_cmd_line_to
- path_cmd_curve3 = 3, //----path_cmd_curve3
- path_cmd_curve4 = 4, //----path_cmd_curve4
- path_cmd_curveN = 5, //----path_cmd_curveN
- path_cmd_catrom = 6, //----path_cmd_catrom
- path_cmd_ubspline = 7, //----path_cmd_ubspline
- path_cmd_end_poly = 0x0F, //----path_cmd_end_poly
- path_cmd_mask = 0x0F //----path_cmd_mask
- };
path_commands_e还 能和path_flags_e组 合:
- enum path_flags_e
- {
- path_flags_none = 0, //----path_flags_none
- path_flags_ccw = 0x10, //----path_flags_ccw
- path_flags_cw = 0x20, //----path_flags_cw
- path_flags_close = 0x40, //----path_flags_close
- path_flags_mask = 0xF0 //----path_flags_mask
- };
vertex()返回的命令中含有path_cmd_stop时 表示结束。
- // 等边三角形
- class triangle{
- public:
- triangle(double cx, double cy, double r)//中心点,r为中心点到边的长度
- {
- // 直接准备好三个点
- m_step = 0;
- m_pt[0].x = cx; m_pt[0].y = cy-r;
- m_pt[1].x = cx+r*0.866; m_pt[1].y = cy+r*0.5;
- m_pt[2].x = cx-r*0.866; m_pt[2].y = cy+r*0.5;
- //AGG把方向作为区分多边形内部和外部的依据,可以试试m_pt[1]和m_pt[2]对调
- }
- void rewind(unsigned)
- {
- m_step = 0;
- }
- unsigned vertex(double* x, double* y)
- {
- switch(m_step++)
- {
- case 0:
- //第一步,move_to
- *x = m_pt[0].x;
- *y = m_pt[0].y;
- return agg::path_cmd_move_to;
- case 1:
- case 2:
- //第二、三步,line_to
- *x = m_pt[m_step-1].x;
- *y = m_pt[m_step-1].y;
- return agg::path_cmd_line_to;
- case 3:
- // 第四步,闭合多边形
- return agg::path_cmd_end_poly|agg::path_flags_close;
- default:
- // 第五步,结束
- return agg::path_cmd_stop;
- }
- }
- private:
- agg::point_d m_pt[3];
- unsigned m_step;
- };
在on_draw()方法里把
agg::ellipse ell(100,100,50 ,50); 改成 triangle ell(100,100,50);typedef agg::conv_contour< agg::ellipse> ell_cc_type;改成typedef agg::conv_contour< triangle> ell_cc_type;
得到的图形是:
除了文字输出功能(gsv_text只能输出ASCII文字),上面这些顶点源提供的图形丰富程度已经超过了系统API。文字输出功能 将以单独的篇幅讲述。