本文上接《用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>
|