翻譯:唐風(fēng)
Rendering Buffer我們先從這里開(kāi)始:在內(nèi)存中開(kāi)辟一塊存儲(chǔ)區(qū),然后將它的內(nèi)容以最簡(jiǎn)單的光柵格式寫到文件中,也就是 PPM(Portable Pixel Map)格式。雖然 Windows 對(duì)這種格式并沒(méi)有原生的支持,但很多圖像瀏覽器和轉(zhuǎn)換器都能使用這種格式,比如 IrfanView(www.irfanview.com)。所有 AGG 的控制臺(tái)例子都使用了 P6 256 格式,也就是 RGB,每個(gè)字節(jié)代碼一個(gè)顏色?,F(xiàn)在假設(shè)我們將在下圖所示的 RGB-buffer 內(nèi)存區(qū)中工作: The first and the Simplest Example這是第一個(gè)例子: #include <stdio.h> #include <string.h> #include "agg_rendering_buffer.h" enum { frame_width = 320, frame_height = 200 }; // Writing the buffer to a .PPM file, assuming it has // RGB-structure, one byte per color component //-------------------------------------------------- bool write_ppm(const unsigned char* buf, unsigned width, unsigned height, const char* file_name) { FILE* fd = fopen(file_name, "wb"); if(fd) { fprintf(fd, "P6 %d %d 255 ", width, height); fwrite(buf, 1, width * height * 3, fd); fclose(fd); return true; } return false; } // Draw a black frame around the rendering buffer, assuming it has // RGB-structure, one byte per color component //-------------------------------------------------- void draw_black_frame(agg::rendering_buffer& rbuf) { unsigned i; for(i = 0; i < rbuf.height(); ++i) { unsigned char* p = rbuf.row_ptr(i); *p++ = 0; *p++ = 0; *p++ = 0; p += (rbuf.width() - 2) * 3; *p++ = 0; *p++ = 0; *p++ = 0; } memset(rbuf.row_ptr(0), 0, rbuf.width() * 3); memset(rbuf.row_ptr(rbuf.height() - 1), 0, rbuf.width() * 3); } int main() { // In the first example we do the following: //-------------------- // Allocate the buffer. // Clear the buffer, for now "manually" // Create the rendering buffer object // Do something simple, draw a diagonal line // Write the buffer to agg_test.ppm // Free memory unsigned char* buffer = new unsigned char[frame_width * frame_height * 3]; memset(buffer, 255, frame_width * frame_height * 3); agg::rendering_buffer rbuf(buffer, frame_width, frame_height, frame_width * 3); unsigned i; for(i = 0; i < rbuf.height()/2; ++i) { // Get the pointer to the beginning of the i-th row (Y-coordinate) // and shift it to the i-th position, that is, X-coordinate. //--------------- unsigned char* ptr = rbuf.row_ptr(i) + i * 3; // PutPixel, very sophisticated, huh? :) //------------- *ptr++ = 127; // R *ptr++ = 200; // G *ptr++ = 98; // B } draw_black_frame(rbuf); write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); delete [] buffer; return 0; } 在這個(gè)例子中,你甚至不需要鏈接任何的 AGG 的代碼文件,你只需要在你的編譯器命令行中設(shè)置好 AGG 的包含路徑就行了。 編譯并運(yùn)行它,你會(huì)看到現(xiàn)圖所示的結(jié)果: 這個(gè)例子中幾乎所有東西都“手工打制”的,使用的唯一一個(gè)現(xiàn)成的類是 rendering_buffer。這個(gè)類本身并不知道關(guān)于內(nèi)存中像素格式的任何信息,它只是保存了一個(gè)數(shù)組,數(shù)組中的元素分別指向每行(像素的開(kāi)頭)。為申請(qǐng)和釋放這塊存儲(chǔ)區(qū)是使用者的責(zé)任,你可以使用任何可行的方式來(lái)申請(qǐng)和釋放內(nèi)存,比如使用系統(tǒng)提供的 API 函數(shù),或是簡(jiǎn)單的用內(nèi)存分配器(譯注:應(yīng)該是new、delete、malloc、free等),甚至是直接使用一個(gè)靜態(tài)數(shù)組。在上面這個(gè)例子中,因?yàn)槊總€(gè)像素要占用3個(gè)字節(jié),所以我們申請(qǐng)了 width*height*3 字節(jié)的內(nèi)存,在實(shí)際內(nèi)存中是不存在“行”這種布局方式的,但這樣做可以提高程序的性能,而且有時(shí)候在使用 API 的時(shí)候需要。 Class rendering_buffer包含文件: agg_rendering_buffer.h rendering_buffer 這個(gè)類保存了指向每一行像素的指針,基本上這個(gè)類做的事就是這些了??雌饋?lái)好像不是什么了不起的事,不過(guò)我們還是繼續(xù)分析下去。這個(gè)類的接口和功能都很簡(jiǎn)單,它只是模板類 row_ptr_cache 的一個(gè) typedef 而已: typedef row_ptr_cache<int8u> rendering_buffer; row_prt_cache 這個(gè)類的接口的功能如下: template<class T> class row_ptr_cache { public: row_ptr_cache(); row_ptr_cache(T* buf, unsigned width, unsigned height, int stride); void attach(T* buf, unsigned width, unsigned height, int stride); T* buf(); const T* buf() const; unsigned width() const; unsigned height() const; int stride() const; unsigned stride_abs() const; T* row_ptr(int, int y, unsigned); T* row_ptr(int y); const T* row_ptr(int y) const; row_data row (int y) const; T const* const* rows() const; template<class RenBuf> void copy_from(const RenBuf& src); void clear(T value) }; 這個(gè)類的實(shí)現(xiàn)里沒(méi)有使用斷言或是驗(yàn)證的措施,所以,使用者有責(zé)任在用這個(gè)類對(duì)象時(shí)正確地將它初始化到實(shí)際的內(nèi)存塊中,這可以在構(gòu)造函數(shù)中完成,也可以使用 attach() 函數(shù)。它們的參數(shù)解釋如下:
attach()函數(shù)會(huì)改變緩沖區(qū)或是它的參數(shù),它自己會(huì)為“行指針”數(shù)組重新分配內(nèi)存,所以你可以在任何時(shí)候調(diào)用它。當(dāng)(且僅當(dāng))新的height值比之前使用過(guò)的最大的height值還要大時(shí),它才會(huì)重新申請(qǐng)內(nèi)存。 構(gòu)造的這個(gè)對(duì)象的開(kāi)銷僅僅是初始化它的成員變量(設(shè)置為0),attach()的開(kāi)銷則是分配sizeof(ptr)*height個(gè)字節(jié)的內(nèi)存,然后將這些指針指向?qū)?yīng)的“行”。 最常使用的函數(shù)是 row_prt(y),這個(gè)函數(shù)只是簡(jiǎn)單地返回指向第y函數(shù)指針,這個(gè)指針指向的位置已經(jīng)考慮到了Y軸的方向了。
buf(), width(), height(), stride(), stride_abs() 這些函數(shù)的意義顯而易見(jiàn),就不解釋了。 copy_from()函數(shù)會(huì)將其它內(nèi)存的內(nèi)容拷貝至“本”內(nèi)存中。這個(gè)函數(shù)是安全的,如果(兩者的)width和height值不相同,那它會(huì)盡可能拷貝大一些的區(qū)域。一般來(lái)講都用于拷貝相同大小區(qū)域。 Two Modifications of the Example首先,在創(chuàng)建 rendering buffer 的對(duì)象時(shí)將 stride 取負(fù)值: agg::rendering_buffer rbuf(buffer, frame_width, frame_height, -frame_width * 3); 然后,我們?cè)囅聦?rendering buffer 附著(attach)到被分配的內(nèi)存區(qū)的某部分。這個(gè)修改會(huì)使得 rendering buffer 兩次附著在同一塊內(nèi)存區(qū)上,第一次是整個(gè)被分配的內(nèi)存區(qū)域,第二次是其中的一部分: int main() { unsigned char* buffer = new unsigned char[frame_width * frame_height * 3]; memset(buffer, 255, frame_width * frame_height * 3); agg::rendering_buffer rbuf(buffer, frame_width, frame_height, frame_width * 3); // Draw the outer black frame //------------------------ draw_black_frame(rbuf); // Attach to the part of the buffer, // with 20 pixel margins at each side. rbuf.attach(buffer + frame_width * 3 * 20 + // initial Y-offset 3 * 20, // initial X-offset frame_width - 40, frame_height - 40, frame_width * 3 // Note that the stride // remains the same ); // Draw a diagonal line //------------------------ unsigned i; for(i = 0; i < rbuf.height()/2; ++i) { // Get the pointer to the beginning of the i-th row (Y-coordinate) // and shift it to the i-th position, that is, X-coordinate. //--------------- unsigned char* ptr = rbuf.row_ptr(i) + i * 3; // PutPixel, very sophisticated, huh? :) //------------- *ptr++ = 127; // R *ptr++ = 200; // G *ptr++ = 98; // B } // Draw the inner black frame //------------------------ draw_black_frame(rbuf); // Write to a file //------------------------ write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); delete [] buffer; return 0; } 最后描畫出來(lái)的結(jié)果是這樣: 最后一處修改是: // Attach to the part of the buffer, // with 20 pixel margins at each side and negative 'stride' rbuf.attach(buffer + frame_width * 3 * 20 + // initial Y-offset 3 * 20, // initial X-offset frame_width - 40, frame_height - 40, -frame_width * 3 // Negate the stride ); 運(yùn)行結(jié)果如下: 在最后的一個(gè)例子里,我們只是使 stride 取了負(fù)值,而指針則和上個(gè)例子一樣,仍然指向內(nèi)存區(qū)的起啟處。
Pixel Format Renderers首先,我們創(chuàng)建一個(gè)更“文明”(譯注:顯得更高級(jí)一點(diǎn))的例子: #include <stdio.h> #include <string.h> #include "agg_pixfmt_rgb24.h" enum { frame_width = 320, frame_height = 200 }; // [...write_ppm is skipped...] // Draw a black frame around the rendering buffer //-------------------------------------------------- template<class Ren> void draw_black_frame(Ren& ren) { unsigned i; agg::rgba8 c(0,0,0); for(i = 0; i < ren.height(); ++i) { ren.copy_pixel(0, i, c); ren.copy_pixel(ren.width() - 1, i, c); } for(i = 0; i < ren.width(); ++i) { ren.copy_pixel(i, 0, c); ren.copy_pixel(i, ren.height() - 1, c); } } int main() { //-------------------- // Allocate the buffer. // Clear the buffer, for now "manually" // Create the rendering buffer object // Create the Pixel Format renderer // Do something simple, draw a diagonal line // Write the buffer to agg_test.ppm // Free memory unsigned char* buffer = new unsigned char[frame_width * frame_height * 3]; memset(buffer, 255, frame_width * frame_height * 3); agg::rendering_buffer rbuf(buffer, frame_width, frame_height, frame_width * 3); agg::pixfmt_rgb24 pixf(rbuf); unsigned i; for(i = 0; i < pixf.height()/2; ++i) { pixf.copy_pixel(i, i, agg::rgba8(127, 200, 98)); } draw_black_frame(pixf); write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); delete [] buffer; return 0; } 這個(gè)例子看起來(lái)和前面的沒(méi)什么不一樣的,但其實(shí)他們差別很大,看看這個(gè)聲明: agg::pixfmt_rgb24 pixf(rbuf); 這里我們創(chuàng)建了一個(gè)底層的像素渲染對(duì)象(pixel rendering object)并將它附著到渲染內(nèi)存區(qū)(rendering buffer)上,它是這樣定義的: typedef pixel_formats_rgb24<order_rgb24> pixfmt_rgb24; 類模板 pixel_formats_rgb24 掌握了內(nèi)存中具體的像素格式信息。唯一的模板參數(shù)可以是 order_rgb24 或是 order_rgb23,它們定義了顏色字節(jié)(color channels)的順序。 與 rendering buffer 不同的是,這些類使用整型的像素坐標(biāo)進(jìn)行操作,因?yàn)樗鼈冎涝趺从?jì)算對(duì)于特定點(diǎn) X 的偏移。你可能會(huì)說(shuō),如果在 rendering buffer 中保存像素的寬度值的話會(huì)更容易,但是在實(shí)踐中會(huì)有很多限制。別忘了,像素寬度可能比一個(gè)字節(jié)還小,比如在打印機(jī)渲染高解析度的 B&W 圖像的時(shí)候就是這樣。因此,我們需要將這個(gè)功能分離出來(lái),rendering_buffer 這個(gè)類就用于加速對(duì)“行”的訪問(wèn),而 pixel format renderers 就負(fù)責(zé)如何解析“行”是什么。 現(xiàn)在,AGG 里下面這些文件實(shí)現(xiàn)了各種不同的像素格式:
像素格式的類定義了它們?cè)嫉念伾臻g和顏色類型,像下面這樣: typedef rgba8 color_type; 對(duì)于 pixfmt_gray8_nnn,這些都是 gray8,這種機(jī)制允許你寫自己的像素和顏色格式,舉個(gè)例子,HSV、CMYK 等等,AGG 的其它部分可以完全無(wú)誤地和你自己定義的新的像素格式一起工作。
Creation
pixel_formats_rgb24(rendering_buffer& rb); 像素格式渲染器(pixecl format renderers)的構(gòu)造函數(shù)需要一已經(jīng)創(chuàng)建并良好初始化的rendering_buffer對(duì)象的引用。這個(gè)構(gòu)建工作的開(kāi)銷很小,基本上只是初始化一個(gè)指針。 Member Functions像素格式渲染器(pixecl format renderers)必須要實(shí)現(xiàn)以下這些接口: unsigned width() const { return m_rbuf->width(); } unsigned height() const { return m_rbuf->height(); } 返回內(nèi)存區(qū)的寬和高(以像素?cái)?shù)來(lái)衡量) color_type pixel(int x, int y); 返回(x,y)坐標(biāo)處的像素的顏色 void copy_pixel(int x, int y, const color_type& c); 將帶顏色的像素拷入緩存區(qū)中。如果是本身 RGB 像素格式,那么它就不考慮 rgba8 拷貝源中的存在的 alpha 通道。如果本身是 RGBA,那么它就簡(jiǎn)單地把所有值都拷貝過(guò)來(lái),包括 R、G、B,以及 alpha 通道值。 void blend_pixel(int x, int y, const color_type& c, int8u cover); 這個(gè)函數(shù)將帶顏色信息的像素 c 與緩存區(qū)(x,y)處的像素進(jìn)行混合(blending)?,F(xiàn)在我們來(lái)解釋一下“混合”的概念?;旌?blending)是抗鋸齒(anti-aliasing)的關(guān)鍵特性。在 RGBA 的顏色空間中,我們使用 rgba8 結(jié)構(gòu)體來(lái)代表顏色。這個(gè)結(jié)構(gòu)體有一個(gè)數(shù)據(jù)成員 int8u a ,它就是 alpha 通道。不過(guò),在這個(gè)函數(shù)里,我們還看到一個(gè)參數(shù) cover ,表示像素的覆蓋值大小,比如,這個(gè)像素被多邊形所“覆蓋”的部分的大?。ㄗg注:這涉及到亞像素精度,因?yàn)橐粋€(gè)像素可以分為 256*256 份,所以這個(gè)像素并不一定全部被“覆蓋”,詳細(xì)可參考 AGG 對(duì)于亞像素的說(shuō)明)。其實(shí)你可以把它看成是另一個(gè) alpha(或者應(yīng)該叫它Beta?:))。這么做有兩個(gè)原因,首先,顏色類型(color type)不一定非要包含 alpha 值)。就算顏色類型帶有 alpha 值,它的類型也不一定非要與抗鋸齒算法中使用的顏色類型一致。假設(shè)你現(xiàn)在使用的是 "Hi-End" RGBA 顏色空間,這種顏色空間使用4個(gè)取值范圍是[0,1]浮點(diǎn)型來(lái)表示,alpha 通道值也使用浮點(diǎn)數(shù)————對(duì)于這種情況來(lái)說(shuō),混合時(shí)使用一個(gè)byte實(shí)在太少了,但在去鋸齒時(shí)卻非常夠用。所以,cover 值就是為去鋸齒而使用的一個(gè)統(tǒng)一的備用 alpha 值。在大部分情況來(lái)說(shuō),用 cover_type 來(lái)定義它,但在光柵化處理器(rasterizers)中是直接顯示地使用 int8u 類型的。這是故意這么定義的,因?yàn)槿绻枰訌?qiáng) cover_type 的能力時(shí),會(huì)使得所有已經(jīng)存在的像素格式光柵化處理器(pixel format rasterizres)變得與 cover_type 不兼容。它們確實(shí)是不兼容的,在進(jìn)行顏色混合時(shí),如果中間值使用 32-bit 值來(lái)暫存的話,那么最大只能使用 8-bit 的覆蓋值(coverage value)和 8-bit 的 alpha 值(alpha) 。如果使用 16-bit 的值的話,就要用 64-bit 的中間值暫存,這對(duì)于 32-bit 的平臺(tái)來(lái)說(shuō)會(huì)有非常昂貴的開(kāi)銷。 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); 使用某種顏色描畫一條水平或是垂直的線。 void blend_hline(int x, int y, unsigned len, const color_type& c, int8u cover); void blend_vline(int x, int y, unsigned len, const color_type& c, int8u cover); 采用混合顏色的模式描畫一帶某種顏色的水平(或垂直線)線。之所以要分開(kāi) copy 和 blend 兩個(gè)版本,是因?yàn)榭紤]到效率問(wèn)題。雖然可以使用一個(gè) if/else (其實(shí)在 blend 版的描畫函數(shù)中就有)來(lái)區(qū)分,但對(duì)于某些場(chǎng)合,比如要描畫很多小型標(biāo)識(shí)(markers)時(shí),這會(huì)很影響效率,這種場(chǎng)景在不同的散點(diǎn)圖描畫程序(scatter plot applicatioin)中常常遇到。 void blend_solid_hspan(int x, int y, unsigned len, const color_type& c, const int8u* covers); void blend_solid_vspan(int x, int y, unsigned len, const color_type& c, const int8u* covers); 混合描畫一條水平或是垂直的 solid-color 的 span, Span與 hline 和 vline 幾乎是一樣的,但它擁有一個(gè)存有 coverage value 的數(shù)組。這兩個(gè)函數(shù)在渲染實(shí)心的去鋸齒多邊形時(shí)會(huì)用到。 void blend_color_hspan(int x, int y, unsigned len, const color_type* colors, const int8u* covers); void blend_color_vspan(int x, int y, unsigned len, const color_type* colors, const int8u* covers); 混合描畫水平或是垂直的顏色 span ,這兩個(gè)函數(shù)用于不同的 span 產(chǎn)生器中,比如說(shuō) gradient,image,patterns,Gouraud interpolation 等等。函數(shù)接受一個(gè)顏色數(shù)組參數(shù),這個(gè)顏色數(shù)組必須與所使用的像素格式兼容。比如說(shuō),所有 AGG 中已經(jīng)有的 RGB 像素格式都與 rgb8 類型是兼容的。 covers 參數(shù)是一個(gè) coverage value 的數(shù)組,這與 blend_solid_hspan 中的是一樣的。這是參數(shù)可選,可以設(shè)置為 0 。
下面這個(gè)例子是描畫陽(yáng)光的光譜。rgba 類包含有 4 個(gè)浮點(diǎn)數(shù)的顏色部分(包括alpha),這個(gè)類有一個(gè)靜態(tài)函數(shù) from_wavelength ,以及相應(yīng)的構(gòu)造函數(shù)。rgba8 可以用 rgba 來(lái)構(gòu)造(在 AGG 中這是一個(gè)常見(jiàn)的策略,也就是任何的顏色類型都可以用 rgba 來(lái)構(gòu)造)。 #include <stdio.h> #include <string.h> #include "agg_pixfmt_rgb24.h" enum { frame_width = 320, frame_height = 200 }; // [...write_ppm is skipped...] int main() { //-------------------- // Allocate the buffer. // Clear the buffer, for now "manually" // Create the rendering buffer object // Create the Pixel Format renderer // Create one line (span) of type rgba8. // Fill the buffer using blend_color_span // Write the buffer to agg_test.ppm // Free memory unsigned char* buffer = new unsigned char[frame_width * frame_height * 3]; memset(buffer, 255, frame_width * frame_height * 3); agg::rendering_buffer rbuf(buffer, frame_width, frame_height, frame_width * 3); agg::pixfmt_rgb24 pixf(rbuf); agg::rgba8 span[frame_width]; unsigned i; for(i = 0; i < frame_width; ++i) { agg::rgba c(380.0 + 400.0 * i / frame_width, 0.8); span[i] = agg::rgba8(c); } for(i = 0; i < frame_height; ++i) { pixf.blend_color_hspan(0, i, frame_width, span, 0); } write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); delete [] buffer; return 0; } 運(yùn)行結(jié)果如下: Alpha-Mask AdaptorAlpha-Mask 是一個(gè)分離出來(lái)的緩存區(qū),通常用于在底層實(shí)現(xiàn)任意形狀的裁剪。有一個(gè)特制的適配器類,可以將所有對(duì)像素格式渲染器(pixel format renderes)的調(diào)用先丟給 alpha-mask 過(guò)濾器。alpha-mask 一般是一個(gè)灰度緩存區(qū)(每像素一個(gè)字節(jié)),大小與主渲染緩存區(qū)(main rendering buffer)是一樣的。在 alpha-mask 中的每個(gè)像素就是對(duì)應(yīng)著主渲染緩存區(qū)相應(yīng)像素的一個(gè)額外的覆蓋值(coverage value)。copy_hline()之類沒(méi)有酸辣值作為參數(shù)的函數(shù)會(huì)將調(diào)用轉(zhuǎn)向相應(yīng)的有覆蓋值的函數(shù)。比如,copy_hline()會(huì)從 alpha mask 緩存區(qū)中取出水平 span ,再用它來(lái)調(diào)用 blend_solid_hspan() 函數(shù)。 包含文件: #include "agg_pixfmt_amask_adaptor.h" #include "agg_alpha_mask_u8.h" 下面是一個(gè)例子,它展示了如何聲明一個(gè)帶有 alpha-mask 適配器的像素格式渲染器。 #include "agg_pixfmt_rgb24.h" #include "agg_pixfmt_amask_adaptor.h" #include "agg_alpha_mask_u8.h" //. . . // Allocate the alpha-mask buffer, create the rendering buffer object // and create the alpha-mask object. //-------------------------------- agg::int8u* amask_buf = new agg::int8u[frame_width * frame_height]; agg::rendering_buffer amask_rbuf(amask_buf, frame_width, frame_height, frame_width); agg::amask_no_clip_gray8 amask(amask_rbuf); // Create the alpha-mask adaptor attached to the alpha-mask object // and the pixel format renderer. Here pixf is a previously // created pixel format renderer of type agg::pixfmt_rgb24. agg::pixfmt_amask_adaptor<agg::pixfmt_rgb24, agg::amask_no_clip_gray8> pixf_amask(pixf, amask); //. . . 注意在這里我們用的是 amask_no_clip_gray8 ,它不帶區(qū)域裁剪功能。因?yàn)槲覀兪褂玫?alpha-mask 緩存區(qū)和主緩存區(qū)的大小是一樣的,所以只要主緩存區(qū)沒(méi)有非法訪問(wèn)的話,alpha-mask 緩存區(qū)也不會(huì)存在非法訪問(wèn)。裁剪在高層中實(shí)現(xiàn),如果你使用的 alpha-mask 緩存區(qū)比主緩存區(qū)小的話,你必須使用 alpha_mask_gray8 這個(gè)類來(lái)代替。 下面是一個(gè)完整的例子: #include <stdio.h> #include <string.h> #include "agg_pixfmt_rgb24.h" #include "agg_pixfmt_amask_adaptor.h" #include "agg_alpha_mask_u8.h" enum { frame_width = 320, frame_height = 200 }; // [...write_ppm is skipped...] int main() { // Allocate the main rendering buffer and clear it, for now "manually", // and create the rendering_buffer object and the pixel format renderer //-------------------------------- agg::int8u* buffer = new agg::int8u[frame_width * frame_height * 3]; memset(buffer, 255, frame_width * frame_height * 3); agg::rendering_buffer rbuf(buffer, frame_width, frame_height, frame_width * 3); agg::pixfmt_rgb24 pixf(rbuf); // Allocate the alpha-mask buffer, create the rendering buffer object // and create the alpha-mask object. //-------------------------------- agg::int8u* amask_buf = new agg::int8u[frame_width * frame_height]; agg::rendering_buffer amask_rbuf(amask_buf, frame_width, frame_height, frame_width); agg::amask_no_clip_gray8 amask(amask_rbuf); // Create the alpha-mask adaptor attached to the alpha-mask object // and the pixel format renderer agg::pixfmt_amask_adaptor<agg::pixfmt_rgb24, agg::amask_no_clip_gray8> pixf_amask(pixf, amask); // Draw something in the alpha-mask buffer. // In this case we fill the buffer with a simple verical gradient unsigned i; for(i = 0; i < frame_height; ++i) { unsigned val = 255 * i / frame_height; memset(amask_rbuf.row_ptr(i), val, frame_width); } // Draw the spectrum, write a .ppm and free memory //---------------------- agg::rgba8 span[frame_width]; for(i = 0; i < frame_width; ++i) { agg::rgba c(380.0 + 400.0 * i / frame_width, 0.8); span[i] = agg::rgba8(c); } for(i = 0; i < frame_height; ++i) { pixf_amask.blend_color_hspan(0, i, frame_width, span, 0); } write_ppm(buffer, frame_width, frame_height, "agg_test.ppm"); delete [] amask_buf; delete [] buffer; return 0; } 這是運(yùn)行結(jié)果: 你看到了,我們是用白色來(lái)清空主緩存的,如果我們將: memset(buffer, 255, frame_width * frame_height * 3); 修改成: memset(buffer, 0, frame_width * frame_height * 3); 那么結(jié)果就像下面這樣: 換句話說(shuō),alpha-mask 是這樣工作的:它是一個(gè)分離出來(lái)的 alpha 通道,用于混合渲染基本的描畫物。因?yàn)樗氖?8-bit 的值,所以你可以將描畫裁剪成任意的形狀,而且這種裁剪可以有很非常好的去鋸齒效果。 Basic Renderers有兩種基礎(chǔ)渲染器,renderer_base 和 renderer_mclip 它們的功能幾乎是一樣的。主要使用的是前者(renderer_base),它進(jìn)行低層次的裁剪處理。通用的裁剪處理(clipping)是一個(gè)復(fù)雜的任務(wù)。在 AGG 中,至少可以有兩層的裁剪,底層(像素級(jí))的,和高層的(向量級(jí))。這兩個(gè)類可以進(jìn)行像素級(jí)別的裁剪,用以防止對(duì)緩存區(qū)的越界訪問(wèn)。 renderer_mclip 類可以支持多個(gè)矩形區(qū)域的裁剪區(qū)域,但它的性能和裁剪區(qū)域的數(shù)量有關(guān)。 renderer_base 和 renderer_mclip 都是模板類,它們的模板參數(shù)就是像素格式渲染器(pixel format renderer)。 template<class PixelFormat> class renderer_base { public: typedef PixelFormat pixfmt_type; typedef typename pixfmt_type::color_type color_type; . . . }; Creationrenderer_base(pixfmt_type& ren); renderer_mclip(pixfmt_type& ren); 兩個(gè)類的構(gòu)造函數(shù)都可以接受像素格式渲染器(pixel format renderer)對(duì)象作為參數(shù)。 renderer_mclip 在內(nèi)部使用 renderer_base<PixelFormat> ,用來(lái)進(jìn)行單矩形區(qū)域的裁剪。注意,你也可以使用 pixfmt_amask_adaptor 作為參數(shù) PixelFormat。 構(gòu)造的開(kāi)銷是非常小的,它只是初始化各個(gè)成員變量。不過(guò),如果你添加新的裁剪區(qū)域,那么 renderer_mclip 需要申請(qǐng)新的內(nèi)存,在析構(gòu)時(shí)也會(huì)有相應(yīng)的釋放動(dòng)作。它會(huì)用 pod_deque 類來(lái)完成內(nèi)存塊的申請(qǐng),并且它不會(huì)(提前)釋放不需要的內(nèi)存,當(dāng)你重新設(shè)置裁剪區(qū)域的時(shí)候,它會(huì)重用原來(lái)申請(qǐng)的內(nèi)存區(qū)。 AGG 中廣泛使用了這種技術(shù),這可以避免產(chǎn)生過(guò)多的內(nèi)存碎片。 Member Functionsconst pixfmt_type& ren() const; pixfmt_type& ren(); 返回指向像素渲染器(pixel format renderer)的引用。 unsigned width() const; unsigned height() const; 返回渲染緩存區(qū)(rendering buffer)的高和度。 void reset_clipping(bool visibility); 這個(gè)函數(shù)會(huì)重設(shè)裁剪區(qū)域,如果 visibility 值設(shè)置為 true ,那么裁剪區(qū)域會(huì)被設(shè)置為 (0, 0, width()-1, height()-1),如果設(shè)置為 false,會(huì)設(shè)置一個(gè)不可見(jiàn)的區(qū)域,比如(1,1,0,0)。對(duì)于 renderer_mclip 的這個(gè)函數(shù)來(lái)說(shuō),它會(huì)移除之前添加的所有剪裁區(qū)域。
bool clip_box(int x1, int y1, int x2, int y2); 這個(gè)函數(shù)用于設(shè)置新的裁剪區(qū)域(clipping box)。只有 renderer_base 才有這個(gè)函數(shù)。裁剪區(qū)域包含了邊界位置,所以最大的裁剪區(qū)域是 (0, 0, width()-1, height()-1) 。裁剪區(qū)域在(重新)設(shè)置之前,會(huì)被設(shè)置為最大值。所以,就算你設(shè)置新區(qū)域的比 (0, 0, width()-1, height()-1) 還要大,也是安全的(譯注:因?yàn)椴粫?huì)被采用) void add_clip_box(int x1, int y1, int x2, int y2); 添加一個(gè)新的裁剪區(qū)域。只有 renderer_mclip 才有這個(gè)函數(shù)。你可以添加任何個(gè)新的矩形區(qū)域,但他們之間不能有重疊的部分。如果有重疊的區(qū)域的話,有些元素就會(huì)被描畫兩次或兩次以上。被添加的新區(qū)域會(huì)在加入之時(shí)被 (0, 0, width()-1, height()-1) 這個(gè)區(qū)域預(yù)先裁剪掉,這主要是有效率上的考量。這也意味著調(diào)用 reset_clipping(false)沒(méi)有任何意義,因?yàn)樗斜惶砑拥男聟^(qū)域都會(huì)用一個(gè)不可見(jiàn)區(qū)域內(nèi)先裁剪掉,因此也就不會(huì)被添加。(譯注:原句如下:t also means that calling reset_clipping(false) for the renderer_mclip doesn't make any sense because all adding regions will be clipped by an invisible area and will not be actually added.對(duì)這個(gè)我不能理解它的意思,后面看了代碼再來(lái)修正吧)。可見(jiàn)區(qū)域包含裁剪區(qū)域的邊界,也就是說(shuō),調(diào)用 add_clip_box(100,100,100,100) 會(huì)添加一個(gè) 1 像素的裁剪區(qū)域。 void clip_box_naked(int x1, int y1, int x2, int y2); 只有 renderer_base 有這個(gè)函數(shù),它用來(lái)給 rendering buffer 大小設(shè)置一個(gè)新的裁剪區(qū)域。這個(gè)函數(shù)不安全,主要是 renderer_mclip 類在使用它,用來(lái)在不同的子區(qū)域之間快速的切換。這個(gè)函數(shù)的目的就是為了避免額外的開(kāi)銷。 bool inbox(int x, int y) const; 檢查 (x,y) 這個(gè)點(diǎn)是不是在裁剪區(qū)域內(nèi)。只有 renderer_base 才有這個(gè)函數(shù)。 void first_clip_box(); bool next_clip_box(); 這兩個(gè)函數(shù)用于枚舉渲染器所有的裁剪區(qū)域。對(duì)于 renderer_base 類來(lái)說(shuō),它是返回的是空的, next_clip_box() 始終返回 false。 const rect& clip_box() const; int xmin() const; int ymin() const; int xmax() const; int ymax() const; 以一個(gè)矩形的形式可是以四個(gè)獨(dú)立的整數(shù)值形式返回裁剪區(qū)域。 renderer_mclip 的這個(gè)函數(shù)始終返回 (0, 0, width()-1, height()-1)。 const rect& bounding_clip_box() const; int bounding_xmin() const; int bounding_ymin() const; int bounding_xmax() const; int bounding_ymax() const; 以一個(gè)矩形的形式可是以四個(gè)獨(dú)立的整數(shù)值形式返回裁剪區(qū)域的邊界。對(duì)于 renderer_base ,這個(gè)函數(shù)返回的值于上面那組函數(shù)是一樣的。對(duì)于 renderer_mclip 來(lái)說(shuō),它們返回的邊界是由所有被添加的矩形合計(jì)得出的。 void clear(const color_type& c); 用 c 這個(gè)顏色來(lái)清除緩存區(qū)內(nèi)的所有區(qū)域(不考慮裁剪區(qū)域)。 void copy_pixel(int x, int y, const color_type& c); 設(shè)置一個(gè)像素的顏色(考慮裁剪區(qū)域clipping)。 void blend_pixel(int x, int y, const color_type& c, cover_type cover); 混合描畫一個(gè)像素。它的行為與 pixel format renderer 的對(duì)應(yīng)函數(shù)是一樣的。 color_type pixel(int x, int y) const; 獲取指定坐標(biāo)(x,y)的顏色值,如果這個(gè)點(diǎn)在裁剪區(qū)域外,這個(gè)函數(shù)返回 color_type::no_color(). 對(duì)于 rgba8 來(lái)說(shuō),就是 (0,0,0,0). void copy_hline(int x1, int y, int x2, const color_type& c); void copy_vline(int x, int y1, int y2, const color_type& c); void blend_hline(int x1, int y, int x2, const color_type& c, cover_type cover); void blend_vline(int x, int y1, int y2, const color_type& c, cover_type cover); 描畫(拷貝)或是混合渲染水平或垂直的像素線。行為與 pixel format renders 的相應(yīng)函數(shù)一樣。但在這里,使用的是線的起始點(diǎn)和終點(diǎn)坐標(biāo),而不是 pixfmt 中的(x, y, length)。 void copy_bar(int x1, int y1, int x2, int y2, const color_type& c); void blend_bar(int x1, int y1, int x2, int y2, const color_type& c, cover_type cover); 描畫(拷貝)或是混合渲染一個(gè)矩形區(qū)。 void blend_solid_hspan(int x, int y, int len, const color_type& c, const cover_type* covers); void blend_solid_vspan(int x, int y, int len, const color_type& c, const cover_type* covers); 混合渲染一個(gè)水平或是垂直的純色(solid-color)的span。這些在渲染實(shí)多邊形時(shí)會(huì)用到。
void blend_color_hspan(int x, int y, int len, const color_type* colors, const cover_type* covers); void blend_color_vspan(int x, int y, int len, const color_type* colors, const cover_type* covers); 混合渲染一個(gè)水平或是垂直的(? vertical -color)的span。這個(gè)函數(shù)與不同的 span 生成器一起使用,比如 gradients, images, patterns, Gouraud interpolation 等等。函數(shù)接受一個(gè)顏色數(shù)組,顏色的類型必須與正在使用 pixel format 兼容。 void blend_color_hspan_no_clip(int x, int y, int len, const color_type* colors, const cover_type* covers); void blend_color_vspan_no_clip(int x, int y, int len, const color_type* colors, const cover_type* covers); 與上面的函數(shù)是一樣的,但不考慮裁剪區(qū)域。這兩個(gè)函數(shù)用到 scanline renderers 中。分離出這兩個(gè)函數(shù)的原因也是為了效率,scanline 由很多的 spans 組合而成,在進(jìn)行區(qū)域裁剪時(shí),擁有整個(gè) scanline 的信息會(huì)比逐個(gè)的裁剪每個(gè) span 來(lái)得更有效率一些,對(duì)于 renderer_mclip 這個(gè)類尤其如此。 void copy_from(const rendering_buffer& from, const rect* rc=0, int x_to=0, int y_to=0); 將源緩存區(qū)的內(nèi)容拷入本緩存區(qū)中(考慮區(qū)域裁剪)。它假設(shè)兩塊緩存區(qū)的像素格式是一樣的。rc是一個(gè)可選項(xiàng),它指示的是源緩存區(qū)內(nèi)的一個(gè)矩形,x_to 、 y_to ———— rc->x1, rc->y1 坐標(biāo)值映射到目標(biāo)緩存區(qū)。 A common example在使用 rendering buffer 和底層 renderers 時(shí),下面的代碼是非常常用的。 // Typedefs of the low level renderers to simplify the declarations. // Here you can use any other pixel format renderer and // agg::renderer_mclip if necessary. //-------------------------- typedef agg::pixfmt_rgb24 pixfmt_type; typedef agg::renderer_base<agg::pixfmt_rgb24> renbase_type; enum { bytes_per_pixel = 3 }; unsigned char* buffer = new unsigned char[frame_width * frame_height * bytes_per_pixel]; agg::rendering_buffer rbuf(buffer, frame_width, frame_height, frame_width * bytes_per_pixel); pixfmt_type pixf(rbuf); renbase_type rbase(pixf); rbase.clear(clear_color); //. . . 在最后,我們使用clear()函數(shù),以某種顏色清除了緩存區(qū)中,而沒(méi)有“手動(dòng)地”使用 memset() :-)。同時(shí)要注意,與例子所不同的是,stride 值并不是必須要和 frame_width * bytes_per_pixel 值一致。很多時(shí)候應(yīng)用時(shí)會(huì)有一些對(duì)齊上的要求,比如,對(duì)于 windows中的位圖(bitmap)來(lái)說(shuō),這個(gè)值必須是4的倍數(shù)。 Primitives and Markers RenderersAGG 中也加入了一些基礎(chǔ)描畫對(duì)象(primitives)和標(biāo)記(marker)的渲染器。這個(gè)機(jī)制可以讓你很快地描畫一些常見(jiàn)的、帶鋸齒的對(duì)象,比如直線,矩形,橢圓等。標(biāo)記(marker)可以畫出一些在散點(diǎn)圖(scatter plots)中常見(jiàn)的形狀。如果你不打算用它們,那么你可以跳過(guò)這一節(jié)的內(nèi)容。 Primitives Renderer頭文件: agg_renderer_primitives.h Declarationtemplate<class BaseRenderer> class renderer_primitives { public: typedef BaseRenderer base_ren_type; typedef typename base_ren_type::color_type color_type; //. . . }; 這里的 BaseRenderer 可以用 renderer_base 或是 renderer_mclip. Creationrenderer_primitives(base_ren_type& ren) : m_ren(&ren), m_fill_color(), m_line_color(), m_curr_x(0), m_curr_y(0) {} 創(chuàng)建的開(kāi)銷非常小,只是初始化指向 base renderer 對(duì)象的指針、兩個(gè)顏色信息、以及初始化坐標(biāo)值,這些坐標(biāo)值會(huì)在 move_to 和 line_to 函數(shù)中用到。 Member functions static int coord(double c); 將一個(gè) double 型的坐標(biāo)值轉(zhuǎn)換成亞像素精度的 int 型。它做的事就是將 double 值乘以256 ,然后返回它的整型部分。 void fill_color(const color_type& c); void line_color(const color_type& c); const color_type& fill_color() const; const color_type& line_color() const; 獲取或是設(shè)置 fill 或是直線的顏色。顏色可以帶有 alpha 值并且這個(gè)值會(huì)生效,這樣的話,基礎(chǔ)畫面對(duì)象(primitives)就可以進(jìn)行 alpha 混合渲染。 void rectangle(int x1, int y1, int x2, int y2); 描畫一個(gè)矩形,但不填充線的顏色。使用的是正規(guī)的像素坐標(biāo),邊界的寬度始終是 1 像素,不能修改。 void solid_rectangle(int x1, int y1, int x2, int y2); 描畫一個(gè)不帶邊界的實(shí)心矩形,填充線的顏色。使用的是像素坐標(biāo)。 void outlined_rectangle(int x1, int y1, int x2, int y2); 描畫一個(gè)帶邊界的實(shí)心矩形。使用了顏色線(colors line)和填充(fill?)。 void ellipse(int x, int y, int rx, int ry); void solid_ellipse(int x, int y, int rx, int ry); void outlined_ellipse(int x, int y, int rx, int ry); 描畫橢圓,空心的、實(shí)心的或是實(shí)心帶邊界的。坐標(biāo)是整數(shù)值,以像素?cái)?shù)來(lái)衡量。rx 和 ry 是像素?cái)?shù)衡量的橢圓半徑。 void line(int x1, int y1, int x2, int y2, bool last=false); 使用亞像素精度描畫一條 bresenham 光柵線。坐標(biāo)的格式是 24.8,即,1/256的像素精度。 last 決定是否描畫最后一個(gè)像素,這對(duì)于使用 alpha混合渲染來(lái)描畫 consecutive 線段會(huì)非常重要。不應(yīng)該有像素被多次描畫。 void move_to(int x, int y); void line_to(int x, int y, bool last=false); line()版本。 const base_ren_type& ren() const { return *m_ren; } base_ren_type& ren() { return *m_ren; } 返回 base renderer 對(duì)象的引用。 const rendering_buffer& rbuf() const { return m_ren->rbuf(); } rendering_buffer& rbuf() { return m_ren->rbuf(); } 返回附著于 base renderer 的 rendering buffer 的引用。 Marker Renderer頭文件: agg_renderer_markers.h marker renderer 可以描畫或是 alpha混合沉浸下面的這些圖形: enum marker_e { marker_square, marker_diamond, marker_circle, marker_crossed_circle, marker_semiellipse_left, marker_semiellipse_right, marker_semiellipse_up, marker_semiellipse_down, marker_triangle_left, marker_triangle_right, marker_triangle_up, marker_triangle_down, marker_four_rays, marker_cross, marker_x, marker_dash, marker_dot, marker_pixel }; Declarationtemplate<class BaseRenderer> class renderer_markers : public renderer_primitives<BaseRenderer> { public: typedef renderer_primitives<BaseRenderer> base_type; typedef BaseRenderer base_ren_type; typedef typename base_ren_type::color_type color_type; // . . . }; Creationrenderer_markers(base_ren_type& rbuf) : base_type(rbuf) {} 正如你所見(jiàn),構(gòu)造的過(guò)程和 renderer_primitives 是一樣簡(jiǎn)單的。 Member Functionsmarker 的所有函數(shù)都接受 x 、y 和半徑作為參數(shù),這些值以像素?cái)?shù)來(lái)衡量。在pixel() 中,半徑值沒(méi)有任何作用。填充(fill)和邊線(line)顏色都來(lái)自 renderer_primitives 基類。 void square(int x, int y, int r); void diamond(int x, int y, int r); void circle(int x, int y, int r); void crossed_circle(int x, int y, int r); void semiellipse_left(int x, int y, int r); void semiellipse_right(int x, int y, int r); void semiellipse_up(int x, int y, int r); void semiellipse_down(int x, int y, int r); void triangle_left(int x, int y, int r); void triangle_right(int x, int y, int r); void triangle_up(int x, int y, int r); void triangle_down(int x, int y, int r); void four_rays(int x, int y, int r); void cross(int x, int y, int r); void xing(int x, int y, int r); void dash(int x, int y, int r); void dot(int x, int y, int r); void pixel(int x, int y, int); 當(dāng)然,有一些為圖方便的函數(shù)存在,這些函數(shù)基本上就是使用 switch/case 來(lái)構(gòu)造: void marker(int x, int y, int r, marker_e type); 根據(jù)給定的類型來(lái)描畫一個(gè) marker。 template<class T> void markers(int n, const T* x, const T* y, T r, marker_e type); 根據(jù)給定的類型描畫一系列的 marker,坐標(biāo)存儲(chǔ)在 x 和 y 兩個(gè)數(shù)組中。 template<class T> void markers(int n, const T* x, const T* y, const T* r, marker_e type); 根據(jù)給定的類型描畫一系列的 marker,坐標(biāo)和半徑值分別存儲(chǔ)在 x、 y 和 r 三個(gè)數(shù)組中。 template<class T> void markers(int n, const T* x, const T* y, const T* r, const color_type* fill_colors, marker_e type); 根據(jù)給定的類型描畫一系列的 marker,坐標(biāo)、半徑值以及顏色分別存儲(chǔ)在 x、 y 、 r 和 fill_colors 四個(gè)數(shù)組中。 template<class T> void markers(int n, const T* x, const T* y, const T* r, const color_type* fc, const color_type* lc, marker_e type); 根據(jù)給定的類型描畫一系列的 marker,坐標(biāo)、半徑值以及顏色分別存儲(chǔ)在 x、 y 、 r 、fc 和 lc 等數(shù)組中。 |
|