|
|
1.1 root 1: /******************************Module*Header*******************************\
2: * Module Name: fillpath.c
3: *
4: * DrvFillPath
5: *
6: * Copyright (c) 1992-1993 Microsoft Corporation
7: \**************************************************************************/
8:
9: // LATER identify convex polygons and special-case?
10: // LATER identify vertical edges and special-case?
11: // LATER move pointed-to variables into automatics in search loops
12:
13: #include "driver.h"
14:
15: #define MAX_PATH_RECTS 200 // maximum number of rects we'll fill per call to
16: // the fill code
17:
18: // Describe a single non-horizontal edge of a path to fill.
19: typedef struct _EDGE {
20: PVOID pNext;
21: INT iScansLeft;
22: INT X;
23: INT Y;
24: INT iErrorTerm;
25: INT iErrorAdjustUp;
26: INT iErrorAdjustDown;
27: INT iXWhole;
28: INT iXDirection;
29: INT iWindingDirection;
30: } EDGE, *PEDGE;
31:
32: //MIX translation table. Translates a mix 1-16, into an old style Rop 0-255.
33: extern BYTE gaMix[];
34:
35: VOID AdvanceAETEdges(EDGE *pAETHead);
36: VOID XSortAETEdges(EDGE *pAETHead);
37: VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY);
38: EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge, POINTFIX *ppfxEdgeStart,
39: POINTFIX *ppfxEdgeEnd);
40: VOID ConstructGET(EDGE *pGETHead, EDGE *pFreeEdges, PATHOBJ *ppo,
41: PATHDATA *pd, BOOL bMore);
42:
43: /******************************Public*Routine******************************\
44: * DrvFillPath
45: *
46: * Fill the specified path with the specified brush and ROP.
47: *
48: \**************************************************************************/
49:
50: BOOL DrvFillPath
51: (
52: SURFOBJ *pso,
53: PATHOBJ *ppo,
54: CLIPOBJ *pco,
55: BRUSHOBJ *pbo,
56: POINTL *pptlBrush,
57: MIX mix,
58: FLONG flOptions
59: )
60: {
61: PPDEV ppdev;
62: BYTE jClipping; // clipping type
63: EDGE *pCurrentEdge;
64: EDGE AETHead; // dummy head/tail node & sentinel for Active Edge Table
65: EDGE *pAETHead; // pointer to AETHead
66: EDGE GETHead; // dummy head/tail node & sentinel for Global Edge Table
67: EDGE *pGETHead; // pointer to GETHead
68: EDGE *pFreeEdges; // pointer to memory free for use to store edges
69: ULONG ulNumRects; // # of rectangles to draw currently in rectangle list
70: RECTL *prclRects; // pointer to start of rectangle draw list
71: INT iCurrentY; // scan line for which we're currently scanning out the
72: // fill
73:
74: RBRUSH_COLOR rbc; // Realized brush or solid color
75: PFNFILL pfnFill;// Points to appropriate fill routine
76:
77: BOOL bMore;
78: PATHDATA pd;
79:
80: // The drawing surface
81: ppdev = (PPDEV) pso->dhsurf;
82:
83: // Our rectangle fill routines work only in planar mode:
84: if (!(ppdev->fl & DRIVER_PLANAR_CAPABLE))
85: return(FALSE);
86:
87: // Is there clipping? We don't handle that
88: // LATER handle rectangle clipping
89:
90: // Set up the clipping type
91: if (pco == (CLIPOBJ *) NULL) {
92: // No CLIPOBJ provided, so we don't have to worry about clipping
93: jClipping = DC_TRIVIAL;
94: } else {
95: // Use the CLIPOBJ-provided clipping
96: jClipping = pco->iDComplexity;
97: }
98:
99: if (jClipping != DC_TRIVIAL) {
100: return(FALSE); // there is clipping; let GDI fill the path
101: }
102:
103: // There's nothing to do if there's only one point
104: // LATER is this needed?
105: if (ppo->cCurves <= 1) {
106: return(TRUE);
107: }
108:
109: // Do we have enough memory for all the edges?
110: // LATER does cCurves include closure?
111: if (ppo->cCurves > ((TMP_BUFFER_SIZE -
112: (MAX_PATH_RECTS * (ULONG)sizeof(RECTL))) / sizeof(EDGE))) {
113: return(FALSE); // too many edges; let GDI fill the path
114: }
115:
116: // See if we can handle this pattern and ROP
117:
118: // We don't handle ROP4s
119: // LATER how could we possibly get a ROP4?
120: if ((mix & 0xFF) != ((mix >> 8) & 0xFF)) {
121: return(FALSE); // it's a ROP4; let GDI fill the path
122: }
123:
124:
125: // See if we can use the solid brush accelerators, or have to draw a
126: // pattern
127: mix &= 0xFF;
128: switch (mix) {
129: case 0:
130: return(FALSE); // LATER should this ever happen?
131:
132: case R2_MASKNOTPEN:
133: case R2_NOTCOPYPEN:
134: case R2_XORPEN:
135: case R2_MASKPEN:
136: case R2_NOTXORPEN:
137: case R2_MERGENOTPEN:
138: case R2_COPYPEN:
139: case R2_MERGEPEN:
140: case R2_NOTMERGEPEN:
141: case R2_MASKPENNOT:
142: case R2_NOTMASKPEN:
143: case R2_MERGEPENNOT:
144:
145: // vTrgBlt can only handle solid color fills
146:
147: if (pbo->iSolidColor != 0xffffffff)
148: {
149: rbc.iSolidColor = pbo->iSolidColor;
150: pfnFill = vTrgBlt;
151: }
152: else
153: {
154: rbc.prb = (RBRUSH*) pbo->pvRbrush;
155: if (rbc.prb == NULL)
156: {
157: rbc.prb = (RBRUSH*) BRUSHOBJ_pvGetRbrush(pbo);
158: if (rbc.prb == NULL)
159: {
160: // If we haven't realized the brush, punt the call:
161:
162: return(FALSE);
163: }
164: }
165: if (!(rbc.prb->fl & RBRUSH_BLACKWHITE) &&
166: ((mix & 0xff) != R2_COPYPEN))
167: {
168: // Only black/white brushes can handle ROPs other
169: // than COPYPEN:
170:
171: return(FALSE);
172: }
173:
174: if (rbc.prb->fl & RBRUSH_NCOLOR)
175: pfnFill = vColorPat;
176: else
177: pfnFill = vMonoPat;
178: }
179:
180: break;
181:
182: // Rops that are implicit solid colors
183:
184: case R2_NOT:
185: case R2_WHITE:
186: case R2_BLACK:
187:
188: // Brush color parameter doesn't matter for these rops
189:
190: pfnFill = vTrgBlt;
191: break;
192:
193: case R2_NOP:
194: return TRUE;
195: }
196:
197: /* set up working storage in the temporary buffer */
198:
199: prclRects = ppdev->pvTmp; // storage for list of rectangles
200: // to draw
201:
202: /* enumerate path here first time to check for special
203: cases (rectangles, single pixel and monotone polygons) */
204:
205: /* it is too difficult to determine interaction between
206: multiple paths, if there is more than one, skip this */
207:
208: if (! (bMore = PATHOBJ_bEnum(ppo, &pd))) {
209: RECTL *rectangle;
210: INT i = pd.count;
211:
212: /* if the count is less than three than it is at best a
213: line which means nothing gets drawn so get out now */
214:
215: if (i < 3) return(TRUE);
216:
217: /* if the count is four, check to see if the polygon is
218: really a rectangle since we can really speed that up */
219:
220: if (i == 4) {
221: rectangle = prclRects;
222:
223: /* we have to start somewhere so assume that most
224: applications specify the top left point first
225:
226: we want to check that the first two points are
227: either vertically or horizontally aligned. if
228: they are then we check that the last point [3]
229: is either horizontally or vertically aligned,
230: and finally that the 3rd point [2] is aligned
231: with both the first point and the last point */
232:
233: #define FIX_SHIFT 4L
234: #define FIX_MASK (- (1 << FIX_SHIFT))
235:
236: rectangle->top = pd.pptfx[0].y - 1 & FIX_MASK;
237: rectangle->left = pd.pptfx[0].x - 1 & FIX_MASK;
238: rectangle->right = pd.pptfx[1].x - 1 & FIX_MASK;
239:
240: if (rectangle->left ^ rectangle->right) {
241: if (rectangle->top ^ (pd.pptfx[1].y - 1 & FIX_MASK))
242: goto not_rectangle;
243:
244: if (rectangle->left ^ (pd.pptfx[3].x - 1 & FIX_MASK))
245: goto not_rectangle;
246:
247: if (rectangle->right ^ (pd.pptfx[2].x - 1 & FIX_MASK))
248: goto not_rectangle;
249:
250: rectangle->bottom = pd.pptfx[2].y - 1 & FIX_MASK;
251: if (rectangle->bottom ^ (pd.pptfx[3].y - 1 & FIX_MASK))
252: goto not_rectangle;
253: }
254: else {
255: if (rectangle->top ^ (pd.pptfx[3].y - 1 & FIX_MASK))
256: goto not_rectangle;
257:
258: rectangle->bottom = pd.pptfx[1].y - 1 & FIX_MASK;
259: if (rectangle->bottom ^ (pd.pptfx[2].y - 1 & FIX_MASK))
260: goto not_rectangle;
261:
262: rectangle->right = pd.pptfx[2].x - 1 & FIX_MASK;
263: if (rectangle->right ^ (pd.pptfx[3].x - 1 & FIX_MASK))
264: goto not_rectangle;
265: }
266:
267: /* if the left is greater than the right then
268: swap them so the blt code doesn't wig out */
269:
270: if (rectangle->left > rectangle->right) {
271: FIX temp;
272:
273: temp = rectangle->left;
274: rectangle->left = rectangle->right;
275: rectangle->right = temp;
276: }
277: else {
278:
279: /* if left == right there's nothing to draw */
280:
281: if (rectangle->left == rectangle->right) {
282: return(TRUE);
283: }
284: }
285:
286: /* shift the values to get pixel coordinates */
287:
288: rectangle->left = (rectangle->left >> FIX_SHIFT) + 1;
289: rectangle->right = (rectangle->right >> FIX_SHIFT) + 1;
290:
291: if (rectangle->top > rectangle->bottom) {
292: FIX temp;
293:
294: temp = rectangle->top;
295: rectangle->top = rectangle->bottom;
296: rectangle->bottom = temp;
297: }
298: else {
299: if (rectangle->top == rectangle->bottom) {
300: return(TRUE);
301: }
302: }
303:
304: /* shift the values to get pixel coordinates */
305:
306: rectangle->top = (rectangle->top >> FIX_SHIFT) + 1;
307: rectangle->bottom = (rectangle->bottom >> FIX_SHIFT) + 1;
308:
309: /* if we get here then the polygon is a rectangle,
310: set count to 1 and goto bottom to draw it */
311:
312: ulNumRects = 1;
313: goto draw_remaining_rectangles;
314: }
315: }
316:
317:
318: /* if this is not one of the special cases then we find
319: ourselves here and we scan convert by building edges */
320:
321: not_rectangle:
322: pFreeEdges = (EDGE *) (((BYTE *) prclRects) +
323: (MAX_PATH_RECTS * sizeof(RECTL))); // storage for list of edges
324: // between which to fill
325:
326: // Initialize an empty list of rectangles to fill
327: ulNumRects = 0;
328:
329: // Enumerate the path edges and build a Global Edge Table (GET) from them
330: // in YX-sorted order.
331: pGETHead = &GETHead;
332: ConstructGET(pGETHead, pFreeEdges, ppo, &pd, bMore);
333:
334: // Create an empty AET with the head node also a tail sentinel
335: pAETHead = &AETHead;
336: AETHead.pNext = pAETHead; // mark that the AET is empty
337: AETHead.X = 0x7FFFFFFF; // this is greater than any valid X value, so
338: // searches will always terminate
339:
340: // Top scan of polygon is the top of the first edge we come to
341: iCurrentY = ((EDGE *)GETHead.pNext)->Y;
342:
343: // Loop through all the scans in the polygon, adding edges from the GET to
344: // the Active Edge Table (AET) as we come to their starts, and scanning out
345: // the AET at each scan into a rectangle list. Each time it fills up, the
346: // rectangle list is passed to the filling routine, and then once again at
347: // the end if any rectangles remain undrawn. We continue so long as there
348: // are edges to be scanned out
349: while (1) {
350:
351: // Advance the edges in the AET one scan, discarding any that have
352: // reached the end (if there are any edges in the AET)
353: if (AETHead.pNext != pAETHead) {
354: AdvanceAETEdges(pAETHead);
355: }
356:
357: // If the AET is empty, done if the GET is empty, else jump ahead to
358: // the next edge in the GET; if the AET isn't empty, re-sort the AET
359: if (AETHead.pNext == pAETHead) {
360: if (GETHead.pNext == pGETHead) {
361: // Done if there are no edges in either the AET or the GET
362: break;
363: }
364: // There are no edges in the AET, so jump ahead to the next edge in
365: // the GET
366: iCurrentY = ((EDGE *)GETHead.pNext)->Y;
367: } else {
368: // Re-sort the edges in the AET by X coordinate, if there are at
369: // least two edges in the AET (there could be one edge if the
370: // balancing edge hasn't yet been added from the GET)
371: if (((EDGE *)AETHead.pNext)->pNext != pAETHead) {
372: XSortAETEdges(pAETHead);
373: }
374: }
375:
376: // Move any new edges that start on this scan from the GET to the AET;
377: // bother calling only if there's at least one edge to add
378: if (((EDGE *)GETHead.pNext)->Y == iCurrentY) {
379: MoveNewEdges(pGETHead, pAETHead, iCurrentY);
380: }
381:
382: // Scan the AET into rectangles to fill (there's always at least one
383: // edge pair in the AET)
384: pCurrentEdge = AETHead.pNext; // point to the first edge
385: do {
386:
387: INT iLeftEdge;
388:
389: // The left edge of any given edge pair is easy to find; it's just
390: // wherever we happen to be currently
391: iLeftEdge = pCurrentEdge->X;
392:
393: // Find the matching right edge according to the current fill rule
394: if ((flOptions & FP_WINDINGMODE) != 0) {
395:
396: INT iWindingCount;
397:
398: // Do winding fill; scan across until we've found equal numbers
399: // of up and down edges
400: iWindingCount = pCurrentEdge->iWindingDirection;
401: do {
402: pCurrentEdge = pCurrentEdge->pNext;
403: iWindingCount += pCurrentEdge->iWindingDirection;
404: } while (iWindingCount != 0);
405: } else {
406: // Odd-even fill; the next edge is the matching right edge
407: pCurrentEdge = pCurrentEdge->pNext;
408: }
409:
410: // See if the resulting span encompasses at least one pixel, and
411: // add it to the list of rectangles to draw if so
412: if (iLeftEdge < pCurrentEdge->X) {
413:
414: // We've got an edge pair to add to the list to be filled; see
415: // if there's room for one more rectangle
416: if (ulNumRects >= MAX_PATH_RECTS) {
417: // No more room; draw the rectangles in the list and reset
418: // it to empty
419:
420: (*pfnFill)(ppdev, ulNumRects, prclRects, mix, rbc,
421: pptlBrush);
422:
423: // Reset the list to empty
424: ulNumRects = 0;
425: }
426:
427: // Add the rectangle representing the current edge pair
428: // LATER coalesce rectangles
429: prclRects[ulNumRects].top = iCurrentY;
430: prclRects[ulNumRects].bottom = iCurrentY+1;
431: prclRects[ulNumRects].left = iLeftEdge;
432: prclRects[ulNumRects].right = pCurrentEdge->X;
433: ulNumRects++;
434: }
435: } while ((pCurrentEdge = pCurrentEdge->pNext) != pAETHead);
436:
437: iCurrentY++; // next scan
438: }
439:
440: /* draw the remaining rectangles, if there are any */
441:
442: draw_remaining_rectangles:
443:
444: if (ulNumRects > 0) {
445: (*pfnFill)(ppdev, ulNumRects, prclRects, mix, rbc, pptlBrush);
446: }
447:
448: return(TRUE); // done successfully
449: }
450:
451: // Advance the edges in the AET to the next scan, dropping any for which we've
452: // done all scans. Assumes there is at least one edge in the AET.
453: VOID AdvanceAETEdges(EDGE *pAETHead)
454: {
455: EDGE *pLastEdge, *pCurrentEdge;
456:
457: pLastEdge = pAETHead;
458: pCurrentEdge = pLastEdge->pNext;
459: do {
460:
461: // Count down this edge's remaining scans
462: if (--pCurrentEdge->iScansLeft == 0) {
463: // We've done all scans for this edge; drop this edge from the AET
464: pLastEdge->pNext = pCurrentEdge->pNext;
465: } else {
466: // Advance the edge's X coordinate for a 1-scan Y advance
467: // Advance by the minimum amount
468: pCurrentEdge->X += pCurrentEdge->iXWhole;
469: // Advance the error term and see if we got one extra pixel this
470: // time
471: pCurrentEdge->iErrorTerm += pCurrentEdge->iErrorAdjustUp;
472: if (pCurrentEdge->iErrorTerm >= 0) {
473: // The error term turned over, so adjust the error term and
474: // advance the extra pixel
475: pCurrentEdge->iErrorTerm -= pCurrentEdge->iErrorAdjustDown;
476: pCurrentEdge->X += pCurrentEdge->iXDirection;
477: }
478:
479: pLastEdge = pCurrentEdge;
480: }
481: } while ((pCurrentEdge = pLastEdge->pNext) != pAETHead);
482: }
483:
484: // X-sort the AET, because the edges may have moved around relative to
485: // one another when we advanced them. We'll use a multipass bubble
486: // sort, which is actually okay for this application because edges
487: // rarely move relative to one another, so we usually do just one pass.
488: // Also, this makes it easy to keep just a singly-linked list. Assumes there
489: // are at least two edges in the AET.
490: VOID XSortAETEdges(EDGE *pAETHead)
491: {
492: BOOL bEdgesSwapped;
493: EDGE *pLastEdge, *pCurrentEdge, *pNextEdge;
494:
495: do {
496:
497: bEdgesSwapped = FALSE;
498: pLastEdge = pAETHead;
499: pCurrentEdge = pLastEdge->pNext;
500: pNextEdge = pCurrentEdge->pNext;
501:
502: do {
503: if (pNextEdge->X < pCurrentEdge->X) {
504:
505: // Next edge is to the left of the current edge; swap them
506: pLastEdge->pNext = pNextEdge;
507: pCurrentEdge->pNext = pNextEdge->pNext;
508: pNextEdge->pNext = pCurrentEdge;
509: bEdgesSwapped = TRUE;
510: pCurrentEdge = pNextEdge; // continue sorting before the edge
511: // we just swapped; it might move
512: // farther yet
513: }
514: pLastEdge = pCurrentEdge;
515: pCurrentEdge = pLastEdge->pNext;
516: } while ((pNextEdge = pCurrentEdge->pNext) != pAETHead);
517: } while (bEdgesSwapped);
518: }
519:
520: // Moves all edges that start on the current scan from the GET to the AET in
521: // X-sorted order. Parameters are pointer to head of GET and pointer to dummy
522: // edge at head of AET, plus current scan line. Assumes there's at least one
523: // edge to be moved.
524: VOID MoveNewEdges(EDGE *pGETHead, EDGE *pAETHead, INT iCurrentY)
525: {
526: EDGE *pCurrentEdge = pAETHead;
527: EDGE *pGETNext = pGETHead->pNext;
528:
529: do {
530:
531: // Scan through the AET until the X-sorted insertion point for this
532: // edge is found. We can continue from where the last search left
533: // off because the edges in the GET are in X sorted order, as is
534: // the AET. The search always terminates because the AET sentinel
535: // is greater than any valid X
536: while (pGETNext->X > ((EDGE *)pCurrentEdge->pNext)->X) {
537: pCurrentEdge = pCurrentEdge->pNext;
538: }
539:
540: // We've found the insertion point; add the GET edge to the AET, and
541: // remove it from the GET
542: pGETHead->pNext = pGETNext->pNext;
543: pGETNext->pNext = pCurrentEdge->pNext;
544: pCurrentEdge->pNext = pGETNext;
545: pCurrentEdge = pGETNext; // continue insertion search for the next
546: // GET edge after the edge we just added
547: pGETNext = pGETHead->pNext;
548:
549: } while (pGETNext->Y == iCurrentY);
550: }
551:
552:
553:
554:
555:
556: // Build the Global Edge Table from the path. There must be enough memory in
557: // the free edge area to hold all edges. The GET is constructed in Y-X order,
558: // and has a head/tail/sentinel node at pGETHead.
559:
560: VOID ConstructGET(
561: EDGE *pGETHead,
562: EDGE *pFreeEdges,
563: PATHOBJ *ppo,
564: PATHDATA *pd,
565: BOOL bMore)
566: {
567: POINTFIX pfxPathStart; // point that started the current subpath
568: POINTFIX pfxPathPrevious; // point before the current point in a subpath;
569: // starts the current edge
570:
571: /* Create an empty GET with the head node also a tail sentinel */
572:
573: pGETHead->pNext = pGETHead; // mark that the GET is empty
574: pGETHead->Y = 0x7FFFFFFF; // this is greater than any valid Y value, so
575: // searches will always terminate
576:
577: /* PATHOBJ_vEnumStart is implicitly performed by engine
578: already and first path is enumerated by the caller */
579:
580: next_subpath:
581:
582: /* Make sure the PATHDATA is not empty (is this necessary) */
583:
584: if (pd->count != 0) {
585:
586: /* If first point starts a subpath, remember it as such
587: and go on to the next point, so we can get an edge */
588:
589: if (pd->flags & PD_BEGINSUBPATH) {
590:
591: /* the first point starts the subpath; remember it */
592:
593: pfxPathStart = *pd->pptfx; /* the subpath starts here */
594: pfxPathPrevious = *pd->pptfx; /* this points starts the next edge */
595: pd->pptfx++; /* advance to the next point */
596: pd->count--; /* count off this point */
597: }
598:
599:
600: /* add edges in PATHDATA to GET, in Y-X sorted order */
601:
602: while (pd->count--) {
603: pFreeEdges =
604: AddEdgeToGET(pGETHead, pFreeEdges, &pfxPathPrevious, pd->pptfx);
605: pfxPathPrevious = *pd->pptfx; /* current point becomes previous */
606: pd->pptfx++; /* advance to the next point */
607: }
608:
609:
610: /* If last point ends the subpath, insert the edge that
611: connects to firs t point (is this built in already) */
612:
613: if (pd->flags & PD_ENDSUBPATH) {
614: pFreeEdges = AddEdgeToGET
615: (pGETHead, pFreeEdges, &pfxPathPrevious, &pfxPathStart);
616: }
617: }
618:
619: /* the initial loop conditions preclude a do, while or for */
620:
621: if (bMore) {
622: bMore = PATHOBJ_bEnum(ppo, pd);
623: goto next_subpath;
624: }
625: }
626:
627: // Adds the edge described by the two passed-in points to the Global Edge
628: // Table, if the edge spans at least one pixel vertically.
629: EDGE * AddEdgeToGET(EDGE *pGETHead, EDGE *pFreeEdge,
630: POINTFIX *ppfxEdgeStart, POINTFIX *ppfxEdgeEnd)
631: {
632: INT iYStart, iYEnd, iXStart, iXEnd, iYHeight, iXWidth;
633:
634: // Set the winding-rule direction of the edge, and put the endpoints in
635: // top-to-bottom order
636: iYHeight = ppfxEdgeEnd->y - ppfxEdgeStart->y;
637: if (iYHeight >= 0) {
638: iXStart = ppfxEdgeStart->x;
639: iYStart = ppfxEdgeStart->y;
640: iXEnd = ppfxEdgeEnd->x;
641: iYEnd = ppfxEdgeEnd->y;
642: pFreeEdge->iWindingDirection = 1;
643: } else {
644: iYHeight = -iYHeight;
645: iXEnd = ppfxEdgeStart->x;
646: iYEnd = ppfxEdgeStart->y;
647: iXStart = ppfxEdgeEnd->x;
648: iYStart = ppfxEdgeEnd->y;
649: pFreeEdge->iWindingDirection = -1;
650: }
651:
652: // First pixel scan line (non-fractional GIQ Y coordinate) edge intersects.
653: // Dividing by 16 with a shift is okay because Y is always positive
654: pFreeEdge->Y = (iYStart + 15) >> 4;
655:
656: // Calculate the number of pixels spanned by this edge
657: pFreeEdge->iScansLeft = ((iYEnd + 15) >> 4) - pFreeEdge->Y;
658: if (pFreeEdge->iScansLeft <= 0) {
659: return(pFreeEdge); // no pixels at all are spanned, so we can ignore
660: // this edge
661: }
662:
663: // Set the error term and adjustment factors, all in GIQ coordinates for
664: // now
665: iXWidth = iXEnd - iXStart;
666: if (iXWidth >= 0) {
667: // Left to right, so we change X as soon as we move at all
668: pFreeEdge->iXDirection = 1;
669: pFreeEdge->iErrorTerm = -1;
670: } else {
671: // Right to left, so we don't change X until we've moved a full GIQ
672: // coordinate
673: iXWidth = -iXWidth;
674: pFreeEdge->iXDirection = -1;
675: pFreeEdge->iErrorTerm = -iYHeight;
676: }
677:
678: if (iXWidth >= iYHeight) {
679: // Calculate base run length (minimum distance advanced in X for a 1-
680: // scan advance in Y)
681: pFreeEdge->iXWhole = iXWidth / iYHeight;
682: // Add sign back into base run length if going right to left
683: if (pFreeEdge->iXDirection == -1) {
684: pFreeEdge->iXWhole = -pFreeEdge->iXWhole;
685: }
686: pFreeEdge->iErrorAdjustUp = iXWidth % iYHeight;
687: } else {
688: // Base run length is 0, because line is closer to vertical than
689: // horizontal
690: pFreeEdge->iXWhole = 0;
691: pFreeEdge->iErrorAdjustUp = iXWidth;
692: }
693: pFreeEdge->iErrorAdjustDown = iYHeight;
694:
695: // If the edge doesn't start on a pixel scan (that is, it starts at a
696: // fractional GIQ coordinate), advance it to the first pixel scan it
697: // intersects
698: // LATER might be faster to use multiplication and division to jump ahead,
699: // rather than looping
700: while ((iYStart & 0x0F) != 0) {
701: // Starts at a fractional GIQ coordinate, not exactly on a pixel scan
702:
703: // Advance the edge's GIQ X coordinate for a 1-GIQ-pixel Y advance
704: // Advance by the minimum amount
705: iXStart += pFreeEdge->iXWhole;
706: // Advance the error term and see if we got one extra pixel this time
707: pFreeEdge->iErrorTerm += pFreeEdge->iErrorAdjustUp;
708: if (pFreeEdge->iErrorTerm >= 0) {
709: // The error term turned over, so adjust the error term and
710: // advance the extra pixel
711: pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown;
712: iXStart += pFreeEdge->iXDirection;
713: }
714: iYStart++; // advance to the next GIQ Y coordinate
715: }
716:
717: // Turn the calculations into pixel rather than GIQ calculations
718:
719: // Move the X coordinate to the nearest pixel, and adjust the error term
720: // accordingly
721: // Dividing by 16 with a shift is okay because X is always positive
722: pFreeEdge->X = (iXStart + 15) >> 4; // convert from GIQ to pixel coordinates
723:
724: // LATER adjust only if needed (if prestepped above)?
725: if (pFreeEdge->iXDirection == 1) {
726: // Left to right
727: pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
728: (((iXStart + 15) & ~0x0F) - iXStart);
729: } else {
730: // Right to left
731: pFreeEdge->iErrorTerm -= pFreeEdge->iErrorAdjustDown *
732: ((iXStart - 1) & 0x0F);
733: }
734:
735: // Scale the error adjusts up by 16 times, to move 16 GIQ pixels at a time.
736: // Shifts work to do the multiplying because these values are always
737: // non-negative
738: pFreeEdge->iErrorAdjustUp <<= 4;
739: pFreeEdge->iErrorAdjustDown <<= 4;
740:
741: // Insert the edge into the GET in YX-sorted order. The search always ends
742: // because the GET has a sentinel with a greater-than-possible Y value
743: while ((pFreeEdge->Y > ((EDGE *)pGETHead->pNext)->Y) ||
744: ((pFreeEdge->Y == ((EDGE *)pGETHead->pNext)->Y) &&
745: (pFreeEdge->X > ((EDGE *)pGETHead->pNext)->X))) {
746: pGETHead = pGETHead->pNext;
747: }
748:
749: pFreeEdge->pNext = pGETHead->pNext; // link the edge into the GET
750: pGETHead->pNext = pFreeEdge;
751:
752: return(++pFreeEdge); // point to the next edge storage location for next
753: // time
754: }
755:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.