(轉)用AGG實現高質量圖形輸出(二)


本文上接《用AGG實現高質量圖形輸出(一)》,分別介紹了AGG顯示流程中的各個環節。

上次講了AGG的顯示原理並舉了一個簡單的例子,這一篇文章開始講AGG工作流程里的每個環節。為了方便對照,再放一次AGG顯示流程 圖

20090816015959443

另外,上一篇文章里的例程也很重要,后面的例子都將基於這個代碼。

下面,我們來考察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參數的意義見下圖

20090821044820908

例,畫一條簡單的箭頭直線(基於此處代碼)

在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>
得到的圖形是:

20090821044820318

注意要加頭文件:

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;

得到的圖形是:

20090821044820532

除了文字輸出功能(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>
得到的圖形是:

20090821044820601

注: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。

20090821044820576

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);
...
20090821044820246

template<class MarkerLocator,
class MarkerShapes>
class conv_marker;

建立標記

請參考arrowhead示例代碼

20090821044820318

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)); 20090821044820547

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));
20090821044820120

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和變換矩陣

見變換矩陣一節的例子
20090821044820601

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>
得到的圖形是:

20090821044820595

去掉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); // 設置可寫區域
得到的圖形是:

20090821044820502

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);
得到的圖形是:

20090821044820392

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::int 8 u* p = rbuf.row_ptr( 20 );//得到第 20 行指針
memset(p, 0 ,rbuf.stride_abs());//整行以 0 填充
得到的圖形是:

20090821044820853

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>
得到的圖形是:

20090821044820710

使用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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM