抗锯齿线图
下图显示了使用Bresenham的线算法(左)和Xiaolin Wu的线算法(右)绘制的线条,使线条平滑。哪一个对您来说看起来更好?
反锯齿概念
假设我们要画一条从point(1,1)到point(8,4)的矩形边线。理想的线将是图A所示的线。因为我想在屏幕上显示它,所以我不能使用它。行需要经过一个称为“栅格化”的过程,该过程将确定各个像素的颜色。可以使用多种算法,例如布雷森纳姆(Bresenham)的线路算法,数字差分分析仪,吴小林的线路算法,古普塔-斯普鲁尔(Gupta-Sproull)算法。稍后两个执行抗锯齿或线条平滑。
前两种算法产生的结果如图B所示。
Line(图B)几乎没有问题。
1. Pixel(4,2)的覆盖范围小于Pixel(3,2),但是它们都被绘制为全黑。
2.像素(2,2)的覆盖范围几乎与(4,2)相同,但是它被绘制为全白色。
为了克服这些缺点并产生更平滑的线条,我们使用吴小林的线条算法
吴小林的直线算法
考虑下图,该图是使用Bresenham的Line Generation Algorithm绘制的。取一个线段及其初始坐标x。循环中的X会在该段的末尾加1。在每个步骤中,都会计算误差–该位置处的实际y坐标与最近的网格单元之间的距离。如果错误不超过单元格高度的一半,则将其填充。这就是整个算法。
我们将修改此算法,以便它可以产生抗锯齿的线条。
Wu Xiaolin Wu的线算法的特征在于,在计算的每个步骤中,都对最接近像素线的两个像素进行计算,并且根据距离对它们进行不同强度的着色。如果像素在0.9像素以内,则当前交点中间像素强度为100%,则强度为10%。换句话说,在两侧限制矢量线的像素之间分配了百分之一百的强度。
在图片中,红色和绿色表示到两个相邻像素的距离。要计算误差,可以使用浮点数并取小数部分的误差值。
注意:以下实现使用SDL库在屏幕上绘制像素。如果您使用的是像ubuntu这样的debian系统,只需运行以下命令即可安装SDL库。
sudo apt-get install libsdl2-dev
建立使用
gcc filename.c -lSDL2
注意:如果线段在x轴上的投影小于y轴上的投影,或者该线段的开始和结束被交换,则该算法将不起作用。为避免这种情况,您需要检查矢量的方向及其斜率,然后交换线的坐标,最终将所有内容减少到一种或至少两种情况。
由于像素值不能为浮点数,因此以下算法假定仅将整数坐标作为输入。
// C program to implement Xiaolin Wu's line drawing
// algorithm.
// We must install SDL library using above steps
// to run this prorgram
#include
// SDL stuff
SDL_Window* pWindow = 0;
SDL_Renderer* pRenderer = 0;
// swaps two numbers
void swap(int* a , int*b)
{
int temp = *a;
*a = *b;
*b = temp;
}
// returns absolute value of number
float absolute(float x )
{
if (x < 0) return -x;
else return x;
}
//returns integer part of a floating point number
int iPartOfNumber(float x)
{
return (int)x;
}
//rounds off a number
int roundNumber(float x)
{
return iPartOfNumber(x + 0.5) ;
}
//returns fractional part of a number
float fPartOfNumber(float x)
{
if (x>0) return x - iPartOfNumber(x);
else return x - (iPartOfNumber(x)+1);
}
//returns 1 - fractional part of number
float rfPartOfNumber(float x)
{
return 1 - fPartOfNumber(x);
}
// draws a pixel on screen of given brightness
// 0<=brightness<=1. We can use your own library
// to draw on screen
void drawPixel( int x , int y , float brightness)
{
int c = 255*brightness;
SDL_SetRenderDrawColor(pRenderer, c, c, c, 255);
SDL_RenderDrawPoint(pRenderer, x, y);
}
void drawAALine(int x0 , int y0 , int x1 , int y1)
{
int steep = absolute(y1 - y0) > absolute(x1 - x0) ;
// swap the co-ordinates if slope > 1 or we
// draw backwards
if (steep)
{
swap(&x0 , &y0);
swap(&x1 , &y1);
}
if (x0 > x1)
{
swap(&x0 ,&x1);
swap(&y0 ,&y1);
}
//compute the slope
float dx = x1-x0;
float dy = y1-y0;
float gradient = dy/dx;
if (dx == 0.0)
gradient = 1;
int xpxl1 = x0;
int xpxl2 = x1;
float intersectY = y0;
// main loop
if (steep)
{
int x;
for (x = xpxl1 ; x <=xpxl2 ; x++)
{
// pixel coverage is determined by fractional
// part of y co-ordinate
drawPixel(iPartOfNumber(intersectY), x,
rfPartOfNumber(intersectY));
drawPixel(iPartOfNumber(intersectY)-1, x,
fPartOfNumber(intersectY));
intersectY += gradient;
}
}
else
{
int x;
for (x = xpxl1 ; x <=xpxl2 ; x++)
{
// pixel coverage is determined by fractional
// part of y co-ordinate
drawPixel(x, iPartOfNumber(intersectY),
rfPartOfNumber(intersectY));
drawPixel(x, iPartOfNumber(intersectY)-1,
fPartOfNumber(intersectY));
intersectY += gradient;
}
}
}
// Driver code
int main(int argc, char* args[])
{
SDL_Event event;
// initialize SDL
if (SDL_Init(SDL_INIT_EVERYTHING) >= 0)
{
// if succeeded create our window
pWindow = SDL_CreateWindow("Anti-Aliased Line ",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480,
SDL_WINDOW_SHOWN);
// if the window creation succeeded create our renderer
if (pWindow != 0)
pRenderer = SDL_CreateRenderer(pWindow, -1, 0);
}
else
return 1; // sdl could not initialize
while (1)
{
if (SDL_PollEvent(&event) && event.type == SDL_QUIT)
break;
// Sets background color to white
SDL_SetRenderDrawColor(pRenderer, 255, 255, 255, 255);
SDL_RenderClear(pRenderer);
// draws a black AALine
drawAALine(80 , 200 , 550, 150);
// show the window
SDL_RenderPresent(pRenderer);
}
// clean up SDL
SDL_Quit();
return 0;
}
输出: