📜  抗锯齿线|吴小林的算法

📅  最后修改于: 2021-05-07 05:14:24             🧑  作者: Mango

抗锯齿线图
下图显示了使用Bresenham的线算法(左)和Xiaolin Wu的线算法(右)绘制的线条,使线条平滑。哪一个对您来说看起来更好?
AALine
反锯齿概念
假设我们要画一条从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坐标与最近的网格单元之间的距离。如果错误不超过单元格高度的一半,则将其填充。这就是整个算法。
原始的-a04cf694-556504fba7413
我们将修改此算法,以便它可以产生抗锯齿的线条。
Wu Xiaolin Wu的线算法的特征在于,在计算的每个步骤中,都对最接近像素线的两个像素进行计算,并且根据距离对它们进行不同强度的着色。如果像素在0.9像素以内,则当前交点中间像素强度为100%,则强度为10%。换句话说,在两侧限制矢量线的像素之间分配了百分之一百的强度。
原始-ccf57794-556504fc0787f
在图片中,红色和绿色表示到两个相邻像素的距离。要计算误差,可以使用浮点数并取小数部分的误差值。

注意:以下实现使用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;
}

输出:

outputAALine