(转)用AGG实现高质量图形输出(二)

本文上接《用AGG实现高质量图形输出(一)》,分别介绍了AGG显示流程中的各个环节。

上次讲了AGG的显示原理并举了一个简单的例子,这一篇文章开始讲AGG工作流程里的每个环节。为了方便对照,再放一次AGG显示流程 图

另外,上一篇文章里的例程也很重要,后面的例子都将基于这个代码。

下面,我们来考察AGG显示流程中的每个环节。理解每个环节最好的方法是编写实验代码,建议先参照这里建 立一个可以运行的AGG实验环境。

顶点源(Vertex Source)

顶点源是一种可以产生多边形所需要的“带命令的顶点”的对象。比如三角形顶点源,就应该会产生一个带“MoveTo”命令的点,另外二 个带"LineTo"命令的点和最终闭合的“ClosePoly”命令。

头文件

1

2

3

4

5

6

7

#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</agg_rounded_rect.h></agg_gsv_text.h></agg_curves.h></agg_arrowhead.h></agg_arc.h></agg_ellipse.h></agg_path_storage.h>

类型
自定义类 所有实现了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颇为特殊,它一般作为线段的标记点,具体用法是这样的:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

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> csVS(VS);

...draw_term

// 用conv_marker指定ah作为线段marker点的标记

conv_marker<vcgen_markers_term> arrow(csVS.markers(), ah);

ras.add_path(csVS);

ras.add_path(arrow); //marker要紧随其后加入

...render</vcgen_markers_term></vertexsource>

ah.head()和ah.tail()方法中的d1,d2,d3,d4参数的意义见下图

例,画一条简单的箭头直线(基于此处代码)

在on_draw()方法最后加上下列代码:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

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<?xml:namespace prefix = agg /><agg:agg:agg:agg:agg:agg::path_storage> csps(ps);

     agg::conv_marker<agg:agg:agg:agg:agg:agg::vcgen_markers_term>

         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));</agg:agg:agg:agg:agg:agg::vcgen_markers_term></agg:agg:agg:agg:agg:agg::path_storage>

得到的图形是:

注意要加头文件:


1

2

3

4

#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类 型,定义如下:


1

2

3

4

5

6

7

8

9

10

11

12

13

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组合:


1

2

3

4

5

6

7

8

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时 表示结束。


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

// 等边三角形

    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。文字输出功能 将以单独的篇幅讲述。

Coordinate conversion pipeline 坐标转换管道

Coordinate conversion pipeline 坐标转换管道

坐标转换管道用于改变顶点源产生的顶点,包括坐标、命令、产生新顶点等。如对顶点进行矩阵变换、插入顶点形成虚线之类的功能。

变换矩阵(trans_affine)

在认识转换管道之前,先了解一下AGG的变换矩阵。通过顶点坐标与矩阵的运行,我们可以得到新的坐标。关于图像的矩阵运算,MSDN里 有一篇关 于GDI+矩阵运算的文章,很值得一看

头文件

#include <agg_trans_affine.h>

类型

trans_affine

成员变量
double sx, shy, shx, sy, tx, ty;
这六个变量组成一个2*3的矩阵,与坐标计算后得到一个新的坐标。比如对点(x,y)进行变换,则新的点(x‘,y‘) 为:

x‘ = x*sx  + y*shx + tx;
y‘ = x*shy + y*sy  + ty;
成员方法
void transform(double* x, double* y) const; 用上面的公式转换x,y坐标

const trans_affine& scale(double s);
const trans_affine& scale(double x, double y);

缩放
const trans_affine& rotate(double a); 旋转,弧度单位(pi/180)
const trans_affine& translate(double x, double y); 平移
trans_affine operator * (const trans_affine& m); 矩阵乘法
const trans_affine&  invert(); 取反矩阵

坐标转换管道中有个叫conv_transform的 转换器,它能利用矩阵对源顶点进行变换,我们先在这里玩玩吧^_^

实验代码(基于此处代码)

加入头文件 #include "agg_conv_transform.h"

把on_draw()方法的里从“// Vertex Source”到“// Scanline Rasterizer”之间的代码改写成:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

// Vertex Source

agg::ellipse ell(0,0,50,50); //圆心在中间

 

// Coordinate conversion pipeline

agg::trans_affine mtx;

mtx.scale(0.5,1); // x轴缩小到原来的一半

mtx.rotate(agg::deg2rad(30)); // 旋转30度

mtx.translate(100,100); // 平移100,100

typedef agg::conv_transform<agg:agg:agg:agg:agg:agg::ellipse> ell_ct_type;

ell_ct_type ctell(ell, mtx); // 矩阵变换

 

typedef agg::conv_contour<ell_ct_type> ell_cc_type;

ell_cc_type ccell(ctell); // 轮廓变换

 

typedef agg::conv_stroke<ell_cc_type> ell_cc_cs_type;

ell_cc_cs_type csccell(ccell); // 转换成多义线</ell_cc_type></ell_ct_type></agg:agg:agg:agg:agg:agg::ellipse>

得到的图形是:

注:trans_affine不 仅仅用于源顶点的变换,在AGG库中有不少地方都能看到它。比如后面会讲到的线段(span)生成器,通过变换矩阵,就能够 自由变换填充于多边形之内的图案。

坐标转换管道
头文件

1

2

3

4

5

6

7

8

#include <agg_conv_stroke.h> // conv_stroke

#include <agg_conv_dash.h> // conv_dash

#include <agg_conv_marker.h> // conv_marker

#include <agg_conv_curve.h> // conv_curve

#include <agg_conv_contour.h> // conv_contour

#include <agg_conv_smooth_poly1.h> // conv_smooth_poly1.h

#include <agg_conv_bspline.h> // conv_bspline

#include
<agg_conv_transform.h> //
conv_transform</agg_conv_transform.h></agg_conv_bspline.h></agg_conv_smooth_poly1.h></agg_conv_contour.h></agg_conv_curve.h></agg_conv_marker.h></agg_conv_dash.h></agg_conv_stroke.h>

类型(演示程序基于基于此处代码)

template<class VertexSource,

class Markers = null_markers>

struct conv_stroke;


变成连续线

构造参数为VertexSource

width属性决定线宽。


例程的ell_cc_cs_type csccell(ccell);

后面加上csccell.width(3);线宽就会变成3。


template<class VertexSource,

class Markers = null_markers>

struct conv_dash;


虚线

构造参数为VertexSource

用add_dash设置虚线长度和间隔

与conv_stroke套用

// Coordinate conversion pipeline
typedef agg::conv_contour<agg::ellipse> ell_cc_type;
ell_cc_type ccell(ell);

typedef agg::conv_dash<ell_cc_type> ell_cd_type;
ell_cd_type cdccell(ccell);
cdccell.add_dash(5,5);

typedef agg::conv_stroke<ell_cd_type> ell_cc_cs_type;
ell_cc_cs_type csccell(cdccell);
...

template<class MarkerLocator,
class MarkerShapes>

class conv_marker;

建立标记
请参考arrowhead示例代码


template<class VertexSource>

struct conv_contour;


轮廓变换

构造参数为VertexSource

width属性决定扩展或收缩轮廓。

例程代码

template<class VertexSource>

struct conv_smooth_poly1_curve;


圆滑过渡多边形各顶点(贝塞尔)

构造参数为VertexSource

smooth_value属性决定圆滑度(默认为1)


例 程on_draw()方法最后加入下面代码

triangle t(100,100,50);//自定义顶点源
agg::conv_smooth_poly1_curve<triangle> cspct(t);
ras.add_path(cspct);
agg::render_scanlines_aa_solid(
 ras,sl,renb,agg::rgba8(255,0,0));

template<class VertexSource>
struct conv_bspline;


圆滑过渡多义线各顶点(贝塞尔)

构造参数为VertexSource

interpolation_step属性决定步长。


例 程on_draw()方法最后加入下面代码

triangle t(100,100,50);
agg::conv_bspline<triangle> cspct(t);
ras.add_path(cspct);
agg::render_scanlines_aa_solid(
 ras,sl,renb,agg::rgba8(255,0,0));

template<class VertexSource,
class Curve3 = curve3,

