作者魏永明
主题七MiniGUI 引入的新 GDI 功能和函数之二
引言
我们在本系列主题五中曾经详细描述了在 MiniGUI 版本开发过程中添加的新 GDI 功能和函数这些接口首次出现在版本 Pre 当中目前 MiniGUI Pre 版本已经发布该版本中的新 GDI 接口趋于稳定相对 Pre 版本而言又新增了若干高级图形接口这些接口涉及到直线和曲线生成器复杂曲线的绘制封闭曲线填充复杂区域的创建直接的显示缓沖区访问YUV 覆盖和 Gamma 校正等等本文将就这些主题详细描述各个接口的用法
曲线和填充生成器
在一般的图形系统中通常给用户提供若干用于进行直线或者复杂曲线比如圆弧椭圆和样条曲线的绘图函数用户可以通过这些函数进行绘图但不能利用这些系统中已有的曲线生成算法完成其他的工作在 MiniGUI 新的 GDI 接口设计当中我们采用了一种特殊的设计方法来实现曲线和封闭曲线的填充这种方法非常灵活而且给用户提供了直接使用系统内部算法的机会
)系统中定义了若干用来生成直线和曲线的函数我们称之为曲线生成器
)用户在调用生成器之前需要定义一个回调函数并将函数地址传递给曲线生成器曲线生成器在生成了一个曲线上的点或者封闭曲线中的一条水平填充线时将调用这个回调函数
)用户可以在回调函数当中完成针对新的点或者新的水平填充线的操作对 MiniGUI 绘图函数来说就是完成绘图工作
)因为回调函数在生成器的运行过程中不断调用为了保持一致的上下文环境系统允许用户在调用曲线生成器时传递一个表示上下文的指针生成器将把该指针传递给回调函数
下面将分小节讲述目前的 MiniGUI 版本所提供的曲线和填充生成器
直线剪切器和直线生成器
直线剪切器和生成器的原型如下
/* Line clipper */
BOOL GUIAPI LineClipper (const RECT* cliprc int *_x int *_y int *_x int *_y);
/* Line generators */
typedef void (* CB_LINE) (void* context int stepx int stepy);
void GUIAPI LineGenerator (void* context int x int y int x int y CB_LINE cb);
直线剪切器并不是生成器它用于对给定的直线进行剪切操作cliprc 是给定的直线而 _x_y_x 和 _y 传递要剪切的直线起始端点并通过这些指针返回剪切之后的直线起始端点MiniGUI 内部使用了 CohenSutherland 算法
LineGenerator 是采用 Breshenham 算法的生成器该生成器从给定直线的起始端点开始每生成一个点调用一次 cb 回调函数并传递上下文 context以及新的点相对于上一个点的步进值或者差量比如传递 stepx =stepy = 表示新的点比上一个点在 X 轴上前进一步而在 Y 轴上保持不变回调函数可以在步进值基础上实现某种程度上的优化
圆生成器
MiniGUI 定义的圆生成器原型如下
/* Circle generator */
typedef void (* CB_CIRCLE) (void* context int x int x int y);
void GUIAPI CircleGenerator (void* context int sx int sy int r CB_CIRCLE cb);
首先要指定圆心坐标以及半径并传递上下文信息以及回调函数每生成一个点生成器将调用一次 cb 回调函数并传递三个值xx 和 y这三个值实际表示了圆上的两个点(x y) 和 (x y)因为圆的对称性生成器只要计算圆上的四分之一圆弧点即可得出圆上所有的点
椭圆生成器
椭圆生成器和圆生成器类似原型如下
/* Ellipse generator */
typedef void (* CB_ELLIPSE) (void* context int x int x int y);
void GUIAPI EllipseGenerator (void* context int sx int sy int rx int ry CB_ELLIPSE cb);
首先要指定椭圆心坐标以及 X 轴和 Y 轴半径并传递上下文信息以及回调函数每生成一个点生成器将调用一次 cb 回调函数并传递三个值xx 和 y这三个值实际表示了椭圆上的两个点(x y) 和 (x y)因为椭圆的对称性生成器只要计算椭圆上的二分之一圆弧点即可得出椭圆上所有的点
圆弧生成器
MiniGUI 定义的圆弧生成器如下所示
/* Arc generator */
typedef void (* CB_ARC) (void* context int x int y);
void GUIAPI ArcGenerator (void* context int sx int sy int r fixed ang fixed ang CB_ARC cb);
首先要指定圆弧的圆心半径起始弧度和终止弧度需要注意的是起始弧度和终止弧度是采用定点数表示的而不是浮点数并且是弧度而不是角度然后传递 cb 回调函数每生成一个圆弧上的点该函数将调用回调函数并传递新点的坐标值 (x y)
有关定点数的信息请参阅本系列主题六MiniGUI 提供的非 GUI/GDI 接口一文
垂直单调多边形生成器
通常而言多边形有凸多边形和凹多边形之分这里的垂直单调多边形是为了优化多边形填充算法而针对计算机图形特点而提出的一种特殊多边形这种多边形的定义如下
垂直单调多边形是指多边形的边和计算机屏幕上的所有水平扫描线只能有一个或者两个交点不会有更多交点
图 给出了凸多边形凹多边形和垂直单调多边形的几个示例
需要注意的是凸多边形一定是垂直单调多边形但垂直单调多边形可以是凹多边形显然普通的多边形填充算法需要判断多边形边和每条屏幕扫描线之间的交点个数而垂直单调多边形则可以免去这一判断所以可以大大提高多边形填充的速度
MiniGUI 所定义的垂直单调多边形相关函数原型如下
/* To determine whether the specified Polygon is Monotone Vertical Polygon */
BOOL GUIAPI PolygonIsMonotoneVertical (const POINT* pts int vertices);
/* Monotone vertical polygon generator */
typedef void (* CB_POLYGON) (void* context int x int x int y);
BOOL GUIAPI MonotoneVerticalPolygonGenerator (void* context const POINT* pts int vertices CB_POLYGON cb);
PolygonIsMonotoneVertical 用来判断给定的多边形是否是垂直单调多边形而 MonotoneVerticalPolygonGenerator 函数是垂直多边形生成器在 MiniGUI 当中多边形是由组成多边形的顶点来表示的pts 表示顶点数组而 vertices 表示顶点个数生成器生成的实际是填充多边形的每一条水平线端点为 (x y) 和 (x y)
一般矩形生成器
MiniGUI 还提供了一般的矩形生成器该生成器可以处理凸多边形也可以处理凹多边形原型如下
/* General polygon generator */
typedef void (* CB_POLYGON) (void* context int x int x int y);
BOOL GUIAPI PolygonGenerator (void* context const POINT* pts int vertices CB_POLYGON cb);
和垂直单调多边形生成器一样该函数生成的是填充多边形的每一条水平扫描线x 是水平线的起始X坐标x 是水平线的终止 X 坐标y 是水平线的 Y 坐标值
填注生成器
填注(flood filling)生成器比较复杂这个函数在 MiniGUI 内部用于 FloodFill 函数我们知道FloodFill 函数从给定的起始位置开始以给定的颜色向四面八方填充某个区域(像水一样蔓延因此叫 Flood Filling)一直到遇到与给定起始位置的象素值不同的点为止因此在这一过程中我们需要两个回调函数一个回调函数用来判断蔓延过程中遇到的点的象素值是否和起始点相同另外一个回调函数用来生成填充该区域的水平扫描线在进行绘图时该函数比较的是象素值但实际上该函数也可以比较任何其他值从而完成特有的蔓延动作这就是将填注生成器单独出来的初衷MiniGUI 如下定义填注生成器
/* General Flood Filling generator */
typedef BOOL (* CB_EQUAL_PIXEL) (void* context int x int y);
typedef void (* CB_FLOOD_FILL) (void* context int x int x int y);
BOOL GUIAPI FloodFillGenerator (void* context const RECT* src_rc int x int y
CB_EQUAL_PIXEL cb_equal_pixel CB_FLOOD_FILL cb_flood_fill);
cb_equal_pixel 被调用以便判断目标点的象素值是否和起始点一样起始点的象素值可以通过 context 来传递cb_flood_fill 函数用来填充一条扫描线传递的是水平扫描线的端点即(x y) 和 (x y)
曲线和填充生成器的用法
曲线和填充生成器的用法非常简单为了对曲线和填充生成器有个更好的了解我们首先看 MiniGUI 内部是如何使用曲线和填充生成器的
下面的程序段来自 MiniGUI 的 FloodFill 函数(src/newgdi/floodc)
static void _flood_fill_draw_hline (void* context int x int x int y)
{
PDC pdc = (PDC)context;
RECT rcOutput = {MIN (x x) y MAX (x x) + y + };
ENTER_DRAWING (pdc rcOutput);