Scan-line Polygon filling using OPENGL in C

06 Jun, 2022
Figures on a computer screen can be drawn using polygons. To fill those figures with color, we need to develop some algorithm.There are two famous algorithms for this purpose: Boundary fill and Scanline fill algorithms.Boundary filling requires a lot of processing and thus encounters few problems in real time. Thus the viable alternative is scanline filling as it is very robust in nature. This article discusses how to use Scanline filling algorithm to fill colors in an image.  
Scanline Polygon filling Algorithm
Scanline filling is basically filling up of polygons using horizontal lines or scanlines. The purpose of the SLPF algorithm is to fill (color) the interior pixels of a polygon given only the vertices of the figure. To understand Scanline, think of the image being drawn by a single pen starting from bottom left, continuing to the right, plotting only points where there is a point present in the image, and when the line is complete, start from the next line and continue. This algorithm works by intersecting scanline with polygon edges and fills the polygon between pairs of intersections.  
Special cases of polygon vertices:  
Chapters
descriptions off, selected
captions settings, opens captions settings dialog
captions off, selected
English
This is a modal window.
Beginning of dialog window. Escape will cancel and close the window.
End of dialog window.
If both lines intersecting at the vertex are on the same side of the scanline, consider it as two points.If lines intersecting at the vertex are at opposite sides of the scanline, consider it as only one point.
If both lines intersecting at the vertex are on the same side of the scanline, consider it as two points.
If lines intersecting at the vertex are at opposite sides of the scanline, consider it as only one point.
Components of Polygon fill:  
Edge Buckets: It contains an edge’s information. The entries of edge bucket vary according to data structure you have used.In the example we are taking below, there are three edge buckets namely: ymax, xofymin, slopeinverse.Edge Table: It consistsof several edge lists -> holds all of the edges that compose the figure. When creating edges, the vertices of the edge need to be ordered from left to right and the edges are maintained in increasing yMin order. Filling is complete once all of the edges are removed from the ETActive List: IT maintains the current edges being used to fill in the polygon.Edges are pushed into the AL from the Edge Table when an edge’s yMin is equal to the current scan line being processed. The Active List will be re-sorted after every pass.
Edge Buckets: It contains an edge’s information. The entries of edge bucket vary according to data structure you have used.In the example we are taking below, there are three edge buckets namely: ymax, xofymin, slopeinverse.
Edge Table: It consistsof several edge lists -> holds all of the edges that compose the figure. When creating edges, the vertices of the edge need to be ordered from left to right and the edges are maintained in increasing yMin order. Filling is complete once all of the edges are removed from the ET
Active List: IT maintains the current edges being used to fill in the polygon.Edges are pushed into the AL from the Edge Table when an edge’s yMin is equal to the current scan line being processed. The Active List will be re-sorted after every pass.
Data Structure:  
 
Algorithm:

1. We will process the polygon edge after edge, and store in the edge Table.
2. Storing is done by storing the edge in the same scanline edge tuple as 
   the lowermost point's y-coordinate value of the edge.
3. After addition of any edge in an edge tuple, the tuple is 
   sorted using insertion sort, according to its xofymin value.
4. After the whole polygon is added to the edge table, 
   the figure is now filled.
5. Filling is started from the first scanline at the bottom,
   and continued till the top.
6. Now the active edge table is taken and the following things 
   are repeated for each scanline:
       i. Copy all edge buckets of the designated scanline 
          to the active edge tuple
       ii. Perform an insertion sort according
          to the xofymin values
       iii. Remove all edge buckets whose ymax is equal 
            or greater than the scanline
       iv. Fillup pairs of edges in active tuple, if any vertex is got, 
           follow these instructions:
            o If both lines intersecting at the vertex are on
              the same side of the scanline, consider it as two points.
            o If lines intersecting at the vertex are at 
              opposite sides of the scanline, consider it as only one point.
       v. Update the xofymin by adding slopeinverse for each bucket.
   
 
Sample Image
We are using an example of a polygon dinosaur. Paste the following in a textfile in the same folder as the executable and rename it as PolyDino.txt . Link: PolyDino.txt 
 