class Curve4 = curve4>

class conv_curve;


可识别VertexSource中的曲线信息

构造参数为VertexSource。conv_smooth_poly1_curve

就是基于它实现的。


例程里的顶点都没有曲线信息,算了,

到后面讲到文字输出时会用到它的。


template<class VertexSource,

class Transformer = trans_affine>

class conv_transform;


矩阵变换

用变换矩阵重新计算顶点位置

构造参数为VertexSource和变换矩阵

见变换矩阵一节的例子

Scanline Rasterizer

Scanline Rasterizer能够把顶点数据转换成一组水平扫描线,扫描线由一组线段(Span)组成,线段(Span)包含了起始位置、长度和覆盖率(可以理解 为透明度)信息。AGG的抗锯齿(Anti-Aliasing)功能也是在这时引入的。

扫描线Scanline

扫描线是一种保存span的容器,span用于表示一小条(水平方向)细线。图像中同一行的span组成一个Scanline。

头文件

1

2

3

#include <agg_scanline_u.h> // scanline_u8,scanline32_u8

#include <agg_scanline_p.h> // scanline_p8,scanline32_p8

#include
<agg_scanline_bin.h> //
scanline_bin,scanline32_bin</agg_scanline_bin.h></agg_scanline_p.h></agg_scanline_u.h>

类型
scanline_bin,scanline32_bin 不携带AA信息的span容器。scanline32_bin中的32代表坐标位数,一般16位已经足够了,所以前一版 本用得更多些(下同)
scanline_u8,scanline32_u8 unpacked版的span容器,用每个span来保存各自的线段信息
scanline_p8,scanline32_p8 packed版的span容器,相同属性的span会合并成一个
成员类型
struct span; 线段数据,其中的成员变量有:x起始位置,len长度,*covers覆盖率
typename iterator,const_iterator; span迭代器
typename cover_type; span中covers类型(覆盖率)
成员方法

iterator begin();

unsigned num_spans();


用于遍历span,begin()取得指向第一个span的迭代器

num_spans()取得容器中span的数目

void reset(int min_x, int max_x);
设置容器大小

void add_span(int x, unsigned len, unsigned cover)
加入一条线段

void add_cell(int x, unsigned cover) 加入一个点
void add_cells(int x, unsigned len, const cover_type* covers) 加入一组点

void finalize(int y)

int y();

Scanline容器对应的Y坐标
Rasterizer

怎么翻译呢?光栅化?光栅制造机?嗯~~算了,还是直接叫它Rasterizer(雷死特拉倒)吧-_-!!!

Rasterizer就是把相当于矢量数据的一堆顶点和命令转换成一行行的扫描线的设备,它就象粉刷工人对照着图纸把彩漆刷到墙上一 样。可以说是AGG里最重要的类型之一,套用建翔兄的话就是:

立功了!立功了!不要给GDI任何的机会!伟大的AGG的Rasterizer类!他了继承开源社区的光荣传统!达芬奇、Linus、 唐寅,在这一刻灵魂附体!

Rasterizer是关键对象!他代表了AGG伟大的设计理念!在这一刻!他不是一个人的战斗!他不是一个人!面对着全世界人民的目 光和期待,他深知责任的重大,0.001秒种之后将会是什么样的图像?

头文件
#include <agg_rasterizer_scanline_aa.h>
类型

1

2

template<class clip="rasterizer_sl_clip_int">

class rasterizer_scanline_aa;</class>

成员方法

template<class GammaF>
void gamma(const GammaF& gamma_function);


设置gamma值。

GammaF为一种仿函数

AGG自带有gamma_power、gamma_none、gamma_threshold、 gamma_linear、gamma_multiply

bool rewind_scanlines();
跳到第一个scanline位置,同时设置sorted为true。

这时再加入其它顶点会先清空现有顶点

bool navigate_scanline(int y); 跳到y行
bool sweep_scanline(Scanline&); 把当前行画入Scanline,当下移一行
void reset(); 清空

void move_to(int x, int y);

void line_to(int x, int y);


简单的画线功能,单位为1/poly_subpixel_scale

(poly_subpixel_scale一般为256)


