资源描述
AGG参考手册
1. 前言
1.1. 简介
1.1.1. ……
Anti-Grain Geometry (AGG)是一个用标准的平台无关的C++开发的通用图形工具包。在把高质量二维图形作为关键的计算机程序中,它可以应用被应用于许多方面。例如,AGG可以用于渲染二维地图。AGG仅仅使用C++和标准C的函数,如memcpy,sin,cos,sqrt等。基本的算法甚至没有使用C++的标准模板库。因此,AGG能够在大量的应用程序中使用,包括嵌入式系统。
另一方面,AGG允许使用者替换图形库的任何部分,比如当它不能满足性能的要求时进行库的替换。使用者也可以根据需要添加其它的颜色空间。这一切的实现都是因为AGG广泛采用了C++的模板机制。
AGG不是一个结构紧密的图形库,且不容易使用。我认为AGG是一个“创建其它工具的工具”。这意味着AGG中没有“Graphics”对象或其它类似的结构,它包含了许多组织松散、能组合或是单独使用的算法。这些算法都有定义良好的接口,并且算法之间隐式或显式的依赖关系尽可能最小。
1.1.2. ……
大部分图形库都有一个包含成百上千方法的单独类,如GDI+中的“Graphics”。这个对象也可以隐式存在,如OpenGL。总之,所有常用的图形工具包,包括Java2D,DispalyPDF,SVG以及其它优秀的图形工具包都显式或隐式的含有这个类。这种做法简单而且非常适用于一些场合,但是去一直受到限制。它在简单的场合中性能很好,但至少我仍未遇到可以完全满足所有需求的图形库。此外,所有此种类型的库或标准都过于庞大。大部分功能从未被使用,而一些简单的操作却不能实现。图形引擎(或库)是一兆字节来计算的。如果使用最先进的SVG浏览器,它只在显示最简单的基元时表现良好。只要试图使用一些高级操作,如用不同的图形过滤器与SVG交互,将会发生内存泄露,甚至崩溃。这并不是因为它有不良设计,而是因为采取了极端复杂的设计。这种设计本身成为了不可能完成的任务,不能被人感知,,就像不能感知无穷大一样。
1.1.3. 建议
AGG主要的目标是要打破上述的传统,展现其稳定、轻巧、灵活、自由的优点。它的基本概念开始似乎并不遵循惯例,非常接近于STL,但还是存在着很大的区别。STL是一个通用的C++工具,而AGG是C++图形库。STL作为一个方便的工具包直接在应用程序中使用,但我并不建议以同样的方式使用AGG。比较好的方法是针对需要处理的问题,对AGG进行轻量级的、面向问题的封装后再使用。这与GDI+又有什么不同呢?首先,可以完全控制封装后的AGG。AGG只是提供了一系列基本的算法和灵活的设计,使得算法间显式或隐式的联系都最小。使用者可以只定义接口、转换管道和输出格式,甚至可以模拟任何已经存在的图形接口的一部分。例如,使用AGG光栅化器来在屏幕上显示图形并直接调用Windows GDI来打印,并合并为一个单独的API。不信吗?下图是GDI+和AGG的渲染质量的比较。
但是最重要的是,如果设计足够灵活,程序将是完全可移植的。AGG还是一个可以将不同的输出合并为一个统一的API的工具。另外,在基于Web的应用程序中,可以使用AGG在服务器端生成栅格图像。而且AGG是完全跨平台的。
1.1.4. 反走样和子像素精度
反走样是在低分辨率设备上显示图像时用于改善视觉质量的一种广为人知的技术。它基于人的视觉特性。看看下图,尝试猜测一下它表达的意思。
这是用反走样发生绘制的单词。根据Kotelnikov-Shannon定理,图像的最大频率远高于Shannon极限。
现在来看看正常大小的同一幅图,能够很容易地认出“stereo”这个词。然而,这两幅图是完全相同的。第一幅仅仅是第二副的放大版本。这个特性允许在累积经验的基础上重新构建缺失的信息。反走样并没有让你看得更清楚,只是让你的大脑更好得运转并重构丢失的信息。反走样的成果显而易见。如它使得我们可以绘制出更加详尽的地图。
但是关键并不是反走样本身,而是可以利用子像素精度的技术绘制基元。这对于线的视觉厚度尤其重要。首先,如果利用了子像素精度,即使只使用简单的Bresenham线插值法也可以获得比较好的效果。下图显示了使用简单的Bresenham插值后放大的结果。
如图中的(2)和(3),细的黑线就是需要插值的线。如果利用子像素精度,虽然线的起点和终点落在同一个像元中,但是会显示两种不同的像素集合。而且两条线有完全不同的切线,这是非常重要的。如果只使用经典的Bresenham插值算法而不考虑子像素精度,结果都会如图中的(1)。这在用短线段近似表示曲线时尤为重要。但是如果同时考虑反走样和子像素精度,能够获得更好的效果。观察下图的不同。
这三个螺旋形都是由短的直线段近似表示的。左边的那幅图采用的是规则的整数Bresenham,坐标与像素匹配(使用Winwows GDI的MoveTo和LineTo函数也可以得到类似的结果)。中间的采用了改进的整数Bresenham,精度为1/256像素。而右边的图同样采用1/256像素的精度,但同时使用了反走样技术。注意,将线段中的真实字像素定位的能力非常重要。如果对规则像素坐标进行反走样,螺旋形会看起来更平滑,但仍然同左边的图一样难看。
子像素精度甚至比控制线的视觉厚度更加重要。只有当有了好的反走样算法,精确性才能成为可能。另一方面,如果只用一个像素的离散性来确定线的宽度,反走样就没有什么意义了。反走样和子像素精度总是配合使用。
现在显示器的分辨率最多是120DPI,而利用子像素精度可以达到300DPI。下图显示的线的宽度从0.3像素开始,并以0.3像素递增。
用反走样和子像素精度渲染的线
下面是另外两个用字像素精度渲染的例子。
用反走样和子像素精度渲染的圆形
可爱的狮子
注意,那些小狮子虽然丢失了一些细节,但还是保持了与大狮子的一致性。
1.2. 基本概念
1.2.1. 库的设计
AGG被设计成为由共同理念结合的一系列松散耦合的算法和类模板,因此所有的组件都能很容易的组合起来。同时,以模板为基础的设计使得不需要修改已有代码就能替换图形库中的任何部分。
AGG的设计也考虑了扩展性和灵活性。我想创建一个允许我(和任何人)很容易得添加新算法的工具。
AGG没有指定任何的使用形式,可以自由得使用它的任何部分。然而,AGG常被当作一个在内存中渲染图像的工具。这并不十分准确,却是一个学习AGG的不错的着手点。教程中介绍AGG的使用是从处理帧缓冲和像素的简单功能的。然后,将逐渐了解到怎样提取库中的不同部分和怎样单独的使用它们。记住栅格图像常常并不是要获取的唯一结果,你可能想要打印高质量的图形,在这种情况下,可以简单地把库中的矢量部分与一些类似于Windows GDI的API结合起来,从而形成一个统一的对外接口。如果API能够用non-zero和even-odd的填充规则渲染多个多边形,那么你要做的就只是将AGG合并到应用程序中。例如,Windows 的API PolyPolygon完全符合这些要求,除了像梯度填充、Gouraud着色、图形变换等高级功能。或者,换一种方式,可以使用所有AGG的算法生成高分辨率的像素图像,然后把结果作为一个像素地图发送到打印机。
下面是一个AGG渲染管道的典型结构。
渲染管道的典型结构
请注意在“Vertex Source”和“Screen Output”之间的组件并不是必要的,取决于应用的需要。例如,你可以使用自己的基于Windows API的光栅化器。这种情况下不需要AGG的光栅化器和渲染器。另外,如果只要绘制线,可以使用AGG的外形线光栅化器,这样虽然有一定的限制,但运行得更快。还有许多其他的可能性。
u Vertex Source通过“MoveTo”, “LineTo”等命令产生多边形或者多段线作为一系列连续的二维顶点的对象。它可以是一个容器或按需产生顶点的其它对象。
u Coordinate conversion pipeline由许多的坐标转换器组成。它通常作用于由浮点数(双精度)表达的矢量数据(X,Y)。例如,它可以包括仿射变换器、轮廓生成器、标记生成器(如箭头/箭尾)、虚线生成器等。管道有分支并且可以你拥有任意数量的不同管道,也可以自己写转换器并将其加入管道中。
u Scanline Rasterizer把矢量数据转换成许多的水平扫描线。扫描线通常(非必须)以“coverage”值携带反走样的信息。
u Renderers重复进行扫描线的渲染。最简单的例子是固体填充,渲染器只向扫描线添加一种颜色并将结果写入渲染缓冲区。其它复杂的渲染器可以产生彩色的渲染结果,如梯度填充、Gouraud着色、图形变换、样式等等。
Rendering Buffer是一个内存中的缓冲区,用于之后的显示。它通常(非必须)包含适合显示系统的像素格式。例如,24位的B-G-R格式、32位的B-G-R-A格式,Windows的15 位R-G-B-555格式。但是一般来说,如果自定义支持像素格式或颜色空间的底层类,那么就不存在任何限制。
1.2.2. 颜色,颜色空间和像素格式
AGG中颜色仅仅在渲染器中出现,即将数据放入渲染缓冲区的过程。通常不存在一般意义的“color”结构体和类,AGG总是对具体的颜色空间进行操作。颜色空间的种类很多,包括RGB,,HSV,CMYK等,并且所有的颜色空间都有一定的限制。例如,RGB空间仅仅是人眼可见颜色的一个很小的子集。如果看完整的CIE色度图,会发现RGB三角仅仅是其中的一小部分。
CIE色度图和RGB域
换句话说,在现实世界中有很多颜色是不能用RGB,CMYK,HSV等颜色空间来表示的。除了自然中存在的,任何一种颜色空间都是有限制的。因此,为了避免将来可能出现的限制,决定不引入类似于“color”的对象。相反,AGG 中存在对具体颜色空间进行操作的对象。目前对最普遍的RGB颜色空间(严格的说是RGB+Alpha)进行操作的对象有agg::gba 和agg::rgba8。RGB 颜色空间用于许多像素格式,如24位RGB或者不同排列顺序的32位RGBA。虽然AGG没有明确支持其它任何颜色空间,但是至少有增加的可能性。这意味着所有依赖“color”类型的类和功能模板都被“ColorT”参数化。
1.2.3. 坐标单位
AGG主要使用输出设备的坐标,在屏幕上表现为像素。但是与其它库和APIs不同,AGG开始支持子像素精度。这意味着在小数部分会产生影响的地方,坐标由双精度的浮点数来表达。为了不限制使用者的自由,AGG没有嵌入从世界坐标到屏幕坐标的转换机制。所以,当不同的应用程序需要不同的转换方法时,何时何处作转换就显得尤为重要。AGG仅提供了从视口到设备的进行转换的转换器。并且必须将其加到管道的合适位置。同时也可以添加自定义的简单类来支持以毫米、英寸或其它任何物理单位为单位进行的转换。
光栅化器在内部使用24.8位格式的整数坐标,整数部分24位,小数部分8位,即所有的内部坐标都被乘以256。如果想要在不能处理浮点数的嵌入系统中使用AGG,尽管不能使用浮点坐标管道,但仍然可以采用带整数接口的光栅化器。
1.2.4. AGG 创建和编码注意事项
Anti-Grain Geometry 没有丰富和自动的创建环境。AGG主张“仅编译和运行”(“It just compiles and works”)。它没有其他的安装包。假如从自动配置并工作的making 的应用程序角度来看它可能不是非常好的,但是所有你需要做的仅是将AGG源文件当作是你自己的文件一样,添加到你的分发包中。这种方法的好处是,你不会有任何配置文件和无止境的#ifdef…#elif…#endif 宏定义的问题。这可能是因为AGG 绝对有最小的外部依赖。对于Unix有最简单的Makefiles 来创建.a 库,对于Windows已经有个创建好的库。这实际上使得调试过程更便利。事实上,几乎所有都真实地实现并调用了其算法。这也进一步地稳定了库,因为所有算法都在操作的情况下通过了深度测试。
注意
如果想要在Windows Visual C++环境下使用 AGG,请注意其没有使用 “stdafx.h”文件。它是Microsoft特殊的并且不是C/C++标准库的一部分,但是Microsoft强迫使用它。为了成功地在Visual C++工程中使用 AGG ,不要忘记关掉所有AGG 源文件的“预编译头文件” (“Precompiled Headers”)的选项 。此外,如果将AGG 和静态的MFC连接,在连接的时候或许需要重复新建new 和销毁delete操作。 这不是因为AGG,而是因为MFC。在调用其他C++代码而不包含stdafx.h的时候,都会有同样的问题new/delete,需要调用。为了解决这个情况,请参考Microsoft recommendations 或者在Google上搜索“148652 LNK2005”.
如上所述, AGG 积极使用 C++ 的模板机制。但是,它仅使用知名的并已被验证过的语句。良好的兼容性是首要追求。C++的领袖们可能惊讶,例如AGG 竟然不使用STL。它不是故意的,为了避免额外的依赖,使用STL 容器的必要性则微乎其微。当然,它不会阻碍你在高层使用STL或者其它流行的工具。 AGG被设计成绝对与已存在的C++库,工具和技术潜在冲突最小。
1.2.5. 关于这个手册
如上所说,AGG提供一些不同层次上的功能,因此你可以不同的方式使用它。例如,你可能想使用没有扫描线渲染器的AGG 光栅。但为了连贯性和渐进性,我们将从头开始,并通过例子来描述所有功能。该方法可能比快速开头“Quick Start”要更慢,但是它将使你理解设计的理念。这是非常有用的因为ini将会知道如何自己替代特定的类和算法,或者如何扩展库。特别是,扫描线是没有平台依赖的,但不是最快的。你可能想自己写,优化,但是请面向相同的硬件体系架构,诸如 SSE2。
2. 基本渲染器
从简单的控制台应用程序开始
2.1. 渲染缓冲区
我们从在内存中创建一桢缓冲,并将其以最简单的光栅格式写到文件中,也就是PPM (Portable Pixel Map)。虽然,它本身并不支持Microsoft Windows,但是有很多阅读器和转换器都是基于它实现的,例如IrfanView( )。所有AGG控制台例子是用P6 256格式,也就是RGB,每个通道占一字节。假设我们操作的RGB-缓冲在内存中的组织如下所示:
2.1.1. 第一个示例也是最简单的一个
这是第一个例子,在agg2/tutorial/t01_rendering_buffer.cpp中
#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;
}
在这个例子中,你甚至都不需要连接任何AGG文件,你只需要在你编译器的命令行中说明AGG的include目录。
当你编译并运行这个程序,你可以看到下面的结果。
这里几乎都是手动编码的。我们只使用了一个类“redering_buffer”。这个类不知道内存中像素格式,它只是保存了指向每一行的指针数组。你应当分配和销毁缓冲区的实际内存。可以使用一些可行的机制来做,比如:系统API函数,简单内存分配,或者静态定义的数组。在上面这个例子里我们分配宽*高*3字节的内存,因为每个像素使用3个字节。行数据并不需要内存对齐,但是如果使用API,他们仍拥有很好的性能。
2.1.2. rendering_buffer 类
包含文件: agg_rendering_buffer.h
渲染缓冲区类保存每一行的指针,这就是它主要做的。这看上去不像个伟大的实现,但是尝试保持可读性。它的接口和功能都是非常简单的。这是一个类模板的定义类型(typedef)用法。
模板类 row_ptr_cache的类型定义:
typedef row_ptr_cache<int8u> rendering_buffer;
row_ptr_cache类的接口和函数是:
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)
};
源码: row_ptr_cache
这个类没有任何声明或者验证码,因此,在使用前应当将实际内存缓冲区合适地绑定到对象。既可以在构造函数又可以通过attach()函数来完成。它的参数有:
buf — 内存缓冲区的指针
width — 图像像素(pixels)的宽度. 渲染缓冲区不晓得像素格式以及内存中一个像素的大小。这个值通过数据成员m_width简单的存储,并通过width()函数返回。
height — 缓冲区像素(pixels)的高度(行数)
stride —行间的跨度"stride"(大步)是指T类型里的对象数。Class rendering_buffer类被定义类型“typedefed”为 row_ptr_cache<int8u>,所以,这个值是以字节为单位的。跨度Stride决定了内存中一行的物理宽度。如果这个值是负数,那么Y轴的方向是反向的, 也就是说,Y==0 将指向缓冲区的最后一行,Y==height-1 — 到第一个。跨度值的绝对值也是很重要的,因为它允许操作内存中那些行对齐的缓冲区(例如,像在Windows BMP)。此外,这个参数允许就像操作完整的缓冲区一样去操作缓冲区中一些矩形区域,函数attach()更换缓冲区或者参数。它为row-pointers 缓冲区重新分配内存, 因此,可以随时调用。如果新的高度比以前任何一次绑定的都大,那么将会重新分配内存。
创建的代价就是初始化变量(设置为0),attach()的代价是分配sizeof(ptr) * height个字节的内存以及初始化行的指针。
最经常使用的函数是row_ptr(y),简单的返回第yth行起始的指针,考虑Y-轴的方向。
重要!
渲染器缓冲不执行任何裁剪或者边界校验,这是高层次类的任务。
buf(), width(), height(), stride(), stride_abs()的存取应当是显式的。
copy_from()函数拷贝另一个缓冲区的内容到这个“this” 缓冲区. 这个函数是安全的,如果宽width或者高height是不同的,它将会拷贝最大可能的区域。基本上,它用来拷贝相同大小的渲染缓冲区。
2.1.3. 例子的两个修改
第一个,在创建渲染缓冲区对象的时候将跨度设置为负the
stride: agg::rendering_buffer rbuf(buffer,
frame_width,
frame_height,
-frame_width * 3);
结果是:
第二,允许尝试绑定到一些已分配的缓冲区的局部。这个修改实际上两次绑定到同一个缓冲区,第一次,到整个帧,然后到它的局部,有20像素的间隙。
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;
}
结果:
最后一个修改是:
// 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
);
结果:
在最后一个例子,我们仅仅将跨度的值设置为负,像前面的例子一样保持缓冲区的起始地址指针。
注意
write_ppm()函数将像素图写到文件中。从此以后它将省略,但是当需要的时候将在agg2/tutorial的目录下的源码中复制出来。
2.2. 像素格式渲染器
首先,我们创建另一个更大众化的例子,在agg2/tutorial/t02_pixel_formats中:
像素格式(pixel format)类定义它们各自的色彩空间和色彩类型,例如:
typedef rgba8 color_type;
pixfmt_gray8_nnn 是 gray8. 这个机制允许书写自己的像素和色彩格式,例如,HSV, CMYK,等等。其余的库将以和已实现的像素格式一样的方式,正确的新的像素格式协同工作。
注意
不搞混像素格式渲染器所操作的颜色类型和缓冲区描述的色彩空间是非常重要的。例如,假设使用RGB缓冲区来操作CMYK色彩空间 (你仅是书写一个简单的转换函数,从一些CMYK 结构创建一个rgba8 对象)。但是它将仅仅是一个模仿,而且你会有颜色损失,因为在CMYK中有些颜色不能用RGB显示。为了充分使用有些色彩空间的性能,需要为特殊色彩空间写一个可在其中工作的像素格式渲染器,而不需要任何中间转换。
2.2.1. 创新
重要!
像素格式(pixel format)类不执行任何裁剪操作,这意味着直接操作这些类通常不是安全的。裁剪是高级类的功能。这样设计的原因非常简单—它必须尽可能的简单来书写你自己的像素格式(pixel format)类。. There can be many of them while the clipping code remains exactly the same.
pixel_formats_rgb24(rendering_buffer& rb);
像素格式渲染器的构造函数expects 一个指向已经创建的并全面初始化的rendering_buffer的引用。创建的代价是最小的,它基本上纸初始化一个指针。
2.2.2. 成员函数
像素格式渲染器必须公开下面的功能(接口)。
unsigned width() const { return m_rbuf->width(); }
unsigned height() const { return m_rbuf->height(); }
Returns width and height of the buffer in pixels.
color_type pixel(int x, int y);
以坐标形式(x,y)返回像素的颜色值。
void copy_pixel(int x, int y, const color_type& c);
复制颜色c的一个像素到缓冲区。RGB像素格式不考虑在rgba8类型中存在的透明alpha通道, RGBA —简单地和R, G, 和 B一起拷贝透明alpha值到缓冲区 。
void blend_pixel(int x, int y, const color_type& c, int8u cover);
将颜色c的一个像素和缓冲区中的一个像素进行混合。现在是解释混合blending的时候了。混合是反走样的一个关键特征。在RGBA色彩空间,使用rgba8结构来表示颜色。这个结构已经具有数据成员int8u a;这就是透明alpha通道。但是在这个函数中,我们还可以看到cover参数用来指定deterines像素的覆盖值,等等。即像素中的部分是被一个多边形覆盖的。实际上,你可以解释成第二个"Alpha" ("Beta"?)。这么做有两个原因。首先,色彩类型并不是要必须包括透明alpha值。另外,就算色彩类型具有透明alpha域,它的类型也不需要非得和反走样算法中使用的兼容。假设用四
展开阅读全文