打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
AGG入门(转)

 

AGG入门

 

一、配置开发环境

 

AGG是一个高效的、高质量的、开源的矢量图形库,类似的有:GTK+CairoMicrosoftGDI+。在三者中,AGG的性能是最高的(不讨论SkiaDirect2D,他们有OGLDX的硬件加速,绘图速度根本不是一个档次的)。让我们细数一下他的优缺点:

  • 优点:效率高,质量高(有反锯齿),功能强大,跨平台和平台GUI支持,……
  • 缺点:没有硬件加速,文档少,门槛有那么点高,……

 

一、准备

 

二、工作

  1. VC6里新建一个工程“AGG”,类型为静态库Win32 Static Library
  2. 将下列源文件都加入工程:
    1. ./src/ 目录下所有cpp文件【核心库】*
    2. ./src/ctrl/ 目录下所有cpp文件【控件库】*
    3. ./src/platform/ 下对应平台目录(win32)下的所有cpp文件【平台支持库】*
    4. ./font_freetype/ 下所有的cpp文件【Freetype字体支持库】
    5. ./font_win32_tt/ 下所有的cpp文件【Truetype字体支持库】*
    6. ./gpc/ 下所有的cpp文件【Generic Polygon Clipper裁切库】
  3. 将下列目录加入Tools(工具)菜单– Options(选项)菜单 – Directory(目录)选项卡中的Include Files目录列表中:
    1. ./include/ *
    2. ./font_freetype/
    3. ./font_win32_tt/ *
    4. ./gpc/
  4. 选择配置为Win32 Release,编译
  5. 编译成功后,Tools(工具)菜单– Options(选项)菜单 – Directory(目录)选项卡中的Library Files目录列表中加入 【工程所在目录】\Release\
  6. 没有成功编译的童鞋,奖励已经编译好的AGG.lib一枚…… AGG.zip

三、测试

  1. 新建一个Win32 Application 空工程,新建一源文件,加入下面的代码:

#include <agg_pixfmt_rgb.h>
#include <agg_renderer_base.h>
#include <platform/agg_platform_support.h>

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<agg::pixfmt_rgb24> ren_bas;

};

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;
}

  1. 在【工程】菜单 - 【设置】菜单 - 【连接】选项卡 - 【对象/库模块】文本框 中加入AGG.lib
  2. 编译运行,不出意外,可以得到下面的结果:

 

二、平台支持

AGG入门(二) - 平台支持

一、先看看下面的代码,并试着编译下:

#include <platform/agg_platform_support.h>
#include <agg_pixfmt_rgb.h>
#include <agg_renderer_base.h>
#include <stdio.h>

#include <agg_path_storage.h>

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<agg::pixfmt_rgb24> ren_bas;

};

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——它允许你建立一个窗口来测试你的图形,并用鼠标键盘去控制它。

类型

  • class platform_support 

主要成员函数

  • platform_support(pix_format_e, bool) : 构造函数。设置窗口风格和y轴是否上下翻转;
  • width() :返回窗口的宽;
  • height() :返回窗口的高;
  • caption([ const char* ]) :设置标题或返回标题字符串;
  • format() 返回窗口风格;
  • message(const char*) :弹出对话框(没有风格可选);
  • run() :运行窗口;
  • force_redraw() :重绘窗口,调用on_draw()
  • update_window() :更新窗口,既是把渲染缓存中已有的内容写入窗口,不调用on_draw()
  • platform_support为我们提供了一个很好地绘图平台,它有一系列的函数可用于操作位图(BMPPPM):
    • create_img():创建一个编号为idx的位图;
    • save_img():将位图idx保存到文件中;
    • load_img():从文件中加载位图到idx中;
    • copy_img_to_window():把idx拷贝到渲染缓存里;
    • copy_img_to_img():把idx拷贝到另一幅位图里;
    • copy_window_to_img() :将渲染缓存里的内容拷贝到位图。

虚函数(一般都是些消息,要覆盖它以让消息循环调用)

  • on_init() :窗口初始化时调用;
  • on_resize(int, int) :改变大小时调用;
  • on_idle()  :空闲时调用;
  • on_mouse_move(int, int, unsigned) :鼠标移动时调用;
  • on_mouse_button_down(int, int, unsigned) :鼠标按下时调用;
  • on_mouse_button_up(int, int, unsigned)  :鼠标弹起时调用;
  • on_key(int, int, unsigned, unsigned)  :键盘打字时调用;
  • on_draw()  :窗口重绘时调用。

三、结语