void move_to_d(double x, double y);

void line_to_d(double x, double y);

简单的画线功能,单位为像素
void add_path(VertexSource& vs, unsigned path_id=0) 加入顶点

Renderers 渲染器

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

渲 染器被分成底中高三层。其中底层负责像素包装,由PixelFormat Renderer实现;中层是基础层,在PixelFormat
Renderer的基础上提供更多方法,是所有高层渲染器依赖的基础,由Base
Renderer实现;高层负责渲染Scanline中的线段,由Scanline Renderer等实现。

Scanline Renderer
头文件
#include <agg_renderer_scanline.h>
类型

1

2

3

4

5

6

template<class> class renderer_scanline_aa_solid; //实色AA渲染

template<class> class renderer_scanline_bin_solid; //实色原始渲染

template<class>

 class renderer_scanline_aa; // 自定义AA渲染

template<class>

 class renderer_scanline_bin; // 自定义原始渲染</class></class></class></class>

以及自己写的实现了void prepare() 和 template<class Scanline> void render(const Scanline& sl) 方法的类

另外,头文件agg_renderer_scanline.h中 的render_scanlines函 数很重要,它是AGG显示流程的实现。


1

void render_scanlines(Rasterizer& ras, Scanline& sl, Renderer& ren);

从Rasterizer生成逐行的Scanline,然后交给Scanline Renderer渲染。

这 里还要提一下render_scanlines_aa_solid、render_scanlines_aa、 render_scanlines_bin_solid、render_scanlines_bin这 几个函数。它们的作用和 render_scanlines一 样,只是跳过了Scanline Renderer环节,直接向Base Renderer渲染。


1

2

3

4

5

void render_scanlines_aa_solid(Rasterizer& ras, Scanline& sl,

 BaseRenderer& ren, const ColorT& color)

template<class>

void render_scanlines_aa(Rasterizer& ras, Scanline& sl, BaseRenderer& ren,

 SpanAllocator& alloc, SpanGenerator& span_gen);</class>

实验代码(基于此 处代码)

把on_draw()方法里原


1

typedef agg::renderer_scanline_aa_solid<renderer_base_type> renderer_scanline_type;</renderer_base_type>

改成


1

typedef agg::renderer_scanline_bin_solid<renderer_base_type> renderer_scanline_type;</renderer_base_type>

得到的图形是:

去掉renderer_scanline_type以及所有的rensl相关语句,把


1

agg::render_scanlines(ras,sl,rensl);

改成


1

agg::render_scanlines_aa_solid(ras,sl,renb,agg::rgba8(0,0,i*50));

同样可以得到我们想要的图形

Basic Renderers
头文件
#include <agg_renderer_base.h>
#include <agg_renderer_mclip.h>
类型

1

2

3

template<class> class renderer_base;

template<class> class renderer_mclip;</class></class>

构造函数
renderer_base(pixfmt_type& ren);
参数ren指定底层的PixelFormat Renderer
成员方法
pixfmt_type& ren(); 返回底层的PixelFormat Renderer

unsigned width()  const;
unsigned height() const;

宽高
void reset_clipping(bool visibility);
设置是否可见

clipping box=visibility?(0,0,width-1,height-1):(1,1,0,0)

bool clip_box(int x1, int y1, int x2, int y2); 设置clipping box,renderer_base专有
void add_clip_box(int x1, int y1, int x2, int y2); 添加clipping box,renderer_mclip专有
bool inbox(int x, int y) const; x,y点是否在clipping box内,renderer_base专有

void first_clip_box();

bool next_clip_box();

切换clipping box,renderer_mclip专用

const rect& clip_box() const;

int         xmin()     const;

int         ymin()     const;

int         xmax()     const;

int         ymax()     const;

const rect& bounding_clip_box() const;

int         bounding_xmin()     const;

int         bounding_ymin()     const;

int         bounding_xmax()     const;

int         bounding_ymax()     const;

返回clipping box大小
void clear(const color_type& c); 以颜色c填充所有区域

void copy_pixel(int x, int y, const color_type& c);

void blend_pixel(int x, int y, const color_type& c, cover_type cover);

color_type pixel(int x, int y) const;

void copy_h(v)line(int x1, int y, int x2, const color_type& c);

void blend_h(v)line(int x1, int y, int x2,

const color_type& c, cover_type cover);

void blend_solid_h(v)span(int x, int y, int len,

const color_type& c, const cover_type* covers);

void blend_color_h(v)span(_no_slip)(int x, int y, int len,

const color_type* colors, const cover_type* covers);

见后文的PixelFormat Renderer

void copy_from(const rendering_buffer& from,

const rect* rc=0,

int x_to=0,

int y_to=0);

从from复制一个矩形区域过来,rc指定源区域,x_to,y_to指定目标位置
实验代码(基于此处代码)

在on_draw()方法的renb.clear(agg::rgba8(255,255,255));语句后面加上:


1

2

renb.clear(agg::rgba8(255,255,255));

    renb.clip_box(30,30,160,160); // 设置可写区域

得到的图形是:

PixelFormat Renderer

PixelFormat Renderer的作用是以指定的颜色空间来包装原始的Rendering Buffer(见后文),AGG把它归类于底层Renderer。

Rendering Buffer是以字节为单位的,而PixelFormat Renderer则是以像素为单位的。

头文件
#include "agg_pixfmt_rgb.h
#include "agg_pixfmt_gray.h"
类型

1

2

3

4

5

6

pixfmt_gray8

pixfmt_rgb24

pixfmt_bgr24

pixfmt_rgba32

pixfmt_bgr24_gamma

...

构造函数

pixfmt_base(rbuf_type& rb);

rb参数为Rendering Buffer类型

类型定义
typedef color_type;
像素类型
需要了解的是在AGG中像素也是一个功能完善的类,常用的有rgba、rgba8、gray8。

rgba里每个颜色分量用double表示,范围从0~1。其它像素类后面的数字代表每个颜色分量占用的位数。大部分像素类都可以从rgba构造。

同时, 像素类还有gradient等牛X的颜色计算方法。

typedef value_type; 单个颜色分量的类型
typedef order_type;
颜色排序方式,我们可以通过里面的枚举值R G B A得到各颜色分量所在位置,常用的有order_rgb,order_bgr,order_rgba。

这是order_rgb的定义: struct order_rgb { enum rgb_e { R=0, G=1, B=2, rgb_tag }; };

成员方法

unsigned width()

unsigned height()

宽高

color_type pixel(int x, int y);

void copy_pixel(int x, int y, const color_type& c);

取得、设置指定点的颜色
void blend_pixel(int x, int y, const color_type& c, int8u cover); 设置指定点颜色,与原颜色有混合效果,强度由cover指定

void copy_hline(int x, int y, unsigned len, const color_type& c);

void copy_vline(int x, int y, unsigned len, const color_type& c);

从x,y开始画一条长度为len的线,颜色为c,同样有blend_版本

void blend_solid_h(v)span(int x, int y, unsigned len,

const color_type& c, const int8u* covers);

void blend_color_h(v)span(int x, int y, unsigned len,

const color_type* colors, const int8u* covers);

类似hline和vline版本,color版指定一组颜色,依次着色。covers指定覆盖率
实验代码(基于此处代码)

在on_draw()方法的最后加上:


1

2

3

//从50,20开始,画20条长度为100的坚线,颜色从黑渐变到红,覆盖率为128(半透明)

    for(int i=0; i<20; i++)

        pixf.blend_vline(50+i,20,100,agg::rgba(i/20.0,0,0),128);

得到的图形是:

Rendering Buffer

Rendering
Buffer是一个内存块,用于保存图像数据。这是AGG与显示器之间的桥梁,我们要显示AGG图形实际上就是识别这个内存块并使用系统的API显示出来
而已(实际上几乎不需要做转换工作,因为无论是Windows还是Linux,API所用的图像存储格式与Rendering
Buffer都是兼容的)。

头文件:
#include "agg_rendering_buffer.h"
类型:

rendering_buffer

构造函数:
rendering_buffer(int8u* buf, unsigned width, unsigned height, int stride);

参数分别表示内存块指针,宽、高、每行的步幅(当步幅<0时,表示上下颠倒)