C
// CPP program to illustrate// Scanline Polygon fill Algorithm #include <stdio.h>#include <math.h>#include <GL/glut.h>#define maxHt 800#define maxWd 600#define maxVer 10000 FILE *fp; // Start from lower left cornertypedef struct edgebucket{    int ymax;   //max y-coordinate of edge    float xofymin;  //x-coordinate of lowest edge point updated only in aet    float slopeinverse;}EdgeBucket; typedef struct edgetabletup{    // the array will give the scanline number    // The edge table (ET) with edges entries sorted    // in increasing y and x of the lower end         int countEdgeBucket;    //no. of edgebuckets    EdgeBucket buckets[maxVer];}EdgeTableTuple; EdgeTableTuple EdgeTable[maxHt], ActiveEdgeTuple;  // Scanline Functionvoid initEdgeTable(){    int i;    for (i=0; i<maxHt; i++)    {        EdgeTable[i].countEdgeBucket = 0;    }         ActiveEdgeTuple.countEdgeBucket = 0;}  void printTuple(EdgeTableTuple *tup){    int j;         if (tup->countEdgeBucket)        printf("\nCount %d-----\n",tup->countEdgeBucket);                 for (j=0; j<tup->countEdgeBucket; j++)        {            printf(" %d+%.2f+%.2f",            tup->buckets[j].ymax, tup->buckets[j].xofymin,tup->buckets[j].slopeinverse);        }} void printTable(){    int i,j;         for (i=0; i<maxHt; i++)    {        if (EdgeTable[i].countEdgeBucket)            printf("\nScanline %d", i);                     printTuple(&EdgeTable[i]);    }}  /* Function to sort an array using insertion sort*/void insertionSort(EdgeTableTuple *ett){    int i,j;    EdgeBucket temp;     for (i = 1; i < ett->countEdgeBucket; i++)    {        temp.ymax = ett->buckets[i].ymax;        temp.xofymin = ett->buckets[i].xofymin;        temp.slopeinverse = ett->buckets[i].slopeinverse;        j = i - 1;     while ((temp.xofymin < ett->buckets[j].xofymin) && (j >= 0))    {        ett->buckets[j + 1].ymax = ett->buckets[j].ymax;        ett->buckets[j + 1].xofymin = ett->buckets[j].xofymin;        ett->buckets[j + 1].slopeinverse = ett->buckets[j].slopeinverse;        j = j - 1;    }    ett->buckets[j + 1].ymax = temp.ymax;    ett->buckets[j + 1].xofymin = temp.xofymin;    ett->buckets[j + 1].slopeinverse = temp.slopeinverse;    }}  void storeEdgeInTuple (EdgeTableTuple *receiver,int ym,int xm,float slopInv){    // both used for edgetable and active edge table..    // The edge tuple sorted in increasing ymax and x of the lower end.    (receiver->buckets[(receiver)->countEdgeBucket]).ymax = ym;    (receiver->buckets[(receiver)->countEdgeBucket]).xofymin = (float)xm;    (receiver->buckets[(receiver)->countEdgeBucket]).slopeinverse = slopInv;                 // sort the buckets    insertionSort(receiver);             (receiver->countEdgeBucket)++;          } void storeEdgeInTable (int x1,int y1, int x2, int y2){    float m,minv;    int ymaxTS,xwithyminTS, scanline; //ts stands for to store         if (x2==x1)    {        minv=0.000000;    }    else    {    m = ((float)(y2-y1))/((float)(x2-x1));         // horizontal lines are not stored in edge table    if (y2==y1)        return;             minv = (float)1.0/m;    printf("\nSlope string for %d %d & %d %d: %f",x1,y1,x2,y2,minv);    }         if (y1>y2)    {        scanline=y2;        ymaxTS=y1;        xwithyminTS=x2;    }    else    {        scanline=y1;        ymaxTS=y2;        xwithyminTS=x1;        }    // the assignment part is done..now storage..    storeEdgeInTuple(&EdgeTable[scanline],ymaxTS,xwithyminTS,minv);          } void removeEdgeByYmax(EdgeTableTuple *Tup,int yy){    int i,j;    for (i=0; i< Tup->countEdgeBucket; i++)    {        if (Tup->buckets[i].ymax == yy)        {            printf("\nRemoved at %d",yy);                         for ( j = i ; j < Tup->countEdgeBucket -1 ; j++ )                {                Tup->buckets[j].ymax =Tup->buckets[j+1].ymax;                Tup->buckets[j].xofymin =Tup->buckets[j+1].xofymin;                Tup->buckets[j].slopeinverse = Tup->buckets[j+1].slopeinverse;                }                Tup->countEdgeBucket--;            i--;        }    }}      void updatexbyslopeinv(EdgeTableTuple *Tup){    int i;         for (i=0; i<Tup->countEdgeBucket; i++)    {        (Tup->buckets[i]).xofymin =(Tup->buckets[i]).xofymin + (Tup->buckets[i]).slopeinverse;    }}  void ScanlineFill(){    /* Follow the following rules:    1. Horizontal edges: Do not include in edge table    2. Horizontal edges: Drawn either on the bottom or on the top.    3. Vertices: If local max or min, then count twice, else count        once.    4. Either vertices at local minima or at local maxima are drawn.*/      int i, j, x1, ymax1, x2, ymax2, FillFlag = 0, coordCount;         // we will start from scanline 0;    // Repeat until last scanline:    for (i=0; i<maxHt; i++)//4. Increment y by 1 (next scan line)    {                 // 1. Move from ET bucket y to the        // AET those edges whose ymin = y (entering edges)        for (j=0; j<EdgeTable[i].countEdgeBucket; j++)        {            storeEdgeInTuple(&ActiveEdgeTuple,EdgeTable[i].buckets[j].                     ymax,EdgeTable[i].buckets[j].xofymin,                    EdgeTable[i].buckets[j].slopeinverse);        }        printTuple(&ActiveEdgeTuple);                 // 2. Remove from AET those edges for        // which y=ymax (not involved in next scan line)        removeEdgeByYmax(&ActiveEdgeTuple, i);                 //sort AET (remember: ET is presorted)        insertionSort(&ActiveEdgeTuple);                 printTuple(&ActiveEdgeTuple);                 //3. Fill lines on scan line y by using pairs of x-coords from AET        j = 0;        FillFlag = 0;        coordCount = 0;        x1 = 0;        x2 = 0;        ymax1 = 0;        ymax2 = 0;        while (j<ActiveEdgeTuple.countEdgeBucket)        {            if (coordCount%2==0)            {                x1 = (int)(ActiveEdgeTuple.buckets[j].xofymin);                ymax1 = ActiveEdgeTuple.buckets[j].ymax;                if (x1==x2)                {                /* three cases can arrive-                    1. lines are towards top of the intersection                    2. lines are towards bottom                    3. one line is towards top and other is towards bottom                */                    if (((x1==ymax1)&&(x2!=ymax2))||((x1!=ymax1)&&(x2==ymax2)))                    {                        x2 = x1;                        ymax2 = ymax1;                    }                                     else                    {                        coordCount++;                    }                }                                 else                {                        coordCount++;                }            }            else            {                x2 = (int)ActiveEdgeTuple.buckets[j].xofymin;                ymax2 = ActiveEdgeTuple.buckets[j].ymax;                             FillFlag = 0;                                 // checking for intersection...                if (x1==x2)                {                /*three cases can arrive-                    1. lines are towards top of the intersection                    2. lines are towards bottom                    3. one line is towards top and other is towards bottom                */                    if (((x1==ymax1)&&(x2!=ymax2))||((x1!=ymax1)&&(x2==ymax2)))                    {                        x1 = x2;                        ymax1 = ymax2;                    }                    else                    {                        coordCount++;                        FillFlag = 1;                    }                }                else                {                        coordCount++;                        FillFlag = 1;                }                                      if(FillFlag)            {                //drawing actual lines...                glColor3f(0.0f,0.7f,0.0f);                                 glBegin(GL_LINES);                glVertex2i(x1,i);                glVertex2i(x2,i);                glEnd();                glFlush();                                         // printf("\nLine drawn from %d,%d to %d,%d",x1,i,x2,i);            }                     }                     j++;    }                          // 5. For each nonvertical edge remaining in AET, update x for new y    updatexbyslopeinv(&ActiveEdgeTuple);}  printf("\nScanline filling complete"); }  void myInit(void){     glClearColor(1.0,1.0,1.0,0.0);    glMatrixMode(GL_PROJECTION);         glLoadIdentity();    gluOrtho2D(0,maxHt,0,maxWd);    glClear(GL_COLOR_BUFFER_BIT);} void drawPolyDino(){     glColor3f(1.0f,0.0f,0.0f);    int count = 0,x1,y1,x2,y2;    rewind(fp);    while(!feof(fp) )    {        count++;        if (count>2)        {            x1 = x2;            y1 = y2;            count=2;        }        if (count==1)        {            fscanf(fp, "%d,%d", &x1, &y1);        }        else        {            fscanf(fp, "%d,%d", &x2, &y2);            printf("\n%d,%d", x2, y2);            glBegin(GL_LINES);                glVertex2i( x1, y1);                glVertex2i( x2, y2);            glEnd();            storeEdgeInTable(x1, y1, x2, y2);//storage of edges in edge table.                                      glFlush();        }    }                  } void drawDino(void){    initEdgeTable();    drawPolyDino();    printf("\nTable");    printTable();         ScanlineFill();//actual calling of scanline filling..} void main(int argc, char** argv){    fp=fopen ("PolyDino.txt","r");    if ( fp == NULL )    {        printf( "Could not open file" ) ;        return;    }    glutInit(&argc, argv);    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);    glutInitWindowSize(maxHt,maxWd);    glutInitWindowPosition(100, 150);    glutCreateWindow("Scanline filled dinosaur");    myInit();    glutDisplayFunc(drawDino);         glutMainLoop();    fclose(fp);}
Output: Filled up dinosaur:  
Note: See your output on an opengl window. Mind that you have to have glut installed. You may see this video for watching the output. This article is contributed by Suprotik Dey. If you like GeeksforGeeks and would like to contribute, you can also write an article using write.geeksforgeeks.org or mail your article to review-team@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above. 
gabaa406
varshagumber28
sumitgumber28
computer-graphics
OpenGL
Algorithms
Algorithms
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
DSA Sheet by Love Babbar
SDE SHEET - A Complete Guide for SDE Preparation
What is Hashing | A Complete Tutorial
Understanding Time Complexity with Simple Examples
CPU Scheduling in Operating Systems
Find if there is a path between two vertices in an undirected graph
A* Search Algorithm
What is Algorithm | Introduction to Algorithms
Analysis of Algorithms | Set 3 (Asymptotic Notations)
Analysis of Algorithms | Set 2 (Worst, Average and Best Cases)