1 /* $XConsortium: hints.c,v 1.4 91/10/10 11:18:13 rws Exp $ */
2 /* Copyright International Business Machines, Corp. 1991
4 * Copyright Lexmark International, Inc. 1991
7 * License to use, copy, modify, and distribute this software and its
8 * documentation for any purpose and without fee is hereby granted,
9 * provided that the above copyright notice appear in all copies and that
10 * both that copyright notice and this permission notice appear in
11 * supporting documentation, and that the name of IBM or Lexmark not be
12 * used in advertising or publicity pertaining to distribution of the
13 * software without specific, written prior permission.
15 * IBM AND LEXMARK PROVIDE THIS SOFTWARE "AS IS", WITHOUT ANY WARRANTIES OF
16 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO ANY
17 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
18 * AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. THE ENTIRE RISK AS TO THE
19 * QUALITY AND PERFORMANCE OF THE SOFTWARE, INCLUDING ANY DUTY TO SUPPORT
20 * OR MAINTAIN, BELONGS TO THE LICENSEE. SHOULD ANY PORTION OF THE
21 * SOFTWARE PROVE DEFECTIVE, THE LICENSEE (NOT IBM OR LEXMARK) ASSUMES THE
22 * ENTIRE COST OF ALL SERVICING, REPAIR AND CORRECTION. IN NO EVENT SHALL
23 * IBM OR LEXMARK BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
24 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
25 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
26 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
29 /* HINTS CWEB V0006 ******** */
31 :h1.HINTS Module - Processing Rasterization Hints
33 &author. Sten F. Andler; continuity by Jeffrey B. Lotspiech (lotspiech@almaden.ibm.com) and Duaine
39 The included files are:
49 :h3.Functions Provided to the TYPE1IMAGER User
55 :h3.Functions Provided to Other Modules
57 This module provides the following entry point to other modules:
61 /*SHARED LINE(S) ORIGINATED HERE*/
64 :h3.Macros Provided to Other Modules
70 :h2.InitHints() - Initialize hint data structure
77 struct fractpoint hint;
80 #define ODD(x) (((int)(x)) & 01)
81 #define FPFLOOR(fp) TOFRACTPEL((fp) >> FRACTBITS)
82 #define FPROUND(fp) FPFLOOR((fp) + FPHALF)
88 for (i = 0; i < MAXLABEL; i++)
90 oldHint[i].inuse = FALSE;
91 oldHint[i].computed = FALSE;
96 :h3.CloseHints(hintP) - Reverse hints that are still open
99 void CloseHints(hintP)
100 struct fractpoint *hintP;
104 for (i = 0; i < MAXLABEL; i++)
106 if (oldHint[i].inuse)
108 hintP->x -= oldHint[i].hint.x;
109 hintP->y -= oldHint[i].hint.y;
111 oldHint[i].inuse = FALSE;
113 IfTrace3((HintDebug > 1)," Hint %d was open, hint=(%p,%p)\n",
114 i, hintP->x, hintP->y);
120 :h3.ComputeHint(hP, currX, currY, hintP) - Compute the value of a hint
123 static void ComputeHint(hP, currX, currY, hintP)
124 struct hintsegment *hP;
125 fractpel currX, currY;
126 struct fractpoint *hintP;
128 fractpel currRef, currWidth;
134 By construction, width is never zero. Therefore we can use the
135 width value to determine if the hint has been rotated by a
136 multiple of 90 degrees.
139 if (hP->width.y == 0)
141 orientation = 'v'; /* vertical */
142 IfTrace0((HintDebug > 0)," vertical hint\n");
144 else if (hP->width.x == 0)
146 orientation = 'h'; /* horizontal */
147 IfTrace0((HintDebug > 0)," horizontal hint\n");
151 IfTrace0((HintDebug > 0)," hint not vertical or horizontal\n");
152 hintP->x = hintP->y = 0;
156 /* Compute currRef and currWidth with a unit of 1 pel */
157 if (orientation == 'v') /* vertical */
159 currRef = hP->ref.x + currX;
160 currWidth = ABS(hP->width.x);
162 else if (orientation == 'h') /* horizontal */
164 currRef = hP->ref.y + currY;
165 currWidth = ABS(hP->width.y);
169 abort("ComputeHint: invalid orientation");
172 IfTrace4((HintDebug > 1),
173 " currX=%p, currY=%p, currRef=%p, currWidth=%p\n",
177 if ((hP->hinttype == 'b') /* Bar or stem */
178 || (hP->hinttype == 's')) /* Serif */
180 idealWidth = NEARESTPEL(currWidth);
181 if (idealWidth == 0) idealWidth = 1;
182 if (ODD(idealWidth)) /* Is ideal width odd? */
184 /* center "ref" over pel */
185 hintValue = FPFLOOR(currRef) + FPHALF - currRef;
189 /* align "ref" on pel boundary */
190 hintValue = FPROUND(currRef) - currRef;
193 IfTrace1(TRUE," idealWidth=%d, ", idealWidth);
196 else if (hP->hinttype == 'c') /* Curve extrema */
198 /* align "ref" on pel boundary */
199 hintValue = FPROUND(currRef) - currRef;
203 abort("ComputeHint: invalid hinttype");
206 IfTrace1((HintDebug > 1)," hintValue=%p", hintValue);
208 if (orientation == 'v') /* vertical */
210 hintP->x = hintValue;
213 else if (orientation == 'h') /* horizontal */
216 hintP->y = hintValue;
220 abort("ComputeHint: invalid orientation");
225 :h3.ProcessHint(hP, currX, currY, hintP) - Process a rasterization hint
228 void ProcessHint(hP, currX, currY, hintP)
229 struct hintsegment *hP;
230 fractpel currX, currY;
231 struct fractpoint *hintP;
233 struct fractpoint thisHint;
235 IfTrace4((HintDebug > 1)," ref=(%p,%p), width=(%p,%p)",
236 hP->ref.x, hP->ref.y,
237 hP->width.x, hP->width.y);
238 IfTrace4((HintDebug > 1),", %c %c %c %c",
239 hP->orientation, hP->hinttype,
240 hP->adjusttype, hP->direction);
241 IfTrace1((HintDebug > 1),", label=%d\n", hP->label);
243 if ((hP->adjusttype == 'm') /* Move */
244 || (hP->adjusttype == 'a')) /* Adjust */
246 /* Look up hint in oldHint table */
247 if ((hP->label >= 0) && (hP->label < MAXLABEL))
249 if (oldHint[hP->label].computed)
250 /* Use old hint value if already computed */
252 thisHint.x = oldHint[hP->label].hint.x;
253 thisHint.y = oldHint[hP->label].hint.y;
254 oldHint[hP->label].inuse = TRUE;
257 /* Compute new value for hint and store it for future use */
259 ComputeHint(hP, currX, currY, &thisHint);
261 oldHint[hP->label].hint.x = thisHint.x;
262 oldHint[hP->label].hint.y = thisHint.y;
263 oldHint[hP->label].inuse = TRUE;
264 oldHint[hP->label].computed = TRUE;
269 abort("ProcessHint: invalid label");
272 else if (hP->adjusttype == 'r') /* Reverse */
274 /* Use the inverse of the existing hint value to reverse hint */
275 if ((hP->label >= 0) && (hP->label < MAXLABEL))
277 if (oldHint[hP->label].inuse)
279 thisHint.x = -oldHint[hP->label].hint.x;
280 thisHint.y = -oldHint[hP->label].hint.y;
281 oldHint[hP->label].inuse = FALSE;
285 abort("ProcessHint: label is not in use");
290 abort("ProcessHint: invalid label");
296 abort("ProcessHint: invalid adjusttype");
298 IfTrace3((HintDebug > 1)," label=%d, thisHint=(%p,%p)\n",
299 hP->label, thisHint.x, thisHint.y);
301 hintP->x += thisHint.x;
302 hintP->y += thisHint.y;
304 IfTrace2((HintDebug > 1)," hint=(%p,%p)\n",
309 :h2 id=subpath.Navigation Through Edge Lists
311 For continuity checking purposes, we need to navigate through edge
312 lists by the "subpath" chains and answer questions about edges. The
313 subpath chain links together edges that were part of the same subpath
314 (no intervening move segments) when the interior of the path was
315 calculated. Here we use the term "edge" to mean every edge list
316 that was created in between changes of direction.
318 The subpath chains are singly-linked circular chains. For the convenience
319 of building them, they direction of the list (from edge to edge) is the
320 reverse of the order in which they were built. Within any single edge,
321 the subpath chain goes from top-to-bottom. (There might be a violation
322 of this because of the way the user started the first chain; see
323 :hdref refid=fixsubp..).
325 :h3.ISTOP() and ISBOTTOM() - Flag Bits for Edge Lists at the Top and
326 Bottom of Their SubPaths
329 #define ISTOP(flag) ((flag)&0x20)
330 #define ISBOTTOM(flag) ((flag)&0x10)
332 :h3.ISLEFT() - Flag Bit for Left Edges
335 #define ISLEFT(flag) ((flag)&0x08)
338 :h3.XofY() - Macro to Find X Value at Given Y
340 This macro can only be used if it is known that the Y is within the
341 given edgelist's ymin and ymax.
344 #define XofY(edge, y) edge->xvalues[y - edge->ymin]
347 :h3.findXofY() - Like XofY(), Except not Restricted
349 If the Y is out of bounds of the given edgelist, this macro will
350 call SearchXofY to search the edge's subpath chain for the correct
351 Y range. If the Y value is off the edge, MINPEL is returned.
353 #define findXofY(edge, y) ((y < edge->ymin || y >= edge->ymax) ? SearchXofY(edge, y) : XofY(edge, y))
356 :h4.SearchXofY() - Routine Called by FindXofY() for Difficult Cases
358 The concept of this routine is to follow the subpath chain to find the
359 edge just below (i.e., next in chain) or just above (i.e., immediately
360 before in chain. It is assumed that the Y value is no more than one
361 off of the edge's range; XofY() could be replace by FindXofY() to
362 call ourselves recursively if this were not true.
365 static pel SearchXofY(edge, y)
366 register struct edgelist *edge; /* represents edge */
367 register pel y; /* 'y' value to find edge for */
369 register struct edgelist *e; /* loop variable */
371 if (y < edge->ymin) {
372 if (ISTOP(edge->flag))
374 for (e = edge->subpath; e->subpath != edge; e = e->subpath) { ; }
375 if (e->ymax == edge->ymin)
378 else if (y >= edge->ymax) {
379 if (ISBOTTOM(edge->flag))
382 if (e->ymin == edge->ymax)
386 return(XofY(edge, y));
388 abort("bad subpath chain");
392 :h3.ISBREAK() Macro - Tests if an Edge List is at a "Break"
394 The subpath chains are organized top to bottom. When the bottom of
395 a given edge is reached, the subpath chain points to the top of the
396 next edge. We call this a "break" in the chain. The following macro
397 is the simple test for the break condition:
400 #define ISBREAK(top,bot) (top->ymax != bot->ymin)
404 :h3.ImpliedHorizontalLine() - Tests for Horizontal Connectivity
406 This function returns true if two edges are connected horizontally.
407 They are connected horizontally if they are consecutive in the subpath,
408 and either we are at the bottom and the first edge is going down or we
409 are at the top and the first edge is going up.
412 #define BLACKABOVE -1
413 #define BLACKBELOW +1
416 static int ImpliedHorizontalLine(e1, e2, y)
417 register struct edgelist *e1,*e2; /* two edges to check */
418 register int y; /* y where they might be connected */
420 register struct edgelist *e3,*e4;
422 if (ISDOWN(e1->flag) == ISDOWN(e2->flag))
423 return(NONE); /* can't be consecutive unless different directions */
425 Now we check for consecutiveness: Can we get from 'e1' to 'e2' with
426 only one intervening break? Can we get from 'e2' to 'e1' with only one
427 intervening break? 'e3' will be as far as we can get after 'e1'; 'e4'
428 will be has far as we can get after 'e2':
430 for (e3 = e1; !ISBREAK(e3, e3->subpath); e3 = e3->subpath) { ; }
431 for (e3 = e3->subpath; e3 != e2; e3 = e3->subpath)
432 if (ISBREAK(e3, e3->subpath))
435 for (e4 = e2; !ISBREAK(e4, e4->subpath); e4 = e4->subpath) { ; }
436 for (e4 = e4->subpath; e4 != e1; e4 = e4->subpath)
437 if (ISBREAK(e4, e4->subpath))
440 If the edges are mutually consecutive, we must have horizontal lines
443 if (e3 == e2 && e4 == e1)
446 If the edges are not consecutive either way, no horizontal lines are
449 if (e3 != e2 && e4 != e1)
452 Now let's swap 'e1' and 'e2' if necessary to enforce the rule that 'e2'
453 follows 'e1'. Remember that subpath chains go in the opposite direction
454 from the way the subpaths were built; this led to the simplest way
459 e1 = e3; /* remember e3 == e2, this just swaps 'e1' and 'e2' */
462 Now we have everything to return the answer:
464 if (ISTOP(e1->flag) && y == e1->ymin)
465 return(ISDOWN(e2->flag));
466 else if (ISBOTTOM(e1->flag) && y == e1->ymax)
467 return(!ISDOWN(e2->flag));
469 abort("ImpliedHorizontalLine: why ask?");
474 :h3 id=fixsubp.FixSubPaths() - Must be Called to Organize Subpath Chains
476 The region-building code in Interior(), in particular splitedge(),
477 maintains the rule that sub-paths are linked top-to-bottom except
478 at breaks. However, it is possible that there may be a "false break"
479 because the user started the subpath in the middle of an edge (and
480 went in the "wrong" direction from there, up instead of down). This
481 routine finds and fixes false breaks.
483 Also, this routine sets the ISTOP and ISBOTTOM flags in the edge lists.
486 static void FixSubPaths(R)
487 register struct region *R; /* anchor of region */
489 register struct edgelist *e; /* fast loop variable */
490 register struct edgelist *edge; /* current edge in region */
491 register struct edgelist *next; /* next in subpath after 'edge' */
492 register struct edgelist *break1; /* first break after 'next' */
493 register struct edgelist *break2; /* last break before 'edge' */
494 register struct edgelist *prev; /* previous edge for fixing links */
497 for (edge = R->anchor; edge != NULL; edge = edge->link) {
500 edge->flag |= ISLEFT(ON);
503 next = edge->subpath;
505 if (!ISBREAK(edge, next))
507 if (edge->ymax < next->ymin)
508 abort("disjoint subpath?");
510 'edge' now contains an edgelist at the bottom of an edge, and 'next'
511 contains the next subsequent edgelist in the subpath, which must be at
512 the top. We refer to this a "break" in the subpath.
514 next->flag |= ISTOP(ON);
515 edge->flag |= ISBOTTOM(ON);
517 if (ISDOWN(edge->flag) != ISDOWN(next->flag))
520 We are now in the unusual case; both edges are going in the same
521 direction so this must be a "false break" due to the way that the user
522 created the path. We'll have to fix it.
524 for (break1 = next; !ISBREAK(break1, break1->subpath); break1 = break1->subpath) { ; }
526 for (e = break1->subpath; e != edge; e = e->subpath)
527 if (ISBREAK(e, e->subpath))
530 Now we've set up 'break1' and 'break2'. I've found the following
531 diagram invaluable. 'break1' is the first break after 'next'. 'break2'
532 is the LAST break before 'edge'.
535 +------+ +---->+------+
536 +--->| >-----+ | | >-----+
538 | +-------------+ | +-------------+
540 | +->| >-------+ +->| >-----+
542 | | | +-------------+
544 | +----------------+ | | |
545 | | +------+ | +->| >-----+
546 | +->| >-----+ | | | |
547 | | | | | +-------------+
548 | +-------------+ | | | |
549 | | |edge | | | |break2|
550 | +->| >-----+ | +->| >-----+
554 | +------+ | | +------+ |
556 +---------------+ +---------------+
559 We want to fix this situation by having 'edge' point to where 'break1'
560 now points, and having 'break1' point to where 'break2' now points.
561 Finally, 'break2' should point to 'next'. Also, we observe that
562 'break1' can't be a bottom, and is also not a top unless it is the same
565 edge->subpath = break1->subpath;
567 break1->subpath = break2->subpath;
568 if (ISBREAK(break1, break1->subpath))
569 abort("unable to fix subpath break?");
571 break2->subpath = next;
573 break1->flag &= ~ISBOTTOM(ON);
575 break1->flag &= ~ISTOP(ON);
578 This region might contain "ambiguous" edges; edges exactly equal to
579 edge->link. Due to the random dynamics of where they get sorted into
580 the list, they can yield false crossings, where the edges appear
581 to cross. This confuses our continuity logic no end. Since we can
582 swap them without changing the region, we do.
584 for (edge = R->anchor, prev = NULL; VALIDEDGE(edge); prev = edge, edge = prev->link) {
586 if (! ISAMBIGUOUS(edge->flag))
589 next = edge->subpath;
591 while (ISAMBIGUOUS(next->flag) && next != edge)
592 next = next->subpath;
594 We've finally found a non-ambiguous edge; we make sure it is left/right
595 compatible with 'edge':
597 if ( (ISLEFT(edge->flag) == ISLEFT(next->flag) && ISDOWN(edge->flag) == ISDOWN(next->flag) )
598 || (ISLEFT(edge->flag) != ISLEFT(next->flag) && ISDOWN(edge->flag) != ISDOWN(next->flag) ) )
602 Incompatible, we will swap 'edge' and the following edge in the list.
603 You may think that there must be a next edge in this swath. So did I.
604 No! If there is a totally ambiguous inner loop, for example, we could
605 get all the way to the outside without resolving ambiguity.
607 next = edge->link; /* note new meaning of 'next' */
608 if (next == NULL || edge->ymin != next->ymin)
614 edge->link = next->link;
616 edge->flag ^= ISLEFT(ON);
617 edge->flag &= ~ISAMBIGUOUS(ON);
618 next->flag ^= ISLEFT(ON);
619 next->flag &= ~ISAMBIGUOUS(ON);
629 static struct edgelist *before(); /* subroutine of DumpSubPaths */
631 static void DumpSubPaths(anchor)
632 struct edgelist *anchor;
635 register struct edgelist *edge,*e,*e2;
638 for (edge = anchor; VALIDEDGE(edge); edge = edge->link) {
639 if (ISPERMANENT(edge->flag))
641 IfTrace0(TRUE, "BEGIN Subpath\n");
642 for (e2 = edge; !ISPERMANENT(e2->flag);) {
643 if (ISDOWN(e2->flag)) {
644 IfTrace1(TRUE, ". Downgoing edge's top at %x\n", e2);
645 for (e = e2;; e = e->subpath) {
646 IfTrace4(TRUE, ". . [%5d] %5d @ %x[%x]\n",
647 e->ymin, *e->xvalues, e, e->flag);
648 for (y=e->ymin+1; y < e->ymax; y++)
649 IfTrace2(TRUE, ". . [%5d] %5d \"\n", y, e->xvalues[y-e->ymin]);
650 e->flag |= ISPERMANENT(ON);
651 if (ISBREAK(e, e->subpath))
656 IfTrace1(TRUE, ". Upgoing edge's top at %x\n", e2);
657 for (e = e2; !ISBREAK(e, e->subpath); e = e->subpath) { ; }
658 for (;; e=before(e)) {
659 IfTrace4(TRUE, ". . [%5d] %5d @ %x[%x]\n",
660 e->ymax-1, e->xvalues[e->ymax-1-e->ymin], e, e->flag);
661 for (y=e->ymax-2; y >= e->ymin; y--)
662 IfTrace2(TRUE, ". . [%5d] %5d \"\n", y, e->xvalues[y-e->ymin]);
663 e->flag |= ISPERMANENT(ON);
670 } while (!ISBREAK(before(e2), e2));
675 static struct edgelist *before(e)
679 for (r = e->subpath; r->subpath != e; r = r->subpath) { ; }
684 :h2.Fixing Region Continuity Problems
686 Small regions may become disconnected when their connecting segments are
687 less than a pel wide. This may be correct in some applications, but in
688 many (especially small font characters), it is more pleasing to keep
689 connectivity. ApplyContinuity() (invoked by +CONTINUITY on the
690 Interior() fill rule) fixes connection breaks. The resulting region
691 is geometrically less accurate, but may be more pleasing to the eye.
694 Here are some macros which we will need:
697 #define IsValidPel(j) (j!=MINPEL)
700 :h3.writeXofY() - Stuffs an X Value Into an "edgelist"
702 writeXofY writes an x value into an edge at position 'y'. It must
703 update the edge's xmin and xmax. If there is a possibility that this
704 new x might exceed the region's bounds, updating those are the
705 responsibility of the caller.
708 static void writeXofY(e, y, x)
709 struct edgelist *e; /* relevant edgelist */
711 int x; /* new x value */
713 if (e->xmin > x) e->xmin = x;
714 if (e->xmax < x) e->xmax = x;
715 e->xvalues[y - e->ymin] = x;
718 /*-------------------------------------------------------------------------*/
719 /* the following three macros tell us whether we are at a birth point, a */
720 /* death point, or simply in the middle of the character */
721 /*-------------------------------------------------------------------------*/
722 #define WeAreAtTop(e,i) (ISTOP(e->flag) && e->ymin == i)
723 #define WeAreAtBottom(e,i) (ISBOTTOM(e->flag) && e->ymax-1 == i)
724 #define WeAreInMiddle(e,i) \
725 ((!ISTOP(e->flag) && !ISBOTTOM(e->flag))||(i < e->ymax-1 && i > e->ymin))
727 The following macro tests if two "edgelist" structures are in the same
730 #define SAMESWATH(e1,e2) (e1->ymin == e2->ymin)
733 :h3.CollapseWhiteRun() - Subroutine of ApplyContinuity()
735 When we have a white run with an implied horizontal line above or
736 below it, we better have black on the other side of this line. This
737 function both tests to see if black is there, and adjusts the end
738 points (collapses) the white run as necessary if it is not. The
739 goal is to collapse the white run as little as possible.
742 static void CollapseWhiteRun(anchor, yblack, left, right, ywhite)
743 struct edgelist *anchor; /* anchor of edge list */
744 pel yblack; /* y of (hopefully) black run above or below */
745 struct edgelist *left; /* edgelist at left of WHITE run */
746 struct edgelist *right; /* edgelist at right of WHITE run */
747 pel ywhite; /* y location of white run */
749 struct edgelist *edge;
750 struct edgelist *swathstart = anchor;
753 if (XofY(left, ywhite) >= XofY(right, ywhite))
756 Find the swath with 'yblack'. If we don't find it, completely collapse
757 the white run and return:
759 while (VALIDEDGE(swathstart)) {
760 if (yblack < swathstart->ymin) {
761 writeXofY(left, ywhite, XofY(right, ywhite));
764 if (yblack < swathstart->ymax) break;
765 swathstart = swathstart->link->link;
767 if(!VALIDEDGE(swathstart)) {
768 writeXofY(left, ywhite, XofY(right, ywhite));
772 Now we are in the swath that contains 'y', the reference line above
773 or below that we are trying to maintain continuity with. If black
774 in this line begins in the middle of our white run, we must collapse
775 the white run from the left to that point. If black ends in the
776 middle of our white run, we must collapse the white run from the right
779 for (edge = swathstart; VALIDEDGE(edge); edge = edge->link) {
781 if (!SAMESWATH(swathstart,edge))
783 if( XofY(edge, yblack) > XofY(left, ywhite)) {
784 if (ISLEFT(edge->flag)) {
785 x = XofY(edge, yblack);
786 if (XofY(right, ywhite) < x)
787 x = XofY(right, ywhite);
788 writeXofY(left, ywhite, x);
791 x = XofY(edge, yblack);
792 while (edge->link != NULL && SAMESWATH(edge, edge->link)
793 && x >= XofY(edge->link, yblack) ) {
794 edge = edge->link->link;
795 x = XofY(edge, yblack);
797 if (x < XofY(right, ywhite))
798 writeXofY(right, ywhite, x);
803 writeXofY(left, ywhite, XofY(right, ywhite));
807 :h3.ApplyContinuity() - Fix False Breaks in a Region
809 This is the externally visible routine called from the REGIONS module
810 when the +CONTINUITY flag is on the Interior() fill rule.
813 void ApplyContinuity(R)
816 struct edgelist *left;
817 struct edgelist *right;
818 struct edgelist *edge,*e2;
819 pel rightXabove,rightXbelow,leftXabove,leftXbelow;
822 long newcenter,abovecenter,belowcenter;
825 if (RegionDebug >= 3)
826 DumpSubPaths(R->anchor);
828 /* loop through and do all of the easy checking. ( no tops or bottoms) */
829 while(VALIDEDGE(left))
832 for(i=left->ymin;i<left->ymax;++i)
834 leftX = findXofY(left,i);
835 rightX = findXofY(right,i);
836 leftXbelow = findXofY(left,i+1);
837 rightXbelow = findXofY(right,i+1);
840 /* then, we have a break in a near vertical line */
841 leftXabove = findXofY(left,i-1);
842 rightXabove = findXofY(right,i-1);
843 if( IsValidPel(leftXabove) && IsValidPel(rightXabove) )
845 abovecenter = leftXabove + rightXabove;
849 abovecenter = leftX + rightX;
851 if( IsValidPel(leftXbelow) && IsValidPel(rightXbelow) )
853 belowcenter = leftXbelow + rightXbelow;
857 belowcenter = leftX + rightX;
859 newcenter = abovecenter + belowcenter;
860 if( newcenter > 4*leftX )
864 else if( newcenter < 4*leftX)
872 writeXofY(right,i,rightX);
873 writeXofY(left,i,leftX);
874 if(rightX > R->xmax) {R->xmax = rightX;}
875 if(leftX < R->xmin) {R->xmin = leftX;}
877 if( !WeAreAtBottom(left,i) && (leftXbelow>=rightX))
879 /* then we have a break in a near horizontal line in the middle */
880 writeXofY(right,i,leftXbelow);
882 if( !WeAreAtBottom(right,i) && (leftX >=rightXbelow))
884 /* then we have a break in a near horizontal line in the middle */
885 writeXofY(left,i,rightXbelow);
891 There may be "implied horizontal lines" between edges that have
892 implications for continuity. This loop looks for white runs that
893 have implied horizontal lines on the top or bottom, and calls
894 CollapseWhiteRuns to check and fix any continuity problems from
897 for (edge = R->anchor; VALIDEDGE(edge); edge = edge->link) {
898 if ((!ISTOP(edge->flag) && !ISBOTTOM(edge->flag)) || ISLEFT(edge->flag))
899 continue; /* at some future date we may want left edge logic here too */
900 for (e2 = edge->link; VALIDEDGE(e2) && SAMESWATH(edge,e2); e2 = e2->link) {
901 if (ISTOP(e2->flag) && ISTOP(edge->flag)
902 && NONE != ImpliedHorizontalLine(edge,e2,edge->ymin)) {
903 if (ISLEFT(e2->flag))
904 CollapseWhiteRun(R->anchor, edge->ymin-1,
905 edge, e2, edge->ymin);
907 if (ISBOTTOM(e2->flag) && ISBOTTOM(edge->flag)
908 && NONE != ImpliedHorizontalLine(edge,e2, edge->ymax)) {
909 if (ISLEFT(e2->flag))
910 CollapseWhiteRun(R->anchor, edge->ymax,
911 edge, e2, edge->ymax-1);