用AGG实现高质量图形输出上.docx
《用AGG实现高质量图形输出上.docx》由会员分享,可在线阅读,更多相关《用AGG实现高质量图形输出上.docx(37页珍藏版)》请在冰豆网上搜索。
用AGG实现高质量图形输出上
用AGG实现高质量图形输出(上)
AGG是一个开源、高效的跨平台2D图形库。
AGG的功能与GDI+的功能非常类似,但提供了比GDI+更灵活的编程接口,其产生的图形的质量也非常高(自称超过GDI+)
使用前AGG的准备工作
1.下载AGG库,它的家在,目前最高版本是AGG2.5
2.解压,后面以[AGG]表示AGG的解压目录.
3.把[AGG]\include加入到include搜索目录中
4.把[AGG]\src里所有cpp加入到项目中(或者用makefile一起编译)
5.另外,AGG还有一些其它组件,用到时也要把它们(都是些.h和.cpp文件)加入项目:
o如果要用AGG的控件和窗体,要加入[AGG]\src\ctrl\*.cpp和[AGG]\src\platform\\*.cpp,头文件在[AGG]\include\ctrl和[AGG]\include\platform里
o如果要用到TrueType字体显示,要加入[AGG]\font_win32_tt目录下的源码和头文件。
利用freetype库,则是[AGG]\font_freetype目录。
o如果要用到GenericPolygonClipper库(一个区域剪裁计算库),加入[AGG]\gpc目录下的源码和头文件。
AGG图形显示原理见下图:
其中:
1.VertexSource顶点源,里面存放了一堆2D顶点以及对应的命令,如"MoveTo"、"LineTo"等。
2.Coordinateconversionpipeline坐标转换管道,它可以变换VertexSource中的顶点,比如矩阵变换,轮廓提取,转换为虚线等。
3.ScanlineRasterizer把顶点数据(矢量数据)转换成一组水平扫描线,扫描线由一组线段(Span)组成,线段(Span)包含了起始位置、长度和覆盖率(可以理解为透明度)信息。
AGG的抗锯齿(Anti-Aliasing)功能也是在这时引入的。
4.Renderers渲染器,渲染扫描线(Scanline)中的线段(Span),最简单的就是为Span提供单一颜色,复杂的有多种颜色(如渐变)、使用图像数据、Pattern等。
5.RenderingBuffer用于存放像素点阵数据的内存块,这里是最终形成的图像数据。
要理解AGG的工作原理,先看一段代码:
1.#include "agg_basics.h"
2.#include "agg_rendering_buffer.h"
3.#include "agg_rasterizer_scanline_aa.h"
4.#include "agg_scanline_u.h"
5.#include "agg_renderer_scanline.h"
6.#include "agg_pixfmt_rgb.h"
7.#include "platform/agg_platform_support.h"
8.#include "agg_ellipse.h"
9.#include "agg_conv_contour.h"
10.#include "agg_conv_stroke.h"
11.
12.class the_application :
public agg:
:
platform_support
13.{
14.public:
15. the_application(agg:
:
pix_format_e format, bool flip_y) :
16. agg:
:
platform_support(format, flip_y)
17. {
18. }
19.
20. virtual void on_draw()
21. {
22. //Rendering Buffer
23. agg:
:
rendering_buffer &rbuf = rbuf_window();
24. agg:
:
pixfmt_bgr24 pixf(rbuf);
25.
26. // Renderers
27. typedef agg:
:
renderer_base:
pixfmt_bgr24> renderer_base_type;
28. renderer_base_type renb(pixf);
29.
30. typedef agg:
:
renderer_scanline_aa_solid renderer_scanline_type;
31. renderer_scanline_type rensl(renb);
32.
33. // Vertex Source
34. agg:
:
ellipse ell(100,100,50,50);
35.
36. // Coordinate conversion pipeline
37. typedef agg:
:
conv_contour:
ellipse> ell_cc_type;
38. ell_cc_type ccell(ell);
39.
40. typedef agg:
:
conv_stroke ell_cc_cs_type;
41. ell_cc_cs_type csccell(ccell);
42.
43. // Scanline Rasterizer
44. agg:
:
rasterizer_scanline_aa<> ras;
45. agg:
:
scanline_u8 sl;
46.
47. // Draw
48. renb.clear(agg:
:
rgba8(255,255,255));
49. for(int i=0; i<5; i++)
50. {
51. ccell.width(i*20);
52. ras.add_path(csccell);
53. rensl.color( agg:
:
rgba8(0,0,i*50));
54. agg:
:
render_scanlines(ras,sl,rensl);
55. }
56. }
57.};
58.
59.int agg_main(int argc, char* argv[])
60.{
61. the_application app(agg:
:
pix_format_bgr24, false);
62. app.caption("AGG Example. Anti-Aliasing Demo");
63.
64. if(app.init(600, 400, agg:
:
window_resize))
65. {
66. return app.run();
67. }
68. return -1;
69.}
编译这段代码的方法是(以VC为例):
1.新建空白GUI项目(就是有WinMain的项目)
2.把[AGG]\src里所有*.cpp加入到项目中
3.把[AGG]\src\platform\Win32\*.cpp加入到项目中
4.Ctrl+C/Ctrl+V上面的代码
5.编译!
显示效果:
我们先不管agg_main及agg:
:
platform_support的问题,实际上agg:
:
platform_support只是AGG给我们方便显示AGG图形用的,真正应用时几乎不会用到(后面会讲到怎样把AGG图形画到HDC上)。
现在我们只需要知道这个框架可以生成一个窗体,当窗体重画时会调用virtualvoidon_draw()就行了。
现在直接从on_draw()开始看
1.通过rbuf_window()方法得到一个agg:
:
rendering_buffer,它就是“RenderingBuffer”,是一块用于存放图像的内存块。
通过pixfmt_bgr24包装,我们就可以以像素为单位存取图像。
2.agg:
:
renderer_base和agg:
:
renderer_scanline_aa_solid都属于"渲染器Renderer"。
renderer_base为底层渲染器,它支撑起所有的高层渲染器。
这里的renderer_scanline_aa_solid就是一个高层渲染器。
3.agg:
:
ellipse是“顶点源VertexSource”,这个顶点源呈现的是一个圆形。
4.agg:
:
conv_contour和agg:
:
conv_stroke作为“坐标转换管道Coordinateconversionpipeline”,conv_contour扩展轮廓线,conv_stroke只显示轮廓线(如果没有conv_stroke就会显示实心圆,可以去掉试试)。
5.agg:
:
rasterizer_scanline_aa<>就是“ScanlineRasterizer”啦。
6.agg:
:
render_scanlines函数执行这个AGG工作流程。
用AGG实现高质量图形输出
(二)
本文上接《用AGG实现高质量图形输出
(一)》,分别介绍了AGG显示流程中的各个环节。
顶点源(VertexSource)
顶点源是一种可以产生多边形所需要的“带命令的顶点”的对象。
比如三角形顶点源,就应该会产生一个带“MoveTo”命令的点,另外二个带"LineTo"命令的点和最终闭合的“ClosePoly”命令。
头文件
#include//path_storage
#include//ellipse
#include//arc
#include//arrowhead
#include//curve3,curve4
#include//gsv_text,gsv_text_outline
#include//rounded_rect
...
类型
自定义类
所有实现了voidrewind(unsignedpath_id);和unsignedvertex(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颇为特殊,它一般作为线段的标记点,具体用法是这样的:
arrowheadah;
ah.head(d1,d2,d3,d4);//定义箭头
ah.tail(d1,d2,d3,d4);//定义箭尾
VertexSourceVS;//其它顶点源
//使用顶点转换器,并指定Markers类型为vcgen_markers_term
//顶点转换器可以是conv_dash、conv_stroke或conv_marker_adaptor,见后文《坐标转换管道》
//vcgen_markers_term:
以端点作为标记点
conv_strokecsVS(VS);
...draw_term
//用conv_marker指定ah作为线段marker点的标记
conv_markerarrow(csVS.markers(),ah);
ras.add_path(csVS);
ras.add_path(arrow);//marker要紧随其后加入
...render
ah.head()和ah.tail()方法中的d1,d2,d3,d4参数的意义见下图
例,画一条简单的箭头直线(基于此处代码)
在on_draw()方法最后加上下列代码:
1.agg:
:
arrowhead ah;
2. ah.head(0,10,5,5);
3. ah.tail(10,10,5,5);
4. // 用path_storage生成一条直线
5. agg:
:
path_storage ps;
6. ps.move_to(160,60);
7. ps.line_to(100,100);
8. // 转换
9. agg:
:
conv_stroke:
path_storage, agg:
:
vcgen_markers_term> csps(ps);
10. agg:
:
conv_marker:
vcgen_markers_term, agg:
:
arrowhead>
11. arrow(csps.markers(), ah);
12. // 画线
13. ras.add_path(csps);
14. agg:
:
render_scanlines_aa_solid(ras,sl,renb,agg:
:
rgba8(0,0,0));
15. // 画箭头
16. ras.add_path(arrow);
17. 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"
试验代码,自定义一个顶点源(基于此处代码)
为了对顶点源有更深入的了解,我们要自己实现一个顶点源,这个顶点源只是很简单的一个三角形。
前面说过,只要实现了voidrewind(unsignedpath_id);和unsignedvertex(double*x,double*y);方法就可以作为顶点源。
rewind方法指示顶点源回到第一个顶点;vertex方法取出当前顶点然后把当前顶点下移,返回值是当前顶点所带的命令。
所谓的命令是一个enumpath_commands_e类型,定义如下:
1.enum path_commands_e
2.{
3. path_cmd_stop = 0, //----path_cmd_stop
4. path_cmd_move_to = 1, //----path_cmd_move_to
5. path_cmd_line_to = 2, //----path_cmd_line_to
6. path_cmd_curve3 = 3, //----path_cmd_curve3
7. path_cmd_curve4 = 4, //----path_cmd_curve4
8. path_cmd_curveN = 5, //----path_cmd_curveN
9. path_cmd_catrom = 6, //----path_cmd_catrom
10. path_cmd_ubspline = 7, //----path_cmd_ubspline
11. path_cmd_end_poly = 0x0F, //----path_cmd_end_poly
12. path_cmd_mask = 0x0F //----path_cmd_mask
13.};
path_commands_e还能和path_flags_e组合:
1.enum path_flags_e
2.{
3. path_flags_none = 0, //----path_flags_none
4. path_flags_ccw = 0x10, //----path_flags_ccw
5. path_flags_cw = 0x20, //----path_flags_cw
6. path_flags_close = 0x40, //----path_flags_close
7. path_flags_mask = 0xF0 //----path_flags_mask
8.};
vertex()返回的命令中含有path_cmd_stop时表示结束。
1.// 等边三角形
2.class triangle{
3.public:
4. triangle(double cx, double cy, double r)//中心点,r为中心点到边的长度
5. {
6. // 直接准备好三个点
7. m_step = 0;
8. m_pt[0].x = cx; m_pt[0].y = cy-r;
9. m_pt[1].x = cx+r*0.866; m_pt[1].y = cy+r*0.5;
10. m_pt[2].x = cx-r*0.866; m_pt[2].y = cy+r*0.5;
11. //AGG把方向作为区分多边形内部和外部的依据,可以试试m_pt[1]和m_pt[2]对调
12. }
13. void rewind(unsigned)
14. {
15. m_step = 0;
16. }
17. unsigned vertex(double* x, double* y)
18. {
19. switch(m_step++)
20. {
21. case 0:
22. //第一步,move_to
23. *x = m_pt[0].x;
24. *y = m_pt[0].y;
25. return agg:
:
path_cmd_move_to;
26. case 1:
27. case 2:
28. //第二、三步,line_to
29. *x = m_pt[m_step-1].x;
30. *y = m_pt[m_step-1].y;
31. return agg:
:
path_cmd_line_to;
32. case 3:
33. // 第四步,闭合多边形
34. return agg:
:
path_cmd_end_poly|agg:
:
path_flags_close;
35. default:
36. // 第五步,结束
37. return agg:
:
path_cmd_stop;
38. }
39. }
40.private:
41. agg:
:
point_d m_pt[3];
42. unsigned m_step;
43.};
在on_draw()方法里把
agg:
:
ellipseell(100,100,50,50);改成triangleell(100,100,50);
typedefagg:
:
conv_contour:
ellipse>ell_cc_type;改成typedefagg:
:
conv_contourell_cc_type;
得到的图形是:
除了文字输出功能(gsv_text只能输出ASCII文字),上面这些顶点源提供的图形丰富程度已经超过了系统API。
文字输出功能将以单独的篇幅讲述。
Coordinateconversionpipeline坐标转换管道
坐标转换管道用于改变顶点源产生的顶点,包括坐标、命令、产生新顶点等。
如对顶点进行矩阵变换、插入顶点形成虚线之类的功能。
变换矩阵(trans_affine)
在认识转换管道之前,先了解一下AGG的变换矩阵。
通过顶点坐标与矩阵的运行,我们可以得到新的坐标。
关于图像的矩阵运算,MSDN里有一篇关于GDI+矩阵运算的文章,很值得一看
头文件
#include<