OpenCPN Partial API docs
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 
53 typedef enum { OGDK_EVEN_ODD_RULE, OGDK_WINDING_RULE } OGdkFillRule;
54 
55 typedef 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 
64 typedef struct _OGdkPoint OGdkPoint;
65 struct _OGdkPoint {
66  int x;
67  int y;
68 };
69 
70 typedef struct _OGdkRectangle OGdkRectangle;
72  int x;
73  int y;
74  int width;
75  int height;
76 };
77 
78 //#define gboolean bool;
79 
80 typedef struct _OGdkSegment OGdkSegment;
81 struct _OGdkSegment {
82  int x1;
83  int y1;
84  int x2;
85  int y2;
86 };
87 
89 
90 typedef struct _OGdkRegion OGdkRegion;
91 struct _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  */
108 typedef struct _OPOINTBLOCK {
109  OGdkPoint pts[NUMPTSTOBUFFER];
110  struct _OPOINTBLOCK *next;
111 } OPOINTBLOCK;
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  */
244 typedef 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 
265 typedef 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 
274 typedef struct _OScanLineList {
275  int scanline; /* the scanline represented */
276  OEdgeTableEntry *edgelist; /* header node */
277  struct _OScanLineList *next; /* next in the list */
278 } OScanLineList;
279 
280 typedef 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 
293 typedef 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 
345 OGdkRegion *gdk_region_copy(const OGdkRegion *region);
346 void gdk_region_destroy(OGdkRegion *region);
347 OGdkRegion *gdk_region_rectangle(const OGdkRectangle *rectangle);
348 bool ogdk_region_equal(const OGdkRegion *region1, const OGdkRegion *region2);
349 bool gdk_region_point_in(const OGdkRegion *region, int x, int y);
350 OGdkOverlapType gdk_region_rect_in(const OGdkRegion *region,
351  const OGdkRectangle *rectangle);
352 void gdk_region_offset(OGdkRegion *region, int dx, int dy);
353 void gdk_region_union_with_rect(OGdkRegion *region, const OGdkRectangle *rect);
354 void gdk_region_union(OGdkRegion *source1, const OGdkRegion *source2);
355 void gdk_region_intersect(OGdkRegion *source1, const OGdkRegion *source2);
356 OGdkRegion *gdk_region_polygon(const OGdkPoint *points, int n_points,
357  OGdkFillRule fill_rule);
358 
359 OGdkRegion *gdk_region_new(void);
360 void gdk_region_subtract(OGdkRegion *source1, const OGdkRegion *source2);
361 bool gdk_region_empty(const OGdkRegion *region);
362 
363 void gdk_region_get_rectangles(const OGdkRegion *region,
364  OGdkRectangle **rectangles, int *n_rectangles);
365 void gdk_region_get_clipbox(const OGdkRegion *region, OGdkRectangle *rectangle);
366 
367 // ----------------------------------------------------------------------------
368 // OCPNRegionRefData: private class containing the information about the region
369 // ----------------------------------------------------------------------------
370 
371 class OCPNRegionRefData : public wxObjectRefData {
372 public:
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 
394 IMPLEMENT_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 
404 OCPNRegion::OCPNRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h)
405  : wxRegion(x, y, w, h)
406 
407 {}
408 
409 OCPNRegion::OCPNRegion(const wxPoint &topLeft, const wxPoint &bottomRight)
410  : wxRegion(topLeft.x, topLeft.y, bottomRight.x - topLeft.x,
411  bottomRight.y - topLeft.y) {}
412 
413 OCPNRegion::OCPNRegion(const wxRect &rect)
414  : wxRegion(rect.x, rect.y, rect.width, rect.height) {}
415 
416 OCPNRegion::OCPNRegion(const wxRegion &region) : wxRegion(region) {}
417 
418 OCPNRegion::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 
426 wxRegion *OCPNRegion::GetNew_wxRegion() const { return new wxRegion(this); }
427 
428 #endif
429 
430 #ifdef USE_NEW_REGION
431 
432 OCPNRegion::OCPNRegion(wxCoord x, wxCoord y, wxCoord w, wxCoord h) {
433  InitRect(x, y, w, h);
434 }
435 
436 OCPNRegion::OCPNRegion(const wxPoint &topLeft, const wxPoint &bottomRight) {
437  InitRect(topLeft.x, topLeft.y, bottomRight.x - topLeft.x,
438  bottomRight.y - topLeft.y);
439 }
440 
441 OCPNRegion::OCPNRegion(const wxRect &rect) {
442  InitRect(rect.x, rect.y, rect.width, rect.height);
443 }
444 
445 OCPNRegion::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 
459 OCPNRegion::~OCPNRegion() {}
460 
461 void 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 
479 OCPNRegion::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 
502 wxObjectRefData *OCPNRegion::CreateRefData() const {
503  return new OCPNRegionRefData;
504 }
505 
506 wxObjectRefData *OCPNRegion::CloneRefData(const wxObjectRefData *data) const {
507  return new OCPNRegionRefData(*(OCPNRegionRefData *)data);
508 }
509 
510 // ----------------------------------------------------------------------------
511 // OCPNRegion comparison
512 // ----------------------------------------------------------------------------
513 
514 bool 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 
525 void OCPNRegion::Clear() { UnRef(); }
526 
527 bool 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 
550 bool 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 
565 bool 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 
581 bool 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
596 bool 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 
613 bool 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 
627 bool 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 
648 bool OCPNRegion::IsEmpty() const {
649  if (!m_refData) return true;
650 
651  return gdk_region_empty(M_REGIONDATA->m_region);
652 }
653 
654 wxRegionContain 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 
663 wxRegionContain 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 
684 void *OCPNRegion::GetRegion() const {
685  if (!m_refData) return NULL;
686 
687  return M_REGIONDATA->m_region;
688 }
689 
690 wxRegion *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 
723 OCPNRegionIterator::OCPNRegionIterator(const OCPNRegion &region) {
724  m_ri = new wxRegionIterator(region);
725 }
726 
727 OCPNRegionIterator::~OCPNRegionIterator() { delete m_ri; }
728 
729 void OCPNRegionIterator::Reset() { m_ri->Reset(); }
730 
731 void OCPNRegionIterator::Reset(const OCPNRegion &region) {
732  m_ri->Reset(region);
733 }
734 
735 wxRect OCPNRegionIterator::GetRect() const { return m_ri->GetRect(); }
736 
737 bool OCPNRegionIterator::HaveRects() const { return m_ri->HaveRects(); }
738 
739 void OCPNRegionIterator::NextRect() { ++(*m_ri); }
740 
741 #endif
742 
743 #ifdef USE_NEW_REGION
744 
745 OCPNRegionIterator::OCPNRegionIterator() {
746  Init();
747  Reset();
748 }
749 
750 OCPNRegionIterator::OCPNRegionIterator(const OCPNRegion &region) {
751  Init();
752  Reset(region);
753 }
754 
755 void OCPNRegionIterator::Init() {
756  m_rects = NULL;
757  m_numRects = 0;
758 }
759 
760 OCPNRegionIterator::~OCPNRegionIterator() { wxDELETEA(m_rects); }
761 
762 void OCPNRegionIterator::Reset() { m_current = 0u; }
763 
764 void OCPNRegionIterator::NextRect() {
765  if (HaveRects()) ++m_current;
766 }
767 
768 void 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 
794 void OCPNRegionIterator::Reset(const OCPNRegion &region) {
795  m_region = region;
796  CreateRects(region);
797  Reset();
798 }
799 
800 bool OCPNRegionIterator::HaveRects() const { return m_current < m_numRects; }
801 
802 wxRect 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 
891 typedef void (*overlapFunc)(OGdkRegion *pReg, OGdkRegionBox *r1,
892  OGdkRegionBox *r1End, OGdkRegionBox *r2,
893  OGdkRegionBox *r2End, int y1, int y2);
894 typedef void (*nonOverlapFunc)(OGdkRegion *pReg, OGdkRegionBox *r,
895  OGdkRegionBox *rEnd, int y1, int y2);
896 
897 static void miRegionCopy(OGdkRegion *dstrgn, const OGdkRegion *rgn);
898 static void miRegionOp(OGdkRegion *newReg, OGdkRegion *reg1,
899  const OGdkRegion *reg2, overlapFunc overlapFn,
900  nonOverlapFunc nonOverlap1Fn,
901  nonOverlapFunc nonOverlap2Fn);
902 
910 OGdkRegion *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 
934 OGdkRegion *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 
962 OGdkRegion *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 
982 void 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 
1002 void 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 
1033 void 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  */
1067 static 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 
1113 void gdk_region_destroy(OGdkRegion *region) {
1115 
1116  if (region->rects != &region->extents) free(region->rects);
1118 }
1119 
1128 void 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 
1183 static 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 
1215 void 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*/
1256 static 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 
1314 void 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 
1335 static 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*/
1375 static 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*/
1510 static 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  */
1763 static 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*/
1803 static 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 
1856 void 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*/
1924 static 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*/
1963 static 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 
2077 void 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 
2107 void 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 
2131 bool 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 
2149 bool 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 
2180 bool 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 
2204 OGdkOverlapType 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  */
2502 static 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 
2579 static 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 
2658 static 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  */
2700 static 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 
2733 static 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  */
2764 static 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  */
2782 static 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 
2853 OGdkRegion *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