]> Pileus Git - ~andy/gtk/blob - gdk/gdkpolyreg-generic.c
Replace "effect" by "affect" where appropriate. (#165108, Dan Winship)
[~andy/gtk] / gdk / gdkpolyreg-generic.c
1 /* $TOG: PolyReg.c /main/15 1998/02/06 17:47:08 kaleb $ */
2 /************************************************************************
3
4 Copyright 1987, 1998  The Open Group
5
6 All Rights Reserved.
7
8 The above copyright notice and this permission notice shall be included in
9 all copies or substantial portions of the Software.
10
11 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
14 OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
15 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
16 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
17
18 Except as contained in this notice, the name of The Open Group shall not be
19 used in advertising or otherwise to promote the sale, use or other dealings
20 in this Software without prior written authorization from The Open Group.
21
22
23 Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
24
25                         All Rights Reserved
26
27 Permission to use, copy, modify, and distribute this software and its 
28 documentation for any purpose and without fee is hereby granted, 
29 provided that the above copyright notice appear in all copies and that
30 both that copyright notice and this permission notice appear in 
31 supporting documentation, and that the name of Digital not be
32 used in advertising or publicity pertaining to distribution of the
33 software without specific, written prior permission.  
34
35 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
36 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
37 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
38 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
39 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
40 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
41 SOFTWARE.
42
43 ************************************************************************/
44 /* $XFree86: xc/lib/X11/PolyReg.c,v 1.4 1998/10/03 08:41:21 dawes Exp $ */
45
46 #define LARGE_COORDINATE 1000000
47 #define SMALL_COORDINATE -LARGE_COORDINATE
48
49 #include <config.h>
50 #include "gdkalias.h"
51 #include <gdkregion.h>
52 #include "gdkregion-generic.h"
53 #include "gdkpoly-generic.h"
54
55 /*
56  *     InsertEdgeInET
57  *
58  *     Insert the given edge into the edge table.
59  *     First we must find the correct bucket in the
60  *     Edge table, then find the right slot in the
61  *     bucket.  Finally, we can insert it.
62  *
63  */
64 static void
65 InsertEdgeInET(ET, ETE, scanline, SLLBlock, iSLLBlock)
66     EdgeTable *ET;
67     EdgeTableEntry *ETE;
68     int scanline;
69     ScanLineListBlock **SLLBlock;
70     int *iSLLBlock;
71 {
72     EdgeTableEntry *start, *prev;
73     ScanLineList *pSLL, *pPrevSLL;
74     ScanLineListBlock *tmpSLLBlock;
75
76     /*
77      * find the right bucket to put the edge into
78      */
79     pPrevSLL = &ET->scanlines;
80     pSLL = pPrevSLL->next;
81     while (pSLL && (pSLL->scanline < scanline)) 
82     {
83         pPrevSLL = pSLL;
84         pSLL = pSLL->next;
85     }
86
87     /*
88      * reassign pSLL (pointer to ScanLineList) if necessary
89      */
90     if ((!pSLL) || (pSLL->scanline > scanline)) 
91     {
92         if (*iSLLBlock > SLLSPERBLOCK-1) 
93         {
94             tmpSLLBlock = 
95                   (ScanLineListBlock *)g_malloc(sizeof(ScanLineListBlock));
96             (*SLLBlock)->next = tmpSLLBlock;
97             tmpSLLBlock->next = (ScanLineListBlock *)NULL;
98             *SLLBlock = tmpSLLBlock;
99             *iSLLBlock = 0;
100         }
101         pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
102
103         pSLL->next = pPrevSLL->next;
104         pSLL->edgelist = (EdgeTableEntry *)NULL;
105         pPrevSLL->next = pSLL;
106     }
107     pSLL->scanline = scanline;
108
109     /*
110      * now insert the edge in the right bucket
111      */
112     prev = (EdgeTableEntry *)NULL;
113     start = pSLL->edgelist;
114     while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) 
115     {
116         prev = start;
117         start = start->next;
118     }
119     ETE->next = start;
120
121     if (prev)
122         prev->next = ETE;
123     else
124         pSLL->edgelist = ETE;
125 }
126 \f
127 /*
128  *     CreateEdgeTable
129  *
130  *     This routine creates the edge table for
131  *     scan converting polygons. 
132  *     The Edge Table (ET) looks like:
133  *
134  *    EdgeTable
135  *     --------
136  *    |  ymax  |        ScanLineLists
137  *    |scanline|-->------------>-------------->...
138  *     --------   |scanline|   |scanline|
139  *                |edgelist|   |edgelist|
140  *                ---------    ---------
141  *                    |             |
142  *                    |             |
143  *                    V             V
144  *              list of ETEs   list of ETEs
145  *
146  *     where ETE is an EdgeTableEntry data structure,
147  *     and there is one ScanLineList per scanline at
148  *     which an edge is initially entered.
149  *
150  */
151
152 static void
153 CreateETandAET(count, pts, ET, AET, pETEs, pSLLBlock)
154     int count;
155     GdkPoint *pts;
156     EdgeTable *ET;
157     EdgeTableEntry *AET;
158     EdgeTableEntry *pETEs;
159     ScanLineListBlock   *pSLLBlock;
160 {
161     GdkPoint *top, *bottom;
162     GdkPoint *PrevPt, *CurrPt;
163     int iSLLBlock = 0;
164     int dy;
165
166     if (count < 2)  return;
167
168     /*
169      *  initialize the Active Edge Table
170      */
171     AET->next = (EdgeTableEntry *)NULL;
172     AET->back = (EdgeTableEntry *)NULL;
173     AET->nextWETE = (EdgeTableEntry *)NULL;
174     AET->bres.minor_axis = SMALL_COORDINATE;
175
176     /*
177      *  initialize the Edge Table.
178      */
179     ET->scanlines.next = (ScanLineList *)NULL;
180     ET->ymax = SMALL_COORDINATE;
181     ET->ymin = LARGE_COORDINATE;
182     pSLLBlock->next = (ScanLineListBlock *)NULL;
183
184     PrevPt = &pts[count-1];
185
186     /*
187      *  for each vertex in the array of points.
188      *  In this loop we are dealing with two vertices at
189      *  a time -- these make up one edge of the polygon.
190      */
191     while (count--) 
192     {
193         CurrPt = pts++;
194
195         /*
196          *  find out which point is above and which is below.
197          */
198         if (PrevPt->y > CurrPt->y) 
199         {
200             bottom = PrevPt, top = CurrPt;
201             pETEs->ClockWise = 0;
202         }
203         else 
204         {
205             bottom = CurrPt, top = PrevPt;
206             pETEs->ClockWise = 1;
207         }
208
209         /*
210          * don't add horizontal edges to the Edge table.
211          */
212         if (bottom->y != top->y) 
213         {
214             pETEs->ymax = bottom->y-1;  /* -1 so we don't get last scanline */
215
216             /*
217              *  initialize integer edge algorithm
218              */
219             dy = bottom->y - top->y;
220             BRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
221
222             InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock);
223
224             if (PrevPt->y > ET->ymax)
225                 ET->ymax = PrevPt->y;
226             if (PrevPt->y < ET->ymin)
227                 ET->ymin = PrevPt->y;
228             pETEs++;
229         }
230
231         PrevPt = CurrPt;
232     }
233 }
234 \f
235 /*
236  *     loadAET
237  *
238  *     This routine moves EdgeTableEntries from the
239  *     EdgeTable into the Active Edge Table,
240  *     leaving them sorted by smaller x coordinate.
241  *
242  */
243
244 static void
245 loadAET(AET, ETEs)
246     EdgeTableEntry *AET, *ETEs;
247 {
248     EdgeTableEntry *pPrevAET;
249     EdgeTableEntry *tmp;
250
251     pPrevAET = AET;
252     AET = AET->next;
253     while (ETEs) 
254     {
255         while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) 
256         {
257             pPrevAET = AET;
258             AET = AET->next;
259         }
260         tmp = ETEs->next;
261         ETEs->next = AET;
262         if (AET)
263             AET->back = ETEs;
264         ETEs->back = pPrevAET;
265         pPrevAET->next = ETEs;
266         pPrevAET = ETEs;
267
268         ETEs = tmp;
269     }
270 }
271 \f
272 /*
273  *     computeWAET
274  *
275  *     This routine links the AET by the
276  *     nextWETE (winding EdgeTableEntry) link for
277  *     use by the winding number rule.  The final 
278  *     Active Edge Table (AET) might look something
279  *     like:
280  *
281  *     AET
282  *     ----------  ---------   ---------
283  *     |ymax    |  |ymax    |  |ymax    | 
284  *     | ...    |  |...     |  |...     |
285  *     |next    |->|next    |->|next    |->...
286  *     |nextWETE|  |nextWETE|  |nextWETE|
287  *     ---------   ---------   ^--------
288  *         |                   |       |
289  *         V------------------->       V---> ...
290  *
291  */
292 static void
293 computeWAET(AET)
294     EdgeTableEntry *AET;
295 {
296     EdgeTableEntry *pWETE;
297     int inside = 1;
298     int isInside = 0;
299
300     AET->nextWETE = (EdgeTableEntry *)NULL;
301     pWETE = AET;
302     AET = AET->next;
303     while (AET) 
304     {
305         if (AET->ClockWise)
306             isInside++;
307         else
308             isInside--;
309
310         if ((!inside && !isInside) ||
311             ( inside &&  isInside)) 
312         {
313             pWETE->nextWETE = AET;
314             pWETE = AET;
315             inside = !inside;
316         }
317         AET = AET->next;
318     }
319     pWETE->nextWETE = (EdgeTableEntry *)NULL;
320 }
321 \f
322 /*
323  *     InsertionSort
324  *
325  *     Just a simple insertion sort using
326  *     pointers and back pointers to sort the Active
327  *     Edge Table.
328  *
329  */
330
331 static int
332 InsertionSort(AET)
333     EdgeTableEntry *AET;
334 {
335     EdgeTableEntry *pETEchase;
336     EdgeTableEntry *pETEinsert;
337     EdgeTableEntry *pETEchaseBackTMP;
338     int changed = 0;
339
340     AET = AET->next;
341     while (AET) 
342     {
343         pETEinsert = AET;
344         pETEchase = AET;
345         while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
346             pETEchase = pETEchase->back;
347
348         AET = AET->next;
349         if (pETEchase != pETEinsert) 
350         {
351             pETEchaseBackTMP = pETEchase->back;
352             pETEinsert->back->next = AET;
353             if (AET)
354                 AET->back = pETEinsert->back;
355             pETEinsert->next = pETEchase;
356             pETEchase->back->next = pETEinsert;
357             pETEchase->back = pETEinsert;
358             pETEinsert->back = pETEchaseBackTMP;
359             changed = 1;
360         }
361     }
362     return(changed);
363 }
364 \f
365 /*
366  *     Clean up our act.
367  */
368 static void
369 FreeStorage(pSLLBlock)
370     ScanLineListBlock   *pSLLBlock;
371 {
372     ScanLineListBlock   *tmpSLLBlock;
373
374     while (pSLLBlock) 
375     {
376         tmpSLLBlock = pSLLBlock->next;
377         g_free (pSLLBlock);
378         pSLLBlock = tmpSLLBlock;
379     }
380 }
381
382 /*
383  *     Create an array of rectangles from a list of points.
384  *     If indeed these things (POINTS, RECTS) are the same,
385  *     then this proc is still needed, because it allocates
386  *     storage for the array, which was allocated on the
387  *     stack by the calling procedure.
388  *
389  */
390 static int PtsToRegion(numFullPtBlocks, iCurPtBlock, FirstPtBlock, reg)
391     int  numFullPtBlocks, iCurPtBlock;
392     POINTBLOCK *FirstPtBlock;
393     GdkRegion *reg;
394 {
395     GdkRegionBox *rects;
396     GdkPoint *pts;
397     POINTBLOCK *CurPtBlock;
398     int i;
399     GdkRegionBox *extents;
400     int numRects;
401
402     extents = &reg->extents;
403  
404     numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
405  
406     reg->rects = g_renew (GdkRegionBox, reg->rects, numRects);
407  
408     reg->size = numRects;
409     CurPtBlock = FirstPtBlock;
410     rects = reg->rects - 1;
411     numRects = 0;
412     extents->x1 = G_MAXSHORT,  extents->x2 = G_MINSHORT;
413  
414     for ( ; numFullPtBlocks >= 0; numFullPtBlocks--) {
415         /* the loop uses 2 points per iteration */
416         i = NUMPTSTOBUFFER >> 1;
417         if (!numFullPtBlocks)
418             i = iCurPtBlock >> 1;
419         for (pts = CurPtBlock->pts; i--; pts += 2) {
420             if (pts->x == pts[1].x)
421                 continue;
422             if (numRects && pts->x == rects->x1 && pts->y == rects->y2 &&
423                 pts[1].x == rects->x2 &&
424                 (numRects == 1 || rects[-1].y1 != rects->y1) &&
425                 (i && pts[2].y > pts[1].y)) {
426                 rects->y2 = pts[1].y + 1;
427                 continue;
428             }
429             numRects++;
430             rects++;
431             rects->x1 = pts->x;  rects->y1 = pts->y;
432             rects->x2 = pts[1].x;  rects->y2 = pts[1].y + 1;
433             if (rects->x1 < extents->x1)
434                 extents->x1 = rects->x1;
435             if (rects->x2 > extents->x2)
436                 extents->x2 = rects->x2;
437         }
438         CurPtBlock = CurPtBlock->next;
439     }
440
441     if (numRects) {
442         extents->y1 = reg->rects->y1;
443         extents->y2 = rects->y2;
444     } else {
445         extents->x1 = 0;
446         extents->y1 = 0;
447         extents->x2 = 0;
448         extents->y2 = 0;
449     }
450     reg->numRects = numRects;
451  
452     return(TRUE);
453 }
454
455 /*
456  *     polytoregion
457  *
458  *     Scan converts a polygon by returning a run-length
459  *     encoding of the resultant bitmap -- the run-length
460  *     encoding is in the form of an array of rectangles.
461  */
462 GdkRegion *
463 gdk_region_polygon(GdkPoint *Pts, gint Count, GdkFillRule rule)
464 {
465     GdkRegion *region;
466     EdgeTableEntry *pAET;   /* Active Edge Table       */
467     int y;                  /* current scanline        */
468     int iPts = 0;           /* number of pts in buffer */
469     EdgeTableEntry *pWETE;  /* Winding Edge Table Entry*/
470     ScanLineList *pSLL;     /* current scanLineList    */
471     GdkPoint *pts;             /* output buffer           */
472     EdgeTableEntry *pPrevAET;        /* ptr to previous AET     */
473     EdgeTable ET;                    /* header node for ET      */
474     EdgeTableEntry AET;              /* header node for AET     */
475     EdgeTableEntry *pETEs;           /* EdgeTableEntries pool   */
476     ScanLineListBlock SLLBlock;      /* header for scanlinelist */
477     int fixWAET = FALSE;
478     POINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers    */
479     POINTBLOCK *tmpPtBlock;
480     int numFullPtBlocks = 0;
481  
482     region = gdk_region_new ();
483
484     /* special case a rectangle */
485     pts = Pts;
486     if (((Count == 4) ||
487          ((Count == 5) && (pts[4].x == pts[0].x) && (pts[4].y == pts[0].y))) &&
488         (((pts[0].y == pts[1].y) &&
489           (pts[1].x == pts[2].x) &&
490           (pts[2].y == pts[3].y) &&
491           (pts[3].x == pts[0].x)) ||
492          ((pts[0].x == pts[1].x) &&
493           (pts[1].y == pts[2].y) &&
494           (pts[2].x == pts[3].x) &&
495           (pts[3].y == pts[0].y)))) {
496         region->extents.x1 = MIN(pts[0].x, pts[2].x);
497         region->extents.y1 = MIN(pts[0].y, pts[2].y);
498         region->extents.x2 = MAX(pts[0].x, pts[2].x);
499         region->extents.y2 = MAX(pts[0].y, pts[2].y);
500         if ((region->extents.x1 != region->extents.x2) &&
501             (region->extents.y1 != region->extents.y2)) {
502             region->numRects = 1;
503             *(region->rects) = region->extents;
504         }
505         return(region);
506     }
507
508     pETEs = g_new (EdgeTableEntry, Count);
509
510     pts = FirstPtBlock.pts;
511     CreateETandAET(Count, Pts, &ET, &AET, pETEs, &SLLBlock);
512     pSLL = ET.scanlines.next;
513     curPtBlock = &FirstPtBlock;
514  
515     if (rule == GDK_EVEN_ODD_RULE) {
516         /*
517          *  for each scanline
518          */
519         for (y = ET.ymin; y < ET.ymax; y++) {
520             /*
521              *  Add a new edge to the active edge table when we
522              *  get to the next edge.
523              */
524             if (pSLL != NULL && y == pSLL->scanline) {
525                 loadAET(&AET, pSLL->edgelist);
526                 pSLL = pSLL->next;
527             }
528             pPrevAET = &AET;
529             pAET = AET.next;
530  
531             /*
532              *  for each active edge
533              */
534             while (pAET) {
535                 pts->x = pAET->bres.minor_axis,  pts->y = y;
536                 pts++, iPts++;
537  
538                 /*
539                  *  send out the buffer
540                  */
541                 if (iPts == NUMPTSTOBUFFER) {
542                     tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK));
543                     tmpPtBlock->next = NULL;
544                     curPtBlock->next = tmpPtBlock;
545                     curPtBlock = tmpPtBlock;
546                     pts = curPtBlock->pts;
547                     numFullPtBlocks++;
548                     iPts = 0;
549                 }
550                 EVALUATEEDGEEVENODD(pAET, pPrevAET, y);
551             }
552             (void) InsertionSort(&AET);
553         }
554     }
555     else {
556         /*
557          *  for each scanline
558          */
559         for (y = ET.ymin; y < ET.ymax; y++) {
560             /*
561              *  Add a new edge to the active edge table when we
562              *  get to the next edge.
563              */
564             if (pSLL != NULL && y == pSLL->scanline) {
565                 loadAET(&AET, pSLL->edgelist);
566                 computeWAET(&AET);
567                 pSLL = pSLL->next;
568             }
569             pPrevAET = &AET;
570             pAET = AET.next;
571             pWETE = pAET;
572  
573             /*
574              *  for each active edge
575              */
576             while (pAET) {
577                 /*
578                  *  add to the buffer only those edges that
579                  *  are in the Winding active edge table.
580                  */
581                 if (pWETE == pAET) {
582                     pts->x = pAET->bres.minor_axis,  pts->y = y;
583                     pts++, iPts++;
584  
585                     /*
586                      *  send out the buffer
587                      */
588                     if (iPts == NUMPTSTOBUFFER) {
589                         tmpPtBlock = (POINTBLOCK *)g_malloc(sizeof(POINTBLOCK));
590                         tmpPtBlock->next = NULL;
591                         curPtBlock->next = tmpPtBlock;
592                         curPtBlock = tmpPtBlock;
593                         pts = curPtBlock->pts;
594                         numFullPtBlocks++;    iPts = 0;
595                     }
596                     pWETE = pWETE->nextWETE;
597                 }
598                 EVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
599             }
600  
601             /*
602              *  recompute the winding active edge table if
603              *  we just resorted or have exited an edge.
604              */
605             if (InsertionSort(&AET) || fixWAET) {
606                 computeWAET(&AET);
607                 fixWAET = FALSE;
608             }
609         }
610     }
611     FreeStorage(SLLBlock.next); 
612     (void) PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
613     for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
614         tmpPtBlock = curPtBlock->next;
615         g_free (curPtBlock);
616         curPtBlock = tmpPtBlock;
617     }
618     g_free (pETEs);
619     return(region);
620 }