lib/mgPolygon.c

Go to the documentation of this file.
00001 /* mgPolygon - stuff to draw polygons in memory.  This code dates
00002  * back to 1984, was used in the Aegis Animator for the Atari ST. 
00003  * It was an exceedingly speed critical routine for that program.
00004  * For us it may be overkill, but the major speed tweak - the special
00005  * case for convex polygons - is only 1/3 of the module, so I'm leaving
00006  * it in.  I've lightly adapted the code to current conventions, but
00007  * you can still see some relics in the local and static variable names of 
00008  * other coding conventions.  */
00009 
00010 #include "common.h"
00011 #include "bits.h"
00012 #include "memgfx.h"
00013 #include "gfxPoly.h"
00014 
00015 void mgDrawPolyOutline(struct memGfx *mg, struct gfxPoly *poly, Color color)
00016 /* Draw a singe pixel line around polygon. */
00017 {
00018 struct gfxPoint *a, *b, *end;
00019 
00020 a = end = poly->ptList;
00021 b = a->next;
00022 for (;;)
00023     {
00024     mgDrawLine(mg, a->x, a->y, b->x, b->y, color);
00025     a = b;
00026     b = b->next;
00027     if (a == end)
00028         break;
00029     }
00030 }
00031 
00032 /* ----- Stuff for convex polygons that may contain crossing lines ----- 
00033  * This code works by allocating a bitmap big enough to hold the polygon
00034  * and drawing lines on the bitmap in a special fashion, and then
00035  * scanning the bitmap to connect together the dots with horizontal lines. */
00036 
00037 #define UPDIR 1
00038 #define DOWNDIR 0
00039 
00040 static UBYTE *on_off_buf;       /* Our bitplane. */
00041 
00042 static int pxmin, pxmax, pymin, pymax;  /* Bounds of polygon. */
00043 
00044 static void find_pminmax(struct gfxPoly *poly)
00045 {
00046 struct gfxPoint *pointpt;
00047 int i;
00048 
00049 pointpt = poly->ptList;
00050 pxmin = pxmax = pointpt->x;
00051 pymin = pymax = pointpt->y;
00052 pointpt = pointpt->next;
00053 
00054 i = poly->ptCount;
00055 while (--i > 0)
00056    {
00057    if (pxmin > pointpt->x) pxmin = pointpt->x;
00058    if (pxmax < pointpt->x) pxmax = pointpt->x;
00059    if (pymin > pointpt->y) pymin = pointpt->y;
00060    if (pymax < pointpt->y) pymax = pointpt->y;
00061    pointpt = pointpt->next;
00062    }
00063 }
00064 
00065 static void xor_pt(int bpr, int x, int y)
00066 /* Xor in a bit at given location. */
00067 {
00068 UBYTE rot;
00069 
00070 rot = ((unsigned)0x80) >> (x&7);
00071 on_off_buf[ bpr*y + (x>>3) ] ^= rot;
00072 }
00073 
00074 
00075 static void y_xor_line(int bpr, int x1, int y1, int x2, int y2)
00076 /* Xor in a line onto on_off_buf, only plotting when we hit a new y-value. */
00077 {
00078 UBYTE *imagept = on_off_buf;
00079 UBYTE rot;
00080 int   duty_cycle;
00081 int   delta_x, delta_y;
00082 int dots;
00083 int swap;
00084 
00085 if (x1 > x2)
00086     {
00087     swap = x1;
00088     x1 = x2;
00089     x2 = swap;
00090     swap = y1;
00091     y1 = y2;
00092     y2 = swap;
00093     }
00094 delta_y = y2-y1;
00095 delta_x = x2-x1;
00096 rot = ((unsigned)0x80) >> (x1&7);
00097 imagept += bpr*y1 + (x1>>3);
00098 
00099 
00100 if (delta_y < 0) 
00101     {
00102     delta_y = -delta_y;
00103     bpr = -bpr;
00104     }
00105 duty_cycle = (delta_x - delta_y)/2;
00106 *(imagept) ^= rot;
00107 if (delta_x < delta_y)
00108     {
00109     dots = delta_y;
00110     while (--dots >= 0)
00111         {
00112         *(imagept) ^= rot;
00113         duty_cycle += delta_x;    /* update duty cycle */
00114         imagept += bpr;
00115         if (duty_cycle > 0)
00116             {
00117             duty_cycle -= delta_y;
00118             rot >>= 1;
00119             if (rot == 0)
00120                 {
00121                 imagept++;
00122                 rot = 0x80;
00123                 }
00124             }
00125         }
00126     }
00127 else
00128     {
00129     dots = delta_x;
00130     while (--dots >= 0)
00131         {
00132         duty_cycle -= delta_y;    /* update duty cycle */
00133         if (duty_cycle < 0)
00134             {
00135             *(imagept) ^= rot;
00136             duty_cycle += delta_x;
00137             imagept += bpr;
00138             }
00139         rot >>= 1;
00140         if (rot == 0)
00141             {
00142             imagept++;
00143             rot = 0x80;
00144             }
00145         }
00146     }
00147 }
00148 
00149  
00150 static void drawAfterOnOff(struct memGfx *mg, Color color, int bpr, int width, 
00151         int height)
00152 /* Examine bitmap and draw horizontal lines between set bits. */
00153 {
00154 UBYTE *linept = on_off_buf;
00155 int y;
00156 
00157 for (y=0; y<height; ++y)
00158         {
00159         int start = 0;
00160         int end;
00161         for (;;)
00162             {
00163             start = bitFindSet(linept, start, width);
00164             if (start >= width)
00165                 break;
00166             end = bitFindSet(linept, start+1, width);
00167             mgLineH(mg, y+pymin, start+pxmin, end+pxmin, color);
00168             start = end+1;
00169             }
00170         linept += bpr;
00171         }
00172 }
00173 
00174 static void fillConcave(struct memGfx *mg, struct gfxPoly *poly, Color color)
00175 /* Draw concave polygon */
00176 {
00177 struct gfxPoint *pointpt;
00178 int x,y;
00179 int ox,oy;
00180 int lastdir;
00181 int i;
00182 int bpr;        /* Bytes per row */
00183 int width, height;
00184 long size;
00185 
00186 find_pminmax(poly);
00187 if (pymin==pymax)  /*Complex code can't cope with trivial case*/
00188     {
00189     mgLineH(mg, pymin, pxmin, pxmax,color);
00190     return;
00191     }
00192 width = pxmax - pxmin + 1;
00193 height = pymax - pymin + 1;
00194 bpr = ((width+7)>>3);
00195 size = (long)bpr*height;
00196 if ((on_off_buf = needMem(size)) == NULL)       
00197     return;
00198 pointpt = poly->ptList;
00199 x = pointpt->x;
00200 y = pointpt->y;
00201 
00202 do
00203         {
00204         pointpt = pointpt->next;
00205         ox = pointpt->x;
00206         oy = pointpt->y;
00207         }
00208 while (oy == y);
00209 
00210 if (oy>y)
00211         lastdir = UPDIR;
00212 else
00213         lastdir = DOWNDIR;
00214 
00215 i = poly->ptCount;
00216 while (--i >= 0)
00217    {
00218    pointpt = pointpt->next;
00219    x = pointpt->x;
00220    y = pointpt->y;
00221    if (y!=oy)
00222           {
00223           y_xor_line(bpr,ox-pxmin,oy-pymin,x-pxmin,y-pymin);
00224           if (y>oy)
00225                  if (lastdir == UPDIR)
00226                         xor_pt(bpr,ox-pxmin,oy-pymin);
00227                  else
00228                         lastdir = UPDIR;
00229           else
00230                  if (lastdir == DOWNDIR)
00231                         xor_pt(bpr,ox-pxmin,oy-pymin);
00232                  else
00233                         lastdir = DOWNDIR;
00234           }
00235    ox = x;
00236    oy = y;
00237    }
00238 
00239 drawAfterOnOff(mg, color, bpr, width, height);
00240 freez(&on_off_buf);
00241 return;
00242 }
00243 
00244 /*  ---- This section of code is for convex, polygons.  It's faster. ---- */
00245 
00246 #ifdef SOME_OFF_BY_ONE_PROBLEMS
00247 static int *xdda_ebuf(int *ebuf, int x1, int y1, int x2, int y2)
00248 /* Calculate the x-positions of a line from x1,y1  to x2/y2  */
00249 {
00250 int incx, incy;
00251 int x;
00252 int duty_cycle;
00253 int delta_x, delta_y;
00254 int dots;
00255 
00256 delta_y = y2-y1;
00257 delta_x = x2-x1;
00258 incx =  incy = 1;
00259 x = x1;
00260 if (delta_y < 0) 
00261    {
00262    delta_y = -delta_y;
00263    incy = -1;
00264    }
00265 
00266 if (delta_x < 0) 
00267    {
00268    delta_x = -delta_x;
00269    incx = -1;
00270    }
00271 
00272 duty_cycle = (delta_x - delta_y)/2;
00273 if (delta_x >= delta_y)
00274     {
00275     int lasty = y1-1;
00276     dots = delta_x+1;
00277     while (--dots >= 0)
00278         {
00279         if (lasty != y1)
00280             {
00281             *ebuf++ = x1;
00282             lasty = y1;
00283             }
00284         duty_cycle -= delta_y;
00285         x1 += incx;
00286         if (duty_cycle < 0)
00287             {
00288             duty_cycle += delta_x;        /* update duty cycle */
00289             y1+=incy;
00290             }
00291         }
00292     }
00293 else
00294     {
00295     dots = delta_y+1;
00296     while (--dots >= 0)
00297         {
00298         *ebuf++ = x1;
00299         duty_cycle += delta_x;
00300         y1+=incy;
00301         if (duty_cycle > 0)
00302             {
00303             duty_cycle -= delta_y;        /* update duty cycle */
00304             x1 += incx;
00305             }
00306         }
00307     }
00308 return(ebuf);
00309 }
00310 #endif /* SOME_OFF_BY_ONE_PROBLEMS */
00311 
00312 
00313 #ifdef CONVEX_OPTIMIZATION
00314 int *fill_ebuf(struct gfxPoint *thread, int count, int *ebuf)
00315 /* Make list of x coordinates for a couple of lines.  Assumes that
00316  * the y coordinates are monotonic.  Ebuf needs to be as big as the
00317  * total height of thread.  Returns next free space in ebuf. */
00318 {
00319 int x, y, ox, oy;
00320 int i;
00321 
00322 ox = thread->x;
00323 oy = thread->y;
00324 
00325 for (i=0; i<count; ++i)
00326    {
00327    thread = thread->next;
00328    x = thread->x;
00329    y = thread->y;
00330    if (y!=oy)
00331       ebuf = xdda_ebuf(ebuf,ox,oy,x,y) - 1;
00332    ox = x;
00333    oy = y;
00334    }
00335 return(ebuf+1);
00336 }
00337 
00338 static void blast_hlines(struct memGfx *mg, 
00339         int *thread1,   /* List of start X positions. */
00340         int *thread2,   /* Backwards list of end positions. */
00341         int y,          /* Y position. */
00342         int count,      /* Number of horizontal lines to draw. */
00343         Color color)    /* Color. */
00344 /* Draw a whole bunch of horizontal lines. */
00345 {
00346 int x1, x2;
00347 thread2 += count;
00348 while (--count >= 0)
00349     {
00350     x1 = *thread1++;
00351     x2 = *(--thread2);
00352     if (x1 > x2)
00353         mgLineH(mg, y, x2, x1, color);
00354     else
00355         mgLineH(mg, y, x1, x2, color);
00356     y += 1;
00357     }
00358 }
00359 
00360 void mgDrawPolyFilled(struct memGfx *mg, struct gfxPoly *poly, Color color)
00361 /* Draw filled polygon, possibly with outline in a seperate color. */
00362 {
00363 struct gfxPoint *p;
00364 struct gfxPoint *np;
00365 struct gfxPoint *peak;
00366 struct gfxPoint *valley;
00367 int highy;
00368 int i;
00369 int pcount;
00370 int lasty;
00371 
00372 peak = p = poly->ptList;
00373 i = poly->ptCount;
00374 highy = p->y;
00375 pcount = 0;
00376 while (--i > 0)
00377     {
00378     p = p->next;
00379     if (p->y <= highy)
00380         {
00381         peak = p;
00382         highy = p->y;
00383         }
00384     }
00385 p = peak;
00386 np = p->next;
00387 i = poly->ptCount;
00388 while (--i >= 0)
00389     {
00390     if (np->y < p->y)
00391         {
00392         int totalHeight;
00393         int *thread1, *thread2;
00394         valley = p;
00395         p = np;
00396         np = np->next;
00397         while (--i >= 0)
00398             {
00399             if (np->y > p->y)
00400                 {
00401                 fillConcave(mg, poly, color);
00402                 return; /*sorry its concave*/
00403                 }
00404             p = np;
00405             np = np->next;              
00406             }
00407         totalHeight = valley->y - highy + 1;
00408         AllocArray(thread1, totalHeight);
00409         AllocArray(thread2, totalHeight);
00410         fill_ebuf(peak, pcount, thread1);
00411         pcount = fill_ebuf(valley, poly->ptCount - pcount, thread2)
00412                 - thread2;
00413         blast_hlines(mg, thread1, thread2, highy, pcount, color);
00414         freeMem(thread1);
00415         freeMem(thread2);
00416         return;
00417         }
00418     pcount++;
00419     p = np;
00420     np = np->next;
00421     }   
00422 return; /*all points of poly have same y value */
00423 }
00424 #endif /* CONVEX_OPTIMIZATION */
00425 
00426 void mgDrawPoly(struct memGfx *mg, struct gfxPoly *poly, Color color,
00427         boolean filled)
00428 /* Draw polygon, possibly filled in color. */
00429 {
00430 if (filled)
00431     fillConcave(mg, poly, color);
00432     // mgDrawPolyFilled(mg, poly, color);
00433 mgDrawPolyOutline(mg, poly, color);
00434 }
00435 

Generated on Tue Dec 25 18:39:31 2007 for blat by  doxygen 1.5.2