home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_progs / prog-asm / talincod.lha / talincode.lha / fill.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-19  |  22.6 KB  |  870 lines

  1. /* test program for bezier curves */
  2.  
  3. #include "std_headers.h"
  4. #include "newlib/yfunctions.h"
  5.  
  6. struct Library            *IntuitionBase,
  7.                         *GfxBase;
  8. struct Window            *Window;
  9. struct Screen            *Screen;
  10. struct RastPort            *rp;
  11.  
  12. USHORT colors[8] = { 0x000,0xFFF,0xE00,0xFE0,0x666,0xABB,0x0E0,0x0A0 };
  13. struct NewScreen NewScreen = { 0,0,320,200, 3, 0,1, NULL, CUSTOMSCREEN, };
  14.  
  15. struct NewWindow back_ground =
  16. {    0,0,320,200, 0,1, MENUPICK | MOUSEMOVE,
  17.     NOCAREREFRESH | ACTIVATE | SMART_REFRESH | BORDERLESS | REPORTMOUSE,
  18.     NULL,NULL,NULL,NULL,NULL,320,200,320,200,CUSTOMSCREEN,
  19. };
  20.  
  21. struct IntuiText quit_tx = { 5,1,JAM2,4,1,NULL,(UBYTE *)"Exit" };
  22. struct MenuItem quit_mi = { NULL,0,0,190,10,ITEMTEXT | ITEMENABLED | HIGHCOMP | COMMSEQ,0,(APTR)&quit_tx,0,'X' };
  23. struct Menu menu1 = { NULL, 1,0,56,10, MENUENABLED | MIDRAWN, (BYTE *)"File", &quit_mi };
  24.  
  25. void test(void);
  26.  
  27. main()
  28. {    if ((IntuitionBase = OpenLibrary("intuition.library",0)) == NULL) LEAVE;
  29.     if ((GfxBase = OpenLibrary("graphics.library",0)) == NULL) LEAVE;
  30.     if ((Screen = OpenScreen(&NewScreen)) == NULL) LEAVE;
  31.     LoadRGB4(&(Screen->ViewPort),colors,elementsof(colors));
  32.     back_ground.Screen = Screen;
  33.  
  34.     unless (Window = OpenWindow(&back_ground)) LEAVE;
  35.     rp = Window->RPort;
  36.     SetMenuStrip(Window,&menu1);
  37.  
  38.     SetAPen(rp,1); SetRast(rp,0);
  39.  
  40.     test();
  41.  
  42.     for (;;)
  43.     {    struct IntuiMessage *imsg;
  44.  
  45.         Wait(1 << Window->UserPort->mp_SigBit);
  46.  
  47.         while (imsg = (struct IntuiMessage *)GetMsg(Window->UserPort))
  48.         {    ULONG        class;
  49.             USHORT        code;
  50.  
  51.             class = imsg->Class;
  52.             code = imsg->Code;
  53.             ReplyMsg(&imsg->ExecMessage);
  54.  
  55.             if (class == MENUPICK && code != MENUNULL) LEAVE;
  56.         }
  57.     }
  58. exitit:
  59.     if (Window){ ClearMenuStrip(Window); CloseWindow(Window); }
  60.     if (Screen) CloseScreen(Screen); 
  61.     if (GfxBase) CloseLibrary(GfxBase);
  62.     if (IntuitionBase) CloseLibrary(IntuitionBase);
  63. }
  64.  
  65. typedef long            Fixed;
  66.  
  67.     /* conversion routines for fixed-point numbers */
  68.  
  69. #define FR(x)            ((x) + 0x8000 >> 16)    /* convert fixed to integer        */
  70. #define ff(x)            ((long)(x) << 16)        /* convert integer to fixed        */
  71. #define AVE(a,b)         (((a) + (b)) / 2)
  72.  
  73. typedef struct {                                /* A 2-D point                    */
  74.     Fixed                x,
  75.                         y;
  76. } point;
  77.  
  78. typedef struct {                                /* 3 points make a curve        */
  79.     point                start,
  80.                         control,
  81.                         end;
  82. } curve;
  83.  
  84.     /*    An edgelist is the output of the scan conversion routine. It points
  85.         to a block of memory which is an array of x coordinates. If there are
  86.         more coordinates than will fit in the array, a new edgelist and a
  87.         new array are created, and the 'next' pointer of the current edgelist
  88.         points to the new edgelist.
  89.     */
  90.  
  91. typedef struct _edgelist {
  92.     struct _edgelist    *next;                    /* pointer to next in chain        */
  93.     WORD                top,                    /* topmost scanline of this list*/
  94.                         bottom;                    /* bottom scanline of this list */
  95.     UBYTE                left_edge;                /* a left edge or right edge?    */
  96.     UBYTE                rising;                    /* a rising edge                */
  97.     WORD                *points;                /* next point to fill in        */
  98. } edgelist;
  99.  
  100.     /*    This structure defines the memory blocks which are allocated for
  101.         storing the coordinates.
  102.     */
  103.  
  104. #define    BLOCKSIZE        1024
  105.  
  106. struct edge_block {
  107.     struct edge_block    *next;
  108.     UBYTE                point_data[BLOCKSIZE];
  109. };
  110.  
  111.     /*    This structure contains all the variables used by the path drawer. */
  112.     /*    NOTE: Added start_xpos and start_ypos to keep track of the first
  113.         "pen-adjusted" point we drew at.
  114.     */
  115.  
  116. typedef struct {
  117.     struct edge_block     *first_block,            /* head of block chain            */
  118.                         *current_block;            /* currently filling block        */
  119.  
  120.     edgelist            *first_edge,            /* head of edgelist chain        */
  121.                         *current_edge;            /* currently filling edgelist    */
  122.  
  123.     WORD                *first_available_point,    /* marks available space in block*/
  124.                         *last_available_point;
  125.  
  126.     Fixed                cx,                        /* current pen positions.        */
  127.                         cy;
  128.  
  129.     WORD                current_xpos,            /* used for tracking edge moves    */
  130.                         current_ypos,
  131. /* new */                start_xpos,                /* original _adjusted_ points    */
  132. /* new */                start_ypos;
  133.  
  134.     WORD                pen_width,                /* makes fill shapes thicker    */
  135.                         pen_height;
  136.                             
  137.     WORD                pen_offset;                /* used in pen calculations        */
  138.     UBYTE                error_code;                /* Set if there was an error    */
  139.     UBYTE                is_hole;                /* TRUE if shape is a 'hole'    */
  140.  
  141.     WORD                min_x,                    /* limits for drawing            */
  142.                         min_y,
  143.                         max_x,
  144.                         max_y;
  145. } PathInfo;
  146.  
  147.     /*    a global PathInfo structure, which will eventually become a parameter... */
  148.  
  149. PathInfo                testpath;
  150. PathInfo                *thePath = &testpath;
  151.  
  152.     /*    move and draw, using fixed-point numbers */
  153.  
  154. #define fmoveto(path,x,y)    { path->current_xpos = FR(path->cx = x); path->current_ypos = FR(path->cy = y); }
  155. #define flineto(path,x,y)    LineEdge(path->cx, path->cy, x, y)
  156.  
  157.     /*    test to determine if an array of coordinates is full */
  158.  
  159. #define EDGELIST_FULL(e)    (thePath->first_available_point >= thePath->last_available_point)
  160.  
  161.     /*    As we finish an edge, we insert it into the list of edges in sorted order.
  162.         Later the edge drawer will traverse that list in drawing the edges.
  163.     */
  164.  
  165. void insert_last_edge(void)
  166. {    
  167.         /*    Look for insertion point, and insert there.
  168.         */
  169.  
  170.     if (thePath->current_edge)
  171.     {    edgelist        **s = &thePath->first_edge;
  172.  
  173.         for (;;)
  174.         {    if ( ((*s) == NULL) ||
  175.                  (*s)->top > thePath->current_edge->top ||
  176.                  ((*s)->top == thePath->current_edge->top &&
  177.                   *(*s)->points > *thePath->current_edge->points) )
  178.                       break;
  179.             s = &(*s)->next;
  180.         }
  181.         thePath->current_edge->next = *s;
  182.         *s = thePath->current_edge;
  183.     }
  184. }
  185.  
  186.     /*    Make a new edgelist. Note that there are two basic types of edgelists,
  187.         Those that are drawing upwards (decreasing scanline order) and those
  188.         that are drawing downwards (increasing scanline order). The upwards
  189.         edgelists fill coordinates from the top of the array down, while
  190.         the downward edgelists fill coordinates from the bottom of the array
  191.         up. (If an edgelist switches direction, it is terminated and a new
  192.         edgelist is begun). Eventually things meet in the middle, at which
  193.         point the array is full and a new array and edglist are created.
  194.     */
  195.  
  196. edgelist *alloc_edgelist(int upwards)
  197. {
  198.         /* put the now-completed previous edge into the sorted list */
  199.  
  200.     insert_last_edge();
  201.  
  202.         /*    The edgelists themselves are actually stored in the buffer
  203.             along with the coordinates. This checks to see if there is
  204.             enough space in the current buffer for an edgelist.
  205.         */
  206.  
  207.     if ( thePath->first_available_point + (sizeof *thePath->current_edge / 2) >=
  208.             thePath->last_available_point )
  209.     {    struct edge_block    *new;
  210.  
  211.             /*    This is the part that actually allocates a new memory block.
  212.                 The Path drawer has been designed to allow the option of
  213.                 re-using the same blocks for each path drawn, rather than
  214.                 allocating and freeing them each time. That's why this
  215.                 next line of code.
  216.             */
  217.  
  218.         if (thePath->current_block && thePath->current_block->next)
  219.             new = thePath->current_block->next;
  220.         else
  221.         {    unless (new = AllocMem(sizeof (*new),0L)) return NULL;
  222.             new->next = NULL;
  223.         }
  224.  
  225.             /*    inform the path structure that there's a new block */
  226.  
  227.         thePath->first_available_point = (WORD *)(new->point_data);
  228.         thePath->last_available_point = (WORD *)(new + 1);
  229.         if (thePath->current_block) thePath->current_block->next = new;
  230.         else thePath->first_block = new;
  231.         thePath->current_block = new;
  232.     }
  233.  
  234.         /* now, mark the space in the block as used by the edgelist header. */
  235.  
  236.     thePath->current_edge = (edgelist *)thePath->first_available_point;
  237.     thePath->first_available_point += sizeof (edgelist) / 2;
  238.  
  239.         /* innitialize the new edgelist structure */
  240.  
  241.     thePath->current_edge->next = NULL;
  242.     if (upwards)
  243.     {    thePath->current_edge->points = thePath->last_available_point;
  244.         thePath->current_edge->bottom = thePath->current_ypos;
  245.     }
  246.     else
  247.     {    thePath->current_edge->points = thePath->first_available_point;
  248.         thePath->current_edge->top = thePath->current_ypos;
  249.     }
  250.     thePath->current_edge->rising = upwards;
  251.     thePath->current_edge->left_edge = upwards;
  252.  
  253.         /*    This commented-out statement could eventually be used to
  254.             provide "holes" by reverse the sense of left / right for
  255.             a particular path.
  256.         */
  257.  
  258. /*    thePath->current_edge->left_edge = is_hole ? !upwards : upwards; */
  259.     return thePath->current_edge;
  260. }
  261.  
  262.     /*    This routine clars out the PathInfo structure. If there are any memory
  263.         blocks left over from the last path we drew, it resets them.
  264.     */
  265.  
  266. BOOL init_edgelists(void)
  267. {
  268.     if ( (thePath->current_block = thePath->first_block) )
  269.     {    thePath->first_available_point = (WORD *)(thePath->current_block->point_data);
  270.         thePath->last_available_point = (WORD *)(thePath->current_block + 1);
  271.     }
  272.     else
  273.     {    thePath->first_available_point = thePath->last_available_point = NULL;
  274.     }
  275.  
  276.     thePath->first_edge = NULL;
  277.     thePath->current_edge = NULL;
  278. }
  279.  
  280.     /*    This routine frees all but the first memory block, unless "all" is
  281.         non-zero, in which case it frees the first one too. The reason for
  282.         this is that it you are drawing a lot of small splines, you may want
  283.         to keep one block around always.
  284.     */
  285.  
  286. void flush_edgelists(BOOL all)
  287. {
  288.     thePath->first_available_point = thePath->last_available_point = NULL;
  289.     if (thePath->first_block)
  290.     {    struct edge_block *new;
  291.     
  292.         while (new = thePath->first_block->next)
  293.         {    thePath->first_block->next = new->next;
  294.             FreeMem(new,sizeof *new);
  295.         }
  296.         if (all)
  297.         {    FreeMem(thePath->first_block,sizeof *thePath->first_block);
  298.             thePath->first_block = thePath->current_block = NULL;
  299.         }
  300.     }
  301. }
  302.  
  303.     /*    This (rather complicated looking) routine adds points to an edgelist.
  304.  
  305.         If the new point is on the same scanline as the previous point, then
  306.         not very much happens. For each scanline of difference between the
  307.         new point and the old, however, a new coordinate is added. Right now,
  308.         the X coordinates of any newly added points will be the same as the
  309.         X coordinte of the new point. This is mainly for the convenience of
  310.         the higher-level line-drawing routines which may not wish to deal
  311.         with vertical or horizontal lines (which some of them indeed do,
  312.         for speed).
  313.         
  314.         Note that this means that each newly added coordinate represents
  315.         the point on the old, not the new, scan line.
  316.     */
  317.  
  318. void add_edge_points(WORD x,WORD y)
  319. {    WORD                old_pen_offset = thePath->pen_offset;
  320.  
  321.         /* note that the ordering of the following operations is important */
  322.  
  323.         /* adjust y position for pen height */
  324.  
  325.     if (x < thePath->current_xpos) thePath->pen_offset = thePath->pen_height;
  326.     else if (x > thePath->current_xpos) thePath->pen_offset = 0;
  327.  
  328.         /* clip y position to screen coords */
  329.  
  330.     y = clamp(thePath->min_y, y + thePath->pen_offset, thePath->max_y);
  331.  
  332. /* new code */
  333.  
  334.         /*    This code is a special case for the first generated point. Basically.
  335.             it attempts to compensate for the fact that because of the pen width
  336.             and height, the current_xpos and current_ypos may not be an actual
  337.             point on the path. We can't adjust that beforehand since we don't
  338.             yet know what direction we are going to be drawing in until we
  339.             get to this point, and pen offsets are derived from drawing direction.
  340.  
  341.             What this does is adjust the current_xpos and current_ypos to
  342.             the correct pen offset and save those points. Later, we will attempt
  343.             to "hook up" to those same points as the last drawing stroke.
  344.         */
  345.  
  346.     if (thePath->current_edge == NULL)
  347.     {
  348.         WORD upwards;
  349.         WORD adjusted_current_ypos;
  350.  
  351.         adjusted_current_ypos = 
  352.             clamp(thePath->min_y, thePath->current_ypos + thePath->pen_offset, thePath->max_y);
  353.  
  354.         if (y == adjusted_current_ypos)        /* if no change in y, do nothing...        */
  355.             return;
  356.  
  357.             /*    At this point, we know we are definately going to draw _something_.
  358.                 We know the drawing direction. So set up all the variables
  359.                 we need to remember the start point.
  360.             */
  361.                 
  362.         thePath->current_ypos = adjusted_current_ypos;
  363.         upwards = (thePath->current_ypos > y);
  364.         
  365.         thePath->start_xpos = thePath->current_xpos;
  366.         thePath->start_ypos = thePath->current_ypos;
  367.  
  368.             /*    All that remains is to make sure that the "first" point also gets
  369.                 added to the edge list.
  370.  
  371.                 We do this by cheating -- basically, we lie about what scan line
  372.                 we're on.
  373.             */
  374.  
  375.         if (upwards)
  376.         {    thePath->current_ypos++;
  377.         }
  378.         else
  379.         {    /* thePath->current_ypos--; */ /* I don't know why this doesn't work */
  380.             thePath->current_xpos += thePath->pen_width;
  381.         }
  382.     }
  383. /* end new code */
  384.  
  385.     if (thePath->current_ypos < y)                    /* if line drawing down */
  386.     {
  387.             /* if old edge was rising, or full, then alloc a new edge */
  388.  
  389.         if (!thePath->current_edge || thePath->current_edge->rising)
  390.         {    unless (alloc_edgelist(FALSE)) return;
  391.         }
  392.  
  393.             /* kludge for pen width */
  394.  
  395.         if (old_pen_offset != thePath->pen_offset)
  396.         {    while (thePath->current_ypos < y-1)
  397.             {
  398.                 if (EDGELIST_FULL(thePath->current_edge))
  399.                 {    unless (alloc_edgelist(FALSE)) return;
  400.                 }
  401.                 
  402.                 *thePath->first_available_point++ = thePath->current_xpos + thePath->pen_width;
  403.                 thePath->current_ypos++;
  404.             }
  405.         }
  406.  
  407.             /* draw edge down the page */
  408.  
  409.         while (thePath->current_ypos < y)
  410.         {
  411.             if (EDGELIST_FULL(thePath->current_edge))
  412.             {    unless (alloc_edgelist(FALSE)) return;
  413.             }
  414.  
  415.             *thePath->first_available_point++ = x + thePath->pen_width;
  416.             thePath->current_ypos++;
  417.         }
  418.         thePath->current_edge->bottom = y;
  419.     }
  420.     else if (thePath->current_ypos > y)
  421.     {    
  422.             /* if old edge was falling, or full, then alloc a new edge */
  423.     
  424.         if (!thePath->current_edge || !thePath->current_edge->rising)
  425.         {    unless (alloc_edgelist(TRUE)) return;
  426.         }
  427.  
  428.             /* kludge for pen offset */
  429.  
  430.         if (old_pen_offset != thePath->pen_offset)
  431.         {    while (thePath->current_ypos > y + 1)
  432.             {
  433.                 if (EDGELIST_FULL(thePath->current_edge))
  434.                 {    unless (alloc_edgelist(TRUE)) return;
  435.                 }
  436.  
  437.                 *--thePath->last_available_point = thePath->current_xpos;
  438.                 thePath->current_ypos--;
  439.             }
  440.         }
  441.  
  442.             /* draw edge up the page */
  443.  
  444.         while (thePath->current_ypos > y)
  445.         {
  446.             if (EDGELIST_FULL(thePath->current_edge))
  447.             {    unless (alloc_edgelist(TRUE)) return;
  448.             }
  449.  
  450.             *--thePath->last_available_point = x;
  451.             thePath->current_ypos--;
  452.         }
  453.         thePath->current_edge->points = thePath->last_available_point;
  454.         thePath->current_edge->top = y;
  455.     }
  456.  
  457.     thePath->current_xpos = x;
  458. }
  459.  
  460.     /*    This is just a glue function which converts fixed to integer and then
  461.         adds the edge points.
  462.     */
  463.  
  464. void VLineEdgePoints(Fixed x, Fixed y)
  465. {    add_edge_points(FR(x),FR(y));
  466. }
  467.  
  468.     /* This routine draws short lines (up to 256 pixels) with accurate jaggies
  469.         based on sub-pixel positioning of endpoints.
  470.  
  471.         For longer lines, we should eventually subdivide and recurse...
  472.     */
  473.  
  474. void LineEdge(Fixed x1, Fixed y1, Fixed x2, Fixed y2)
  475. {    Fixed                dx = x2 - x1;
  476.     Fixed                dy;
  477.     WORD                height,
  478.                         y,
  479.                         yoffset;
  480.  
  481.     x1 += 0x8000;
  482.     y1 += 0x8000;
  483.     y2 += 0x8000;
  484.     y = y1 >> 16;
  485.  
  486.     if (y1 < y2)
  487.     {
  488.         height = (y2 >> 16) - y;
  489.         if (height == 0) return;
  490.  
  491.             /* We could check for very long lines and handle differently... */
  492.  
  493.         dy = (y2 - y1) >> 12;
  494.     
  495.         yoffset = (y1 >> 12) & 0x0f;
  496.  
  497.         if (dy < 1) dy = 1;
  498.         dx = (dx << 4) / dy;
  499.         x1 += (dx * (15 - yoffset)) >> 4;
  500.  
  501.         while (height--)
  502.         {    add_edge_points(x1>>16,++y);
  503.             x1 += dx;
  504.         }
  505.     }
  506.     else
  507.     {
  508.         height = y - (y2 >> 16);
  509.         if (height == 0) return;
  510.  
  511.             /* We could check for very long lines and handle differently... */
  512.  
  513.         dy = (y1 - y2) >> 12;
  514.  
  515.         yoffset = (y1 >> 12) & 0x0f;
  516.     
  517.         if (dy < 1) dy = 1;
  518.         dx = (dx << 4) / dy;
  519.         x1 += (dx * yoffset) >> 4;
  520.  
  521.         while (height--)
  522.         {    add_edge_points(x1>>16,--y);
  523.             x1 += dx;
  524.         }
  525.     }
  526. }
  527.  
  528.     /* A recursive curve-drawer */
  529.  
  530. void CurveEdge(curve *cur, WORD level)
  531. {
  532.     WORD    dx = FR(cur->start.x) - FR(cur->end.x);
  533.     WORD    dy = FR(cur->start.y) - FR(cur->end.y);
  534.  
  535.         /* if all points are on the same scanline */
  536.  
  537.     if ( dy == 0 && FR(cur->start.y) == FR(cur->control.y) )
  538.     {    VLineEdgePoints(cur->control.x, cur->control.y);
  539.         VLineEdgePoints(cur->end.x, cur->end.y);
  540.         return;
  541.     }
  542.  
  543.         /* if all points are in the same column */
  544.  
  545.     if ( dx == 0 && FR(cur->start.x) == FR(cur->control.x) )
  546.     {    VLineEdgePoints(cur->control.x, cur->control.y);
  547.         VLineEdgePoints(cur->end.x, cur->end.y);
  548.         return;
  549.     }
  550.  
  551.     if (level)
  552.     {    curve            left,
  553.                         right;
  554.                         
  555.         left.start = cur->start;
  556.         left.control.x = AVE(cur->start.x, cur->control.x);
  557.         left.control.y = AVE(cur->start.y, cur->control.y);
  558.         right.control.x = AVE(cur->control.x, cur->end.x);
  559.         right.control.y = AVE(cur->control.y, cur->end.y);
  560.         left.end.x = right.start.x = AVE(left.control.x, right.control.x);
  561.         left.end.y = right.start.y = AVE(left.control.y, right.control.y);
  562.  
  563.         if ( ABS(cur->control.x - left.end.x) < 0x800 &&
  564.              ABS(cur->control.y - left.end.y) < 0x800 )
  565.         {
  566.             LineEdge(cur->start.x, cur->start.y, cur->end.x, cur->end.y);
  567.             return;
  568.         }
  569.  
  570.         right.end = cur->end;
  571.  
  572.         CurveEdge(&left,level-1);
  573.         CurveEdge(&right,level-1);
  574.     }
  575.     else
  576.     {    LineEdge(cur->start.x, cur->start.y, cur->end.x, cur->end.y);
  577.     }
  578. }
  579.  
  580.     /* This part probably does't need to be downcoded... */
  581.  
  582. typedef struct {
  583.     long                vectors;
  584.     long                controlBits[1];
  585.     /* point            vector[anyNumber]; */    /* variable-length data            */
  586. } path;
  587.  
  588. BOOL OnCurve(long *bits, long index)
  589. {    bits += index >> 5;
  590.     index &= 31;
  591.     return (*bits & (0x80000000 >> index)) == 0;
  592. }
  593.  
  594. typedef struct {
  595.     int                    isLine;
  596.     curve                c;
  597.     
  598.     /* Private */
  599.     long                index;
  600.     long                ep;
  601.     long                *bits;
  602.     point                *p;
  603. } pathWalker;
  604.  
  605. void InitPathWalker (pathWalker *w, path *aPath)
  606. {    w->index = 0;
  607.     w->ep = aPath->vectors - 1;
  608.     w->bits = aPath->controlBits;
  609.     /* skip path the controlbits to point to the first point */
  610.     w->p = (point *)(w->bits + (aPath->vectors + 31 >> 5));
  611. }
  612.  
  613. int NextPathSegment(pathWalker *w)
  614. {    long                prevIndex,
  615.                         nextIndex;
  616.                         
  617.     if (w->index == 0)
  618.     {    if (OnCurve(w->bits, w->ep))
  619.             w->c.start = w->p[w->ep];
  620.         else
  621.         {    if (OnCurve(w->bits,0))
  622.             {    w->c.start = w->p[0];
  623.                 w->index = 1;
  624.             }
  625.             else    /* start at an implied on-curve point */
  626.             {    w->c.start.x = AVE(w->p[0].x, w->p[w->ep].x);
  627.                 w->c.start.y = AVE(w->p[0].y, w->p[w->ep].y);
  628.             }
  629.         }
  630.     }
  631.     else
  632.         w->c.start = w->c.end;
  633.  
  634. NEXT_SEGMENT:
  635.     /* Compute the point index before and after the current one.
  636.      * This wraps around, since we assume the contour is closed. */
  637.      
  638.      prevIndex = w->index == 0 ? w->ep : w->index - 1;
  639.      nextIndex = w->index == w->ep ? 0 : w->index + 1;
  640.      
  641.      if (OnCurve(w->bits, w->index))
  642.      {    if (OnCurve(w->bits, prevIndex))
  643.          {    w->isLine = TRUE;                /* this means we have a line */
  644.             w->c.end = w->p[w->index];
  645.         }
  646.         else if (w->index++ <= w->ep)
  647.             goto NEXT_SEGMENT;
  648.     }
  649.     else
  650.     {    w->isLine = FALSE;                    /* this means we have a curve */
  651.         w->c.control = w->p[w->index];
  652.         if (OnCurve(w->bits,nextIndex))
  653.             w->c.end = w->p[nextIndex];
  654.         else
  655.         {    w->c.end.x = AVE(w->p[w->index].x, w->p[nextIndex].x);
  656.             w->c.end.y = AVE(w->p[w->index].y, w->p[nextIndex].y);
  657.         }
  658.     }
  659.     return w->index++ <= w->ep;    /* return TRUE if there are still more segments */
  660. }
  661.  
  662. path *NextPath(path *aPath)
  663. {    return (path *)((long *)aPath + 1 + (aPath->vectors + 31 >> 5) +
  664.         aPath->vectors * 2);
  665. }
  666.  
  667.     /*    This routine traverses all the data structures we built, and calls
  668.         a routine to paint the data onto the screen.
  669.     
  670.         This part should be downcoded, and in addition the part that does the
  671.         actual drawing should be a callback.
  672.     */
  673.  
  674. #define kCurveLimit            4
  675.  
  676. BOOL fill_edges(WORD *ystart, UWORD maxlines, UWORD windmask)
  677. {    WORD                ypos = *ystart;
  678.     WORD                start_x;
  679.     WORD                wind;
  680.     edgelist            *alist = NULL,
  681.                         *tlist,
  682.                         *edge,
  683.                         **edge_ptr;
  684.  
  685.     ypos = thePath->first_edge->top;
  686.  
  687.     while (maxlines--)
  688.     {    if (alist == NULL && thePath->first_edge == NULL) return FALSE;
  689.  
  690.             /* transfer polygons from thePath->first_edge to active edge list... */
  691.  
  692.         while (thePath->first_edge &&
  693.                thePath->first_edge->top <= ypos)    /* if scan line intersects next poly */
  694.         {    edge = thePath->first_edge;
  695.             thePath->first_edge = edge->next;
  696.  
  697.             edge->next = alist;
  698.             alist = edge;
  699.             edge->bottom--;
  700.         }
  701.  
  702.             /* if sorting needed, re-sort list of active edges */
  703.  
  704.         tlist = alist;
  705.         alist = NULL;
  706.  
  707.             /* sort the list of active edges...
  708.                 Note that this sort takes advantage of existing coherency
  709.                 in the list and is therefore quite fast.
  710.             */
  711.  
  712.         while (tlist)
  713.         {    edge = tlist;
  714.             tlist = tlist->next;
  715.             if (ypos >= edge->bottom) continue;
  716.  
  717.             for (edge_ptr = &alist; *edge_ptr; edge_ptr = &(*edge_ptr)->next )
  718.             {    if ( *(*edge_ptr)->points > *edge->points ) break;
  719.             }
  720.             edge->next = *edge_ptr;
  721.             *edge_ptr = edge;
  722.         }
  723.  
  724.             /* "winding fill" algorithm */
  725.  
  726.         wind = 0;                            /* initially outside of figure */
  727.  
  728.         for (edge = alist; edge; edge = edge->next)
  729.         {    WORD    stop_x;
  730.  
  731.         /*    stop_x = clamp(thePath->min_x, *edge->points++, thePath->max_x); */
  732.             stop_x = *edge->points++;
  733.  
  734.             if ((wind & windmask) == 0) start_x = stop_x;
  735.  
  736.             if (edge->left_edge) ++wind; else --wind;
  737.  
  738.                 /* Here's where we actualy draw the line. We can replace
  739.                     this with whatever code is appropriate... */
  740.                 
  741.             if ((wind & windmask) == 0)
  742.                 PaintRect(rp,start_x,ypos,stop_x-start_x,1);
  743.         }
  744.  
  745.         ypos++;
  746.     }
  747.     *ystart = ypos;
  748.     return (alist || thePath->first_edge);
  749. }
  750.  
  751.     /* This is the overall routine which calls all the others */
  752.  
  753. path *FramePath(path *cont)
  754. {    pathWalker                walker;
  755.     WORD                    org_x,org_y;
  756.     WORD                    ypos;
  757.  
  758.     unless (init_edgelists()) return NULL;
  759.  
  760.     InitPathWalker(&walker,cont);
  761.  
  762.         /* the first segment is special, since it calls fmoveto */
  763.  
  764.     if (NextPathSegment(&walker))
  765.     {    fmoveto(thePath,walker.c.start.x, walker.c.start.y);
  766.         org_x = thePath->current_xpos;
  767.         org_y = thePath->current_ypos;
  768.         if (walker.isLine)
  769.             flineto(thePath,walker.c.end.x, walker.c.end.y);
  770.         else
  771.             CurveEdge(&walker.c, kCurveLimit);
  772.     }
  773.  
  774.         /* keep looping until we run out of segments */
  775.  
  776.     while (NextPathSegment(&walker))
  777.     {    
  778.         if (walker.isLine)
  779.             flineto(thePath,walker.c.end.x, walker.c.end.y);
  780.         else
  781.             CurveEdge(&walker.c, kCurveLimit);
  782.     }
  783.  
  784. #if 0
  785.     add_edge_points(org_x,org_y);
  786.     thePath->pen_offset = 0;
  787.     add_edge_points(org_x,org_y);
  788.     insert_last_edge();
  789. #endif
  790. /* new code */
  791.     {    WORD        save_width, save_height;
  792.  
  793.             /* draw back to origin point */
  794.  
  795.         add_edge_points(org_x,org_y);
  796.  
  797.             /* now, draw back to EXACT (no pen adjustment) first point. */
  798.  
  799.         save_width = thePath->pen_width; thePath->pen_width = 0;
  800.         save_height = thePath->pen_height; thePath->pen_height = 0;
  801.         thePath->pen_offset = 0;
  802.  
  803.         add_edge_points(thePath->start_xpos,thePath->start_ypos);
  804.  
  805.         thePath->pen_width = save_width;
  806.         thePath->pen_height = save_height;
  807.  
  808.         insert_last_edge();
  809.     }
  810. /* end new code */
  811.  
  812.         /* now, traverse the lists... */
  813.     
  814.     ypos = thePath->first_edge->top;
  815.     fill_edges(&ypos,-1,~0);
  816.  
  817.         /* return the next path, used if this path is one of several within
  818.             a series of paths.
  819.         */
  820.     
  821.     return NextPath(cont);
  822. }
  823.  
  824. void test(void)
  825. {
  826.     long myPath[] = {
  827.         4,
  828.         0x50000000,
  829.         ff(47),ff(200-101),
  830.         ff(146),ff(200-124),
  831.         ff(225),ff(200-68),
  832.         ff(146),ff(200-119),
  833.     };
  834.  
  835.     long myPath2[] = {
  836.         4,
  837.         0x50000000,
  838.         ff(17),ff(7),
  839.         ff(30),ff(17),
  840.         ff(42),ff(5),
  841.         ff(30),ff(19),
  842.     };
  843.  
  844.     int                    i;
  845.     long sec1,sec2,mic1,mic2;
  846.  
  847.     thePath->min_x = 0;
  848.     thePath->max_x = 320;
  849.  
  850.     thePath->min_y = 0;
  851.     thePath->max_y = 200;
  852.  
  853.     thePath->pen_width = 1;
  854.     thePath->pen_height = 1;
  855.         
  856.     FramePath( (path *)myPath2 );
  857.  
  858.     CurrentTime((ULONG *)&sec1,(ULONG *)&mic1);
  859.     for (i=0; i<100; i++) FramePath( (path *)myPath );
  860.     CurrentTime((ULONG *)&sec2,(ULONG *)&mic2);
  861.  
  862.     sec1 = sec1 * 1000 + mic1 / 1000;
  863.     sec2 = sec2 * 1000 + mic2 / 1000;
  864.  
  865.     Printf("Elapsed time was %ld milliseconds\n",sec2-sec1);
  866.  
  867.     flush_edgelists(TRUE);
  868.  
  869. };
  870.