platform_support 的功能不仅仅是这么多,除此之外,他还能使用控件,等等。但很多时候,成熟的应用是不会使用它的,因为它封装了太多,虽然保证了跨平台性,却缺乏了自由性。platform_support 的主要作用是测试图像和修改图像,方便工作和移植……还有,方便初学者入门……

 

三、渲染器介绍

AGG入门(三) - 渲染器介绍

一、看回AGG入门(二)时on_draw()虚函数里的代码:

        agg::rendering_buffer &rbuf = rbuf_window();
        agg::pixfmt_rgb24 pixf(rbuf);

        agg::renderer_base<agg::pixfmt_rgb24> renb(pixf);
        renb.clear(agg::rgba8(255, 255, 255));

        pixf.copy_pixel(20, 20, agg::rgba8(0, 0, 255));

二、渲染器

什么是渲染?

渲染是把内存中的绘图指令真正执行的过程。比如说,绘制一条线段,在内存里只会保存着两个端点的坐标和线段的宽度,而渲染就把这两个端点转换为位图、缓存甚至显示屏上的一个个像素的数据。又比如说,纸飞机下面肯定是要有投影的了,但这个投影的质量,就由渲染器决定;线段是走样的(A),还是反走样的(B),靠的就是渲染器的指令了。

AGG里的渲染器

AGG分有多种渲染器。在AGG中,渲染器负责表现扫描线中的每个线段。在渲染器之前,AGG图形中的线段是没有颜色值的,只是位置、长度和 覆盖率(透明度)。渲染器赋于线段色彩,最终成为一幅完整的图像。其中最常用的是:

  • 像素格式渲染器
  • 基础渲染器
  • 扫描线(反锯齿)渲染器

三、三种渲染器间的关系

  • 像素格式渲染器(PixelFormat Renderer)是最基础的渲染器,不需要任何其他渲染器的支持,所以可以直接声明;
  • 基础渲染器(Base Renderer)是中级渲染器,需要像素格式作为模版和像素格式渲染器的支持;
  • 扫描线渲染器(Scanline Renderer)是高级渲染器,需要基础渲染器作为模版和支持;

(注:模版主要是为了获取像素格式的信息)

所以,除像素格式渲染器声明为:
agg::class object(agg::rendering_buffer &);
之外其他的渲染器都声明为:

agg::class<template> object(template &);

 

 

 

四、渲染缓存和混合器

        agg::rendering_buffer &rbuf = rbuf_window();
        agg::pixfmt_rgb24 pixf(rbuf);

        agg::renderer_base<agg::pixfmt_rgb24> renb(pixf);
        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<int8u> rendering_buffer //int8u8 bit无符号整形

基本成员函数

  • rendering_buffer(int8u* buf, unsigned width, unsigned height, int stride) 
            
    构造函数,指定事先分配好的内存块(到时就画到上面)首地址、宽高、一行的字节数(默认全部都是0);
  • row_ptr(int y)
            
    返回第y行的首地址;
  • copy_from(void *buf)
            
    buf中拷贝像素;
  • clear(int8u value)
            
    value清空缓存
  • buf(), height(), weight(), stride()
            
    返回缓存首地址、宽高、一行的字节数;

注:代码中的rbuf_window()platform_support的一个成员函数,用于返回platform_support一开始帮你申请的缓存引用。

三、混合器

混合器的存在是为了适应不同平台、不同需求下的不同像素格式。混合器有三种:agg::rgbaagg::rgba8agg::rgba16,都是用来指定颜色的,rgba每个通道储存为doublergba8unsigned charrgba16intlong int;混合器起到的作用就像Win32API里的RGBCOLORREF宏。

头文件

#include "agg_pixfmt_rgba.h"

类型定义

struct rgba8; //对,你没有看错,是结构,不是类……

基本成员函数

  • rgba8(unsigned r, unsigned g, unsigned b, unsigned a)
            
    无须解释了吧,最大255
  • clear(), no_color()
            
    四个通道全部清零,也就是变没色咯;
  • transparent()
            alpha
    清零,变透明;
  • opacity()
            
    返回透明度,用double表示;
  • gradient(agg::rgba8 &c, double k)
            
    颜色梯度,就是颜色变为从原先的颜色渐变为c,变化率为k
  • add(agg::rgba8 &c, unsinged cover)
            
    颜色叠加,叠加一个透明度为cover/255的颜色c

成员变量

  • r, g, b, a都是无符号整型;

四、像素格式混合器


