(转)用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& 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,
|
变成连续线
|
在例程的ell_cc_cs_type csccell(ccell);
|
template<class VertexSource,
|
虚线
|
// 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, |
建立标记 |
请参考arrowhead示例代码 |
template<class VertexSource>
|
轮廓变换
|
见例程代码 |
template<class VertexSource>
|
圆滑过渡多边形各顶点(贝塞尔)
|
在例 程on_draw()方法最后加入下面代码 triangle t(100,100,50);//自定义顶点源 |
template<class VertexSource> |
圆滑过渡多义线各顶点(贝塞尔)
|
在例 程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, |
可识别VertexSource中的曲线信息
|
例程里的顶点都没有曲线信息,算了,
|
template<class 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();
|
用于遍历span,begin()取得指向第一个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)
|
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> |
设置gamma值。
|
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);
|
简单的画线功能,单位为1/poly_subpixel_scale
|
void move_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; |
宽高 |
void reset_clipping(bool visibility); |
设置是否可见
|
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();
|
切换clipping box,renderer_mclip专用 |
const rect& clip_box() const;
|
返回clipping box大小 |
void clear(const color_type& c); | 以颜色c填充所有区域 |
void copy_pixel(int x, int y, const color_type& c);
|
见后文的PixelFormat Renderer |
void copy_from(const rendering_buffer& from,
|
从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; |
像素类型 |
typedef value_type; | 单个颜色分量的类型 |
typedef order_type; |
颜色排序方式,我们可以通过里面的枚举值R G B A得到各颜色分量所在位置,常用的有order_rgb,order_bgr,order_rgba。
|
成员方法
unsigned width()
|
宽高 |
color_type pixel(int x, int y);
|
取得、设置指定点的颜色 |
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);
|
从x,y开始画一条长度为len的线,颜色为c,同样有blend_版本 |
void blend_solid_h(v)span(int x, int y, unsigned len,
|
类似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; |
返回宽、高、每行步幅 |
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::int 8 u* 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_color 32 ); //============================================================ // 以下是AGG代码 agg::rendering_buffer rbuf; rbuf.attach(pm.buf(), pm.width(), pm.height(), -pm.stride()); // 像素格式和renderer_base agg::pixfmt_bgra 32 pixf(rbuf); agg::renderer_base<agg:agg:agg:agg:agg:agg::pixfmt_bgra 32 > renb(pixf); renb.clear(agg::rgba 8 ( 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_bgra 32 > > ren(renb); // Rasterizer & scanline agg::rasterizer_scanline_aa<> ras; agg::scanline_p 8 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::rgba 8 ( 80 , 90 , 60 )); agg::render_scanlines(ras, sl, ren); //============================================================ pm.draw(hdc);</agg:agg:agg:agg:agg:agg::pixfmt_bgra 32 ></agg:agg:agg:agg:agg:agg::renderer_base></agg:agg:agg:agg:agg:agg::pixfmt_bgra 32 ></platform></agg_scanline_p.h></agg_rasterizer_scanline_aa.h></agg_renderer_base.h></agg_pixfmt_rgba.h></agg_rendering_buffer.h> |