成员方法:
void attach(int8u* buf, unsigned width, unsigned height, int stride); 参数与构造函数相同
int8u* buf(); 返回内存块指针

unsigned width()  const;
unsigned height() const;

int  stride() const;

unsigned stride_abs() const;

返回宽、高、每行步幅
int8u* row_ptr(int y) 返回指向第y行起点的指针
void clear(int8u value) 以value值填充整个内存块
template<class RenBuf> void copy_from(const RenBuf& src) 从另一rendering_buffer中复制数据
实验代码(基于此处代码)

在on_draw()方法的最后加上:


1

2

agg::int8u* p = rbuf.row_ptr(20);//得到第20行指针

memset(p,0,rbuf.stride_abs());//整行以0填充

得到的图形是:

AGG与GDI显示

Rendering Buffer的图像存储方式和Windows的BMP是一样的,所以让AGG处理BMP是很简单的事情,下面的代码演示了怎样在HDC上显示AGG


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

#include <agg_rendering_buffer.h>

    #include <agg_pixfmt_rgba.h>

    #include <agg_renderer_base.h>

    #include <agg_rasterizer_scanline_aa.h>

    #include <agg_scanline_p.h>

    ...

    // 首先让系统生成一个32位的bmp缓存

    BITMAPINFO bmp_info;

    bmp_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

    bmp_info.bmiHeader.biWidth = width;

    bmp_info.bmiHeader.biHeight = height;

    bmp_info.bmiHeader.biPlanes = 1;

    bmp_info.bmiHeader.biBitCount = 32;

    bmp_info.bmiHeader.biCompression = BI_RGB;

    bmp_info.bmiHeader.biSizeImage = 0;

    bmp_info.bmiHeader.biXPelsPerMeter = 0;

    bmp_info.bmiHeader.biYPelsPerMeter = 0;

    bmp_info.bmiHeader.biClrUsed = 0;

    bmp_info.bmiHeader.biClrImportant = 0;

     

    HDC mem_dc = ::CreateCompatibleDC(hdc);

     

    void* buf = 0;

     

    HBITMAP bmp = ::CreateDIBSection(

        mem_dc,

        &bmp_info,

        DIB_RGB_COLORS,

        &buf,

        0,

        0

        );

     

    // 把bmp与mem_dc关联,这样AGG就可以和原生GDI一起工作了

    HBITMAP temp = (HBITMAP)::SelectObject(mem_dc, bmp);

     

    //============================================================

    // 以下是AGG代码

    agg::rendering_buffer rbuf;

    // 32位位图,每行字节数为width*4。

    // BMP是上下倒置的,为了和GDI习惯相同,最后一个参数是负值。

    rbuf.attach((unsigned char*)buf, width, height, -width*4);

     

    // 像素格式和renderer_base

    agg::pixfmt_bgra32 pixf(rbuf);

    agg::renderer_base<agg:agg:agg:agg:agg:agg::pixfmt_bgra32> renb(pixf);

     

    renb.clear(agg::rgba8(255, 255, 255, 255));

     

    // Scanline renderer

    agg::renderer_scanline_aa_solid<agg:agg:agg:agg:agg:agg::renderer_base><agg:agg:agg:agg:agg:agg::pixfmt_bgra32>
> ren(renb);

     

    // Rasterizer & scanline

    agg::rasterizer_scanline_aa<> ras;

    agg::scanline_p8 sl;

     

    // 多义线(三角形)

    ras.move_to_d(20.7, 34.15);

    ras.line_to_d(398.23, 123.43);

    ras.line_to_d(165.45, 401.87);

     

    // 设置颜色后渲染

    ren.color(agg::rgba8(80, 90, 60));

    agg::render_scanlines(ras, sl, ren);

    //============================================================

     

    // 把bmp显示到hdc上,如果图片中有Alpha通道,可以使用AlphaBlend代替BitBlt。

    ::BitBlt(

        hdc,

        rt.left,

        rt.top,

        width,

        height,

        mem_dc,

        0,

        0,

        SRCCOPY

        );

     

    // 释放资源

    ::SelectObject(mem_dc, temp);

    ::DeleteObject(bmp);

    ::DeleteObject(mem_dc);</agg:agg:agg:agg:agg:agg::pixfmt_bgra32></agg:agg:agg:agg:agg:agg::renderer_base></agg:agg:agg:agg:agg:agg::pixfmt_bgra32></agg_scanline_p.h></agg_rasterizer_scanline_aa.h></agg_renderer_base.h></agg_pixfmt_rgba.h></agg_rendering_buffer.h>