像素格式混合器的作用是直接操作像素(也就是缓存里保存的数据,但起码有个像素的样子),起到Win32API里的SetPixel()GetPixel()的作用。像素格式由两个属性决定:混合器类型agg::rgba8/agg::rgba16bgr/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<blender_rgb<rgba8,  order_rgb>, rendering_buffer> pixfmt_rgb24;

基本成员函数

  • pixfmt_rgb24(agg::rendering_buffer &) 
           
    构造函数,指定缓存就好;
  • blend_pixel(agg::rgba8& c, int x, int y, int8u cover)
            
    用颜色ccover(覆盖率=透明度)的透明度混合像素(x, y)
  • copy_pixel(agg::rgba8& c, int x, int y)pixel(int x, int y)
           
    这个就是相当于SetPixel()GetPixel()了;
  • copy_hline(int x, int y, unsigned len, agg::rgba8& c)
    copy_vline(int x, int y, unsigned len, agg::rgba8& c)  
            
    (x, y)开始打横(竖)顺序设置len长度的像素;
  • blend_hline(int x, int y unsigned len, agg::rgba8& c, int8u cover)
    blend_vline(int x, int y
    unsigned len, agg::rgba8& c, int8u cover) 
            
    (x, y)开始打横(竖)顺序混合len长度的像素;
  • copy_solid_hspan(int x, int y unsigned len, agg::rgba8* colors)
    copy_solid_vspan(int x, int y
    unsigned len, agg::rgba8* colors)
    blend_solid_hspan(int x, int y
    unsigned len, agg::rgba8* colors, int8u* cover, int8u cover)
    blend_solid_vspan(int x, int y
    unsigned len, agg::rgba8* colors, int8u* cover, int8u cover)
            
    同上两个,不过不是一个颜色,是一系列的颜色;
  • for_each_pixel(void (*f)(agg::rgba8* color))
            
    每一像素执行一遍f
  • copy_from(agg::rendering_buffer & from, int xdst, int ydst, int xsrc, int ysrc, unsigned len)
    blend_from(agg::rendering_buffer & from, int xdst, int ydst, int xsrc, int ysrc, unsigned len[, unsigned cover])
      
            
    从缓存form(xsrc, ysrc)顺序复制(混合)到当前缓存的(xdst, ydst)中;

【其他函数和像素格式就要靠大家的举一反三,触类旁通了……

五、结语

上面说的三者关系是:混合器混合RGBA四个通道,像素格式混合器混合像素,像素格式混合器操作的结果是使渲染缓存里的数据发生变化,而混合器则不会,因为它的作用仅仅是表示颜色。

 

 

五、基础渲染器

基础渲染器(Base Renderer)是扫描线渲染器的基础,可以说,正常情况下,你绘画任何图形、做任何事,都需要通过它。而基础渲染器需要你以模版的形式提供像素格式的信息,他将会通过像素格式混合器来实现渲染。其实,基础渲染器比像素格式混合器多了剪裁盒的功能,其他混合、拷贝什么的和像素格式混合器是相似的,这里就不列出来了。

矩形类

AGG封装了一个专门表示矩形的模板类rect_base,方便矩形的操作。下面用rect_i说明一下。        

头文件

#include "agg_basics.h"

类型定义

typedef rect_base<int> rect_i;

基本成员函数

  • rect_i(x1, y1, x2, y2)
            构造函数,给出最小和最大坐标;
  • normalize()
            修正x1>x2y1>y2的不合法矩形;
  • clip(rect_i& r)
           
    取当前矩形与r相交的区域矩形作为当前矩形;
  • is_valid()
           
    检查矩形是否合法;
  • hit_test(int x, int y)
            检查(x, y)是否在矩形内;

基础渲染器

头文件

#include "agg_renderer_base"

类型定义

template<class PixelFormat> class renderer_base

基本成员函数

  • renderer_base(PixelFormat)
            构造函数,提供像素格式;
  • ren()
            返回像素格式对象;
  • clip_box(x1,y1,x2,y2)
    clip_box_naked(x1,y1,x2,y2)
            设置当前剪裁盒为x1,y1,x2,y2围成的剪裁盒,前者检查剪裁盒是否合法,后者不检查。
  • reset_clipping(bool visibility)
            重置剪裁盒,visibility决定剪裁盒是铺满窗口(可视)还是0(不可视);
  • clip_box()
    xmin()
    ymin()
    xmax()
    ymax()
            返回当前剪裁盒矩形、以及纵横坐标;
  • copy_from()
    blend_from()
            可以比较方便地、以矩形方式拷贝和混合缓存里的图像了;

 

六、练习和细节

