AGG入门
一、配置开发环境
AGG入门(一) - 配置开发环境
AGG是一个高效的、高质量的、开源的矢量图形库,类似的有:GTK+的Cairo,Microsoft的GDI+。在三者中,AGG的性能是最高的(不讨论Skia和Direct2D,他们有OGL和DX的硬件加速,绘图速度根本不是一个档次的)。让我们细数一下他的优缺点:
一、准备
二、工作
三、测试
#include
#include
#include
class the_application : public agg::platform_support
{
public:
the_application(agg::pix_format_e format, bool flip_y) :
agg::platform_support(format, flip_y),
pix_fmt(rbuf_window()),
ren_bas(pix_fmt) //
{ }
virtual void on_draw()
{
ren_bas.reset_clipping(true);
ren_bas.clear(agg::rgba8(204, 204, 204));
}
private:
agg::pixfmt_rgb24 pix_fmt;
agg::renderer_base
};
int agg_main(int argc, char* argv[])
{
the_application app(agg::pix_format_rgb24, true);
app.caption("AGG Test");
if(app.init(500, 500, agg::window_resize)) {
return app.run();
}
return -1;
}
二、平台支持
AGG入门(二) - 平台支持
一、先看看下面的代码,并试着编译下:
#include
#include
#include
#include
#include
class the_application : public agg::platform_support
{
public:
the_application(agg::pix_format_e format, bool flip_y) :
agg::platform_support(format, flip_y),
pix_fmt(rbuf_window()),
ren_bas(pix_fmt) //
{
}
virtual void on_draw()
{
ren_bas.reset_clipping(true);
ren_bas.clear(agg::rgba8(255, 255, 255));
}
virtual void on_mouse_button_down(int x, int y, unsigned flags)
{
if(flags == agg::mouse_left) {
char str[50];
sprintf(str, "Mouse location:(%d, %d)", x, y);
message(str);
}
}
virtual void on_key(int x, int y, unsigned key, unsigned flags)
{
if(key == agg::key_return && flags == agg::kbd_shift) {
unsigned img = 0, states;
states = create_img(0, 500, 500);
states = load_img(img, "Steve-and-Bill.bmp");
copy_img_to_window(img);
update_window();
}
}
private:
agg::pixfmt_rgb24 pix_fmt;
agg::renderer_base
};
int agg_main(int argc, char* argv[])
{
the_application app(agg::pix_format_rgb24, true);
app.caption("AGG Test");
if(app.init(500, 500, agg::window_resize)) {
return app.run();
}
return -1;
}
如果不出意外,在窗口中点击鼠标左键将会出现对话框提示当前鼠标的位置,而按下Shift+Enter将会在窗口中显示在工作目录下的位图“Steve-and-Bill.bmp”;
二、解释
先看看头文件:platform/agg_platform_support.h,它里边定义了一个platform_support类——它允许你建立一个窗口来测试你的图形,并用鼠标键盘去控制它。
三、结语
platform_support 的功能不仅仅是这么多,除此之外,他还能使用控件,等等。但很多时候,成熟的应用是不会使用它的,因为它封装了太多,虽然保证了跨平台性,却缺乏了自由性。platform_support 的主要作用是测试图像和修改图像,方便工作和移植……还有,方便初学者入门……
三、渲染器介绍
AGG入门(三) - 渲染器介绍
一、看回AGG入门(二)时on_draw()虚函数里的代码:
agg::rendering_buffer &rbuf = rbuf_window();
agg::pixfmt_rgb24 pixf(rbuf);
agg::renderer_base
renb.clear(agg::rgba8(255, 255, 255));
pixf.copy_pixel(20, 20, agg::rgba8(0, 0, 255));
二、渲染器
渲染是把内存中的绘图指令真正执行的过程。比如说,绘制一条线段,在内存里只会保存着两个端点的坐标和线段的宽度,而渲染就把这两个端点转换为位图、缓存甚至显示屏上的一个个像素的数据。又比如说,纸飞机下面肯定是要有投影的了,但这个投影的质量,就由渲染器决定;线段是走样的(A),还是反走样的(B),靠的就是渲染器的指令了。
AGG分有多种渲染器。在AGG中,渲染器负责表现扫描线中的每个线段。在渲染器之前,AGG图形中的线段是没有颜色值的,只是位置、长度和覆盖率(透明度)。渲染器赋于线段色彩,最终成为一幅完整的图像。其中最常用的是:
三、三种渲染器间的关系
(注:模版主要是为了获取像素格式的信息)
所以,除像素格式渲染器声明为:
agg::class object(agg::rendering_buffer &);
之外其他的渲染器都声明为:
agg::class object(template &);
四、渲染缓存和混合器
AGG入门(四) - 渲染缓存和混合器
一、上一节的代码
agg::rendering_buffer &rbuf = rbuf_window();
agg::pixfmt_rgb24 pixf(rbuf);
agg::renderer_base
renb.clear(agg::rgba8(255, 255, 255));
pixf.copy_pixel(20, 20, agg::rgba8(0, 0, 255));
二、渲染缓存
渲染缓存保存着一个个像素,作为AGG的画布。它仅仅是一个内存块,用来储存像素信息,不提供任何绘图功能,只允许你读取和修改里面的数据。它也不告诉你里面的像素是灰度的、RGB的还是RGBA的,不告诉你从哪里到哪里是一个像素——它只是用来管理内存数据的。
#include "platform/agg_platform_support.h"
typedef row_accessor
注:代码中的rbuf_window()是platform_support的一个成员函数,用于返回platform_support一开始帮你申请的缓存引用。
三、混合器
混合器的存在是为了适应不同平台、不同需求下的不同像素格式。混合器有三种:agg::rgba,agg::rgba8和agg::rgba16,都是用来指定颜色的,rgba每个通道储存为double,rgba8为unsigned char,rgba16为int或long int;混合器起到的作用就像Win32API里的RGB和COLORREF宏。
#include "agg_pixfmt_rgba.h"
struct rgba8; //对,你没有看错,是结构,不是类……
四、像素格式混合器
像素格式混合器的作用是直接操作像素(也就是缓存里保存的数据,但起码有个像素的样子),起到Win32API里的SetPixel()和GetPixel()的作用。像素格式由两个属性决定:混合器类型【agg::rgba8/agg::rgba16】、bgr/rgb/rgba/abgr顺序【agg::order_bgr/agg::order_rgb/agg::order_rgba/agg::order_abgr】——这样,共8种像素格式,它们起名字的规则就是:
agg::pixfmt_[order][bits*3];
下面用最常用的agg::pixfmt_rgb24来解释:
#include "agg_pixfmt_rgb.h"
typedef pixfmt_alpha_blend_rgb
【其他函数和像素格式就要靠大家的举一反三,触类旁通了……】
五、结语
上面说的三者关系是:混合器混合RGBA四个通道,像素格式混合器混合像素,像素格式混合器操作的结果是使渲染缓存里的数据发生变化,而混合器则不会,因为它的作用仅仅是表示颜色。
五、基础渲染器
AGG入门(五) - 基础渲染器
基础渲染器(Base Renderer)是扫描线渲染器的基础,可以说,正常情况下,你绘画任何图形、做任何事,都需要通过它。而基础渲染器需要你以模版的形式提供像素格式的信息,他将会通过像素格式混合器来实现渲染。其实,基础渲染器比像素格式混合器多了剪裁盒的功能,其他混合、拷贝什么的和像素格式混合器是相似的,这里就不列出来了。
矩形类
AGG封装了一个专门表示矩形的模板类rect_base,方便矩形的操作。下面用rect_i说明一下。
#include "agg_basics.h"
typedef rect_base
基础渲染器
#include "agg_renderer_base"
template
六、练习和细节
AGG入门(六) - 练习和细节
学到目前为止,已经认识了六个类型:
现在来做些练习,看看有没有掌握学过的东西,并且灵活运用吧。
一、基本框架
这一节的程序都以这个框架为基础,都是在on_draw中稍微改动的:
#include
#include
#include
class the_application : public agg::platform_support
{
public:
the_application(agg::pix_format_e format, bool flip_y) :
agg::platform_support(format, flip_y),
pix_fmt(rbuf_window()),
ren_bas(pix_fmt) //
{ }
virtual void on_draw()
{
ren_bas.reset_clipping(true);
ren_bas.clear(agg::rgba8(255, 255, 255));
}
private:
agg::pixfmt_rgb24 pix_fmt;
agg::renderer_base
};
int agg_main(int argc, char* argv[])
{
the_application app(agg::pix_format_bgr24, true);
app.caption("AGG Test");
if(app.init(500, 500, agg::window_resize)) {
return app.run();
}
return -1;
}
二、画线函数
编写如下函数,实现在渲染缓存中画线的功能(无需反锯齿):
inline void stroke_line(int x1, int y1, int x2, int y2, agg::rgba8& color);
参数:
三、画圆函数
编写如下函数,实现在渲染缓存中画圆的功能(无需反锯齿):
void stroke_round(int r, int C_x, int C_y, agg::rgba8& color, float step = 0.01)
参数:
四、答案
inline void stroke_line(int x1, int y1, int x2, int y2, agg::rgba8& color)
{
double precision = max(abs(x1 - x2), abs(y1 - y2));
//精度,也就是画多少个点
for(int i=0; i <= precision; i++)
ren_bas.copy_pixel( x1 + ( x2 - x1 ) / precision * i, //x
y1 + ( y2 - y1 ) / precision * i, //y
color);
}
void stroke_round(int r, int C_x, int C_y, agg::rgba8& color, float step = 0.01)
{
int prev_x = int(r * cos(-0.01)) + C_x,
prev_y = int(r * sin(-0.01)) + C_y; //保存上一个点
int x, y; //保存当前的点
for(double rad = 0; rad < 2 * PI + step; rad+= step) {
x = int(r * cos(rad)) + C_x;
y = int(r * sin(rad)) + C_y; //计算弧度为rad时的坐标
stroke_line(x, y, prev_x, prev_y, color);
prev_x = x; prev_y = y;
}
}
可能有的人会觉得奇怪的是,为什么在画线函数中,不用pix_fmt.copy_pixel()而用ren_bas.copy_pixel()呢?因为,在pix_fmt中,混合器不进行检查,像素拷贝的时候会拷贝到剪裁区域以外,这样会造成很奇怪的情况,以至于如果写到了缓存以外,还会出现异常。注意,剪裁盒功能是基础渲染器级别才提供的,更加底层的操作,比如像素格式混合和直接操作缓存,高层次的渲染器是无从管理的。为了安全起见,建议少碰基础渲染器以下的工具……
七、顶点源
AGG入门(七) - 顶点源
一、修改模板
现在终于进入了真正的矢量绘图阶段,我们的模版也需要有所改变;至于为什么,有什么作用,以后会说到;
包含下面的头文件,并且在the_application类中添加两个成员。
//扫描线和扫描线光栅器
#include
#include
private:
//
agg::scanline32_u8 scanline;
agg::rasterizer_scanline_aa<> rasterizer;
二、顶点源
顶点源(Vertex Source)不是一个类,而是一种类的模式。这种类里面有rewind()函数和vertex()函数给AGG内部调用(没错,这就是它的定义)。类如其名,顶点源就是为绘图系统提供顶点信息的,大家能想象得出这两个函数的作用了吗?
rewind():回到最开始个步骤;
vertex(double* x, double* y):每调用一次,跳一个步骤(点),每一个步骤都输出顶点的x,y坐标(灰色字),以及这个坐标的绘图命令(紫色字);
三、内置顶点源
AGG内置了大量的顶点源,我们可以直接调用,他们包括:
agg::path_storage
agg::arc
agg::rounded_rect
agg::ellipse
agg::curve3
agg::curve4 ......
等等,为什么没有线、点顶点源?其实,path_storage已经内置了画线函数、画弧函数、画贝塞尔曲线函数,你可以用path_storage创造几乎任何的图形。至于画点,copy_pixel()或者用椭圆吧……
四、路径储存器
Path storage 是用来管理路径、画复杂图形的。在上面可以任意添加直线、曲线、其他路径。
#include
typedef path_base
五、其他顶点源
其他顶点源就不一一介绍了,只列出其头文件和构造函数:
至于怎样把他们画在渲染内存上呢,我们下一篇会讲到。
头文件也渐渐地多了起来,包含头文件时的工作量有点大;我特地列了一个头文件,里面已经包含了所有的AGG头文件,以后大家只需要包含它就好了。
下载处:http://www.cppblog.com/Files/Shihira/agg.h.zip