我们前面举的例子使用的都是简单的单一实色,如蓝色的圆、黑色的线等。这是因为在例子里我们一直使用renderer_scanline_aa_solid或render_scanlines_aa_solid。
在上篇文章的渲染器一节中除了renderer_scanline_aa_solid外,还提到有一个renderer_scanline_aa,这里再写一遍它的声明:
1
2
|
template
<
class
BaseRenderer,
class
SpanAllocator,
class
SpanGenerator>
class
renderer_scanline_aa;
|
另外,还有一个函数版本:
1
2
3
|
template
<
class
spanallocator, spangenerator baserenderer, scanline, rasterizer,>
void
render_scanlines_aa(Rasterizer& ras, Scanline& sl, BaseRenderer& ren,
SpanAllocator& alloc, SpanGenerator& span_gen);
|
renderer_scanline_aa (还有一个兄弟版本renderer_scanline_bin)可以按指定的图案或不同的颜色(如渐变)填充顶点源里的多边形。其中的模板参数 SpanAllocator用于准备span,我们直接使用agg::span_allocator就行。这里的SpanGenerator就是本节要说 的线段生成器,它决定了最终用什么东西填到rendering_buffer里。
线段生成器品种很多,常用的在致可以分成图案类和色彩类两大部分:图案类线段生成器使用已有图像作为span来源;色彩类线段生成器使用指定的颜色作为span来源。
1
2
3
4
5
6
|
#include <agg_span_image_filter_gray.h>
#include <agg_span_image_filter_rgb.h>
#include <agg_span_image_filter_rgba.h>
#include <agg_span_pattern_gray.h>
#include <agg_span_pattern_rgb.h>
#include <agg_span_pattern_rgba.h>
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
template
<
class
source, interpolator&>
span_image_filter_[gray|rgb|rgba]
template
<
class
source, interpolator>
span_image_filter_[gray|rgb|rgba]_2x2
template
<
class
source, interpolator>
span_image_filter_[gray|rgb|rgba]_bilinear
template
<
class
source, interpolator>
span_image_filter_[gray|rgb|rgba]_bilinear_clip
template
<
class
source, interpolator>
span_image_filter_[gray|rgb|rgba]_nn
template
<
class
source, interpolator>
span_image_resample_[gray|rgb|rgba]
template
<
class
source>
span_image_resample_[gray|rgb|rgba]_affine
template
<
class
source>
class
agg::span_pattern_[gray|rgb|rgba]
|
上面这些线段生成器类的模板参数都比较相似:Source用于指定图像来源,可以是PixelFormat renderer或agg::image_accessor_clip(由不同的线段生成器类决定);Interpolator是一种插值器,用于填充图 像间隙。我们先写一段示例代码,先看一下线段生成器的作用,也为后面的各种实验做准备。
还是基于这个代码(http://www.cppprog.com/2009/0816/146.html),加入下面的头文件
1
2
3
|
#include "platform agg_win32_bmp.h win32"
#include "agg_span_allocator.h"
#include "agg_span_image_filter_rgb.h"
|
在on_draw()方法的最后加上下面这些代码
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
|
...
// 以图像填充
agg::pixel_map pm_img;
if
(pm_img.load_from_bmp(
"d:\\spheres.bmp"
))
{
// pm_img里的图案作为填充来源
agg::rendering_buffer rbuf_img(
pm_img.buf(),
pm_img.width(), pm_img.height(),
-pm_img.stride());
agg::pixfmt_bgr24 pixf_img(rbuf_img);
// 我用的bmp是24位的
// 线段分配器
typedef
agg::span_allocator<!--?xml:
namespace
prefix = agg /--><agg::rgba8> span_allocator_type;
//分配器类型
span_allocator_type span_alloc;
// span_allocator
// 插值器
typedef
agg::span_interpolator_linear<> interpolator_type;
//插值器类型
agg::trans_affine img_mtx;
// 变换矩阵
interpolator_type ip(img_mtx);
// 插值器
// 线段生成器
typedef
agg::span_image_filter_rgb_bilinear_clip<agg::pixfmt_bgr24 interpolator_type=
""
,=
""
> span_gen_type;
// 这个就是Span Generator
span_gen_type span_gen(pixf_img, agg::rgba(0,1,0), ip);
// 组合成渲染器
agg::renderer_scanline_aa<
renderer_base_type,
span_allocator_type,
span_gen_type
> my_renderer(renb, span_alloc, span_gen);
// 插值器的矩阵变换
img_mtx.scale(0.5);
img_mtx.translate(40,40);
img_mtx.invert();
//注意这里
// 用我们的渲染器画圆
ras.add_path(ell);
agg::render_scanlines(ras,sl,my_renderer);
}</agg::pixfmt_bgr24></agg::rgba8>
|
其中的d:\\spheres.bmp是我预先放在D盘里的24位bmp图像,作为填充的来源。
插值器的作用是连接目标位置和源位置,比如要填充一个8*8的图形,对应的填充源是一个4*4的图像,一种简单的线性插值器就要根据目标的位置线性计算得到源对应的位置,如目标点(4,4)、(4,5)、(5,4)、(5,5)这几个位置点对应到源的(2,2)点上。
1
2
3
|
#include <agg_span_interpolator_linear.h>
#include <agg_span_interpolator_persp.h>
#include <agg_span_interpolator_trans.h>
|
1
2
3
4
5
6
7
8
9
10
|
template
<
class
Transformer = trans_affine, unsigned SubpixelShift = 8>
class
agg::span_interpolator_linear
template
<
class
Transformer = trans_affine, unsigned SubpixelShift = 8>
class
agg::span_interpolator_linear_subdiv
template
<unsigned SubpixelShift = 8>
class
agg::span_interpolator_persp_exact
template
<unsigned SubpixelShift = 8>
class
agg::span_interpolator_persp_lerp
template
<
class
Transformer, unsigned SubpixelShift = 8>
class
agg::span_interpolator_trans
|
不同的插值器对于不同的变换有各自的优势,对于大部分应用来说,span_interpolator_linear是比较简单高效的。
把上面的演示代码里的interpolator_type改成span_interpolator_persp_lerp,这是一个透视变换的插值器,输入为源四个角的坐标和目标的四个角上的坐标。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
...
// 插值器
//typedef agg::span_interpolator_linear<> interpolator_type; //插值器类型
//agg::trans_affine img_mtx; // 变换矩阵不需要了
//interpolator_type ip(img_mtx); // 插值器
typedef
agg::span_interpolator_persp_lerp<> interpolator_type;
//插值器类型
const
double
offset = 50;
// 偏移
const
double
scale = 0.5;
// 缩放倍数
double
src[8]={offset+0,offset+0,
offset+pm_img.width()*scale,offset+0,
offset+pm_img.width()*scale,offset+pm_img.height()*scale,
offset+0,offset+pm_img.height()*scale
};
//源四角坐标,按偏移和缩放倍数改变一哈
double
dst[8]={0,0,
pm_img.width(),0,
pm_img.width()+100,pm_img.height(),
100,pm_img.height()-100
};
//目标四角坐标,左右乱扯一哈
interpolator_type ip(src,dst);
...
|
最后别忘了把img_mtx相关代码注释掉:
1
2
3
|
//img_mtx.scale(0.5);
//img_mtx.translate(40,40);
//img_mtx.invert(); //注意这里
|
注意一下我们前面用的span_interpolator_linear,以及曾经使用过的conv_transform,默认的模板参数Transformer为trans_affine。我们已经知道trans_affine是一个2*3的变换矩阵。在AGG中,变换器不仅仅只有矩阵,这里将要介绍的就是其它的一些变换器。
1
2
3
4
5
6
7
|
#include <agg_trans_affine.h>
#include <agg_trans_bilinear.h>
#include <agg_trans_single_path.h>
#include <agg_trans_double_path.h>
#include <agg_trans_perspective.h>
#include <agg_trans_viewport.h>
#include <agg_trans_warp_magnifier.h>
|
1
2
3
4
5
6
7
|
agg::trans_affine
agg::trans_bilinear
agg::trans_single_path
agg::trans_double_path
agg::trans_perspective
agg::trans_viewport
agg::trans_warp_magnifier
|
同样把示例代码中的插值器部分改成下列代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
...
// 插值器
//typedef agg::span_interpolator_linear<> interpolator_type; //插值器类型
//agg::trans_affine img_mtx; // 变换矩阵
//interpolator_type ip(img_mtx); // 插值器
typedef
agg::span_interpolator_trans<
agg::trans_warp_magnifier
// 使用trans_warp_magnifier
> interpolator_type;
//插值器类型
agg::trans_warp_magnifier mag;
interpolator_type ip(mag);
mag.magnification(0.5);
mag.center(100,100);
mag.radius(50);
...
|
建议把后面的ras.add_path(ell)改成ras.add_path(ccell),画得大一点好看清效果,呵呵
也 许有不少同学看到开头的线段生成器一节时,已经尝试修改示例代码中的span_image_filter_rgb_bilinear_clip了(比如改 成span_image_filter_rgb_bilinear)。不过编译时会出错,这是因为大部分的线段生成器类接受的Source模板不是 PixelFormat Renderer,而是Image Accessor即图像存取器。
1
|
#include <agg_image_accessors.h>
|
1
2
3
4
5
6
7
8
|
template
<
class
PixFmt>
class
agg::image_accessor_clip
// 图像以外的地方用指定颜色填充
template
<
class
PixFmt>
class
agg::image_accessor_clone
// 图像以外的地方以图像边缘填充
template
<
class
PixFmt>
class
agg::image_accessor_no_clip
// 图像以外不可读取,否则引发异常
template
<
class
PixFmt,
class
WrapX,
class
WrapY>
class
agg::image_accessor_wrap
// 平铺图像,平铺方式由WrapX和WrapY指定
|
把示例代码中的span_image_filter_rgb_bilinear_clip部分改成下面的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
...
// 线段生成器
//typedef agg::span_image_filter_rgb_bilinear_clip<agg::pixfmt_bgr24 interpolator_type="" ,=""> span_gen_type; // 这个就是Span Generator
//span_gen_type span_gen(pixf_img, agg::rgba(0,1,0), ip);
// 图像访问器
typedef
agg::image_accessor_clone<agg::pixfmt_bgr24> image_accessor_type;
image_accessor_type accessor(pixf_img);
// 使用span_image_filter_rgb_bilinear
typedef
agg::span_image_filter_rgb_bilinear<
image_accessor_type,
interpolator_type > span_gen_type;
span_gen_type span_gen(accessor, ip);
...</agg::pixfmt_bgr24></agg::pixfmt_bgr24>
|
建议把后面的ras.add_path(ell)改成ras.add_path(ccell)
image_accessor_wrap类要指定WrapX和WrapY,可选的有:
1
2
3
4
5
6
|
wrap_mode_reflect
wrap_mode_reflect_auto_pow2
wrap_mode_pow2
wrap_mode_repeat
wrap_mode_repeat_auto_pow2
wrap_mode_repeat_pow2
|
比如我们把本例中的image_accessor_type定义改成
1
2
3
|
//typedef agg::image_accessor_clone>agg::pixfmt_bgr24> image_accessor_type;
typedef
agg::image_accessor_wrap>agg::pixfmt_bgr24,
agg::wrap_mode_reflect,agg::wrap_mode_repeat> image_accessor_type;
|
(为了突出效果,用矩阵img_mtx把源缩小了)
在 一些线段生成器里,比如 span_image_filter_[gray|rgb|rgba],span_image_resample_[gray|rgb|rgba]等类, 它们的构造函数还有一个“const image_filter_lut &filter”参数,这个参数用于变换图像的像素值。它们的名称都以image_filter作为前缀,AGG中称为Image Filter(图像过滤器)。
1
|
#include <agg_image_filters.h>
|
1
2
3
4
5
6
7
8
|
image_filter_bilinear;
image_filter_blackman;
image_filter_blackman[36|64|100|144|196|256];
image_filter_kaiser;
image_filter_lanczos;
image_filter_lanczos[36|64|100|144|196|256];
image_filter_mitchell;
...还有很多呢...
|
把上面的span_image_filter_rgb_bilinear改成span_image_resample_rgb_affine
1
2
3
4
5
6
7
8
9
10
11
12
13
|
...
//typedef agg::image_accessor_clone<agg::pixfmt_bgr24> image_accessor_type;
typedef
agg::image_accessor_wrap<agg::pixfmt_bgr24,
agg::wrap_mode_reflect,agg::wrap_mode_repeat> image_accessor_type;
image_accessor_type accessor(pixf_img);
//typedef agg::span_image_filter_rgb_bilinear<
// image_accessor_type,
// interpolator_type > span_gen_type;
//span_gen_type span_gen(accessor, ip);
typedef
agg::span_image_resample_rgb_affine<image_accessor_type> span_gen_type;
span_gen_type span_gen(accessor, ip, agg::image_filter_sinc36());
...
|
1
2
3
4
5
|
#include <agg_span_solid.h>
#include <agg_span_gradient.h>
#include <agg_span_gradient_alpha.h>
#include <agg_span_gouraud_gray.h>
#include <agg_span_gouraud_rgba.h>
|
1
2
3
4
5
6
7
8
|
template
<
class
ColorT>
class
agg::span_solid;
template
<
class
ColorT,
class
Interpolator,
class
GradientF,
class
ColorF>
class
agg::span_gradient;
template
<
class
ColorT,
class
Interpolator,
class
GradientF,
class
AlphaF>
class
agg::span_gradient_alpha;
template
<
class
ColorT>
class
agg::span_gouraud_[gray|rgba];
|
如果你是从上面的图案类线段生成器看到这里的话,那么色彩类的就相对简单得多了。同样,我们先写一个示例代码,也方便以后做实验。
同样基于这个代码(http://www.cppprog.com/2009/0816/146.html),加入下面的头文件
1
2
|
#include "agg_span_allocator.h"
#include "agg_span_gradient.h"
|
在on_draw()方法的最后加上下面这些代码
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
|
// 色彩类线段生成器demo
// 线段分配器
typedef
agg::span_allocator<agg::rgba8> span_allocator_type;
//分配器类型
span_allocator_type span_alloc;
// span_allocator
// 插值器
typedef
agg::span_interpolator_linear<> interpolator_type;
//插值器类型
agg::trans_affine img_mtx;
// 变换矩阵
interpolator_type ip(img_mtx);
// 插值器
// 渐变方式
typedef
agg::gradient_radial_focus gradientF_type;
gradientF_type grF(1, 0.1, 0.5);
// 渐变颜色
typedef
agg::gradient_linear_color<agg::rgba8> colorF_type;
colorF_type colorF(agg::rgba(1,1,1), agg::rgba(0,0,1));
//白色到蓝色
// 线段生成器
typedef
agg::span_gradient<agg::rgba8,
interpolator_type,
gradientF_type,
colorF_type> span_gen_type;
span_gen_type span_gen(ip,grF,colorF,0,50);
// 组合成渲染器
agg::renderer_scanline_aa<
renderer_base_type,
span_allocator_type,
span_gen_type
> my_renderer(renb, span_alloc, span_gen);
// 矩阵变换
img_mtx.translate(100,100);
img_mtx.invert();
//注意这里
// 使用我们的渲染器画圆
ras.add_path(ell);
agg::render_scanlines(ras,sl,my_renderer);
|
前面说到span_gradient的模板参数ColorF指定渐变的颜色,我们使用的是gradient_linear_color,那么有哪些类可以作为ColorF呢?
AGG文档里说只要实现了“operator []()”和“size()”的类就可以作为ColorF,嗯,std::vector<rgba8>也行哈。
把示例代码的渐变颜色部分改成这样:
1
2
3
4
5
6
7
8
9
10
11
|
...
// 渐变颜色
//typedef agg::gradient_linear_color<agg::rgba8> colorF_type;
//colorF_type colorF(agg::rgba(1,1,1), agg::rgba(0,0,1));//白色到蓝色
typedef
std::vector<agg::rgba8> colorF_type;
colorF_type colorF(256);
agg::rgba begin_color(1,1,1), mid_color(1,0,0), end_color(0,0,1);
for
(
int
i=0; i<128; i++)
//前128从白到红
colorF[i] = begin_color.gradient(mid_color,i/128.0);
for
(
int
i=0; i<128; i++)
//后128从红到蓝
colorF[i+128] = mid_color.gradient(end_color,i/128.0);
|
这里指定的vector容量256指的是用于的颜色,想要更平滑过渡的话可以使用更多的颜色数。
除了用vector实现多种颜色的渐变外,我们还可以用AGG提供的一个gradient_lut类,用它可以方便很多。
gradient_lut的头文件是#include <agg_gradient_lut.h>
1
2
|
template
<
class
ColorInterpolator, unsigned ColorLutSize = 256>
class
agg::gradient_lut
|
其中的ColorInterpolator负责生成两种颜色的中间色,直接使用AGG自带的agg::color_interpolator就行。
通过gradient_lut的add_color(double offset, color_type color)方法添加多种颜色,其中的offset表示添加的颜色所处的偏移位置,取值为0~1之间。
添加完所有颜色后调用build_lut()方法让gradient_lut内部生成颜色数组。
把示例代码的渐变颜色部分改成这样:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
...
// 渐变颜色
//typedef agg::gradient_linear_color<agg::rgba8> colorF_type;
//colorF_type colorF(agg::rgba(1,1,1), agg::rgba(0,0,1));//白色到蓝色
typedef
agg::gradient_lut<
agg::color_interpolator<agg::rgba8>
> colorF_type;
colorF_type colorF;
colorF.add_color(0, agg::rgba(1,1,1));
colorF.add_color(0.2, agg::rgba(1,0,0));
colorF.add_color(0.4, agg::rgba(0,1,0));
colorF.add_color(0.8, agg::rgba(0,0,1));
colorF.build_lut();
...
|
除本例中的gradient_radial_focus以外,AGG还提供了很多渐变方式,它们都定义在#include <agg_span_gradient.h>头文件之中。
修改演示代码的渐变方式是很简单的,如:
1
2
3
4
5
6
7
|
...
// 渐变方式
//typedef agg::gradient_radial_focus gradientF_type;
//gradientF_type grF(1, 0.1, 0.5);
typedef
agg::gradient_x gradientF_type;
gradientF_type grF;
...
|
gradient_x | gradient_y | gradient_diamond |
gradient_xy | gradient_conic | gradient_radial |
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
|
// 色彩类线段生成器demo
// 线段分配器
typedef
agg::span_allocator<agg::rgba8> span_allocator_type;
//分配器类型
span_allocator_type span_alloc;
// span_allocator
typedef
agg::span_gouraud_rgba<agg::rgba8> span_gen_type;
span_gen_type span_gen;
//三种颜色
span_gen.colors(
agg::rgba(1,0,0),
agg::rgba(0,1,0),
agg::rgba(0,0,1)
);
//三角形三个顶点
span_gen.triangle(
100,50,
130,125,
70,125,0
);
agg::renderer_scanline_aa<
renderer_base_type,
span_allocator_type,
span_gen_type
> my_renderer(renb, span_alloc, span_gen);
ras.add_path(ell);
agg::render_scanlines(ras,sl,my_renderer);</agg::rgba8></agg::rgba8>
|
1
|
#include <agg_span_converter.h>
|
1
2
|
template
<
class
SpanGenerator,
class
SpanConverter>
class
agg::span_converter;
|
span_converter的作用是组合两种生成器,比如先由图案类线段生成器产生图案,然后由色彩类线段生成器产生半透明色叠加在图案上。
下面的演示代码演示了怎样组合span_image_filter_rgb_bilinear_clip和span_gradient_alpha两种生成器
1
2
3
4
|
#include "agg_span_allocator.h"
#include "agg_span_gradient_alpha.h"
#include "agg_span_converter.h"
#include "span_image_filter_rgb_bilinear_clip.h"
|
在on_draw()方法的最后加上下面这些代码
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
|
agg::pixel_map pm_img;
if
(pm_img.load_from_bmp(
"d:\\spheres.bmp"
))
{
// pm_img里的图案作为填充来源
agg::rendering_buffer rbuf_img(
pm_img.buf(),
pm_img.width(), pm_img.height(),
-pm_img.stride());
agg::pixfmt_bgr24 pixf_img(rbuf_img);
// 我用的bmp是24位的
// 线段分配器
typedef
agg::span_allocator<agg::rgba8> span_allocator_type;
//分配器类型
span_allocator_type span_alloc;
// span_allocator
// 插值器
typedef
agg::span_interpolator_linear<> interpolator_type;
//插值器类型
agg::trans_affine img_mtx;
// 变换矩阵
interpolator_type ip_img(img_mtx);
// 插值器
agg::trans_affine alpha_mtx;
// 变换矩阵
interpolator_type ip_alpha(alpha_mtx);
// 插值器
// 渐变方式
typedef
agg::gradient_x gradientF_type;
gradientF_type grF;
typedef
std::vector<agg::int8u> alphaF_type;
alphaF_type alphaF(256);
for
(
int
i=0; i<256; i++) alphaF[i] = i;
// Alpha线段生成器
typedef
agg::span_gradient_alpha<agg::rgba8 ,=
""
alphaf_type=
""
gradientf_type,=
""
interpolator_type,=
""
> alpha_span_gen_type;
alpha_span_gen_type alpha_span_gen(ip_alpha,grF,alphaF,0,150);
// 图案线段生成器
typedef
agg::span_image_filter_rgb_bilinear_clip<agg::pixfmt_bgr24 interpolator_type=
""
,=
""
> pic_span_gen_type;
pic_span_gen_type pic_span_gen(pixf_img, agg::rgba(0,1,0), ip_img);
// 使用span_converter组合成新的线段生成器
typedef
agg::span_converter<pic_span_gen_type ,=
""
alpha_span_gen_type=
""
> span_gen_type;
span_gen_type span_gen(pic_span_gen,alpha_span_gen);
// 组合成渲染器
agg::renderer_scanline_aa<
renderer_base_type,
span_allocator_type,
span_gen_type
> my_renderer(renb, span_alloc, span_gen);
// 插值器的矩阵变换
img_mtx.scale(0.5);
img_mtx.translate(40,40);
img_mtx.invert();
//注意这里
// 用我们的渲染器画圆
ras.add_path(ccell);
agg::render_scanlines(ras,sl,my_renderer);
}</pic_span_gen_type></agg::pixfmt_bgr24></agg::rgba8></agg::int8u></agg::rgba8>
|