学到目前为止,已经认识了六个类型:

  • platform_support
  • rendering_buffer
  • rgba8
  • pixfmt_rgb24
  • rect_i
  • renderer_base

现在来做些练习,看看有没有掌握学过的东西,并且灵活运用吧。

一、基本框架

这一节的程序都以这个框架为基础,都是在on_draw中稍微改动的:

#include <agg_pixfmt_rgb.h>
#include <agg_renderer_base.h>
#include <platform/agg_platform_support.h>

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<agg::pixfmt_rgb24> ren_bas;

};

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);
参数:

  • x1, y1, x2, y2分别是两个端点的坐标;
  • color是颜色;

 

三、画圆函数

编写如下函数,实现在渲染缓存中画圆的功能(无需反锯齿):
    void stroke_round(int r, int C_x, int C_y, agg::rgba8& color, float step = 0.01)
参数:

  • C_x, C_y 是圆心的坐标;
  • color是颜色;
  • step是步长,也就是吧圆细分成1/step边形;

 

四、答案

  • 画线函数

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中,混合器不进行检查,像素拷贝的时候会拷贝到剪裁区域以外,这样会造成很奇怪的情况,以至于如果写到了缓存以外,还会出现异常。注意,剪裁盒功能是基础渲染器级别才提供的,更加底层的操作,比如像素格式混合和直接操作缓存,高层次的渲染器是无从管理的。为了安全起见,建议少碰基础渲染器以下的工具……

 

 

七、顶点源

现在终于进入了真正的矢量绘图阶段,我们的模版也需要有所改变;至于为什么,有什么作用,以后会说到;
包含下面的头文件,并且在the_application类中添加两个成员。

//扫描线和扫描线光栅器
#include <agg_scanline_u.h>
#include <agg_rasterizer_scanline_aa.h>

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 <agg_path_storage.h>

类型定义

typedef path_base<vertex_block_storage<double> > path_storage;

基本成员函数

  • move_to()
        
    添加命令为 path_cmd_move_to 的顶点,意为下一条线从这个点开始画;
  • line_to()
        
    添加命令为 path_cmd_line_to 的顶点,意为画线到这个点;
  • arc_to(double rx, double ry, double angle, bool large_arc_flag, bool sweep_flag, double x, double y)
        
    添加一条弧路径,画轴长为rx, ry,角度为angle,优/劣弧,顺逆时针,终点在(x,y)
  • curve3_to()
        
    添加贝塞尔曲线,参数为一个控制点和终点的坐标
  • curve4_to()
        
    添加贝塞尔曲线,参数为两个控制点和终点的坐标
  • join_path()
        
    添加一个顶点源,即组合
  • vertex(unsigned idx, double* x, double* y)
    last_vertex(double* x, double* y)
    vertex(double* x, double* y)

        
    取顶点位置,前者为已知步骤,后两者为顺序或倒序获取
  • modify_vertex()
    modify_command()

        
    修改步骤为idx的顶点坐标和命令

五、其他顶点源

其他顶点源就不一一介绍了,只列出其头文件和构造函数:

  • #include <agg_ellipse.h>
    ellipse(double x, double y, double rx, double ry, unsigned num_steps=0, bool cw=false)
        
    圆心(x, y)和长短半轴分别为rx, ry,步骤数位num_steps(无用),cw决定相交地方是否空出
  • #include <agg_arc.h>
    arc(double x,  double y, double rx, double ry, double a1, double a2, bool ccw=true)
        
    圆心为(x, y)和长短半轴分别为rx, ry,初始角度和终结角度为a1, a2
  • #include <agg_curves.h>
    curve3(double x1, double y1, double x2, double y2, double x3, double y3)
        
    三个点,分别为:初始点,控制点一,终结点
  • #include <agg_curves.h>
    curve4(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
        
    四个点,分别为:初始点,控制点一,控制点二,终结点
  • #include <agg_rounded_rect.h>
    rounded_rect(double x1, double y1, double x2, double r)
        
    对角点的坐标和圆角半径

至于怎样把他们画在渲染内存上呢,我们下一篇会讲到。
头文件也渐渐地多了起来,包含头文件时的工作量有点大;我特地列了一个头文件,里面已经包含了所有的AGG头文件,以后大家只需要包含它就好了。
下载处:http://www.cppblog.com/Files/Shihira/agg.h.zip

 

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
[翻译] AGG Reference 之 Basic Renderers(基础渲染器)
C语言符号意义大全
64位(
C语言-求8的8888次方?
全面整理的C++面试题 - ljzcome的专栏
bayer, yuv, RGB转换方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服