得到的图形是:

使用AGG提供的pixel_map类

如果你觉得上面的方法还是有点烦的话(这个要怪MS的API太麻烦),可以考虑用AGG友情提供的pixel_map类,用它操作 BMP方便多了。(要把[AGG]\src\platform\win32\agg_win32_bmp.cpp加入一起编译)


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

#include <agg_rendering_buffer.h>

    #include <agg_pixfmt_rgba.h>

    #include <agg_renderer_base.h>

    #include <agg_rasterizer_scanline_aa.h>

    #include <agg_scanline_p.h>

    #include <platform>

    ...

    CRect rc;

    GetClientRect(&rc);

     

    agg::pixel_map pm;

    pm.create(rc.right,rc.bottom,agg::org_color32);

     

    //============================================================

    // 以下是AGG代码

    agg::rendering_buffer rbuf;

    rbuf.attach(pm.buf(), pm.width(), pm.height(), -pm.stride());

     

    // 像素格式和renderer_base

    agg::pixfmt_bgra32 pixf(rbuf);

    agg::renderer_base<agg:agg:agg:agg:agg:agg::pixfmt_bgra32> renb(pixf);

     

    renb.clear(agg::rgba8(255, 255, 255, 255));

     

    // Scanline renderer

    agg::renderer_scanline_aa_solid<agg:agg:agg:agg:agg:agg::renderer_base><agg:agg:agg:agg:agg:agg::pixfmt_bgra32> > ren(renb);

     

    // Rasterizer & scanline

    agg::rasterizer_scanline_aa<> ras;

    agg::scanline_p8 sl;

     

    // 多义线(三角形)

    ras.move_to_d(20.7, 34.15);

    ras.line_to_d(398.23, 123.43);

    ras.line_to_d(165.45, 401.87);

     

    // 设置颜色后渲染

    ren.color(agg::rgba8(80, 90, 60));

    agg::render_scanlines(ras, sl, ren);

    //============================================================

    pm.draw(hdc);</agg:agg:agg:agg:agg:agg::pixfmt_bgra32></agg:agg:agg:agg:agg:agg::renderer_base></agg:agg:agg:agg:agg:agg::pixfmt_bgra32></platform></agg_scanline_p.h></agg_rasterizer_scanline_aa.h></agg_renderer_base.h></agg_pixfmt_rgba.h></agg_rendering_buffer.h>

(转)用AGG实现高质量图形输出(二)

时间: 2024-10-08 00:48:08

(转)用AGG实现高质量图形输出(二)的相关文章

编写高质量代码改善C#程序的157个建议——建议13: 为类型输出格式化字符串

建议13: 为类型输出格式化字符串 有两种方法可以为类型提供格式化的字符串输出.一种是意识到类型会产生格式化字符串输出,于是让类型继承接口IFormattable.这对类型来 说,是一种主动实现的方式,要求开发者可以预见类型在格式化方面的要求.更多的时候,类型的使用者需为类型自定义格式化器,这就是第二种方法,也是最灵活 多变的方法,可以根据需求的变化为类型提供多个格式化器.下面就来详细介绍这两种方法. 最简单的字符串输出是为类型重写ToString方法,如果没有为类型重写该方法,默认会调用Obj

R语言绘图高质量输出

R语言通过支持Cairo矢量图形处理的类库,可以创建高质量的矢量图形(PDF,PostScript,SVG) 和 位图(PNG,JPEG, TIFF),同时支持在后台程序中高质量渲染.在ggplot2中,图片输出经过Cairo包处理后,输出个效果更好,可以输出成PDF格式,PDF格式体积小,同时可以储存为其他任何格式,随后再将PDF储存为eps格式并在Photoshop中打开做最终的调整,例如调整比例.色彩空间和dpi(一般杂志和出版社要求dpi=300以上)等.额外需要注意的是ggplot2中

