OpenCPN Partial API docs
Loading...
Searching...
No Matches
OCPNRegion.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 *
5 ***************************************************************************
6 * Portins Copyright (C) 2010 by David S. Register *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22 ***************************************************************************
23 */
24
26// Name: src/gtk/region.cpp
27// Purpose:
28// Author: Robert Roebling
29// Modified: VZ at 05.10.00: use AllocExclusive(), comparison fixed
30// Id: $Id: region.cpp 42903 2006-11-01 12:56:38Z RR $
31// Copyright: (c) 1998 Robert Roebling
32// Licence: wxWindows licence
34
35// ============================================================================
36// declarations
37// ============================================================================
38
39// ----------------------------------------------------------------------------
40// headers
41// ----------------------------------------------------------------------------
42
43// For compilers that support precompilation, includes "wx.h".
44#include <wx/wxprec.h>
45
46#include <wx/region.h>
47#include "OCPNRegion.h"
48
49#ifndef WX_PRECOMP
50#include <wx/log.h>
51#endif
52
53typedef enum { OGDK_EVEN_ODD_RULE, OGDK_WINDING_RULE } OGdkFillRule;
54
55typedef enum {
56 OGDK_OVERLAP_RECTANGLE_IN,
57 OGDK_OVERLAP_RECTANGLE_OUT,
58 OGDK_OVERLAP_RECTANGLE_PART
59} OGdkOverlapType;
60
61#define EMPTY_REGION(pReg) pReg->numRects = 0
62#define REGION_NOT_EMPTY(pReg) pReg->numRects
63
64typedef struct _OGdkPoint OGdkPoint;
65struct _OGdkPoint {
66 int x;
67 int y;
68};
69
70typedef struct _OGdkRectangle OGdkRectangle;
72 int x;
73 int y;
74 int width;
75 int height;
76};
77
78//#define gboolean bool;
79
80typedef struct _OGdkSegment OGdkSegment;
82 int x1;
83 int y1;
84 int x2;
85 int y2;
86};
87
89
90typedef struct _OGdkRegion OGdkRegion;
92 long size;
93 long numRects;
94 OGdkRegionBox *rects;
95 OGdkRegionBox extents;
96};
97
98/*
99 * number of points to buffer before sending them off
100 * to scanlines() : Must be an even number
101 */
102#define NUMPTSTOBUFFER 200
103
104/*
105 * used to allocate buffers for points and link
106 * the buffers together
107 */
108typedef struct _OPOINTBLOCK {
109 OGdkPoint pts[NUMPTSTOBUFFER];
110 struct _OPOINTBLOCK *next;
112
113#define INBOX(r, x, y) \
114 ((((r).x2 > x)) && (((r).x1 <= x)) && (((r).y2 > y)) && (((r).y1 <= y)))
115
116/* 1 if two BOXs overlap.
117 * 0 if two BOXs do not overlap.
118 * Remember, x2 and y2 are not in the region
119 */
120#define EXTENTCHECK(r1, r2) \
121 ((r1)->x2 > (r2)->x1 && (r1)->x1 < (r2)->x2 && (r1)->y2 > (r2)->y1 && \
122 (r1)->y1 < (r2)->y2)
123
124/*
125 * #define _OG_NEW(struct_type, n_structs, func) \
126 * ((struct_type *) malloc ((n_structs), sizeof (struct_type)))
127 * #define _OG_RENEW(struct_type, mem, n_structs, func) \
128 * ((struct_type *) realloc (mem, (n_structs), sizeof (struct_type)))
129 *
130 * #define og_new(struct_type, n_structs) _OG_NEW
131 * (struct_type, n_structs, malloc) #define og_renew(struct_type, mem,
132 * n_structs) _OG_RENEW (struct_type, mem, n_structs, realloc)
133 */
134
135#define OGROWREGION(reg, nRects) \
136 { \
137 if ((nRects) == 0) { \
138 if ((reg)->rects != &(reg)->extents) { \
139 free((reg)->rects); \
140 (reg)->rects = &(reg)->extents; \
141 } \
142 } else if ((reg)->rects == &(reg)->extents) { \
143 (reg)->rects = (OGdkRegionBox *)malloc(nRects * sizeof(OGdkRegionBox)); \
144 (reg)->rects[0] = (reg)->extents; \
145 } else \
146 (reg)->rects = (OGdkRegionBox *)realloc((reg)->rects, \
147 sizeof(OGdkRegionBox) * nRects); \
148 (reg)->size = (nRects); \
149 }
150
151/*
152 * Check to see if there is enough memory in the present region.
153 */
154#define OMEMCHECK(reg, rect, firstrect) \
155 { \
156 if ((reg)->numRects >= ((reg)->size - 1)) { \
157 OGROWREGION(reg, 2 * (reg)->size); \
158 (rect) = &(firstrect)[(reg)->numRects]; \
159 } \
160 }
161
162#ifndef MIN
163#define MIN(a, b) wxMin(a, b)
164#endif
165
166#ifndef MAX
167#define MAX(a, b) wxMax(a, b)
168#endif
169
170/*
171 * In scan converting polygons, we want to choose those pixels
172 * which are inside the polygon. Thus, we add .5 to the starting
173 * x coordinate for both left and right edges. Now we choose the
174 * first pixel which is inside the pgon for the left edge and the
175 * first pixel which is outside the pgon for the right edge.
176 * Draw the left pixel, but not the right.
177 *
178 * How to add .5 to the starting x coordinate:
179 * If the edge is moving to the right, then subtract dy from the
180 * error term from the general form of the algorithm.
181 * If the edge is moving to the left, then add dy to the error term.
182 *
183 * The reason for the difference between edges moving to the left
184 * and edges moving to the right is simple: If an edge is moving
185 * to the right, then we want the algorithm to flip immediately.
186 * If it is moving to the left, then we don't want it to flip until
187 * we traverse an entire pixel.
188 */
189#define OBRESINITPGON(dy, x1, x2, xStart, d, m, m1, incr1, incr2) \
190 { \
191 int dx; /* local storage */ \
192 \
193 /* \
194 * if the edge is horizontal, then it is ignored \
195 * and assumed not to be processed. Otherwise, do this stuff. \
196 */ \
197 if ((dy) != 0) { \
198 xStart = (x1); \
199 dx = (x2)-xStart; \
200 if (dx < 0) { \
201 m = dx / (dy); \
202 m1 = m - 1; \
203 incr1 = -2 * dx + 2 * (dy)*m1; \
204 incr2 = -2 * dx + 2 * (dy)*m; \
205 d = 2 * m * (dy)-2 * dx - 2 * (dy); \
206 } else { \
207 m = dx / (dy); \
208 m1 = m + 1; \
209 incr1 = 2 * dx - 2 * (dy)*m1; \
210 incr2 = 2 * dx - 2 * (dy)*m; \
211 d = -2 * m * (dy) + 2 * dx; \
212 } \
213 } \
214 }
215
216#define OBRESINCRPGON(d, minval, m, m1, incr1, incr2) \
217 { \
218 if (m1 > 0) { \
219 if (d > 0) { \
220 minval += m1; \
221 d += incr1; \
222 } else { \
223 minval += m; \
224 d += incr2; \
225 } \
226 } else { \
227 if (d >= 0) { \
228 minval += m1; \
229 d += incr1; \
230 } else { \
231 minval += m; \
232 d += incr2; \
233 } \
234 } \
235 }
236
237/*
238 * This structure contains all of the information needed
239 * to run the bresenham algorithm.
240 * The variables may be hardcoded into the declarations
241 * instead of using this structure to make use of
242 * register declarations.
243 */
244typedef struct {
245 int minor_axis; /* minor axis */
246 int d; /* decision variable */
247 int m, m1; /* slope and slope+1 */
248 int incr1, incr2; /* error increments */
249} OBRESINFO;
250
251#define OBRESINITPGONSTRUCT(dmaj, min1, min2, bres) \
252 OBRESINITPGON(dmaj, min1, min2, bres.minor_axis, bres.d, bres.m, bres.m1, \
253 bres.incr1, bres.incr2)
254
255#define OBRESINCRPGONSTRUCT(bres) \
256 OBRESINCRPGON(bres.d, bres.minor_axis, bres.m, bres.m1, bres.incr1, \
257 bres.incr2)
258
259/*
260 * for the winding number rule
261 */
262#define CLOCKWISE 1
263#define COUNTERCLOCKWISE -1
264
265typedef struct _OEdgeTableEntry {
266 int ymax; /* ycoord at which we exit this edge. */
267 OBRESINFO bres; /* Bresenham info to run the edge */
268 struct _OEdgeTableEntry *next; /* next in the list */
269 struct _OEdgeTableEntry *back; /* for insertion sort */
270 struct _OEdgeTableEntry *nextWETE; /* for winding num rule */
271 int ClockWise; /* flag for winding number rule */
273
274typedef struct _OScanLineList {
275 int scanline; /* the scanline represented */
276 OEdgeTableEntry *edgelist; /* header node */
277 struct _OScanLineList *next; /* next in the list */
279
280typedef struct {
281 int ymax; /* ymax for the polygon */
282 int ymin; /* ymin for the polygon */
283 OScanLineList scanlines; /* header node */
284} OEdgeTable;
285
286/*
287 * Here is a struct to help with storage allocation
288 * so we can allocate a big chunk at a time, and then take
289 * pieces from this heap when we need to.
290 */
291#define SLLSPERBLOCK 25
292
293typedef struct _OScanLineListBlock {
294 OScanLineList SLLs[SLLSPERBLOCK];
295 struct _OScanLineListBlock *next;
297
298/*
299 *
300 * a few macros for the inner loops of the fill code where
301 * performance considerations don't allow a procedure call.
302 *
303 * Evaluate the given edge at the given scanline.
304 * If the edge has expired, then we leave it and fix up
305 * the active edge table; otherwise, we increment the
306 * x value to be ready for the next scanline.
307 * The winding number rule is in effect, so we must notify
308 * the caller when the edge has been removed so he
309 * can reorder the Winding Active Edge Table.
310 */
311#define OEVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET) \
312 { \
313 if (pAET->ymax == y) { /* leaving this edge */ \
314 pPrevAET->next = pAET->next; \
315 pAET = pPrevAET->next; \
316 fixWAET = 1; \
317 if (pAET) pAET->back = pPrevAET; \
318 } else { \
319 OBRESINCRPGONSTRUCT(pAET->bres); \
320 pPrevAET = pAET; \
321 pAET = pAET->next; \
322 } \
323 }
324
325/*
326 * Evaluate the given edge at the given scanline.
327 * If the edge has expired, then we leave it and fix up
328 * the active edge table; otherwise, we increment the
329 * x value to be ready for the next scanline.
330 * The even-odd rule is in effect.
331 */
332#define OEVALUATEEDGEEVENODD(pAET, pPrevAET, y) \
333 { \
334 if (pAET->ymax == y) { /* leaving this edge */ \
335 pPrevAET->next = pAET->next; \
336 pAET = pPrevAET->next; \
337 if (pAET) pAET->back = pPrevAET; \
338 } else { \
339 OBRESINCRPGONSTRUCT(pAET->bres); \
340 pPrevAET = pAET; \
341 pAET = pAET->next; \
342 } \
343 }
344
345OGdkRegion *gdk_region_copy(const OGdkRegion *region);
346void gdk_region_destroy(OGdkRegion *region);
347OGdkRegion *gdk_region_rectangle(const OGdkRectangle *rectangle);
348bool ogdk_region_equal(const OGdkRegion *region1, const OGdkRegion *region2);
349bool gdk_region_point_in(const OGdkRegion *region, int x, int y);
350OGdkOverlapType gdk_region_rect_in(const OGdkRegion *region,
351 const OGdkRectangle *rectangle);
352void gdk_region_offset(OGdkRegion *region, int dx, int dy);
353void gdk_region_union_with_rect(OGdkRegion *region, const OGdkRectangle *rect);
354void gdk_region_union(OGdkRegion *source1, const OGdkRegion *source2);
355void gdk_region_intersect(OGdkRegion *source1, const OGdkRegion *source2);
356OGdkRegion *gdk_region_polygon(const OGdkPoint *points, int n_points,
357 OGdkFillRule fill_rule);
358
359OGdkRegion *gdk_region_new(void);
360void gdk_region_subtract(OGdkRegion *source1, const OGdkRegion *source2);
361bool gdk_region_empty(const OGdkRegion *region);
362
363void gdk_region_get_rectangles(const OGdkRegion *region,
364 OGdkRectangle **rectangles, int *n_rectangles);
365void gdk_region_get_clipbox(const OGdkRegion *region, OGdkRectangle *rectangle);
366
367// ----------------------------------------------------------------------------
368// OCPNRegionRefData: private class containing the information about the region
369// ----------------------------------------------------------------------------
370
371class OCPNRegionRefData : public wxObjectRefData {
372public:
373 OCPNRegionRefData() { m_region = NULL; }
374
375 OCPNRegionRefData(const OCPNRegionRefData &refData) : wxObjectRefData() {
376 m_region = gdk_region_copy(refData.m_region);
377 }
378
379 virtual ~OCPNRegionRefData() {
380 if (m_region) gdk_region_destroy(m_region);
381 free(m_region);
382 }
383
384 OGdkRegion *m_region;
385};
386
387// ----------------------------------------------------------------------------
388// macros
389// ----------------------------------------------------------------------------
390
391#define M_REGIONDATA ((OCPNRegionRefData *)m_refData)
392#define M_REGIONDATA_OF(rgn) ((OCPNRegionRefData *)(rgn.m_refData))
393
394IMPLEMENT_DYNAMIC_CLASS(OCPNRegion, wxGDIObject)
395
396// ----------------------------------------------------------------------------
397// OCPNRegion construction
398// ----------------------------------------------------------------------------
399
400#define M_REGIONDATA ((OCPNRegionRefData *)m_refData)
401
402#ifndef USE_NEW_REGION
403
404OCPNRegion::OCPNRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
405 : wxRegion(x, y, w, h)
406
407{}
408
409OCPNRegion::OCPNRegion(const wxPoint &topLeft, const wxPoint &bottomRight)
410 : wxRegion(topLeft.x, topLeft.y, bottomRight.x - topLeft.x,
411 bottomRight.y - topLeft.y) {}
412
413OCPNRegion::OCPNRegion(const wxRect &rect)
414 : wxRegion(rect.x, rect.y, rect.width, rect.height) {}
415
416OCPNRegion::OCPNRegion(const wxRegion &region) : wxRegion(region) {}
417
418OCPNRegion::OCPNRegion(size_t n, const wxPoint *points, int fillStyle)
419 : wxRegion(n, points,
420#if wxCHECK_VERSION(2, 9, 0)
421 (wxPolygonFillMode)
422#endif
423 fillStyle) {
424}
425
426wxRegion *OCPNRegion::GetNew_wxRegion() const { return new wxRegion(this); }
427
428#endif
429
430#ifdef USE_NEW_REGION
431
432OCPNRegion::OCPNRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h) {
433 InitRect(x, y, w, h);
434}
435
436OCPNRegion::OCPNRegion(const wxPoint &topLeft, const wxPoint &bottomRight) {
437 InitRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x,
438 bottomRight.y - topLeft.y);
439}
440
441OCPNRegion::OCPNRegion(const wxRect &rect) {
442 InitRect(rect.x, rect.y, rect.width, rect.height);
443}
444
445OCPNRegion::OCPNRegion(const wxRegion &region) {
446 wxRegionIterator ri(region);
447 if (!ri.HaveRects()) return;
448
449 wxRect rect = ri.GetRect();
450 InitRect(rect.x, rect.y, rect.width, rect.height);
451 ri++;
452
453 while (ri.HaveRects()) {
454 Union(ri.GetRect());
455 ri++;
456 }
457}
458
459OCPNRegion::~OCPNRegion() {}
460
461void OCPNRegion::InitRect(wxCoord x, wxCoord y, wxCoord w, wxCoord h) {
462 OGdkRectangle rect;
463 rect.x = x;
464 rect.y = y;
465 rect.width = w;
466 rect.height = h;
467
468 m_refData = new OCPNRegionRefData();
469
470 M_REGIONDATA->m_region = gdk_region_rectangle(&rect);
471}
472
473// OCPNRegion::OCPNRegion( GdkRegion *region )
474//{
475// m_refData = new OCPNRegionRefData();
476// M_REGIONDATA->m_region = gdk_region_copy( region );
477//}
478
479OCPNRegion::OCPNRegion(size_t n, const wxPoint *points, int fillStyle) {
480 OGdkPoint *gdkpoints = new OGdkPoint[n];
481 for (size_t i = 0; i < n; i++) {
482 gdkpoints[i].x = points[i].x;
483 gdkpoints[i].y = points[i].y;
484 }
485
486 m_refData = new OCPNRegionRefData();
487
488 OGdkRegion *reg = gdk_region_polygon(
489 gdkpoints, n,
490 fillStyle == wxWINDING_RULE ? OGDK_WINDING_RULE : OGDK_EVEN_ODD_RULE);
491
492 M_REGIONDATA->m_region = reg;
493
494 delete[] gdkpoints;
495}
496
497// OCPNRegion::~OCPNRegion()
498//{
499// m_refData unrefed in ~wxObject
500//}
501
502wxObjectRefData *OCPNRegion::CreateRefData() const {
503 return new OCPNRegionRefData;
504}
505
506wxObjectRefData *OCPNRegion::CloneRefData(const wxObjectRefData *data) const {
507 return new OCPNRegionRefData(*(OCPNRegionRefData *)data);
508}
509
510// ----------------------------------------------------------------------------
511// OCPNRegion comparison
512// ----------------------------------------------------------------------------
513
514bool OCPNRegion::ODoIsEqual(const OCPNRegion &region) const {
515 if (!region.m_refData) return false;
516
517 return ogdk_region_equal(M_REGIONDATA->m_region,
518 M_REGIONDATA_OF(region)->m_region);
519}
520
521// ----------------------------------------------------------------------------
522// OCPNRegion operations
523// ----------------------------------------------------------------------------
524
525void OCPNRegion::Clear() { UnRef(); }
526
527bool OCPNRegion::ODoUnionWithRect(const wxRect &r) {
528 // workaround for a strange GTK/X11 bug: taking union with an empty
529 // rectangle results in an empty region which is definitely not what we
530 // want
531 if (r.IsEmpty()) return true;
532
533 if (!m_refData) {
534 InitRect(r.x, r.y, r.width, r.height);
535 } else {
536 AllocExclusive();
537
538 OGdkRectangle rect;
539 rect.x = r.x;
540 rect.y = r.y;
541 rect.width = r.width;
542 rect.height = r.height;
543
544 gdk_region_union_with_rect(M_REGIONDATA->m_region, &rect);
545 }
546
547 return true;
548}
549
550bool OCPNRegion::ODoUnionWithRegion(const OCPNRegion &region) {
551 wxCHECK_MSG(region.Ok(), false, _T("invalid region"));
552
553 if (!m_refData) {
554 m_refData = new OCPNRegionRefData();
555 M_REGIONDATA->m_region = gdk_region_new();
556 } else {
557 AllocExclusive();
558 }
559
560 gdk_region_union(M_REGIONDATA->m_region, (OGdkRegion *)region.GetRegion());
561
562 return true;
563}
564
565bool OCPNRegion::ODoIntersect(const OCPNRegion &region) {
566 wxCHECK_MSG(region.Ok(), false, _T("invalid region"));
567
568 if (!m_refData) {
569 // intersecting with invalid region doesn't make sense
570 return false;
571 }
572
573 AllocExclusive();
574
575 gdk_region_intersect(M_REGIONDATA->m_region,
576 (OGdkRegion *)region.GetRegion());
577
578 return true;
579}
580
581bool OCPNRegion::ODoSubtract(const OCPNRegion &region) {
582 wxCHECK_MSG(region.Ok(), false, _T("invalid region"));
583 if (!m_refData) {
584 // subtracting from an invalid region doesn't make sense
585 return false;
586 }
587
588 AllocExclusive();
589
590 gdk_region_subtract(M_REGIONDATA->m_region, (OGdkRegion *)region.GetRegion());
591
592 return true;
593}
594
595#if 0
596bool OCPNRegion::DoXor( const OCPNRegion& region )
597{
598 wxCHECK_MSG( region.Ok(), false, _T("invalid region") );
599
600 if (!m_refData)
601 {
602 return false;
603 }
604
605 AllocExclusive();
606
608
609 return true;
610}
611#endif
612
613bool OCPNRegion::ODoOffset(wxCoord x, wxCoord y) {
614 if (!m_refData) return false;
615
616 AllocExclusive();
617
618 gdk_region_offset(M_REGIONDATA->m_region, x, y);
619
620 return true;
621}
622
623// ----------------------------------------------------------------------------
624// OCPNRegion tests
625// ----------------------------------------------------------------------------
626
627bool OCPNRegion::ODoGetBox(wxCoord &x, wxCoord &y, wxCoord &w,
628 wxCoord &h) const {
629 if (m_refData) {
630 OGdkRectangle rect;
631 gdk_region_get_clipbox(M_REGIONDATA->m_region, &rect);
632 x = rect.x;
633 y = rect.y;
634 w = rect.width;
635 h = rect.height;
636
637 return true;
638 } else {
639 x = 0;
640 y = 0;
641 w = -1;
642 h = -1;
643
644 return false;
645 }
646}
647
648bool OCPNRegion::IsEmpty() const {
649 if (!m_refData) return true;
650
651 return gdk_region_empty(M_REGIONDATA->m_region);
652}
653
654wxRegionContain OCPNRegion::ODoContainsPoint(wxCoord x, wxCoord y) const {
655 if (!m_refData) return wxOutRegion;
656
657 if (gdk_region_point_in(M_REGIONDATA->m_region, x, y))
658 return wxInRegion;
659 else
660 return wxOutRegion;
661}
662
663wxRegionContain OCPNRegion::ODoContainsRect(const wxRect &r) const {
664 if (!m_refData) return wxOutRegion;
665
666 OGdkRectangle rect;
667 rect.x = r.x;
668 rect.y = r.y;
669 rect.width = r.width;
670 rect.height = r.height;
671 OGdkOverlapType res = gdk_region_rect_in(M_REGIONDATA->m_region, &rect);
672 switch (res) {
673 case OGDK_OVERLAP_RECTANGLE_IN:
674 return wxInRegion;
675 case OGDK_OVERLAP_RECTANGLE_OUT:
676 return wxOutRegion;
677 case OGDK_OVERLAP_RECTANGLE_PART:
678 return wxPartRegion;
679 }
680
681 return wxOutRegion;
682}
683
684void *OCPNRegion::GetRegion() const {
685 if (!m_refData) return NULL;
686
687 return M_REGIONDATA->m_region;
688}
689
690wxRegion *OCPNRegion::GetNew_wxRegion() const {
691 wxRegion *r = new wxRegion;
692 r->Clear();
693
694 OGdkRectangle *gdkrects = NULL;
695 int numRects = 0;
696 gdk_region_get_rectangles((OGdkRegion *)GetRegion(), &gdkrects, &numRects);
697
698 if (numRects) {
699 for (int i = 0; i < numRects; ++i) {
700 OGdkRectangle &gr = gdkrects[i];
701
702 wxRect wr;
703 wr.x = gr.x;
704 wr.y = gr.y;
705 wr.width = gr.width;
706 wr.height = gr.height;
707
708 r->Union(wr);
709 }
710 }
711 free(gdkrects);
712
713 return r;
714}
715
716#endif
717// ----------------------------------------------------------------------------
718// OCPNRegionIterator
719// ----------------------------------------------------------------------------
720
721#ifndef USE_NEW_REGION
722
723OCPNRegionIterator::OCPNRegionIterator(const OCPNRegion &region) {
724 m_ri = new wxRegionIterator(region);
725}
726
727OCPNRegionIterator::~OCPNRegionIterator() { delete m_ri; }
728
729void OCPNRegionIterator::Reset() { m_ri->Reset(); }
730
731void OCPNRegionIterator::Reset(const OCPNRegion &region) {
732 m_ri->Reset(region);
733}
734
735wxRect OCPNRegionIterator::GetRect() const { return m_ri->GetRect(); }
736
737bool OCPNRegionIterator::HaveRects() const { return m_ri->HaveRects(); }
738
739void OCPNRegionIterator::NextRect() { ++(*m_ri); }
740
741#endif
742
743#ifdef USE_NEW_REGION
744
745OCPNRegionIterator::OCPNRegionIterator() {
746 Init();
747 Reset();
748}
749
750OCPNRegionIterator::OCPNRegionIterator(const OCPNRegion &region) {
751 Init();
752 Reset(region);
753}
754
755void OCPNRegionIterator::Init() {
756 m_rects = NULL;
757 m_numRects = 0;
758}
759
760OCPNRegionIterator::~OCPNRegionIterator() { wxDELETEA(m_rects); }
761
762void OCPNRegionIterator::Reset() { m_current = 0u; }
763
764void OCPNRegionIterator::NextRect() {
765 if (HaveRects()) ++m_current;
766}
767
768void OCPNRegionIterator::CreateRects(const OCPNRegion &region) {
769 wxDELETEA(m_rects);
770 m_numRects = 0;
771
772 OGdkRegion *gdkregion = (OGdkRegion *)region.GetRegion();
773 if (!gdkregion) return;
774
775 OGdkRectangle *gdkrects = NULL;
776 int numRects = 0;
777 gdk_region_get_rectangles(gdkregion, &gdkrects, &numRects);
778
779 m_numRects = numRects;
780 if (numRects) {
781 m_rects = new wxRect[m_numRects];
782 for (size_t i = 0; i < m_numRects; ++i) {
783 OGdkRectangle &gr = gdkrects[i];
784 wxRect &wr = m_rects[i];
785 wr.x = gr.x;
786 wr.y = gr.y;
787 wr.width = gr.width;
788 wr.height = gr.height;
789 }
790 }
791 free(gdkrects);
792}
793
794void OCPNRegionIterator::Reset(const OCPNRegion &region) {
795 m_region = region;
796 CreateRects(region);
797 Reset();
798}
799
800bool OCPNRegionIterator::HaveRects() const { return m_current < m_numRects; }
801
802wxRect OCPNRegionIterator::GetRect() const {
803 wxRect r;
804 if (HaveRects()) r = m_rects[m_current];
805
806 return r;
807}
808
809#endif
810
811#ifdef USE_NEW_REGION
812
813/* $TOG: Region.c /main/31 1998/02/06 17:50:22 kaleb $ */
814/************************************************************************
815 *
816 * Copyright 1987, 1988, 1998 The Open Group
817 *
818 * All Rights Reserved.
819 *
820 * The above copyright notice and this permission notice shall be included in
821 * all copies or substantial portions of the Software.
822 *
823 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
824 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
825 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
826 * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
827 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
828 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
829 *
830 * Except as contained in this notice, the name of The Open Group shall not be
831 * used in advertising or otherwise to promote the sale, use or other dealings
832 * in this Software without prior written authorization from The Open Group.
833 *
834 *
835 * Copyright 1987, 1988 by Digital Equipment Corporation, Maynard,
836 *Massachusetts.
837 *
838 * All Rights Reserved
839 *
840 * Permission to use, copy, modify, and distribute this software and its
841 * documentation for any purpose and without fee is hereby granted,
842 * provided that the above copyright notice appear in all copies and that
843 * both that copyright notice and this permission notice appear in
844 * supporting documentation, and that the name of Digital not be
845 * used in advertising or publicity pertaining to distribution of the
846 * software without specific, written prior permission.
847 *
848 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
849 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
850 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
851 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
852 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
853 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
854 * SOFTWARE.
855 *
856 ************************************************************************/
857/* $XFree86: xc/lib/X11/Region.c,v 1.5 1999/05/09 10:50:01 dawes Exp $ */
858/*
859 * The functions in this file implement the Region abstraction, similar to one
860 * used in the X11 sample server. A Region is simply an area, as the name
861 * implies, and is implemented as a "y-x-banded" array of rectangles. To
862 * explain: Each Region is made up of a certain number of rectangles sorted
863 * by y coordinate first, and then by x coordinate.
864 *
865 * Furthermore, the rectangles are banded such that every rectangle with a
866 * given upper-left y coordinate (y1) will have the same lower-right y
867 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
868 * will span the entire vertical distance of the band. This means that some
869 * areas that could be merged into a taller rectangle will be represented as
870 * several shorter rectangles to account for shorter rectangles to its left
871 * or right but within its "vertical scope".
872 *
873 * An added constraint on the rectangles is that they must cover as much
874 * horizontal area as possible. E.g. no two rectangles in a band are allowed
875 * to touch.
876 *
877 * Whenever possible, bands will be merged together to cover a greater vertical
878 * distance (and thus reduce the number of rectangles). Two bands can be merged
879 * only if the bottom of one touches the top of the other and they have
880 * rectangles in the same places (of the same width, of course). This maintains
881 * the y-x-banding that's so nice to have...
882 */
883
884//#include "config.h"
885//#include <stdlib.h>
886//#include <string.h>
887//#include <gdkregion.h>
888//#include "gdkregion-generic.h"
889//#include "gdkalias.h"
890
891typedef void (*overlapFunc)(OGdkRegion *pReg, OGdkRegionBox *r1,
892 OGdkRegionBox *r1End, OGdkRegionBox *r2,
893 OGdkRegionBox *r2End, int y1, int y2);
894typedef void (*nonOverlapFunc)(OGdkRegion *pReg, OGdkRegionBox *r,
895 OGdkRegionBox *rEnd, int y1, int y2);
896
897static void miRegionCopy(OGdkRegion *dstrgn, const OGdkRegion *rgn);
898static void miRegionOp(OGdkRegion *newReg, OGdkRegion *reg1,
899 const OGdkRegion *reg2, overlapFunc overlapFn,
900 nonOverlapFunc nonOverlap1Fn,
901 nonOverlapFunc nonOverlap2Fn);
902
910OGdkRegion *gdk_region_new(void) {
911 OGdkRegion *temp;
912
913 temp = (OGdkRegion *)malloc(sizeof(OGdkRegion));
914
915 temp->numRects = 0;
916 temp->rects = &temp->extents;
917 temp->extents.x1 = 0;
918 temp->extents.y1 = 0;
919 temp->extents.x2 = 0;
920 temp->extents.y2 = 0;
921 temp->size = 1;
922
923 return temp;
924}
925
934OGdkRegion *gdk_region_rectangle(const OGdkRectangle *rectangle) {
935 OGdkRegion *temp;
936
938
939 if (rectangle->width <= 0 || rectangle->height <= 0) return gdk_region_new();
940
941 temp = gdk_region_new();
942
943 temp->numRects = 1;
944 temp->rects = &temp->extents;
945 temp->extents.x1 = rectangle->x;
946 temp->extents.y1 = rectangle->y;
947 temp->extents.x2 = rectangle->x + rectangle->width;
948 temp->extents.y2 = rectangle->y + rectangle->height;
949 temp->size = 1;
950
951 return temp;
952}
953
962OGdkRegion *gdk_region_copy(const OGdkRegion *region) {
963 OGdkRegion *temp;
964
966
967 temp = gdk_region_new();
968
969 miRegionCopy(temp, region);
970
971 return temp;
972}
973
982void gdk_region_get_clipbox(const OGdkRegion *region,
983 OGdkRectangle *rectangle) {
986
987 rectangle->x = region->extents.x1;
988 rectangle->y = region->extents.y1;
989 rectangle->width = region->extents.x2 - region->extents.x1;
990 rectangle->height = region->extents.y2 - region->extents.y1;
991}
992
1002void gdk_region_get_rectangles(const OGdkRegion *region,
1003 OGdkRectangle **rectangles, int *n_rectangles) {
1004 int i;
1005
1009
1010 *n_rectangles = region->numRects;
1011 *rectangles =
1012 (OGdkRectangle *)malloc(sizeof(OGdkRectangle) * region->numRects);
1013
1014 for (i = 0; i < region->numRects; i++) {
1015 OGdkRegionBox rect;
1016 rect = region->rects[i];
1017 (*rectangles)[i].x = rect.x1;
1018 (*rectangles)[i].y = rect.y1;
1019 (*rectangles)[i].width = rect.x2 - rect.x1;
1020 (*rectangles)[i].height = rect.y2 - rect.y1;
1021 }
1022}
1023
1033void gdk_region_union_with_rect(OGdkRegion *region, const OGdkRectangle *rect) {
1034 OGdkRegion tmp_region;
1035
1036 // g_return_if_fail (region != NULL);
1037 // g_return_if_fail (rect != NULL);
1038
1039 if (rect->width <= 0 || rect->height <= 0) return;
1040
1041 tmp_region.rects = &tmp_region.extents;
1042 tmp_region.numRects = 1;
1043 tmp_region.extents.x1 = rect->x;
1044 tmp_region.extents.y1 = rect->y;
1045 tmp_region.extents.x2 = rect->x + rect->width;
1046 tmp_region.extents.y2 = rect->y + rect->height;
1047 tmp_region.size = 1;
1048
1049 gdk_region_union(region, &tmp_region);
1050}
1051
1052/*-
1053 * -----------------------------------------------------------------------
1054 * miSetExtents --
1055 * Reset the extents of a region to what they should be. Called by
1056 * miSubtract and miIntersect b/c they can't figure it out along the
1057 * way or do so easily, as miUnion can.
1058 *
1059 * Results:
1060 * None.
1061 *
1062 * Side Effects:
1063 * The region's 'extents' structure is overwritten.
1064 *
1065 *-----------------------------------------------------------------------
1066 */
1067static void miSetExtents(OGdkRegion *pReg) {
1068 OGdkRegionBox *pBox, *pBoxEnd, *pExtents;
1069
1070 if (pReg->numRects == 0) {
1071 pReg->extents.x1 = 0;
1072 pReg->extents.y1 = 0;
1073 pReg->extents.x2 = 0;
1074 pReg->extents.y2 = 0;
1075 return;
1076 }
1077
1078 pExtents = &pReg->extents;
1079 pBox = pReg->rects;
1080 pBoxEnd = &pBox[pReg->numRects - 1];
1081
1082 /*
1083 * Since pBox is the first rectangle in the region, it must have the
1084 * smallest y1 and since pBoxEnd is the last rectangle in the region,
1085 * it must have the largest y2, because of banding. Initialize x1 and
1086 * x2 from pBox and pBoxEnd, resp., as good things to initialize them
1087 * to...
1088 */
1089 pExtents->x1 = pBox->x1;
1090 pExtents->y1 = pBox->y1;
1091 pExtents->x2 = pBoxEnd->x2;
1092 pExtents->y2 = pBoxEnd->y2;
1093
1095 while (pBox <= pBoxEnd) {
1096 if (pBox->x1 < pExtents->x1) {
1097 pExtents->x1 = pBox->x1;
1098 }
1099 if (pBox->x2 > pExtents->x2) {
1100 pExtents->x2 = pBox->x2;
1101 }
1102 pBox++;
1103 }
1105}
1106
1113void gdk_region_destroy(OGdkRegion *region) {
1115
1116 if (region->rects != &region->extents) free(region->rects);
1118}
1119
1128void gdk_region_offset(OGdkRegion *region, int x, int y) {
1129 int nbox;
1130 OGdkRegionBox *pbox;
1131
1133
1134 pbox = region->rects;
1135 nbox = region->numRects;
1136
1137 while (nbox--) {
1138 pbox->x1 += x;
1139 pbox->x2 += x;
1140 pbox->y1 += y;
1141 pbox->y2 += y;
1142 pbox++;
1143 }
1144 if (region->rects != &region->extents) {
1145 region->extents.x1 += x;
1146 region->extents.x2 += x;
1147 region->extents.y1 += y;
1148 region->extents.y2 += y;
1149 }
1150}
1151
1152/*
1153 * Utility procedure Compress:
1154 * Replace r by the region r', where
1155 * p in r' iff (Quantifer m <= dx) (p + m in r), and
1156 * Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
1157 * (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
1158 *
1159 * Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
1160 * of all points p such that p and the next dx points on the same
1161 * horizontal scan line are all in r. We do this using by noting
1162 * that p is the head of a run of length 2^i + k iff p is the head
1163 * of a run of length 2^i and p+2^i is the head of a run of length
1164 * k. Thus, the loop invariant: s contains the region corresponding
1165 * to the runs of length shift. r contains the region corresponding
1166 * to the runs of length 1 + dxo & (shift-1), where dxo is the original
1167 * value of dx. dx = dxo & ~(shift-1). As parameters, s and t are
1168 * scratch regions, so that we don't have to allocate them on every
1169 * call.
1170 */
1171
1172#define ZOpRegion(a, b) \
1173 if (grow) \
1174 gdk_region_union(a, b); \
1175 else \
1176 gdk_region_intersect(a, b)
1177#define ZShiftRegion(a, b) \
1178 if (xdir) \
1179 gdk_region_offset(a, b, 0); \
1180 else \
1181 gdk_region_offset(a, 0, b)
1182
1183static void Compress(OGdkRegion *r, OGdkRegion *s, OGdkRegion *t,
1184 unsigned int dx, int xdir, int grow) {
1185 unsigned int shift = 1;
1186
1187 miRegionCopy(s, r);
1188 while (dx) {
1189 if (dx & shift) {
1190 ZShiftRegion(r, -(int)shift);
1191 ZOpRegion(r, s);
1192 dx -= shift;
1193 if (!dx) break;
1194 }
1195 miRegionCopy(t, s);
1196 ZShiftRegion(s, -(int)shift);
1197 ZOpRegion(s, t);
1198 shift <<= 1;
1199 }
1200}
1201
1202#undef ZOpRegion
1203#undef ZShiftRegion
1204#undef ZCopyRegion
1205
1215void gdk_region_shrink(OGdkRegion *region, int dx, int dy) {
1216 OGdkRegion *s, *t;
1217 int grow;
1218
1220
1221 if (!dx && !dy) return;
1222
1223 s = gdk_region_new();
1224 t = gdk_region_new();
1225
1226 grow = (dx < 0);
1227 if (grow) dx = -dx;
1228 if (dx) Compress(region, s, t, (unsigned)2 * dx, TRUE, grow);
1229
1230 grow = (dy < 0);
1231 if (grow) dy = -dy;
1232 if (dy) Compress(region, s, t, (unsigned)2 * dy, FALSE, grow);
1233
1234 gdk_region_offset(region, dx, dy);
1235 gdk_region_destroy(s);
1236 gdk_region_destroy(t);
1237}
1238
1239/*======================================================================
1240 * Region Intersection
1241 *====================================================================*/
1242/*-
1243 * -----------------------------------------------------------------------
1244 * miIntersectO --
1245 * Handle an overlapping band for miIntersect.
1246 *
1247 * Results:
1248 * None.
1249 *
1250 * Side Effects:
1251 * Rectangles may be added to the region.
1252 *
1253 *-----------------------------------------------------------------------
1254 */
1255/* static void*/
1256static void miIntersectO(OGdkRegion *pReg, OGdkRegionBox *r1,
1257 OGdkRegionBox *r1End, OGdkRegionBox *r2,
1258 OGdkRegionBox *r2End, int y1, int y2) {
1259 int x1;
1260 int x2;
1261 OGdkRegionBox *pNextRect;
1262
1263 pNextRect = &pReg->rects[pReg->numRects];
1264
1265 while ((r1 != r1End) && (r2 != r2End)) {
1266 x1 = MAX(r1->x1, r2->x1);
1267 x2 = MIN(r1->x2, r2->x2);
1268
1269 /*
1270 * If there's any overlap between the two rectangles, add that
1271 * overlap to the new region.
1272 * There's no need to check for subsumption because the only way
1273 * such a need could arise is if some region has two rectangles
1274 * right next to each other. Since that should never happen...
1275 */
1276 if (x1 < x2) {
1278
1279 OMEMCHECK(pReg, pNextRect, pReg->rects);
1280 pNextRect->x1 = x1;
1281 pNextRect->y1 = y1;
1282 pNextRect->x2 = x2;
1283 pNextRect->y2 = y2;
1284 pReg->numRects += 1;
1285 pNextRect++;
1286 // g_assert (pReg->numRects <= pReg->size);
1287 }
1288
1289 /*
1290 * Need to advance the pointers. Shift the one that extends
1291 * to the right the least, since the other still has a chance to
1292 * overlap with that region's next rectangle, if you see what I mean.
1293 */
1294 if (r1->x2 < r2->x2) {
1295 r1++;
1296 } else if (r2->x2 < r1->x2) {
1297 r2++;
1298 } else {
1299 r1++;
1300 r2++;
1301 }
1302 }
1303}
1304
1314void gdk_region_intersect(OGdkRegion *source1, const OGdkRegion *source2) {
1317
1318 /* check for trivial reject */
1319 if ((!(source1->numRects)) || (!(source2->numRects)) ||
1320 (!EXTENTCHECK(&source1->extents, &source2->extents)))
1321 source1->numRects = 0;
1322 else
1323 miRegionOp(source1, source1, source2, miIntersectO, (nonOverlapFunc)NULL,
1324 (nonOverlapFunc)NULL);
1325
1326 /*
1327 * Can't alter source1's extents before miRegionOp depends on the
1328 * extents of the regions being unchanged. Besides, this way there's
1329 * no checking against rectangles that will be nuked due to
1330 * coalescing, so we have to examine fewer rectangles.
1331 */
1332 miSetExtents(source1);
1333}
1334
1335static void miRegionCopy(OGdkRegion *dstrgn, const OGdkRegion *rgn) {
1336 if (dstrgn != rgn) /* don't want to copy to itself */
1337 {
1338 if (dstrgn->size < rgn->numRects) {
1339 if (dstrgn->rects != &dstrgn->extents) free(dstrgn->rects);
1340
1341 dstrgn->rects =
1342 (OGdkRegionBox *)malloc(sizeof(OGdkRegionBox) * rgn->numRects);
1343 dstrgn->size = rgn->numRects;
1344 }
1345
1346 dstrgn->numRects = rgn->numRects;
1347 dstrgn->extents = rgn->extents;
1348
1349 memcpy(dstrgn->rects, rgn->rects, rgn->numRects * sizeof(OGdkRegionBox));
1350 }
1351}
1352
1353/*======================================================================
1354 * Generic Region Operator
1355 *====================================================================*/
1356
1357/*-
1358 * -----------------------------------------------------------------------
1359 * miCoalesce --
1360 * Attempt to merge the boxes in the current band with those in the
1361 * previous one. Used only by miRegionOp.
1362 *
1363 * Results:
1364 * The new index for the previous band.
1365 *
1366 * Side Effects:
1367 * If coalescing takes place:
1368 * - rectangles in the previous band will have their y2 fields
1369 * altered.
1370 * - pReg->numRects will be decreased.
1371 *
1372 *-----------------------------------------------------------------------
1373 */
1374/* static int*/
1375static int miCoalesce(OGdkRegion *pReg, /* Region to coalesce */
1376 int prevStart, /* Index of start of previous band */
1377 int curStart) /* Index of start of current band */
1378{
1379 OGdkRegionBox *pPrevBox; /* Current box in previous band */
1380 OGdkRegionBox *pCurBox; /* Current box in current band */
1381 OGdkRegionBox *pRegEnd; /* End of region */
1382 int curNumRects; /* Number of rectangles in current
1383 * band */
1384 int prevNumRects; /* Number of rectangles in previous
1385 * band */
1386 int bandY1; /* Y1 coordinate for current band */
1387
1388 pRegEnd = &pReg->rects[pReg->numRects];
1389
1390 pPrevBox = &pReg->rects[prevStart];
1391 prevNumRects = curStart - prevStart;
1392
1393 /*
1394 * Figure out how many rectangles are in the current band. Have to do
1395 * this because multiple bands could have been added in miRegionOp
1396 * at the end when one region has been exhausted.
1397 */
1398 pCurBox = &pReg->rects[curStart];
1399 bandY1 = pCurBox->y1;
1400 for (curNumRects = 0; (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
1401 curNumRects++) {
1402 pCurBox++;
1403 }
1404
1405 if (pCurBox != pRegEnd) {
1406 /*
1407 * If more than one band was added, we have to find the start
1408 * of the last band added so the next coalescing job can start
1409 * at the right place... (given when multiple bands are added,
1410 * this may be pointless -- see above).
1411 */
1412 pRegEnd--;
1413 while (pRegEnd[-1].y1 == pRegEnd->y1) {
1414 pRegEnd--;
1415 }
1416 curStart = pRegEnd - pReg->rects;
1417 pRegEnd = pReg->rects + pReg->numRects;
1418 }
1419
1420 if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
1421 pCurBox -= curNumRects;
1422 /*
1423 * The bands may only be coalesced if the bottom of the previous
1424 * matches the top scanline of the current.
1425 */
1426 if (pPrevBox->y2 == pCurBox->y1) {
1427 /*
1428 * Make sure the bands have boxes in the same places. This
1429 * assumes that boxes have been added in such a way that they
1430 * cover the most area possible. I.e. two boxes in a band must
1431 * have some horizontal space between them.
1432 */
1433 do {
1434 if ((pPrevBox->x1 != pCurBox->x1) || (pPrevBox->x2 != pCurBox->x2)) {
1435 /*
1436 * The bands don't line up so they can't be coalesced.
1437 */
1438 return (curStart);
1439 }
1440 pPrevBox++;
1441 pCurBox++;
1442 prevNumRects -= 1;
1443 } while (prevNumRects != 0);
1444
1445 pReg->numRects -= curNumRects;
1446 pCurBox -= curNumRects;
1447 pPrevBox -= curNumRects;
1448
1449 /*
1450 * The bands may be merged, so set the bottom y of each box
1451 * in the previous band to that of the corresponding box in
1452 * the current band.
1453 */
1454 do {
1455 pPrevBox->y2 = pCurBox->y2;
1456 pPrevBox++;
1457 pCurBox++;
1458 curNumRects -= 1;
1459 } while (curNumRects != 0);
1460
1461 /*
1462 * If only one band was added to the region, we have to backup
1463 * curStart to the start of the previous band.
1464 *
1465 * If more than one band was added to the region, copy the
1466 * other bands down. The assumption here is that the other bands
1467 * came from the same region as the current one and no further
1468 * coalescing can be done on them since it's all been done
1469 * already... curStart is already in the right place.
1470 */
1471 if (pCurBox == pRegEnd) {
1472 curStart = prevStart;
1473 } else {
1474 do {
1475 *pPrevBox++ = *pCurBox++;
1476 } while (pCurBox != pRegEnd);
1477 }
1478 }
1479 }
1480 return curStart;
1481}
1482
1483/*-
1484 * -----------------------------------------------------------------------
1485 * miRegionOp --
1486 * Apply an operation to two regions. Called by miUnion, miInverse,
1487 * miSubtract, miIntersect...
1488 *
1489 * Results:
1490 * None.
1491 *
1492 * Side Effects:
1493 * The new region is overwritten.
1494 *
1495 * Notes:
1496 * The idea behind this function is to view the two regions as sets.
1497 * Together they cover a rectangle of area that this function divides
1498 * into horizontal bands where points are covered only by one region
1499 * or by both. For the first case, the nonOverlapFunc is called with
1500 * each the band and the band's upper and lower extents. For the
1501 * second, the overlapFunc is called to process the entire band. It
1502 * is responsible for clipping the rectangles in the band, though
1503 * this function provides the boundaries.
1504 * At the end of each band, the new region is coalesced, if possible,
1505 * to reduce the number of rectangles in the region.
1506 *
1507 *-----------------------------------------------------------------------
1508 */
1509/* static void*/
1510static void miRegionOp(
1511 OGdkRegion *newReg, OGdkRegion *reg1, const OGdkRegion *reg2,
1512 overlapFunc overlapFn, /* Function to call for over-
1513 * lapping bands */
1514 nonOverlapFunc nonOverlap1Fn, /* Function to call for non-
1515 * overlapping bands in region
1516 * 1 */
1517 nonOverlapFunc nonOverlap2Fn) /* Function to call for non-
1518 * overlapping bands in region
1519 * 2 */
1520{
1521 OGdkRegionBox *r1; /* Pointer into first region */
1522 OGdkRegionBox *r2; /* Pointer into 2d region */
1523 OGdkRegionBox *r1End; /* End of 1st region */
1524 OGdkRegionBox *r2End; /* End of 2d region */
1525 int ybot; /* Bottom of intersection */
1526 int ytop; /* Top of intersection */
1527 OGdkRegionBox *oldRects; /* Old rects for newReg */
1528 int prevBand; /* Index of start of
1529 * previous band in newReg */
1530 int curBand; /* Index of start of current
1531 * band in newReg */
1532 OGdkRegionBox *r1BandEnd; /* End of current band in r1 */
1533 OGdkRegionBox *r2BandEnd; /* End of current band in r2 */
1534 int top; /* Top of non-overlapping
1535 * band */
1536 int bot; /* Bottom of non-overlapping
1537 * band */
1538
1539 /*
1540 * Initialization:
1541 * set r1, r2, r1End and r2End appropriately, preserve the important
1542 * parts of the destination region until the end in case it's one of
1543 * the two source regions, then mark the "new" region empty, allocating
1544 * another array of rectangles for it to use.
1545 */
1546 r1 = reg1->rects;
1547 r2 = reg2->rects;
1548 r1End = r1 + reg1->numRects;
1549 r2End = r2 + reg2->numRects;
1550
1551 oldRects = newReg->rects;
1552
1553 EMPTY_REGION(newReg);
1554
1555 /*
1556 * Allocate a reasonable number of rectangles for the new region. The idea
1557 * is to allocate enough so the individual functions don't need to
1558 * reallocate and copy the array, which is time consuming, yet we don't
1559 * have to worry about using too much memory. I hope to be able to
1560 * nuke the Xrealloc() at the end of this function eventually.
1561 */
1562 newReg->size = MAX(reg1->numRects, reg2->numRects) * 2;
1563 newReg->rects = (OGdkRegionBox *)malloc(sizeof(OGdkRegionBox) * newReg->size);
1564
1565 /*
1566 * Initialize ybot and ytop.
1567 * In the upcoming loop, ybot and ytop serve different functions depending
1568 * on whether the band being handled is an overlapping or non-overlapping
1569 * band.
1570 * In the case of a non-overlapping band (only one of the regions
1571 * has points in the band), ybot is the bottom of the most recent
1572 * intersection and thus clips the top of the rectangles in that band.
1573 * ytop is the top of the next intersection between the two regions and
1574 * serves to clip the bottom of the rectangles in the current band.
1575 * For an overlapping band (where the two regions intersect), ytop clips
1576 * the top of the rectangles of both regions and ybot clips the bottoms.
1577 */
1578 if (reg1->extents.y1 < reg2->extents.y1)
1579 ybot = reg1->extents.y1;
1580 else
1581 ybot = reg2->extents.y1;
1582
1583 /*
1584 * prevBand serves to mark the start of the previous band so rectangles
1585 * can be coalesced into larger rectangles. qv. miCoalesce, above.
1586 * In the beginning, there is no previous band, so prevBand == curBand
1587 * (curBand is set later on, of course, but the first band will always
1588 * start at index 0). prevBand and curBand must be indices because of
1589 * the possible expansion, and resultant moving, of the new region's
1590 * array of rectangles.
1591 */
1592 prevBand = 0;
1593
1594 do {
1595 curBand = newReg->numRects;
1596
1597 /*
1598 * This algorithm proceeds one source-band (as opposed to a
1599 * destination band, which is determined by where the two regions
1600 * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
1601 * rectangle after the last one in the current band for their
1602 * respective regions.
1603 */
1604 r1BandEnd = r1;
1605 while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1)) {
1606 r1BandEnd++;
1607 }
1608
1609 r2BandEnd = r2;
1610 while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1)) {
1611 r2BandEnd++;
1612 }
1613
1614 /*
1615 * First handle the band that doesn't intersect, if any.
1616 *
1617 * Note that attention is restricted to one band in the
1618 * non-intersecting region at once, so if a region has n
1619 * bands between the current position and the next place it overlaps
1620 * the other, this entire loop will be passed through n times.
1621 */
1622 if (r1->y1 < r2->y1) {
1623 top = MAX(r1->y1, ybot);
1624 bot = MIN(r1->y2, r2->y1);
1625
1626 if ((top != bot) &&
1627 (nonOverlap1Fn != (void (*)(OGdkRegion *, OGdkRegionBox *,
1628 OGdkRegionBox *, int, int))NULL)) {
1629 (*nonOverlap1Fn)(newReg, r1, r1BandEnd, top, bot);
1630 }
1631
1632 ytop = r2->y1;
1633 } else if (r2->y1 < r1->y1) {
1634 top = MAX(r2->y1, ybot);
1635 bot = MIN(r2->y2, r1->y1);
1636
1637 if ((top != bot) &&
1638 (nonOverlap2Fn != (void (*)(OGdkRegion *, OGdkRegionBox *,
1639 OGdkRegionBox *, int, int))NULL)) {
1640 (*nonOverlap2Fn)(newReg, r2, r2BandEnd, top, bot);
1641 }
1642
1643 ytop = r1->y1;
1644 } else {
1645 ytop = r1->y1;
1646 }
1647
1648 /*
1649 * If any rectangles got added to the region, try and coalesce them
1650 * with rectangles from the previous band. Note we could just do
1651 * this test in miCoalesce, but some machines incur a not
1652 * inconsiderable cost for function calls, so...
1653 */
1654 if (newReg->numRects != curBand) {
1655 prevBand = miCoalesce(newReg, prevBand, curBand);
1656 }
1657
1658 /*
1659 * Now see if we've hit an intersecting band. The two bands only
1660 * intersect if ybot > ytop
1661 */
1662 ybot = MIN(r1->y2, r2->y2);
1663 curBand = newReg->numRects;
1664 if (ybot > ytop) {
1665 (*overlapFn)(newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
1666 }
1667
1668 if (newReg->numRects != curBand) {
1669 prevBand = miCoalesce(newReg, prevBand, curBand);
1670 }
1671
1672 /*
1673 * If we've finished with a band (y2 == ybot) we skip forward
1674 * in the region to the next band.
1675 */
1676 if (r1->y2 == ybot) {
1677 r1 = r1BandEnd;
1678 }
1679 if (r2->y2 == ybot) {
1680 r2 = r2BandEnd;
1681 }
1682 } while ((r1 != r1End) && (r2 != r2End));
1683
1684 /*
1685 * Deal with whichever region still has rectangles left.
1686 */
1687 curBand = newReg->numRects;
1688 if (r1 != r1End) {
1689 if (nonOverlap1Fn != (nonOverlapFunc)NULL) {
1690 do {
1691 r1BandEnd = r1;
1692 while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1)) {
1693 r1BandEnd++;
1694 }
1695 (*nonOverlap1Fn)(newReg, r1, r1BandEnd, MAX(r1->y1, ybot), r1->y2);
1696 r1 = r1BandEnd;
1697 } while (r1 != r1End);
1698 }
1699 } else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc)NULL)) {
1700 do {
1701 r2BandEnd = r2;
1702 while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1)) {
1703 r2BandEnd++;
1704 }
1705 (*nonOverlap2Fn)(newReg, r2, r2BandEnd, MAX(r2->y1, ybot), r2->y2);
1706 r2 = r2BandEnd;
1707 } while (r2 != r2End);
1708 }
1709
1710 if (newReg->numRects != curBand) {
1711 (void)miCoalesce(newReg, prevBand, curBand);
1712 }
1713
1714 /*
1715 * A bit of cleanup. To keep regions from growing without bound,
1716 * we shrink the array of rectangles to match the new number of
1717 * rectangles in the region. This never goes to 0, however...
1718 *
1719 * Only do this stuff if the number of rectangles allocated is more than
1720 * twice the number of rectangles in the region (a simple optimization...).
1721 */
1722 if (newReg->numRects < (newReg->size >> 1)) {
1723 if (REGION_NOT_EMPTY(newReg)) {
1724 newReg->size = newReg->numRects;
1725 // newReg->rects = g_renew (GdkRegionBox,
1726 // newReg->rects, newReg->size);
1727 newReg->rects = (OGdkRegionBox *)realloc(
1728 newReg->rects, sizeof(OGdkRegionBox) * newReg->size);
1729 } else {
1730 /*
1731 * No point in doing the extra work involved in an Xrealloc if
1732 * the region is empty
1733 */
1734 newReg->size = 1;
1735 free(newReg->rects);
1736 newReg->rects = &newReg->extents;
1737 }
1738 }
1739
1740 if (oldRects != &newReg->extents) free(oldRects);
1741}
1742
1743/*======================================================================
1744 * Region Union
1745 *====================================================================*/
1746
1747/*-
1748 * -----------------------------------------------------------------------
1749 * miUnionNonO --
1750 * Handle a non-overlapping band for the union operation. Just
1751 * Adds the rectangles into the region. Doesn't have to check for
1752 * subsumption or anything.
1753 *
1754 * Results:
1755 * None.
1756 *
1757 * Side Effects:
1758 * pReg->numRects is incremented and the final rectangles overwritten
1759 * with the rectangles we're passed.
1760 *
1761 *-----------------------------------------------------------------------
1762 */
1763static void miUnionNonO(OGdkRegion *pReg, OGdkRegionBox *r, OGdkRegionBox *rEnd,
1764 int y1, int y2) {
1765 OGdkRegionBox *pNextRect;
1766
1767 pNextRect = &pReg->rects[pReg->numRects];
1768
1770
1771 while (r != rEnd) {
1773 OMEMCHECK(pReg, pNextRect, pReg->rects);
1774 pNextRect->x1 = r->x1;
1775 pNextRect->y1 = y1;
1776 pNextRect->x2 = r->x2;
1777 pNextRect->y2 = y2;
1778 pReg->numRects += 1;
1779 pNextRect++;
1780
1782 r++;
1783 }
1784}
1785
1786/*-
1787 * -----------------------------------------------------------------------
1788 * miUnionO --
1789 * Handle an overlapping band for the union operation. Picks the
1790 * left-most rectangle each time and merges it into the region.
1791 *
1792 * Results:
1793 * None.
1794 *
1795 * Side Effects:
1796 * Rectangles are overwritten in pReg->rects and pReg->numRects will
1797 * be changed.
1798 *
1799 *-----------------------------------------------------------------------
1800 */
1801
1802/* static void*/
1803static void miUnionO(OGdkRegion *pReg, OGdkRegionBox *r1, OGdkRegionBox *r1End,
1804 OGdkRegionBox *r2, OGdkRegionBox *r2End, int y1, int y2) {
1805 OGdkRegionBox *pNextRect;
1806
1807 pNextRect = &pReg->rects[pReg->numRects];
1808
1809#define MERGERECT(r) \
1810 if ((pReg->numRects != 0) && (pNextRect[-1].y1 == y1) && \
1811 (pNextRect[-1].y2 == y2) && (pNextRect[-1].x2 >= r->x1)) { \
1812 if (pNextRect[-1].x2 < r->x2) { \
1813 pNextRect[-1].x2 = r->x2; \
1814 /*g_assert(pNextRect[-1].x1<pNextRect[-1].x2);*/ \
1815 } \
1816 } else { \
1817 OMEMCHECK(pReg, pNextRect, pReg->rects); \
1818 pNextRect->y1 = y1; \
1819 pNextRect->y2 = y2; \
1820 pNextRect->x1 = r->x1; \
1821 pNextRect->x2 = r->x2; \
1822 pReg->numRects += 1; \
1823 pNextRect += 1; \
1824 } \
1825 /*g_assert(pReg->numRects<=pReg->size);*/ \
1826 r++;
1827
1829 while ((r1 != r1End) && (r2 != r2End)) {
1830 if (r1->x1 < r2->x1) {
1831 MERGERECT(r1);
1832 } else {
1833 MERGERECT(r2);
1834 }
1835 }
1836
1837 if (r1 != r1End) {
1838 do {
1839 MERGERECT(r1);
1840 } while (r1 != r1End);
1841 } else
1842 while (r2 != r2End) {
1843 MERGERECT(r2);
1844 }
1845}
1846
1856void gdk_region_union(OGdkRegion *source1, const OGdkRegion *source2) {
1859
1860 /* checks all the simple cases */
1861
1862 /*
1863 * source1 and source2 are the same or source2 is empty
1864 */
1865 if ((source1 == source2) || (!(source2->numRects))) return;
1866
1867 /*
1868 * source1 is empty
1869 */
1870 if (!(source1->numRects)) {
1871 miRegionCopy(source1, source2);
1872 return;
1873 }
1874
1875 /*
1876 * source1 completely subsumes source2
1877 */
1878 if ((source1->numRects == 1) &&
1879 (source1->extents.x1 <= source2->extents.x1) &&
1880 (source1->extents.y1 <= source2->extents.y1) &&
1881 (source1->extents.x2 >= source2->extents.x2) &&
1882 (source1->extents.y2 >= source2->extents.y2))
1883 return;
1884
1885 /*
1886 * source2 completely subsumes source1
1887 */
1888 if ((source2->numRects == 1) &&
1889 (source2->extents.x1 <= source1->extents.x1) &&
1890 (source2->extents.y1 <= source1->extents.y1) &&
1891 (source2->extents.x2 >= source1->extents.x2) &&
1892 (source2->extents.y2 >= source1->extents.y2)) {
1893 miRegionCopy(source1, source2);
1894 return;
1895 }
1896
1897 miRegionOp(source1, source1, source2, miUnionO, miUnionNonO, miUnionNonO);
1898
1899 source1->extents.x1 = MIN(source1->extents.x1, source2->extents.x1);
1900 source1->extents.y1 = MIN(source1->extents.y1, source2->extents.y1);
1901 source1->extents.x2 = MAX(source1->extents.x2, source2->extents.x2);
1902 source1->extents.y2 = MAX(source1->extents.y2, source2->extents.y2);
1903}
1904
1905/*======================================================================
1906 * Region Subtraction
1907 *====================================================================*/
1908
1909/*-
1910 * -----------------------------------------------------------------------
1911 * miSubtractNonO --
1912 * Deal with non-overlapping band for subtraction. Any parts from
1913 * region 2 we discard. Anything from region 1 we add to the region.
1914 *
1915 * Results:
1916 * None.
1917 *
1918 * Side Effects:
1919 * pReg may be affected.
1920 *
1921 *-----------------------------------------------------------------------
1922 */
1923/* static void*/
1924static void miSubtractNonO1(OGdkRegion *pReg, OGdkRegionBox *r,
1925 OGdkRegionBox *rEnd, int y1, int y2) {
1926 OGdkRegionBox *pNextRect;
1927
1928 pNextRect = &pReg->rects[pReg->numRects];
1929
1931
1932 while (r != rEnd) {
1934 OMEMCHECK(pReg, pNextRect, pReg->rects);
1935 pNextRect->x1 = r->x1;
1936 pNextRect->y1 = y1;
1937 pNextRect->x2 = r->x2;
1938 pNextRect->y2 = y2;
1939 pReg->numRects += 1;
1940 pNextRect++;
1941
1943
1944 r++;
1945 }
1946}
1947
1948/*-
1949 * -----------------------------------------------------------------------
1950 * miSubtractO --
1951 * Overlapping band subtraction. x1 is the left-most point not yet
1952 * checked.
1953 *
1954 * Results:
1955 * None.
1956 *
1957 * Side Effects:
1958 * pReg may have rectangles added to it.
1959 *
1960 *-----------------------------------------------------------------------
1961 */
1962/* static void*/
1963static void miSubtractO(OGdkRegion *pReg, OGdkRegionBox *r1,
1964 OGdkRegionBox *r1End, OGdkRegionBox *r2,
1965 OGdkRegionBox *r2End, int y1, int y2) {
1966 OGdkRegionBox *pNextRect;
1967 int x1;
1968
1969 x1 = r1->x1;
1970
1972 pNextRect = &pReg->rects[pReg->numRects];
1973
1974 while ((r1 != r1End) && (r2 != r2End)) {
1975 if (r2->x2 <= x1) {
1976 /*
1977 * Subtrahend missed the boat: go to next subtrahend.
1978 */
1979 r2++;
1980 } else if (r2->x1 <= x1) {
1981 /*
1982 * Subtrahend preceeds minuend: nuke left edge of minuend.
1983 */
1984 x1 = r2->x2;
1985 if (x1 >= r1->x2) {
1986 /*
1987 * Minuend completely covered: advance to next minuend and
1988 * reset left fence to edge of new minuend.
1989 */
1990 r1++;
1991 if (r1 != r1End) x1 = r1->x1;
1992 } else {
1993 /*
1994 * Subtrahend now used up since it doesn't extend beyond
1995 * minuend
1996 */
1997 r2++;
1998 }
1999 } else if (r2->x1 < r1->x2) {
2000 /*
2001 * Left part of subtrahend covers part of minuend: add uncovered
2002 * part of minuend to region and skip to next subtrahend.
2003 */
2005 OMEMCHECK(pReg, pNextRect, pReg->rects);
2006 pNextRect->x1 = x1;
2007 pNextRect->y1 = y1;
2008 pNextRect->x2 = r2->x1;
2009 pNextRect->y2 = y2;
2010 pReg->numRects += 1;
2011 pNextRect++;
2012
2014
2015 x1 = r2->x2;
2016 if (x1 >= r1->x2) {
2017 /*
2018 * Minuend used up: advance to new...
2019 */
2020 r1++;
2021 if (r1 != r1End) x1 = r1->x1;
2022 } else {
2023 /*
2024 * Subtrahend used up
2025 */
2026 r2++;
2027 }
2028 } else {
2029 /*
2030 * Minuend used up: add any remaining piece before advancing.
2031 */
2032 if (r1->x2 > x1) {
2033 OMEMCHECK(pReg, pNextRect, pReg->rects);
2034 pNextRect->x1 = x1;
2035 pNextRect->y1 = y1;
2036 pNextRect->x2 = r1->x2;
2037 pNextRect->y2 = y2;
2038 pReg->numRects += 1;
2039 pNextRect++;
2041 }
2042 r1++;
2043 if (r1 != r1End) x1 = r1->x1;
2044 }
2045 }
2046
2047 /*
2048 * Add remaining minuend rectangles to region.
2049 */
2050 while (r1 != r1End) {
2052 OMEMCHECK(pReg, pNextRect, pReg->rects);
2053 pNextRect->x1 = x1;
2054 pNextRect->y1 = y1;
2055 pNextRect->x2 = r1->x2;
2056 pNextRect->y2 = y2;
2057 pReg->numRects += 1;
2058 pNextRect++;
2059
2061
2062 r1++;
2063 if (r1 != r1End) {
2064 x1 = r1->x1;
2065 }
2066 }
2067}
2068
2077void gdk_region_subtract(OGdkRegion *source1, const OGdkRegion *source2) {
2078 // g_return_if_fail (source1 != NULL);
2079 // g_return_if_fail (source2 != NULL);
2080
2081 /* check for trivial reject */
2082 if ((!(source1->numRects)) || (!(source2->numRects)) ||
2083 (!EXTENTCHECK(&source1->extents, &source2->extents)))
2084 return;
2085
2086 miRegionOp(source1, source1, source2, miSubtractO, miSubtractNonO1,
2087 (nonOverlapFunc)NULL);
2088
2089 /*
2090 * Can't alter source1's extents before we call miRegionOp because miRegionOp
2091 * depends on the extents of those regions being the unaltered. Besides, this
2092 * way there's no checking against rectangles that will be nuked
2093 * due to coalescing, so we have to examine fewer rectangles.
2094 */
2095 miSetExtents(source1);
2096}
2097
2107void gdk_region_xor(OGdkRegion *source1, const OGdkRegion *source2) {
2108 OGdkRegion *trb;
2109
2110 // g_return_if_fail (source1 != NULL);
2111 // g_return_if_fail (source2 != NULL);
2112
2113 trb = gdk_region_copy(source2);
2114
2115 gdk_region_subtract(trb, source1);
2116 gdk_region_subtract(source1, source2);
2117
2118 gdk_region_union(source1, trb);
2119
2120 gdk_region_destroy(trb);
2121}
2122
2131bool gdk_region_empty(const OGdkRegion *region) {
2132 // g_return_val_if_fail (region != NULL, FALSE);
2133
2134 if (region->numRects == 0)
2135 return TRUE;
2136 else
2137 return FALSE;
2138}
2139
2149bool ogdk_region_equal(const OGdkRegion *region1, const OGdkRegion *region2) {
2150 int i;
2151
2152 // g_return_val_if_fail (region1 != NULL, FALSE);
2153 // g_return_val_if_fail (region2 != NULL, FALSE);
2154
2155 if (region1->numRects != region2->numRects) return FALSE;
2156 if (region1->numRects == 0) return TRUE;
2157 if (region1->extents.x1 != region2->extents.x1) return FALSE;
2158 if (region1->extents.x2 != region2->extents.x2) return FALSE;
2159 if (region1->extents.y1 != region2->extents.y1) return FALSE;
2160 if (region1->extents.y2 != region2->extents.y2) return FALSE;
2161 for (i = 0; i < region1->numRects; i++) {
2162 if (region1->rects[i].x1 != region2->rects[i].x1) return FALSE;
2163 if (region1->rects[i].x2 != region2->rects[i].x2) return FALSE;
2164 if (region1->rects[i].y1 != region2->rects[i].y1) return FALSE;
2165 if (region1->rects[i].y2 != region2->rects[i].y2) return FALSE;
2166 }
2167 return TRUE;
2168}
2169
2180bool gdk_region_point_in(const OGdkRegion *region, int x, int y) {
2181 int i;
2182
2183 // g_return_val_if_fail (region != NULL, FALSE);
2184
2185 if (region->numRects == 0) return FALSE;
2186 if (!INBOX(region->extents, x, y)) return FALSE;
2187 for (i = 0; i < region->numRects; i++) {
2188 if (INBOX(region->rects[i], x, y)) return TRUE;
2189 }
2190 return FALSE;
2191}
2192
2204OGdkOverlapType gdk_region_rect_in(const OGdkRegion *region,
2205 const OGdkRectangle *rectangle) {
2206 OGdkRegionBox *pbox;
2207 OGdkRegionBox *pboxEnd;
2208 OGdkRegionBox rect;
2209 OGdkRegionBox *prect = &rect;
2210 bool partIn, partOut;
2211 int rx, ry;
2212
2213 // g_return_val_if_fail (region != NULL,
2214 // GDK_OVERLAP_RECTANGLE_OUT); g_return_val_if_fail
2215 // (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
2216
2217 rx = rectangle->x;
2218 ry = rectangle->y;
2219
2220 prect->x1 = rx;
2221 prect->y1 = ry;
2222 prect->x2 = rx + rectangle->width;
2223 prect->y2 = ry + rectangle->height;
2224
2225 /* this is (just) a useful optimization */
2226 if ((region->numRects == 0) || !EXTENTCHECK(&region->extents, prect))
2227 return OGDK_OVERLAP_RECTANGLE_OUT;
2228
2229 partOut = FALSE;
2230 partIn = FALSE;
2231
2232 /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
2233 for (pbox = region->rects, pboxEnd = pbox + region->numRects; pbox < pboxEnd;
2234 pbox++) {
2235 if (pbox->y2 <= ry)
2236 continue; /* getting up to speed or skipping remainder of band */
2237
2238 if (pbox->y1 > ry) {
2239 partOut = TRUE; /* missed part of rectangle above */
2240 if (partIn || (pbox->y1 >= prect->y2)) break;
2241 ry = pbox->y1; /* x guaranteed to be == prect->x1 */
2242 }
2243
2244 if (pbox->x2 <= rx) continue; /* not far enough over yet */
2245
2246 if (pbox->x1 > rx) {
2247 partOut = TRUE; /* missed part of rectangle to left */
2248 if (partIn) break;
2249 }
2250
2251 if (pbox->x1 < prect->x2) {
2252 partIn = TRUE; /* definitely overlap */
2253 if (partOut) break;
2254 }
2255
2256 if (pbox->x2 >= prect->x2) {
2257 ry = pbox->y2; /* finished with this band */
2258 if (ry >= prect->y2) break;
2259 rx = prect->x1; /* reset x out to left again */
2260 } else {
2261 /*
2262 * Because boxes in a band are maximal width, if the first box
2263 * to overlap the rectangle doesn't completely cover it in that
2264 * band, the rectangle must be partially out, since some of it
2265 * will be uncovered in that band. partIn will have been set true
2266 * by now...
2267 */
2268 break;
2269 }
2270 }
2271
2272 return (partIn ? ((ry < prect->y2) ? OGDK_OVERLAP_RECTANGLE_PART
2273 : OGDK_OVERLAP_RECTANGLE_IN)
2274 : OGDK_OVERLAP_RECTANGLE_OUT);
2275}
2276
2277#if 0
2278 static void
2279 gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
2280 const GdkSpan *spans,
2281 int n_spans,
2282 GdkSpanFunc function,
2283 gpointer data)
2284 {
2285 gint i, left, right, y;
2286 gint clipped_left, clipped_right;
2287 GdkRegionBox *pbox;
2288 GdkRegionBox *pboxEnd;
2289
2290 if (!region->numRects)
2291 return;
2292
2293 for (i=0;i<n_spans;i++)
2294 {
2295 y = spans[i].y;
2296 left = spans[i].x;
2297 right = left + spans[i].width; /* right is not in the span! */
2298
2299 if (! ((region->extents.y1 <= y) &&
2300 (region->extents.y2 > y) &&
2301 (region->extents.x1 < right) &&
2302 (region->extents.x2 > left)) )
2303 continue;
2304
2305 /* can stop when we passed y */
2306 for (pbox = region->rects, pboxEnd = pbox + region->numRects;
2307 pbox < pboxEnd;
2308 pbox++)
2309 {
2310 if (pbox->y2 <= y)
2311 continue; /* Not quite there yet */
2312
2313 if (pbox->y1 > y)
2314 break; /* passed the spanline */
2315
2316 if ((right > pbox->x1) && (left < pbox->x2))
2317 {
2318 GdkSpan out_span;
2319
2320 clipped_left = MAX (left, pbox->x1);
2321 clipped_right = MIN (right, pbox->x2);
2322
2323 out_span.y = y;
2324 out_span.x = clipped_left;
2325 out_span.width = clipped_right - clipped_left;
2326 (*function) (&out_span, data);
2327 }
2328 }
2329 }
2330 }
2331#endif
2332
2333#if 0
2345 void
2346 gdk_region_spans_intersect_foreach (GdkRegion *region,
2347 const GdkSpan *spans,
2348 int n_spans,
2349 gboolean sorted,
2350 GdkSpanFunc function,
2351 gpointer data)
2352 {
2353 gint left, right, y;
2354 gint clipped_left, clipped_right;
2355 GdkRegionBox *pbox;
2356 GdkRegionBox *pboxEnd;
2357 const GdkSpan *span, *tmpspan;
2358 const GdkSpan *end_span;
2359
2360 g_return_if_fail (region != NULL);
2361 g_return_if_fail (spans != NULL);
2362
2363 if (!sorted)
2364 {
2365 gdk_region_unsorted_spans_intersect_foreach (region,
2366 spans,
2367 n_spans,
2368 function,
2369 data);
2370 return;
2371 }
2372
2373 if ((!region->numRects) || (n_spans == 0))
2374 return;
2375
2376 /* The main method here is to step along the
2377 * sorted rectangles and spans in lock step, and
2378 * clipping the spans that are in the current
2379 * rectangle before going on to the next rectangle.
2380 */
2381
2382 span = spans;
2383 end_span = spans + n_spans;
2384 pbox = region->rects;
2385 pboxEnd = pbox + region->numRects;
2386 while (pbox < pboxEnd)
2387 {
2388 while ((pbox->y2 < span->y) || (span->y < pbox->y1))
2389 {
2390 /* Skip any rectangles that are above the current span */
2391 if (pbox->y2 < span->y)
2392 {
2393 pbox++;
2394 if (pbox == pboxEnd)
2395 return;
2396 }
2397 /* Skip any spans that are above the current rectangle */
2398 if (span->y < pbox->y1)
2399 {
2400 span++;
2401 if (span == end_span)
2402 return;
2403 }
2404 }
2405
2406 /* Ok, we got at least one span that might intersect this rectangle. */
2407 tmpspan = span;
2408 while ((tmpspan < end_span) &&
2409 (tmpspan->y < pbox->y2))
2410 {
2411 y = tmpspan->y;
2412 left = tmpspan->x;
2413 right = left + tmpspan->width; /* right is not in the span! */
2414
2415 if ((right > pbox->x1) && (left < pbox->x2))
2416 {
2417 GdkSpan out_span;
2418
2419 clipped_left = MAX (left, pbox->x1);
2420 clipped_right = MIN (right, pbox->x2);
2421
2422 out_span.y = y;
2423 out_span.x = clipped_left;
2424 out_span.width = clipped_right - clipped_left;
2425 (*function) (&out_span, data);
2426 }
2427
2428 tmpspan++;
2429 }
2430
2431 /* Finished this rectangle.
2432 * The spans could still intersect the next one
2433 */
2434 pbox++;
2435 }
2436 }
2437#endif
2438
2439/* $TOG: PolyReg.c /main/15 1998/02/06 17:47:08 kaleb $ */
2440/************************************************************************
2441 *
2442 * Copyright 1987, 1998 The Open Group
2443 *
2444 * All Rights Reserved.
2445 *
2446 * The above copyright notice and this permission notice shall be included in
2447 * all copies or substantial portions of the Software.
2448 *
2449 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2450 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2451 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
2452 * OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
2453 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2454 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2455 *
2456 * Except as contained in this notice, the name of The Open Group shall not be
2457 * used in advertising or otherwise to promote the sale, use or other dealings
2458 * in this Software without prior written authorization from The Open Group.
2459 *
2460 *
2461 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
2462 *
2463 * All Rights Reserved
2464 *
2465 * Permission to use, copy, modify, and distribute this software and its
2466 * documentation for any purpose and without fee is hereby granted,
2467 * provided that the above copyright notice appear in all copies and that
2468 * both that copyright notice and this permission notice appear in
2469 * supporting documentation, and that the name of Digital not be
2470 * used in advertising or publicity pertaining to distribution of the
2471 * software without specific, written prior permission.
2472 *
2473 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
2474 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
2475 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
2476 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
2477 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
2478 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
2479 * SOFTWARE.
2480 *
2481 ************************************************************************/
2482/* $XFree86: xc/lib/X11/PolyReg.c,v 1.4 1998/10/03 08:41:21 dawes Exp $ */
2483
2484#define LARGE_COORDINATE 1000000
2485#define SMALL_COORDINATE -LARGE_COORDINATE
2486
2487// #include "config.h"
2488// #include <gdkregion.h>
2489// #include "gdkregion-generic.h"
2490// #include "gdkpoly-generic.h"
2491// #include "gdkalias.h"
2492
2493/*
2494 * InsertEdgeInET
2495 *
2496 * Insert the given edge into the edge table.
2497 * First we must find the correct bucket in the
2498 * Edge table, then find the right slot in the
2499 * bucket. Finally, we can insert it.
2500 *
2501 */
2502static void InsertEdgeInET(OEdgeTable *ET, OEdgeTableEntry *ETE, int scanline,
2503 OScanLineListBlock **SLLBlock, int *iSLLBlock) {
2504 OEdgeTableEntry *start, *prev;
2505 OScanLineList *pSLL, *pPrevSLL;
2506 OScanLineListBlock *tmpSLLBlock;
2507
2508 /*
2509 * find the right bucket to put the edge into
2510 */
2511 pPrevSLL = &ET->scanlines;
2512 pSLL = pPrevSLL->next;
2513 while (pSLL && (pSLL->scanline < scanline)) {
2514 pPrevSLL = pSLL;
2515 pSLL = pSLL->next;
2516 }
2517
2518 /*
2519 * reassign pSLL (pointer to ScanLineList) if necessary
2520 */
2521 if ((!pSLL) || (pSLL->scanline > scanline)) {
2522 if (*iSLLBlock > SLLSPERBLOCK - 1) {
2523 tmpSLLBlock = (OScanLineListBlock *)malloc(sizeof(OScanLineListBlock));
2524 (*SLLBlock)->next = tmpSLLBlock;
2525 tmpSLLBlock->next = (OScanLineListBlock *)NULL;
2526 *SLLBlock = tmpSLLBlock;
2527 *iSLLBlock = 0;
2528 }
2529 pSLL = &((*SLLBlock)->SLLs[(*iSLLBlock)++]);
2530
2531 pSLL->next = pPrevSLL->next;
2532 pSLL->edgelist = (OEdgeTableEntry *)NULL;
2533 pPrevSLL->next = pSLL;
2534 }
2535 pSLL->scanline = scanline;
2536
2537 /*
2538 * now insert the edge in the right bucket
2539 */
2540 prev = (OEdgeTableEntry *)NULL;
2541 start = pSLL->edgelist;
2542 while (start && (start->bres.minor_axis < ETE->bres.minor_axis)) {
2543 prev = start;
2544 start = start->next;
2545 }
2546 ETE->next = start;
2547
2548 if (prev)
2549 prev->next = ETE;
2550 else
2551 pSLL->edgelist = ETE;
2552}
2553
2554/*
2555 * CreateEdgeTable
2556 *
2557 * This routine creates the edge table for
2558 * scan converting polygons.
2559 * The Edge Table (ET) looks like:
2560 *
2561 * EdgeTable
2562 * --------
2563 * | ymax | ScanLineLists
2564 * |scanline|-->------------>-------------->...
2565 * -------- |scanline| |scanline|
2566 * |edgelist| |edgelist|
2567 * --------- ---------
2568 * | |
2569 * | |
2570 * V V
2571 * list of ETEs list of ETEs
2572 *
2573 * where ETE is an EdgeTableEntry data structure,
2574 * and there is one ScanLineList per scanline at
2575 * which an edge is initially entered.
2576 *
2577 */
2578
2579static void CreateETandAET(int count, const OGdkPoint *pts, OEdgeTable *ET,
2580 OEdgeTableEntry *AET, OEdgeTableEntry *pETEs,
2581 OScanLineListBlock *pSLLBlock) {
2582 const OGdkPoint *top, *bottom;
2583 const OGdkPoint *PrevPt, *CurrPt;
2584 int iSLLBlock = 0;
2585 int dy;
2586
2587 if (count < 2) return;
2588
2589 /*
2590 * initialize the Active Edge Table
2591 */
2592 AET->next = (OEdgeTableEntry *)NULL;
2593 AET->back = (OEdgeTableEntry *)NULL;
2594 AET->nextWETE = (OEdgeTableEntry *)NULL;
2595 AET->bres.minor_axis = SMALL_COORDINATE;
2596
2597 /*
2598 * initialize the Edge Table.
2599 */
2600 ET->scanlines.next = (OScanLineList *)NULL;
2601 ET->ymax = SMALL_COORDINATE;
2602 ET->ymin = LARGE_COORDINATE;
2603 pSLLBlock->next = (OScanLineListBlock *)NULL;
2604
2605 PrevPt = &pts[count - 1];
2606
2607 /*
2608 * for each vertex in the array of points.
2609 * In this loop we are dealing with two vertices at
2610 * a time -- these make up one edge of the polygon.
2611 */
2612 while (count--) {
2613 CurrPt = pts++;
2614
2615 /*
2616 * find out which point is above and which is below.
2617 */
2618 if (PrevPt->y > CurrPt->y) {
2619 bottom = PrevPt, top = CurrPt;
2620 pETEs->ClockWise = 0;
2621 } else {
2622 bottom = CurrPt, top = PrevPt;
2623 pETEs->ClockWise = 1;
2624 }
2625
2626 /*
2627 * don't add horizontal edges to the Edge table.
2628 */
2629 if (bottom->y != top->y) {
2630 pETEs->ymax = bottom->y - 1; /* -1 so we don't get last scanline */
2631
2632 /*
2633 * initialize integer edge algorithm
2634 */
2635 dy = bottom->y - top->y;
2636 OBRESINITPGONSTRUCT(dy, top->x, bottom->x, pETEs->bres);
2637
2638 InsertEdgeInET(ET, pETEs, top->y, &pSLLBlock, &iSLLBlock);
2639
2640 if (PrevPt->y > ET->ymax) ET->ymax = PrevPt->y;
2641 if (PrevPt->y < ET->ymin) ET->ymin = PrevPt->y;
2642 pETEs++;
2643 }
2644
2645 PrevPt = CurrPt;
2646 }
2647}
2648
2649/*
2650 * loadAET
2651 *
2652 * This routine moves EdgeTableEntries from the
2653 * EdgeTable into the Active Edge Table,
2654 * leaving them sorted by smaller x coordinate.
2655 *
2656 */
2657
2658static void loadAET(OEdgeTableEntry *AET, OEdgeTableEntry *ETEs) {
2659 OEdgeTableEntry *pPrevAET;
2660 OEdgeTableEntry *tmp;
2661
2662 pPrevAET = AET;
2663 AET = AET->next;
2664 while (ETEs) {
2665 while (AET && (AET->bres.minor_axis < ETEs->bres.minor_axis)) {
2666 pPrevAET = AET;
2667 AET = AET->next;
2668 }
2669 tmp = ETEs->next;
2670 ETEs->next = AET;
2671 if (AET) AET->back = ETEs;
2672 ETEs->back = pPrevAET;
2673 pPrevAET->next = ETEs;
2674 pPrevAET = ETEs;
2675
2676 ETEs = tmp;
2677 }
2678}
2679
2680/*
2681 * computeWAET
2682 *
2683 * This routine links the AET by the
2684 * nextWETE (winding EdgeTableEntry) link for
2685 * use by the winding number rule. The final
2686 * Active Edge Table (AET) might look something
2687 * like:
2688 *
2689 * AET
2690 * ---------- --------- ---------
2691 * |ymax | |ymax | |ymax |
2692 * | ... | |... | |... |
2693 * |next |->|next |->|next |->...
2694 * |nextWETE| |nextWETE| |nextWETE|
2695 * --------- --------- ^--------
2696 * | | |
2697 * V-------------------> V---> ...
2698 *
2699 */
2700static void computeWAET(OEdgeTableEntry *AET) {
2701 OEdgeTableEntry *pWETE;
2702 int inside = 1;
2703 int isInside = 0;
2704
2705 AET->nextWETE = (OEdgeTableEntry *)NULL;
2706 pWETE = AET;
2707 AET = AET->next;
2708 while (AET) {
2709 if (AET->ClockWise)
2710 isInside++;
2711 else
2712 isInside--;
2713
2714 if ((!inside && !isInside) || (inside && isInside)) {
2715 pWETE->nextWETE = AET;
2716 pWETE = AET;
2717 inside = !inside;
2718 }
2719 AET = AET->next;
2720 }
2721 pWETE->nextWETE = (OEdgeTableEntry *)NULL;
2722}
2723
2724/*
2725 * InsertionSort
2726 *
2727 * Just a simple insertion sort using
2728 * pointers and back pointers to sort the Active
2729 * Edge Table.
2730 *
2731 */
2732
2733static int InsertionSort(OEdgeTableEntry *AET) {
2734 OEdgeTableEntry *pETEchase;
2735 OEdgeTableEntry *pETEinsert;
2736 OEdgeTableEntry *pETEchaseBackTMP;
2737 int changed = 0;
2738
2739 AET = AET->next;
2740 while (AET) {
2741 pETEinsert = AET;
2742 pETEchase = AET;
2743 while (pETEchase->back->bres.minor_axis > AET->bres.minor_axis)
2744 pETEchase = pETEchase->back;
2745
2746 AET = AET->next;
2747 if (pETEchase != pETEinsert) {
2748 pETEchaseBackTMP = pETEchase->back;
2749 pETEinsert->back->next = AET;
2750 if (AET) AET->back = pETEinsert->back;
2751 pETEinsert->next = pETEchase;
2752 pETEchase->back->next = pETEinsert;
2753 pETEchase->back = pETEinsert;
2754 pETEinsert->back = pETEchaseBackTMP;
2755 changed = 1;
2756 }
2757 }
2758 return (changed);
2759}
2760
2761/*
2762 * Clean up our act.
2763 */
2764static void FreeStorage(OScanLineListBlock *pSLLBlock) {
2765 OScanLineListBlock *tmpSLLBlock;
2766
2767 while (pSLLBlock) {
2768 tmpSLLBlock = pSLLBlock->next;
2769 free(pSLLBlock);
2770 pSLLBlock = tmpSLLBlock;
2771 }
2772}
2773
2774/*
2775 * Create an array of rectangles from a list of points.
2776 * If indeed these things (POINTS, RECTS) are the same,
2777 * then this proc is still needed, because it allocates
2778 * storage for the array, which was allocated on the
2779 * stack by the calling procedure.
2780 *
2781 */
2782static int PtsToRegion(int numFullPtBlocks, int iCurPtBlock,
2783 OPOINTBLOCK *FirstPtBlock, OGdkRegion *reg) {
2784 OGdkRegionBox *rects;
2785 OGdkPoint *pts;
2786 OPOINTBLOCK *CurPtBlock;
2787 int i;
2788 OGdkRegionBox *extents;
2789 int numRects;
2790
2791 extents = &reg->extents;
2792
2793 numRects = ((numFullPtBlocks * NUMPTSTOBUFFER) + iCurPtBlock) >> 1;
2794
2795 OGROWREGION(reg, numRects);
2796
2797 CurPtBlock = FirstPtBlock;
2798 rects = reg->rects - 1;
2799 numRects = 0;
2800 extents->x1 = 1000000 /*G_MAXSHORT*/, extents->x2 = -1000000 /*G_MINSHORT*/;
2801
2802 for (; numFullPtBlocks >= 0; numFullPtBlocks--) {
2803 /* the loop uses 2 points per iteration */
2804 i = NUMPTSTOBUFFER >> 1;
2805 if (!numFullPtBlocks) i = iCurPtBlock >> 1;
2806 for (pts = CurPtBlock->pts; i--; pts += 2) {
2807 if (pts->x == pts[1].x) continue;
2808 if (numRects && pts->x == rects->x1 && pts->y == rects->y2 &&
2809 pts[1].x == rects->x2 &&
2810 (numRects == 1 || rects[-1].y1 != rects->y1) &&
2811 (i && pts[2].y > pts[1].y)) {
2812 rects->y2 = pts[1].y + 1;
2813 continue;
2814 }
2815 numRects++;
2816 rects++;
2817 rects->x1 = pts->x;
2818 rects->y1 = pts->y;
2819 rects->x2 = pts[1].x;
2820 rects->y2 = pts[1].y + 1;
2821 if (rects->x1 < extents->x1) extents->x1 = rects->x1;
2822 if (rects->x2 > extents->x2) extents->x2 = rects->x2;
2823 }
2824 CurPtBlock = CurPtBlock->next;
2825 }
2826
2827 if (numRects) {
2828 extents->y1 = reg->rects->y1;
2829 extents->y2 = rects->y2;
2830 } else {
2831 extents->x1 = 0;
2832 extents->y1 = 0;
2833 extents->x2 = 0;
2834 extents->y2 = 0;
2835 }
2836 reg->numRects = numRects;
2837
2838 return (TRUE);
2839}
2840
2853OGdkRegion *gdk_region_polygon(const OGdkPoint *points, int n_points,
2854 OGdkFillRule fill_rule) {
2855 OGdkRegion *region;
2856 OEdgeTableEntry *pAET; /* Active Edge Table */
2857 int y; /* current scanline */
2858 int iPts = 0; /* number of pts in buffer */
2859 OEdgeTableEntry *pWETE; /* Winding Edge Table Entry*/
2860 OScanLineList *pSLL; /* current scanLineList */
2861 OGdkPoint *pts; /* output buffer */
2862 OEdgeTableEntry *pPrevAET; /* ptr to previous AET */
2863 OEdgeTable ET = {0}; /* header node for ET */
2864 OEdgeTableEntry AET; /* header node for AET */
2865 OEdgeTableEntry *pETEs; /* EdgeTableEntries pool */
2866 OScanLineListBlock SLLBlock; /* header for scanlinelist */
2867 int fixWAET = FALSE;
2868 OPOINTBLOCK FirstPtBlock, *curPtBlock; /* PtBlock buffers */
2869 OPOINTBLOCK *tmpPtBlock;
2870 int numFullPtBlocks = 0;
2871
2872 region = gdk_region_new();
2873
2874 /* special case a rectangle */
2875 if (((n_points == 4) || ((n_points == 5) && (points[4].x == points[0].x) &&
2876 (points[4].y == points[0].y))) &&
2877 (((points[0].y == points[1].y) && (points[1].x == points[2].x) &&
2878 (points[2].y == points[3].y) && (points[3].x == points[0].x)) ||
2879 ((points[0].x == points[1].x) && (points[1].y == points[2].y) &&
2880 (points[2].x == points[3].x) && (points[3].y == points[0].y)))) {
2881 region->extents.x1 = MIN(points[0].x, points[2].x);
2882 region->extents.y1 = MIN(points[0].y, points[2].y);
2883 region->extents.x2 = MAX(points[0].x, points[2].x);
2884 region->extents.y2 = MAX(points[0].y, points[2].y);
2885 if ((region->extents.x1 != region->extents.x2) &&
2886 (region->extents.y1 != region->extents.y2)) {
2887 region->numRects = 1;
2888 *(region->rects) = region->extents;
2889 }
2890 return (region);
2891 }
2892
2893 pETEs = (OEdgeTableEntry *)malloc(sizeof(OEdgeTableEntry) * n_points);
2894
2895 pts = FirstPtBlock.pts;
2896 CreateETandAET(n_points, points, &ET, &AET, pETEs, &SLLBlock);
2897 pSLL = ET.scanlines.next;
2898 curPtBlock = &FirstPtBlock;
2899
2900 if (fill_rule == OGDK_EVEN_ODD_RULE) {
2901 /*
2902 * for each scanline
2903 */
2904 for (y = ET.ymin; y < ET.ymax; y++) {
2905 /*
2906 * Add a new edge to the active edge table when we
2907 * get to the next edge.
2908 */
2909 if (pSLL != NULL && y == pSLL->scanline) {
2910 loadAET(&AET, pSLL->edgelist);
2911 pSLL = pSLL->next;
2912 }
2913 pPrevAET = &AET;
2914 pAET = AET.next;
2915
2916 /*
2917 * for each active edge
2918 */
2919 while (pAET) {
2920 pts->x = pAET->bres.minor_axis, pts->y = y;
2921 pts++, iPts++;
2922
2923 /*
2924 * send out the buffer
2925 */
2926 if (iPts == NUMPTSTOBUFFER) {
2927 tmpPtBlock = (OPOINTBLOCK *)malloc(sizeof(OPOINTBLOCK));
2928 tmpPtBlock->next = NULL;
2929 curPtBlock->next = tmpPtBlock;
2930 curPtBlock = tmpPtBlock;
2931 pts = curPtBlock->pts;
2932 numFullPtBlocks++;
2933 iPts = 0;
2934 }
2935 OEVALUATEEDGEEVENODD(pAET, pPrevAET, y);
2936 }
2937 (void)InsertionSort(&AET);
2938 }
2939 } else {
2940 /*
2941 * for each scanline
2942 */
2943 for (y = ET.ymin; y < ET.ymax; y++) {
2944 /*
2945 * Add a new edge to the active edge table when we
2946 * get to the next edge.
2947 */
2948 if (pSLL != NULL && y == pSLL->scanline) {
2949 loadAET(&AET, pSLL->edgelist);
2950 computeWAET(&AET);
2951 pSLL = pSLL->next;
2952 }
2953 pPrevAET = &AET;
2954 pAET = AET.next;
2955 pWETE = pAET;
2956
2957 /*
2958 * for each active edge
2959 */
2960 while (pAET) {
2961 /*
2962 * add to the buffer only those edges that
2963 * are in the Winding active edge table.
2964 */
2965 if (pWETE == pAET) {
2966 pts->x = pAET->bres.minor_axis, pts->y = y;
2967 pts++, iPts++;
2968
2969 /*
2970 * send out the buffer
2971 */
2972 if (iPts == NUMPTSTOBUFFER) {
2973 tmpPtBlock = (OPOINTBLOCK *)malloc(sizeof(OPOINTBLOCK));
2974 tmpPtBlock->next = NULL;
2975 curPtBlock->next = tmpPtBlock;
2976 curPtBlock = tmpPtBlock;
2977 pts = curPtBlock->pts;
2978 numFullPtBlocks++;
2979 iPts = 0;
2980 }
2981 pWETE = pWETE->nextWETE;
2982 }
2983 OEVALUATEEDGEWINDING(pAET, pPrevAET, y, fixWAET);
2984 }
2985
2986 /*
2987 * recompute the winding active edge table if
2988 * we just resorted or have exited an edge.
2989 */
2990 if (InsertionSort(&AET) || fixWAET) {
2991 computeWAET(&AET);
2992 fixWAET = FALSE;
2993 }
2994 }
2995 }
2996 FreeStorage(SLLBlock.next);
2997 (void)PtsToRegion(numFullPtBlocks, iPts, &FirstPtBlock, region);
2998 for (curPtBlock = FirstPtBlock.next; --numFullPtBlocks >= 0;) {
2999 tmpPtBlock = curPtBlock->next;
3000 free(curPtBlock);
3001 curPtBlock = tmpPtBlock;
3002 }
3003 free(pETEs);
3004 return (region);
3005}
3006
3007// #define __GDK_POLYREG_GENERIC_C__
3008// #include "gdkaliasdef.c"
3009#endif // USE_NEW_REGION