AGG参考手册中文
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
AGG参考手册
1.前言
1.1.简介
1.1.1.……
Anti-GrainGeomet ry (AGG)是一个用标准的平台无关的C++开发的通用图形工具包。
在把高质量二维图形作为关键的计算机程序中,它可以应用被应用于许多方面。
例如,AGG可以用于渲染二维地图。
AGG仅仅使用C++和标准C的函数,如memcp y,sin,cos,sqrt等。
基本的算法甚至没有使用C++的标准模板库。
因此,AGG能够在大量的应用程序中使用,包括嵌入式系统。
另一方面,AGG允许使用者替换图形库的任何部分,比如当它不能满足性能的要求时进行库的替换。
使用者也可以根据需要添加其它的颜色空间。
这一切的实现都是因为A G G广泛采用了C++的模板机制。
AGG不是一个结构紧密的图形库,且不容易使用。
我认为AGG是一个“创建其它工具的工具”。
这意味着AG G中没有“G raphics”对象或其它类似的结构,它包含了许多组织松散、能组合或是单独使用的算法。
这些算法都有定义良好的接口,并且算法之间隐式或显式的依赖关系尽可能最小。
1.1.
2.……
大部分图形库都有一个包含成百上千方法的单独类,如GDI+中的“Graphi c s”。
这个对象也可以隐式存在,如OpenG L。
总之,所有常用的图形工具包,包括Java2D,DispalyPDF,SVG 以及其它优秀的图形工具包都显式或隐式的含有这个类。
这种做法简单而且非常适用于一些场合,但是去一直受到限制。
它在简单的场合中性能很好,但至少我仍未遇到可以完全满足所有需求的图形库。
此外,所有此种类型的库或标准都过于庞大。
大部分功能从未被使用,而一些简单的操作却不能实现。
图形引擎(或库)是一兆字节来计算的。
如果使用最先进的SVG浏览器,它只在显示最简单的基元时表现良好。
只要试图使用一些高级操作,如用不同的图形过滤器与SVG交互,将会发生内存泄露,甚至崩溃。
这并不是因为它有不良设计,而是因为采取了极端复杂的设计。
这种设计本身成为了不可能完成的任务,不能被人感知,,就像不能感知无穷大一样。
1.1.3.建议
AGG主要的目标是要打破上述的传统,展现其稳定、轻巧、灵活、自由的优点。
它的基本概念开始似乎并不遵循惯例,非常接近于S T L,但还是存在着很大的区别。
STL是一个通用的C++工具,而AGG是C++图形库。
STL作为一个方便的工具包直接在应用程序中使用,但我并不建议以同样的方式使用AG G。
比较好的方法是针对需要处理的问题,对AGG进行轻量级的、面向问题的封装后再使用。
这与GDI+又有什么不同呢?首先,可以完全控制封装后的A G G。
AGG只是提供了一系列基本的算法和灵活的设计,使得算法间显式或隐式的联系都最小。
使用者可以只定义接口、转换管道和输出格式,甚至可以模拟任何已经存在的图形接口的一部分。
例如,使用AGG光栅化器来在屏幕上显示图形并直接调用WindowsGDI 来打印,并合并为一个单独的AP I。
不信吗?下图是GDI+和AGG的渲染质量的比较。
但是最重要的是,如果设计足够灵活,程序将是完全可移植的。
A GG还是一个可以将不同的输出合并为一个统一的API的工具。
另外,在基于Web的应用程序中,可以使用AG G 在服务器端生成栅格图像。
而且AGG是完全跨平台的。
1.1.4.反走样和子像素精度
反走样是在低分辨率设备上显示图像时用于改善视觉质量的一种广为人知的技术。
它基于人的视觉特性。
看看下图,尝试猜测一下它表达的意思。
这是用反走样发生绘制的单词。
根据Kotelniko v-Shanno n定理,图像的最大频率远高于S hanno n极限。
现在来看看正常大小的同一幅图,能够很容易地认出“stereo”这个词。
然而,这两幅图是完全相同的。
第一幅仅仅是第二副的放大版本。
这个特性允许在累积经验的基础上重新构建缺失的信息。
反走样并没有让你看得更清楚,只是让你的大脑更好得运转并重构丢失的信息。
反走样的成果显而易见。
如它使得我们可以绘制出更加详尽的地图。
但是关键并不是反走样本身,而是可以利用子像素精度的技术绘制基元。
这对于线的视觉厚度尤其重要。
首先,如果利用了子像素精度,即使只使用简单的Bresenha m线插值法也可以获得比较好的效果。
下图显示了使用简单的B resen ham插值后放大的结果。
如图中的(2)和(3),细的黑线就是需要插值的线。
如果利用子像素精度,虽然线的起点和终点落在同一个像元中,但是会显示两种不同的像素集合。
而且两条线有完全不同的切线,这是非常重要的。
如果只使用经典的Bresenha m插值算法而不考虑子像素精度,结果都会如图中的(1)。
这在用短线段近似表示曲线时尤为重要。
但是如果同时考虑反走样和子像素精度,能够获得更好的效果。
观察下图的不同。
这三个螺旋形都是由短的直线段近似表示的。
左边的那幅图采用的是规则的整数Bresen ham,坐标与像素匹配(使用Winw ows GDI的Mo veTo和LineT o函数也可以得到类似的结果)。
中间的采用了改进的整数Bresenham,精度为1/256像素。
而右边的图同样采用1/256像素的精度,但同时使用了反走样技术。
注意,将线段中的真实字像素定位的能力非常重要。
如果对规则像素坐标进行反走样,螺旋形会看起来更平滑,但仍然同左边的图一样难看。
子像素精度甚至比控制线的视觉厚度更加重要。
只有当有了好的反走样算法,精确性才能成为可能。
另一方面,如果只用一个像素的离散性来确定线的宽度,反走样就没有什么意义了。
反走样和子像素精度总是配合使用。
现在显示器的分辨率最多是120DPI,而利用子像素精度可以达到300DPI。
下图显示的线的宽度从0.3像素开始,并以0.3像素递增。
用反走样和子像素精度渲染的线
下面是另外两个用字像素精度渲染的例子。
用反走样和子像素精度渲染的圆形
可爱的狮子
注意,那些小狮子虽然丢失了一些细节,但还是保持了与大狮子的一致性。
1.2.基本概念
1.2.1.库的设计
AGG被设计成为由共同理念结合的一系列松散耦合的算法和类模板,因此所有的组件都能很容易的组合起来。
同时,以模板为基础的设计使得不需要修改已有代码就能替换图形库中的任何部分。
AGG的设计也考虑了扩展性和灵活性。
我想创建一个允许我(和任何人)很容易得添加新算法的工具。
AGG没有指定任何的使用形式,可以自由得使用它的任何部分。
然而,AGG常被当作一个在内存中渲染图像的工具。
这并不十分准确,却是一个学习A GG的不错的着手点。
教程中介绍A G G的使用是从处理帧缓冲和像素的简单功能的。
然后,将逐渐了解到怎样提取库中的不同部分和怎样单独的使用它们。
记住栅格图像常常并不是要获取的唯一结果,你可能想要打印高质量的图形,在这种情况下,可以简单地把库中的矢量部分与一些类似于Windows GDI的AP I结合起来,从而形成一个统一的对外接口。
如果API能够用non-zero 和e ven-odd的填充规则渲染多个多边形,那么你要做的就只是将A G G合并到应用程序中。
例如,Window s 的API PolyPo lygon完全符合这些要求,除了像梯度填充、Gourau d着色、图形变换等高级功能。
或者,换一种方式,可以使用所有A GG的算法生成高分辨率的像素图像,然后把结果作为一个像素地图发送到打印机。
下面是一个A G G渲染管道的典型结构。
渲染管道的典型结构
请注意在“Vertex Source”和“Screen Output”之间的组件并不是必要的,取决于应用的需要。
例如,你可以使用自己的基于W indow s API的光栅化器。
这种情况下不需要AGG的光栅化器和渲染器。
另外,如果只要绘制线,可以使用AG G的外形线光栅化器,这样虽然有一定的限制,但运行得更快。
还有许多其他的可能性。
◆Vertex Source通过“MoveTo”, “LineTo”等命令产生多边形或者多段线作为一系列连
续的二维顶点的对象。
它可以是一个容器或按需产生顶点的其它对象。
◆Coordi nateconversionpipeline由许多的坐标转换器组成。
它通常作用于由浮点数
(双精度)表达的矢量数据(X,Y)。
例如,它可以包括仿射变换器、轮廓生成器
、标记生成器(如箭头/箭尾)、虚线生成器等。
管道有分支并且可以你拥有任意
数量的不同管道,也可以自己写转换器并将其加入管道中。
◆Scanline Raster izer把矢量数据转换成许多的水平扫描线。
扫描线通常(非必须)以
“covera ge”值携带反走样的信息。
◆Render ers重复进行扫描线的渲染。
最简单的例子是固体填充,渲染器只向扫描线
添加一种颜色并将结果写入渲染缓冲区。
其它复杂的渲染器可以产生彩色的渲染
结果,如梯度填充、Gourau d着色、图形变换、样式等等。
Render ing Buffer是一个内存中的缓冲区,用于之后的显示。
它通常(非必须)包含适合显示系统的像素格式。
例如,24位的B-G-R格式、32位的B-G-R-A格式,Window s的15位R-G-B-555格式。
但是一般来说,如果自定义支持像素格式或颜色空间的底层类,那么就不存在任何限制。
1.2.2.颜色,颜色空间和像素格式
AGG中颜色仅仅在渲染器中出现,即将数据放入渲染缓冲区的过程。
通常不存在一般意义的“color”结构体和类,A GG总是对具体的颜色空间进行操作。
颜色空间的种类很多,包括RGB,,HSV,CMYK等,并且所有的颜色空间都有一定的限制。
例如,RGB空间仅仅是人眼可见颜色的一个很小的子集。
如果看完整的C IE色度图,会发现RGB三角仅仅是其中的一小部分。
CIE色度图和RGB域
换句话说,在现实世界中有很多颜色是不能用R G B,CMYK,HSV等颜色空间来表示的。
除了自然中存在的,任何一种颜色空间都是有限制的。
因此,为了避免将来可能出现的限制,决定不引入类似于“color”的对象。
相反,AGG 中存在对具体颜色空间进行操作的对象。
目前对最普遍的RGB颜色空间(严格的说是R G B+Alpha)进行操作的对象有agg::gba 和agg::rgba8。
RGB 颜色空间用于许多像素格式,如24位RG B或者不同排列顺序的32位RG BA。
虽然AGG没有明确支持其它任何颜色空间,但是至少有增加的可能性。
这意味着所有依赖“color”类型的类和功能模板都被“ColorT”参数化。
1.2.3.坐标单位
AGG主要使用输出设备的坐标,在屏幕上表现为像素。
但是与其它库和APIs不同,AGG 开始支持子像素精度。
这意味着在小数部分会产生影响的地方,坐标由双精度的浮点数来表达。
为了不限制使用者的自由,AGG没有嵌入从世界坐标到屏幕坐标的转换机制。
所以,当不同的应用程序需要不同的转换方法时,何时何处作转换就显得尤为重要。
AGG仅提供了从视口到设备的进行转换的转换器。
并且必须将其加到管道的合适位置。
同时也可以添加自定义的简单类来支持以毫米、英寸或其它任何物理单位为单位进行的转换。
光栅化器在内部使用24.8位格式的整数坐标,整数部分24位,小数部分8位,即所有的内部坐标都被乘以256。
如果想要在不能处理浮点数的嵌入系统中使用A G G,尽管不能使用浮点坐标管道,但仍然可以采用带整数接口的光栅化器。
1.2.4.AGG 创建和编码注意事项
Anti-GrainGeomet ry 没有丰富和自动的创建环境。
AGG主张“仅编译和运行”(“It just compil es and works”)。
它没有其他的安装包。
假如从自动配置并工作的m akin g 的应用程序角度来看它可能不是非常好的,但是所有你需要做的仅是将AGG源文件当作是你自己的文件一样,添加到你的分发包中。
这种方法的好处是,你不会有任何配置文件和无止境的#ifdef…#elif…#endif宏定义的问题。
这可能是因为A GG 绝对有最小的外部依赖。
对于Unix 有最简单的M akefiles来创建.a 库,对于Windows已经有个创建好的库。
这实际上使得调试过程更便利。
事实上,几乎所有都真实地实现并调用了其算法。
这也进一步地稳定了库,因为所有算法都在操作的情况下通过了深度测试。
注意
如果想要在W indow s Visual C++环境下使用 A GG,请注意其没有使用“stdafx.h”文件。
它是Micros oft特殊的并且不是C/C++标准库的一部分,但是Micros oft强迫使用它。
为了成功地在Visua l C++工程中使用 A GG ,不要忘记关掉所有AGG源文件的“预编译头文件”(“Precom piled Header s”)的选项。
此外,如果将AGG和静态的MF C连接,在连接的时候或许需要重复新建ne w 和销毁delete操作。
这不是因为A G G,而是因为MFC。
在调用其他C++代码而不包含s tdaf x.h的时候,都会有同样的问题new/delete,需要调用。
为了解决这个情况,请参考Micros oft recomm endat ions或者在Google上搜索“148652 LNK2005”.
如上所述, AGG 积极使用C++ 的模板机制。
但是,它仅使用知名的并已被验证过的语句。
良好的兼容性是首要追求。
C++的领袖们可能惊讶,例如AGG竟然不使用S TL。
它不是故意的,为了避免额外的依赖,使用STL容器的必要性则微乎其微。
当然,它不会阻碍你在高层使用S TL或者其它流行的工具。
AGG被设计成绝对与已存在的C++库,工具和技术潜在冲突最小。
1.2.5.关于这个手册
如上所说,AGG提供一些不同层次上的功能,因此你可以不同的方式使用它。
例如,你可能想使用没有扫描线渲染器的A G G光栅。
但为了连贯性和渐进性,我们将从头开始,并通过例子来描述所有功能。
该方法可能比快速开头“QuickStart”要更慢,但是它将使你理解设计的理念。
这是非常有用的因为in i将会知道如何自己替代特定的类和算法,或者如何
扩展库。
特别是,扫描线是没有平台依赖的,但不是最快的。
你可能想自己写,优化,但是请面向相同的硬件体系架构,诸如SSE2。
2.基本渲染器
从简单的控制台应用程序开始
2.1.渲染缓冲区
我们从在内存中创建一桢缓冲,并将其以最简单的光栅格式写到文件中,也就是PPM (Portab le PixelMap)。
虽然,它本身并不支持Micr osoft Windows,但是有很多阅读器和转换器都是基于它实现的,例如Irfa nView( www.irfanv )。
所有AGG控制台例子是用P6 256格式,也就是RGB,每个通道占一字节。
假设我们操作的RGB-缓冲在内存中的组织如下所示:
2.1.1.第一个示例也是最简单的一个
这是第一个例子,在agg2/tutori al/t01_re nderi ng_bu ffer.cpp中
#includ e <stdio.h>
#includ e <string.h>
#includ e "agg_re nderi ng_bu ffer.h"
enum
{
frame_width = 320,
frame_heigh t = 200
};
// Writin g the buffer to a .PPM file, assumi ng it has
// RGB-struct ure, one byte per colorcompon ent
//--------------------------------------------------
bool write_ppm(constunsign ed char* buf,
unsign ed width,
unsign ed height,
constchar* file_n ame)
{
FILE* fd = fopen(file_n ame, "wb");
if(fd)
{
fprint f(fd, "P6 %d %d 255 ", width, height);
fwrite(buf, 1, width* height * 3, fd);
fclose(fd);
return true;
}
return false;
}
// Draw a blackframearound the render ing buffer, assumi ng it has
// RGB-struct ure, one byte per colorcompon ent
//--------------------------------------------------
void draw_b lack_frame(agg::render ing_b uffer& rbuf)
{
unsign ed i;
for(i = 0; i < rbuf.height(); ++i)
{
unsign ed char* p = rbuf.row_pt r(i);
*p++ = 0; *p++ = 0; *p++ = 0;
p += (rbuf.width() - 2) * 3;
*p++ = 0; *p++ = 0; *p++ = 0;
}
memset(rbuf.row_pt r(0), 0, rbuf.width() * 3);
memset(rbuf.row_pt r(rbuf.height() - 1), 0, rbuf.width() * 3);
}
int main()
{
// In the firstexampl e we do the follow ing:
//--------------------
// Alloca te the buffer.
// Clearthe buffer, for now "manual ly"
// Create the render ing buffer object
// Do someth ing simple, draw a diagon al line
// Writethe buffer to agg_te st.ppm
// Free memory
unsign ed char* buffer = new unsign ed char[frame_width * frame_heigh t * 3]; memset(buffer, 255, frame_width * frame_heigh t * 3);
agg::render ing_b uffer rbuf(buffer,
frame_width,
frame_heigh t,
frame_width * 3);
unsign ed i;
for(i = 0; i < rbuf.height()/2; ++i)
{
// Get the pointe r to the beginn ing of the i-th row (Y-coordi nate)
// and shiftit to the i-th positi on, that is, X-coordi nate.
//---------------
unsign ed char* ptr = rbuf.row_pt r(i) + i * 3;
// PutPix el, very sophis ticat ed, huh? :)
//-------------
*ptr++ = 127; // R
*ptr++ = 200; // G
*ptr++ = 98; // B
}
draw_b lack_frame(rbuf);
write_ppm(buffer, frame_width, frame_heigh t, "agg_te st.ppm");
delete [] buffer;
return 0;
}
在这个例子中,你甚至都不需要连接任何A GG文件,你只需要在你编译器的命令行中说明A GG的includ e目录。
当你编译并运行这个程序,你可以看到下面的结果。
这里几乎都是手动编码的。
我们只使用了一个类“redering_bu ffer”。
这个类不知道内存中像素格式,它只是保存了指向每一行的指针数组。
你应当分配和销毁缓冲区的实际内存。
可以使用一些可行的机制来做,比如:系统API函数,简单内存分配,或者静态定义的数组。
在上面这个例子里我们分配宽*高*3字节的内存,因为每个像素使用3个字节。
行数据并不需要内存对齐,但是如果使用A PI,他们仍拥有很好的性能。
2.1.2.rendering_b uffer类
包含文件: agg_re nderi ng_bu ffer.h
渲染缓冲区类保存每一行的指针,这就是它主要做的。
这看上去不像个伟大的实现,但是尝试保持可读性。
它的接口和功能都是非常简单的。
这是一个类模板的定义类型(typedef)用法。
模板类row_pt r_cac he的类型定义:
typede f row_pt r_cac he<int8u> render ing_b uffer;
row_pt r_cac he类的接口和函数是:
templa te<classT> classrow_pt r_cac he
{
public:
row_pt r_cac he();
row_pt r_cac he(T* buf, unsign ed width, unsign ed height, int stride);
void attach(T* buf, unsign ed width, unsign ed height, int stride);
T* buf();
constT* buf() const;
unsign ed width() const;
unsign ed height() const;
int stride() const;
unsign ed stride_abs() const;
T* row_pt r(int, int y, unsign ed)
T* row_pt r(int y);
constT* row_pt r(int y) const;
row_da ta row (int y) const;
T const* const* rows() const;
templa te<classRenBuf> void copy_f rom(constRenBuf& src);
void clear(T value)
};
源码: row_pt r_cac he
这个类没有任何声明或者验证码,因此,在使用前应当将实际内存缓冲区合适地绑定
到对象。
既可以在构造函数又可以通过att ach()函数来完成。
它的参数有:buf —内存缓冲区的指针
width—图像像素(pixels)的宽度. 渲染缓冲区不晓得像素格式以及内存中一个像素的大小。
这个值通过数据成员m_width简单的存储,并通过wid th()函数返回。
height—缓冲区像素(pixels)的高度(行数)
stride—行间的跨度"stride"(大步)是指T类型里的对象数。
Classrender ing_b uffer类被定义类型“typede fed”为row_pt r_cac he<int8u>,所以,这个值是以字节为单位的。
跨度Stride决定了内存中一行的物理宽度。
如果这个值是负数,那么Y轴的方向是反向的, 也就是说,Y==0 将指向缓冲区的最后一行,Y==height-1 —到第一个。
跨度值的绝对值也是很重要的,因为它允许操作内存中那些行对齐的缓冲区(例如,像在Wind ows BMP)。
此外,这个参数允许就像操作完整的缓冲区一样去操作缓冲区中一些矩形区域,函数atta ch()更换缓冲区或者参数。
它为row-p ointe rs 缓冲区重新分配内存, 因此,可以随时调用。
如果新的高度比以前任何一次绑定的都大,那么将会重新分配内存。
创建的代价就是初始化变量(设置为0),attach()的代价是分配sizeof(ptr) * height个字节的内存以及初始化行的指针。
最经常使用的函数是row_ptr(y),简单的返回第y th行起始的指针,考虑Y-轴的方向。
重要!
渲染器缓冲不执行任何裁剪或者边界校验,这是高层次类的任务。
buf(), width(), height(), stride(), stride_abs()的存取应当是显式的。
copy_f rom()函数拷贝另一个缓冲区的内容到这个“this” 缓冲区. 这个函数是安全的,如果宽wid th或者高heigh t是不同的,它将会拷贝最大可能的区域。
基本上,它用来拷贝相同大小的渲染缓冲区。
2.1.
3.例子的两个修改
第一个,在创建渲染缓冲区对象的时候将跨度设置为负t he
stride: agg::render ing_b uffer rbuf(buffer,
frame_width,
frame_heigh t,
-frame_width * 3);
结果是:
第二,允许尝试绑定到一些已分配的缓冲区的局部。
这个修改实际上两次绑定到同一个缓冲区,第一次,到整个帧,然后到它的局部,有20像素的间隙。
int main()
{
unsign ed char* buffer = new unsign ed char[frame_width * frame_heigh t * 3];
memset(buffer, 255, frame_width * frame_heigh t * 3);
agg::render ing_b uffer rbuf(buffer,
frame_width,
frame_heigh t,
frame_width * 3);
// Draw the outerblackframe
//------------------------
draw_b lack_frame(rbuf);
// Attach to the part of the buffer,
// with 20 pixelmargin s at each side.
rbuf.attach(buffer +
frame_width * 3 * 20 + // initia l Y-offset
3 * 20, // initia l X-offset
frame_width - 40,
frame_heigh t - 40,
frame_width * 3 // Note that the stride
// remain s the same
);
// Draw a diagon al line
//------------------------
unsign ed i;
for(i = 0; i < rbuf.height()/2; ++i)
{
// Get the pointe r to the beginn ing of the i-th row (Y-coordi nate) // and shiftit to the i-th positi on, that is, X-coordi nate.
//---------------
unsign ed char* ptr = rbuf.row_pt r(i) + i * 3;
// PutPix el, very sophis ticat ed, huh? :)
//-------------
*ptr++ = 127; // R
*ptr++ = 200; // G
*ptr++ = 98; // B
}
// Draw the innerblackframe
//------------------------
draw_b lack_frame(rbuf);
// Writeto a file
//------------------------
write_ppm(buffer, frame_width, frame_heigh t, "agg_te st.ppm");
delete [] buffer;
return 0;
}
结果:
最后一个修改是:
// Attach to the part of the buffer,
// with 20 pixelmargin s at each side and negati ve 'stride'
rbuf.attach(buffer +
frame_width * 3 * 20 + // initia l Y-offset
3 * 20, // initia l X-offset
frame_width - 40,
frame_heigh t - 40,
-frame_width * 3 // Negate the stride
);
结果:
在最后一个例子,我们仅仅将跨度的值设置为负,像前面的例子一样保持缓冲区的起始地址指针。
注意
write_ppm()函数将像素图写到文件中。
从此以后它将省略,但是当需要的时候将在agg2/tutori al的目录下的源码中复制出来。
2.2.像素格式渲染器
首先,我们创建另一个更大众化的例子,在agg2/tutori al/t02_pi xel_f ormat s中:
像素格式(pixelformat)类定义它们各自的色彩空间和色彩类型,例如:
typede f rgba8color_type;
pixfmt_gray8_nnn是gray8.这个机制允许书写自己的像素和色彩格式,例如,HSV, CMYK,等等。
其余的库将以和已实现的像素格式一样的方式,正确的新的像素格式协同工作。
注意
不搞混像素格式渲染器所操作的颜色类型和缓冲区描述的色彩空间是非常重要的。
例如,假设使用RG B缓冲区来操作CMY K色彩空间(你仅是书写一个简单的转换函数,从一些CMY K 结构创建一个rgba8对象)。
但是它将仅仅是一个模仿,而且你会有颜色损失,因为在CMY K 中有些颜色不能用R G B显示。
为了充分使用有些色彩空间的性能,需要为特殊色彩空间写一个可在其中工作的像素格式渲染器,而不需要任何中间转换。
2.2.1.创新
重要!
像素格式(pixelformat)类不执行任何裁剪操作,这意味着直接操作这些类通常不是安全的。
裁剪是高级类的功能。
这样设计的原因非常简单—它必须尽可能的简单来书写你自己的像素格式(pixelformat)类。
. Therecan be many of them whilethe clippi ng code remain s exactl y the same.
pixel_forma ts_rg b24(render ing_b uffer& rb);
像素格式渲染器的构造函数expe cts 一个指向已经创建的并全面初始化的render ing_b uffer的引用。
创建的代价是最小的,它基本上纸初始化一个指针。
2.2.2.成员函数
像素格式渲染器必须公开下面的功能(接口)。
unsign ed width() const{ return m_rbuf->width(); }
unsign ed height() const{ return m_rbuf->height(); }
Return s widthand height of the buffer in pixels.
color_typepixel(int x, int y);
以坐标形式(x,y)返回像素的颜色值。
void copy_p ixel(int x, int y, constcolor_type& c);
复制颜色c的一个像素到缓冲区。
RGB像素格式不考虑在rgba8类型中存在的透明alp ha 通道,RGBA —简单地和R, G, 和B一起拷贝透明alph a值到缓冲区。
void blend_pixel(int x, int y, constcolor_type& c, int8ucover);
将颜色c的一个像素和缓冲区中的一个像素进行混合。
现在是解释混合blending的时候了。
混合是反走样的一个关键特征。
在RGBA色彩空间,使用rgba8结构来表示颜色。
这个结构已经具有数据成员int8ua;这就是透明a lpha通道。
但是在这个函数中,我们还可以看到cove r参数用来指定deterine s像素的覆盖值,等等。
即像素中的部分是被一个多边形覆盖的。
实际上,你可以解释成第二个"Alpha"("Beta"?)。
这么做有两个原因。
首先,色彩类型并不是要必须包括透明alpha值。
另外,就算色彩类型具有透明a lpha域,它的类型也不需要非得和反走样算法中使用的兼容。
假设用四个浮点数[0…1]表示“Hi-End” RGBA色彩空间。
它的透明值a lpha也是浮点数的—在这个情况下一个字节是很不适合通常的混合,但是却很合适反走样。
因此,覆盖值仅是特定的用于反走样目的一个统一的辅助alph a值。
全局的,它定义成cover_type,但是在已描述的光栅中直接使用int8u类型。
这不是故意的,因为如果增加cover_type的功能是必要的话,所有已经存在的像素格式光栅就和c over_type不兼容了。
它们实际上将会不兼容,事实上,8-位覆盖值加8-位透明值是最大的,这与颜色混合时的32-位中间值符合。
在16-位的情况下,我们将使用64-位的整数,这对32-位的系统平台而言实在是代价高昂。
void copy_h line(int x, int y, unsign ed len, constcolor_type& c);
void copy_v line(int x, int y, unsign ed len, constcolor_type& c);
以某个颜色画一条水平的或者垂直的线。
void blend_hline(int x, int y, unsign ed len, constcolor_type& c, int8ucover);
void blend_vline(int x, int y, unsign ed len, constcolor_type& c, int8ucover);
混合某个颜色的一条水平的或者垂直的线,分离拷贝“copy”和混合“blend”版本的原因是为了实现。
当然,这可能是一个额外的if/else语句(在“blend”版本中),但是尽管如此,在不同的散点图应用程序中使用hline/vline来绘制一些小的标识的时候这还是比较关键critica l的。
void blend_solid_hspa n(int x, int y, unsign ed len,
constcolor_type& c, constint8u* covers);
void blend_solid_vspa n(int x, int y, unsign ed len,
constcolor_type& c, constint8u* covers);
混合一个水平的或者一个垂直的纯色的跨度。
跨度和 hline/vline差不多,但是有一个覆盖值的数组。
这些函数被用来渲染纯色的反走样多边形。
void blend_color_hspa n(int x, int y, unsign ed len,
constcolor_type* colors, constint8u* covers);
void blend_color_vspa n(int x, int y, unsign ed len,
constcolor_type* colors, constint8u* covers);
混合一个水平的或者垂直的颜色跨度。
该函数被用在不同跨度产生器中,比如,渐变,图像,图案,高洛德过滤等。
他们接受一个颜色数组,其类型必须与已使用的像素格式兼容。
例如,所有已经存在的RGB像素格式与r gba8类型兼容。
参数cove rs是bl end_s olid_hspan中的一个覆盖值的数组。
它是可选的并且可以为0.
另一个例子是绘制太阳光谱,rgba类, 以双精度doubles的形式保存四个组成,具有静态的f rom_w avele ngth方法和各自的构造函数,rgba8类可以从rgba中创建(这在AGG中是很普通的一个策略,一些颜色类型可以从rgba类型构造). 我们将这么使用.
#includ e <stdio.h>
#includ e <string.h>
#includ e "agg_pi xfmt_rgb24.h"
enum
{
frame_width = 320,
frame_heigh t = 200
};
// [...write_ppm is skippe d...]
int main()
{
//--------------------
// Alloca te the buffer.
// Clearthe buffer, for now "manual ly"
// Create the render ing buffer object
// Create the PixelFormat render er
// Create one line (span) of type rgba8.
// Fill the buffer usingblend_color_span
// Writethe buffer to agg_te st.ppm
// Free memory。