C++高质量编程笔记

/* * 函数介绍: * 输入参数: * 输出参数: * 返回值 : */ void Function(float x, float y, float z) { - } if (-) { - while (-) { - } // end of while - } // end of if 版权和版本的声明位于H和CPP的开头(参见示例 1-1),主要内容有: ( 1)版权信息. ( 2)文件名称,标识符,摘要. ( 3)当前版本号,作者/修改者,完成日期. ( 4)版本历史信息. 头文件里面的内

SWF在线绘本批量制作高质量PDF的新方法(重点在批量制作)

SWF在线绘本批量制作高质量PDF的新方法(重点在批量制作) 2012-12-21  未来决定... http://www.ebama.net/thread-107643-1-1.html 含笑 发表于 2012-12-10 13:51 请教楼主,如何进行压制?是用什么软件吗? 虚拟装载DVD后DVDdcrypter分割提取VOBdgindex制作工程文件和分轨Megui+avs压制mmg封包 具体的教程和软件,TLF小组出过一个打包文件,全在里面了 字幕的话各种情况提取办法不同,图片字幕用su

高质量C++/C编程指南

http://man.chinaunix.net/develop/c&c++/c/c.htm#_Toc520634042 文件状态 [  ] 草稿文件 [√] 正式文件 [  ] 更改正式文件 文件标识: 当前版本: 1.0 作    者: 林锐 博士 完成日期: 2001年7月24日 版 本 历 史 版本/状态 作者 参与者 起止日期 备注 V 0.9 草稿文件 林锐 2001-7-1至 2001-7-18 林锐起草 V 1.0 正式文件 林锐 2001-7-18至 2001-7-24 朱洪海

高质量c c++编程

第1章 文件结构 每一个C++/C程序通常分为两个文件.一个文件用于保存程序的声明(declaration),称为头文件.还有一个文件用于保存程序的实现(implementation),称为定义(definition)文件. C++/C程序的头文件以“.h”为后缀,C程序的定义文件以“.c”为后缀,C++程序的定义文件通常以“.cpp”为后缀(也有一些系统以“.cc”或“.cxx”为后缀). 1.1版权和版本号的声明 版权和版本号的声明位于头文件和定义文件的开头(參见演示样例1-1),主要内容有

《C++/C高质量编程指南》笔记

复习: C/C++高质量编程指南: [规则1-2-1]为了防止头文件被重复引用,应当用ifndef/define/endif结构产生预处理块. [规则1-2-2]用 #include <filename.h> 格式来引用标准库的头文件(编译器将从标准库目录开始搜索). [规则1-2-3]用 #include “filename.h” 格式来引用非标准库的头文件(编译器将从用户的工作目录开始搜索). [建议1-2-1]头文件中只存放“声明”而不存放“定义” [规则2-2-1]一行代码只做一件事情

网站建设和SEO要永远把高质量内容放在首位

想必大家都知道,高质量的网站和内容是搜索引擎都知道的,为此很多人比较吹捧原创,自己以前也犯了这样一个误区,认为原创,代表高质量,现在看来也不尽然. 网页质量是一个网页满足用户需求能力的衡量,是搜索引擎确定结果排序的重要依据.在网页资源内容与用户需求有相关性的基础上,内容是否完整.页面是否美观.对用户是否友好.来源是否权威专业等因素,共同决定着网页质量的高低. 在<百度网页搜素质量白皮书>简版里面,百度指出主要从以下角度评价网页内容质量: 内容制作成本高低; 内容是否有效.完整丰富; 是否原创;

编写高质量代码改善C#程序的157个建议——建议27:在查询中使用Lambda表达式

建议27:在查询中使用Lambda表达式 LINQ实际上是基于扩展方法和Lambda表达式的.任何LINQ查询都能通过扩展方法的方式来代替. var personWithCompanyList = from person in personList select new { PersonName = person.Name, CompanyName = person.CompanyID==0?"Micro":"Sun" }; foreach (var item in