~canonical-dx-team/ubuntu/maverick/gtk+2.0/menuproxy

« back to all changes in this revision

Viewing changes to gdk/gdkregion-generic.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2007-05-04 12:24:25 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20070504122425-0m8midgzrp40y8w2
Tags: 2.10.12-1ubuntu1
* Sync with Debian
* New upstream version:
  Fixed bugs:
  - 379414 file chooser warnings when changing path in the entry
  - 418585 GtkFileChooserDefault sizing code is not DPI independent
  - 419568 Crash in search if start with special letter
  - 435062 build dies with icon cache validation
  - 379399 Segfault to call gtk_print_operation_run twice.
  - 387889 cups backend has problems when there are too many printers
  - 418531 invalid read to gtkicontheme.c gtk_icon_theme_lookup_icon...
  - 423916 crash in color scheme code
  - 424042 Segmentation fault while quickly pressing Alt+arrows
  - 415260 Protect against negative indices when setting values in G...
  - 419171 XGetVisualInfo() may not set nxvisuals
  - 128852 Gdk cursors don't look good on win32
  - 344657 Ctrl-H doesn't toggle "Show Hidden Files" setting
  - 345345 PrintOperation::paginate is not emitted for class handler
  - 347567 GtkPrintOperation::end-print is not emitted if it's cance...
  - 369112 gtk_ui_manager_add_ui should accept unnamed separator
  - 392015 Selected menu item invisible on Windows Vista
  - 399253 MS-Windows Theme Bottom Tab placement rendering glitches
  - 399425 gtk_input_dialog_fill_axes() adds child to gtkscrolledwin...
  - 403251 [patch] little memory leak in GtkPrintJob
  - 403267 [patch] memory leak in GtkPageSetupUnixDialog
  - 403470 MS-Windows Theme tab placement other than on top leaks a ...
  - 404506 Windows system fonts that have multi-byte font names cann...
  - 405089 Incorrect window placement for GtkEventBox private window
  - 405515 Minor leak in gtkfilesystemmodel.c
  - 405539 gdk_pixbuf_save() for PNG saver can return FALSE without ...
  - 415681 gdk_window_clear_area includes an extra line and column o...
  - 418219 GtkRecentChooser should apply filter before sorting and c...
  - 418403 Scroll to printer after selecting it from settings
  - 421985 _gtk_print_operation_platform_backend_launch_preview
  - 421990 gtk_print_job_get_surface
  - 421993 gtk_print_operation_init
  - 423064 Conditional jump or move depends on uninitialised value(s...
  - 423722 Fix printing header in gtk-demo
  - 424168 gtk_print_operation_run on async preview
  - 425655 Don't install gtk+-unix-print-2.0.pc on non-UNIX platforms
  - 425786 GDK segfaults if XineramaQueryScreens fails
  - 428665 Lpr Backend gets stuck in infinite loop during gtk_enumer...
  - 429902 GtkPrintOperation leaks cairo contextes
  - 431997 First delay of GdkPixbufAnimationIter is wrong
  - 433242 Inconsistent scroll arrow position calculations
  - 433972 Placing gtk.Expander inside a gtk.TextView() changes gtk....
  - 434261 _gtk_toolbar_elide_underscores incorrectly handles some s...
  - 383354 ctrl-L should make 'Location' entry disappear
  - 418673 gtk_recent_manager_add_item
  - 429732 gtk_accel_group_finalize accesses invalid memory
  - 435028 WM_CLIENT_LEADER is wrong on the leader_window
  - 431067 Background of the header window is not updated
  - 338843 add recent files support inside the ui manager
  - 148535 add drop shadow to menus, tooltips, etc. under Windows XP
* debian/control.in:
  - Conflicts on ubuntulooks (<= 0.9.11-1)
* debian/patches/15_default-fallback-icon-theme.patch:
  - patch from Debian, fallback on gnome icon theme

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $TOG: Region.c /main/31 1998/02/06 17:50:22 kaleb $ */
 
2
/************************************************************************
 
3
 
 
4
Copyright 1987, 1988, 1998  The Open Group
 
5
 
 
6
All Rights Reserved.
 
7
 
 
8
The above copyright notice and this permission notice shall be included in
 
9
all copies or substantial portions of the Software.
 
10
 
 
11
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 
12
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 
13
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 
14
OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 
15
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 
16
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
17
 
 
18
Except as contained in this notice, the name of The Open Group shall not be
 
19
used in advertising or otherwise to promote the sale, use or other dealings
 
20
in this Software without prior written authorization from The Open Group.
 
21
 
 
22
 
 
23
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
 
24
 
 
25
                        All Rights Reserved
 
26
 
 
27
Permission to use, copy, modify, and distribute this software and its 
 
28
documentation for any purpose and without fee is hereby granted, 
 
29
provided that the above copyright notice appear in all copies and that
 
30
both that copyright notice and this permission notice appear in 
 
31
supporting documentation, and that the name of Digital not be
 
32
used in advertising or publicity pertaining to distribution of the
 
33
software without specific, written prior permission.  
 
34
 
 
35
DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 
36
ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 
37
DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 
38
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 
39
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 
40
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 
41
SOFTWARE.
 
42
 
 
43
************************************************************************/
 
44
/* $XFree86: xc/lib/X11/Region.c,v 1.5 1999/05/09 10:50:01 dawes Exp $ */
 
45
/*
 
46
 * The functions in this file implement the Region abstraction, similar to one
 
47
 * used in the X11 sample server. A Region is simply an area, as the name
 
48
 * implies, and is implemented as a "y-x-banded" array of rectangles. To
 
49
 * explain: Each Region is made up of a certain number of rectangles sorted
 
50
 * by y coordinate first, and then by x coordinate.
 
51
 *
 
52
 * Furthermore, the rectangles are banded such that every rectangle with a
 
53
 * given upper-left y coordinate (y1) will have the same lower-right y
 
54
 * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it
 
55
 * will span the entire vertical distance of the band. This means that some
 
56
 * areas that could be merged into a taller rectangle will be represented as
 
57
 * several shorter rectangles to account for shorter rectangles to its left
 
58
 * or right but within its "vertical scope".
 
59
 *
 
60
 * An added constraint on the rectangles is that they must cover as much
 
61
 * horizontal area as possible. E.g. no two rectangles in a band are allowed
 
62
 * to touch.
 
63
 *
 
64
 * Whenever possible, bands will be merged together to cover a greater vertical
 
65
 * distance (and thus reduce the number of rectangles). Two bands can be merged
 
66
 * only if the bottom of one touches the top of the other and they have
 
67
 * rectangles in the same places (of the same width, of course). This maintains
 
68
 * the y-x-banding that's so nice to have...
 
69
 */
 
70
 
 
71
#include <config.h>
 
72
#include <stdlib.h>
 
73
#include <string.h>
 
74
#include <gdkregion.h>
 
75
#include "gdkregion-generic.h"
 
76
#include "gdkalias.h"
 
77
 
 
78
typedef void (*overlapFunc) (GdkRegion    *pReg,
 
79
                             GdkRegionBox *r1,
 
80
                             GdkRegionBox *r1End,
 
81
                             GdkRegionBox *r2,
 
82
                             GdkRegionBox *r2End,
 
83
                             gint          y1,
 
84
                             gint          y2);
 
85
typedef void (*nonOverlapFunc) (GdkRegion    *pReg,
 
86
                                GdkRegionBox *r,
 
87
                                GdkRegionBox *rEnd,
 
88
                                gint          y1,
 
89
                                gint          y2);
 
90
 
 
91
static void miRegionCopy (GdkRegion      *dstrgn,
 
92
                          GdkRegion      *rgn);
 
93
static void miRegionOp   (GdkRegion      *newReg,
 
94
                          GdkRegion      *reg1,
 
95
                          GdkRegion      *reg2,
 
96
                          overlapFunc     overlapFn,
 
97
                          nonOverlapFunc  nonOverlap1Fn,
 
98
                          nonOverlapFunc  nonOverlap2Fn);
 
99
 
 
100
/**
 
101
 * gdk_region_new:
 
102
 *
 
103
 * Creates a new empty #GdkRegion.
 
104
 *
 
105
 * Returns: a new empty #GdkRegion
 
106
 */
 
107
GdkRegion *
 
108
gdk_region_new ()
 
109
{
 
110
  GdkRegion *temp;
 
111
 
 
112
  temp = g_slice_new (GdkRegion);
 
113
 
 
114
  temp->numRects = 0;
 
115
  temp->rects = &temp->extents;
 
116
  temp->extents.x1 = 0;
 
117
  temp->extents.y1 = 0;
 
118
  temp->extents.x2 = 0;
 
119
  temp->extents.y2 = 0;
 
120
  temp->size = 1;
 
121
  
 
122
  return temp;
 
123
}
 
124
 
 
125
/**
 
126
 * gdk_region_rectangle:
 
127
 * @rectangle: a #GdkRectangle
 
128
 * 
 
129
 * Creates a new region containing the area @rectangle.
 
130
 * 
 
131
 * Return value: a new region
 
132
 **/
 
133
GdkRegion *
 
134
gdk_region_rectangle (GdkRectangle *rectangle)
 
135
{
 
136
  GdkRegion *temp;
 
137
 
 
138
  g_return_val_if_fail (rectangle != NULL, NULL);
 
139
 
 
140
  if (rectangle->width <= 0 || rectangle->height <= 0)
 
141
    return gdk_region_new();
 
142
 
 
143
  temp = g_slice_new (GdkRegion);
 
144
 
 
145
  temp->numRects = 1;
 
146
  temp->rects = &temp->extents;
 
147
  temp->extents.x1 = rectangle->x;
 
148
  temp->extents.y1 = rectangle->y;
 
149
  temp->extents.x2 = rectangle->x + rectangle->width;
 
150
  temp->extents.y2 = rectangle->y + rectangle->height;
 
151
  temp->size = 1;
 
152
  
 
153
  return temp;
 
154
}
 
155
 
 
156
/**
 
157
 * gdk_region_copy:
 
158
 * @region: a #GdkRegion
 
159
 * 
 
160
 * Copies @region, creating an identical new region.
 
161
 * 
 
162
 * Return value: a new region identical to @region
 
163
 **/
 
164
GdkRegion *
 
165
gdk_region_copy (GdkRegion *region)
 
166
{
 
167
  GdkRegion *temp;
 
168
 
 
169
  g_return_val_if_fail (region != NULL, NULL);
 
170
 
 
171
  temp = gdk_region_new ();
 
172
 
 
173
  miRegionCopy (temp, region);
 
174
 
 
175
  return temp;
 
176
}
 
177
 
 
178
/**
 
179
 * gdk_region_get_clipbox:
 
180
 * @region: a #GdkRegion
 
181
 * @rectangle: return location for the clipbox
 
182
 *
 
183
 * Obtains the smallest rectangle which includes the entire #GdkRegion.
 
184
 *
 
185
 */
 
186
void
 
187
gdk_region_get_clipbox (GdkRegion    *region, 
 
188
                        GdkRectangle *rectangle)
 
189
{
 
190
  g_return_if_fail (region != NULL);
 
191
  g_return_if_fail (rectangle != NULL);
 
192
  
 
193
  rectangle->x = region->extents.x1;
 
194
  rectangle->y = region->extents.y1;
 
195
  rectangle->width = region->extents.x2 - region->extents.x1;
 
196
  rectangle->height = region->extents.y2 - region->extents.y1;
 
197
}
 
198
 
 
199
 
 
200
/**
 
201
 * gdk_region_get_rectangles:
 
202
 * @region: a #GdkRegion
 
203
 * @rectangles: return location for an array of rectangles
 
204
 * @n_rectangles: length of returned array
 
205
 *
 
206
 * Obtains the area covered by the region as a list of rectangles.
 
207
 * The array returned in @rectangles must be freed with g_free().
 
208
 **/
 
209
void
 
210
gdk_region_get_rectangles (GdkRegion     *region,
 
211
                           GdkRectangle **rectangles,
 
212
                           gint          *n_rectangles)
 
213
{
 
214
  gint i;
 
215
  
 
216
  g_return_if_fail (region != NULL);
 
217
  g_return_if_fail (rectangles != NULL);
 
218
  g_return_if_fail (n_rectangles != NULL);
 
219
  
 
220
  *n_rectangles = region->numRects;
 
221
  *rectangles = g_new (GdkRectangle, region->numRects);
 
222
 
 
223
  for (i = 0; i < region->numRects; i++)
 
224
    {
 
225
      GdkRegionBox rect;
 
226
      rect = region->rects[i];
 
227
      (*rectangles)[i].x = rect.x1;
 
228
      (*rectangles)[i].y = rect.y1;
 
229
      (*rectangles)[i].width = rect.x2 - rect.x1;
 
230
      (*rectangles)[i].height = rect.y2 - rect.y1;
 
231
    }
 
232
}
 
233
 
 
234
/**
 
235
 * gdk_region_union_with_rect:
 
236
 * @region: a #GdkRegion.
 
237
 * @rect: a #GdkRectangle.
 
238
 * 
 
239
 * Sets the area of @region to the union of the areas of @region and
 
240
 * @rect. The resulting area is the set of pixels contained in
 
241
 * either @region or @rect.
 
242
 **/
 
243
void
 
244
gdk_region_union_with_rect (GdkRegion    *region,
 
245
                            GdkRectangle *rect)
 
246
{
 
247
  GdkRegion tmp_region;
 
248
 
 
249
  g_return_if_fail (region != NULL);
 
250
  g_return_if_fail (rect != NULL);
 
251
 
 
252
  if (rect->width <= 0 || rect->height <= 0)
 
253
    return;
 
254
    
 
255
  tmp_region.rects = &tmp_region.extents;
 
256
  tmp_region.numRects = 1;
 
257
  tmp_region.extents.x1 = rect->x;
 
258
  tmp_region.extents.y1 = rect->y;
 
259
  tmp_region.extents.x2 = rect->x + rect->width;
 
260
  tmp_region.extents.y2 = rect->y + rect->height;
 
261
  tmp_region.size = 1;
 
262
 
 
263
  gdk_region_union (region, &tmp_region);
 
264
}
 
265
 
 
266
/*-
 
267
 *-----------------------------------------------------------------------
 
268
 * miSetExtents --
 
269
 *      Reset the extents of a region to what they should be. Called by
 
270
 *      miSubtract and miIntersect b/c they can't figure it out along the
 
271
 *      way or do so easily, as miUnion can.
 
272
 *
 
273
 * Results:
 
274
 *      None.
 
275
 *
 
276
 * Side Effects:
 
277
 *      The region's 'extents' structure is overwritten.
 
278
 *
 
279
 *-----------------------------------------------------------------------
 
280
 */
 
281
static void
 
282
miSetExtents (GdkRegion *pReg)
 
283
{
 
284
  GdkRegionBox *pBox, *pBoxEnd, *pExtents;
 
285
 
 
286
  if (pReg->numRects == 0)
 
287
    {
 
288
      pReg->extents.x1 = 0;
 
289
      pReg->extents.y1 = 0;
 
290
      pReg->extents.x2 = 0;
 
291
      pReg->extents.y2 = 0;
 
292
      return;
 
293
    }
 
294
  
 
295
  pExtents = &pReg->extents;
 
296
  pBox = pReg->rects;
 
297
  pBoxEnd = &pBox[pReg->numRects - 1];
 
298
 
 
299
    /*
 
300
     * Since pBox is the first rectangle in the region, it must have the
 
301
     * smallest y1 and since pBoxEnd is the last rectangle in the region,
 
302
     * it must have the largest y2, because of banding. Initialize x1 and
 
303
     * x2 from  pBox and pBoxEnd, resp., as good things to initialize them
 
304
     * to...
 
305
     */
 
306
  pExtents->x1 = pBox->x1;
 
307
  pExtents->y1 = pBox->y1;
 
308
  pExtents->x2 = pBoxEnd->x2;
 
309
  pExtents->y2 = pBoxEnd->y2;
 
310
 
 
311
  g_assert(pExtents->y1 < pExtents->y2);
 
312
  while (pBox <= pBoxEnd)
 
313
    {
 
314
      if (pBox->x1 < pExtents->x1)
 
315
        {
 
316
          pExtents->x1 = pBox->x1;
 
317
        }
 
318
      if (pBox->x2 > pExtents->x2)
 
319
        {
 
320
          pExtents->x2 = pBox->x2;
 
321
        }
 
322
      pBox++;
 
323
    }
 
324
  g_assert(pExtents->x1 < pExtents->x2);
 
325
}
 
326
 
 
327
/**
 
328
 * gdk_region_destroy:
 
329
 * @region: a #GdkRegion
 
330
 *
 
331
 * Destroys a #GdkRegion.
 
332
 */
 
333
void
 
334
gdk_region_destroy (GdkRegion *region)
 
335
{
 
336
  g_return_if_fail (region != NULL);
 
337
 
 
338
  if (region->rects != &region->extents)
 
339
    g_free (region->rects);
 
340
  g_slice_free (GdkRegion, region);
 
341
}
 
342
 
 
343
 
 
344
/**
 
345
 * gdk_region_offset:
 
346
 * @region: a #GdkRegion
 
347
 * @dx: the distance to move the region horizontally
 
348
 * @dy: the distance to move the region vertically
 
349
 *
 
350
 * Moves a region the specified distance.
 
351
 */
 
352
void
 
353
gdk_region_offset (GdkRegion *region,
 
354
                   gint       x,
 
355
                   gint       y)
 
356
{
 
357
  int nbox;
 
358
  GdkRegionBox *pbox;
 
359
 
 
360
  g_return_if_fail (region != NULL);
 
361
 
 
362
  pbox = region->rects;
 
363
  nbox = region->numRects;
 
364
 
 
365
  while(nbox--)
 
366
    {
 
367
      pbox->x1 += x;
 
368
      pbox->x2 += x;
 
369
      pbox->y1 += y;
 
370
      pbox->y2 += y;
 
371
      pbox++;
 
372
    }
 
373
  if (region->rects != &region->extents)
 
374
    {
 
375
      region->extents.x1 += x;
 
376
      region->extents.x2 += x;
 
377
      region->extents.y1 += y;
 
378
      region->extents.y2 += y;
 
379
    }
 
380
}
 
381
 
 
382
/* 
 
383
   Utility procedure Compress:
 
384
   Replace r by the region r', where 
 
385
     p in r' iff (Quantifer m <= dx) (p + m in r), and
 
386
     Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and
 
387
     (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE.
 
388
 
 
389
   Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region
 
390
   of all points p such that p and the next dx points on the same
 
391
   horizontal scan line are all in r.  We do this using by noting
 
392
   that p is the head of a run of length 2^i + k iff p is the head
 
393
   of a run of length 2^i and p+2^i is the head of a run of length
 
394
   k. Thus, the loop invariant: s contains the region corresponding
 
395
   to the runs of length shift.  r contains the region corresponding
 
396
   to the runs of length 1 + dxo & (shift-1), where dxo is the original
 
397
   value of dx.  dx = dxo & ~(shift-1).  As parameters, s and t are
 
398
   scratch regions, so that we don't have to allocate them on every
 
399
   call.
 
400
*/
 
401
 
 
402
#define ZOpRegion(a,b) if (grow) gdk_region_union (a, b); \
 
403
                         else gdk_region_intersect (a,b)
 
404
#define ZShiftRegion(a,b) if (xdir) gdk_region_offset (a,b,0); \
 
405
                          else gdk_region_offset (a,0,b)
 
406
 
 
407
static void
 
408
Compress(GdkRegion *r,
 
409
         GdkRegion *s,
 
410
         GdkRegion *t,
 
411
         guint      dx,
 
412
         int        xdir,
 
413
         int        grow)
 
414
{
 
415
  guint shift = 1;
 
416
 
 
417
  miRegionCopy (s, r);
 
418
  while (dx)
 
419
    {
 
420
      if (dx & shift)
 
421
        {
 
422
          ZShiftRegion(r, -(int)shift);
 
423
          ZOpRegion(r, s);
 
424
          dx -= shift;
 
425
          if (!dx) break;
 
426
        }
 
427
      miRegionCopy (t, s);
 
428
      ZShiftRegion(s, -(int)shift);
 
429
      ZOpRegion(s, t);
 
430
      shift <<= 1;
 
431
    }
 
432
}
 
433
 
 
434
#undef ZOpRegion
 
435
#undef ZShiftRegion
 
436
#undef ZCopyRegion
 
437
 
 
438
/**
 
439
 * gdk_region_shrink:
 
440
 * @region: a #GdkRegion
 
441
 * @dx: the number of pixels to shrink the region horizontally
 
442
 * @dy: the number of pixels to shrink the region vertically
 
443
 *
 
444
 * Resizes a region by the specified amount.
 
445
 * Positive values shrink the region. Negative values expand it.
 
446
 */
 
447
void
 
448
gdk_region_shrink (GdkRegion *region,
 
449
                   int        dx,
 
450
                   int        dy)
 
451
{
 
452
  GdkRegion *s, *t;
 
453
  int grow;
 
454
 
 
455
  g_return_if_fail (region != NULL);
 
456
 
 
457
  if (!dx && !dy)
 
458
    return;
 
459
 
 
460
  s = gdk_region_new ();
 
461
  t = gdk_region_new ();
 
462
 
 
463
  grow = (dx < 0);
 
464
  if (grow)
 
465
    dx = -dx;
 
466
  if (dx)
 
467
     Compress(region, s, t, (unsigned) 2*dx, TRUE, grow);
 
468
     
 
469
  grow = (dy < 0);
 
470
  if (grow)
 
471
    dy = -dy;
 
472
  if (dy)
 
473
     Compress(region, s, t, (unsigned) 2*dy, FALSE, grow);
 
474
  
 
475
  gdk_region_offset (region, dx, dy);
 
476
  gdk_region_destroy (s);
 
477
  gdk_region_destroy (t);
 
478
}
 
479
 
 
480
 
 
481
/*======================================================================
 
482
 *          Region Intersection
 
483
 *====================================================================*/
 
484
/*-
 
485
 *-----------------------------------------------------------------------
 
486
 * miIntersectO --
 
487
 *      Handle an overlapping band for miIntersect.
 
488
 *
 
489
 * Results:
 
490
 *      None.
 
491
 *
 
492
 * Side Effects:
 
493
 *      Rectangles may be added to the region.
 
494
 *
 
495
 *-----------------------------------------------------------------------
 
496
 */
 
497
/* static void*/
 
498
static void
 
499
miIntersectO (GdkRegion    *pReg,
 
500
              GdkRegionBox *r1,
 
501
              GdkRegionBox *r1End,
 
502
              GdkRegionBox *r2,
 
503
              GdkRegionBox *r2End,
 
504
              gint          y1,
 
505
              gint          y2)
 
506
{
 
507
  int   x1;
 
508
  int   x2;
 
509
  GdkRegionBox *pNextRect;
 
510
 
 
511
  pNextRect = &pReg->rects[pReg->numRects];
 
512
 
 
513
  while ((r1 != r1End) && (r2 != r2End))
 
514
    {
 
515
      x1 = MAX (r1->x1,r2->x1);
 
516
      x2 = MIN (r1->x2,r2->x2);
 
517
 
 
518
      /*
 
519
       * If there's any overlap between the two rectangles, add that
 
520
       * overlap to the new region.
 
521
       * There's no need to check for subsumption because the only way
 
522
       * such a need could arise is if some region has two rectangles
 
523
       * right next to each other. Since that should never happen...
 
524
       */
 
525
      if (x1 < x2)
 
526
        {
 
527
          g_assert (y1<y2);
 
528
 
 
529
          MEMCHECK (pReg, pNextRect, pReg->rects);
 
530
          pNextRect->x1 = x1;
 
531
          pNextRect->y1 = y1;
 
532
          pNextRect->x2 = x2;
 
533
          pNextRect->y2 = y2;
 
534
          pReg->numRects += 1;
 
535
          pNextRect++;
 
536
          g_assert (pReg->numRects <= pReg->size);
 
537
        }
 
538
 
 
539
      /*
 
540
       * Need to advance the pointers. Shift the one that extends
 
541
       * to the right the least, since the other still has a chance to
 
542
       * overlap with that region's next rectangle, if you see what I mean.
 
543
       */
 
544
      if (r1->x2 < r2->x2)
 
545
        {
 
546
          r1++;
 
547
        }
 
548
      else if (r2->x2 < r1->x2)
 
549
        {
 
550
          r2++;
 
551
        }
 
552
      else
 
553
        {
 
554
          r1++;
 
555
          r2++;
 
556
        }
 
557
    }
 
558
}
 
559
 
 
560
/**
 
561
 * gdk_region_intersect:
 
562
 * @source1: a #GdkRegion
 
563
 * @source2: another #GdkRegion
 
564
 *
 
565
 * Sets the area of @source1 to the intersection of the areas of @source1
 
566
 * and @source2. The resulting area is the set of pixels contained in
 
567
 * both @source1 and @source2.
 
568
 **/
 
569
void
 
570
gdk_region_intersect (GdkRegion *source1,
 
571
                      GdkRegion *source2)
 
572
{
 
573
  g_return_if_fail (source1 != NULL);
 
574
  g_return_if_fail (source2 != NULL);
 
575
  
 
576
  /* check for trivial reject */
 
577
  if ((!(source1->numRects)) || (!(source2->numRects))  ||
 
578
      (!EXTENTCHECK(&source1->extents, &source2->extents)))
 
579
    source1->numRects = 0;
 
580
  else
 
581
    miRegionOp (source1, source1, source2, 
 
582
                miIntersectO, (nonOverlapFunc) NULL, (nonOverlapFunc) NULL);
 
583
    
 
584
  /*
 
585
   * Can't alter source1's extents before miRegionOp depends on the
 
586
   * extents of the regions being unchanged. Besides, this way there's
 
587
   * no checking against rectangles that will be nuked due to
 
588
   * coalescing, so we have to examine fewer rectangles.
 
589
   */
 
590
  miSetExtents(source1);
 
591
}
 
592
 
 
593
static void
 
594
miRegionCopy (GdkRegion *dstrgn, 
 
595
              GdkRegion *rgn)
 
596
{
 
597
  if (dstrgn != rgn) /*  don't want to copy to itself */
 
598
    {  
 
599
      if (dstrgn->size < rgn->numRects)
 
600
        {
 
601
          if (dstrgn->rects != &dstrgn->extents)
 
602
            g_free (dstrgn->rects);
 
603
 
 
604
          dstrgn->rects = g_new (GdkRegionBox, rgn->numRects);
 
605
          dstrgn->size = rgn->numRects;
 
606
        }
 
607
 
 
608
      dstrgn->numRects = rgn->numRects;
 
609
      dstrgn->extents = rgn->extents;
 
610
 
 
611
      memcpy (dstrgn->rects, rgn->rects, rgn->numRects * sizeof (GdkRegionBox));
 
612
    }
 
613
}
 
614
 
 
615
 
 
616
/*======================================================================
 
617
 *          Generic Region Operator
 
618
 *====================================================================*/
 
619
 
 
620
/*-
 
621
 *-----------------------------------------------------------------------
 
622
 * miCoalesce --
 
623
 *      Attempt to merge the boxes in the current band with those in the
 
624
 *      previous one. Used only by miRegionOp.
 
625
 *
 
626
 * Results:
 
627
 *      The new index for the previous band.
 
628
 *
 
629
 * Side Effects:
 
630
 *      If coalescing takes place:
 
631
 *          - rectangles in the previous band will have their y2 fields
 
632
 *            altered.
 
633
 *          - pReg->numRects will be decreased.
 
634
 *
 
635
 *-----------------------------------------------------------------------
 
636
 */
 
637
/* static int*/
 
638
static int
 
639
miCoalesce (GdkRegion *pReg,         /* Region to coalesce */
 
640
            gint       prevStart,    /* Index of start of previous band */
 
641
            gint       curStart)     /* Index of start of current band */
 
642
{
 
643
  GdkRegionBox *pPrevBox;       /* Current box in previous band */
 
644
  GdkRegionBox *pCurBox;        /* Current box in current band */
 
645
  GdkRegionBox *pRegEnd;        /* End of region */
 
646
  int           curNumRects;    /* Number of rectangles in current
 
647
                                 * band */
 
648
  int           prevNumRects;   /* Number of rectangles in previous
 
649
                                 * band */
 
650
  int           bandY1;         /* Y1 coordinate for current band */
 
651
 
 
652
  pRegEnd = &pReg->rects[pReg->numRects];
 
653
 
 
654
  pPrevBox = &pReg->rects[prevStart];
 
655
  prevNumRects = curStart - prevStart;
 
656
 
 
657
    /*
 
658
     * Figure out how many rectangles are in the current band. Have to do
 
659
     * this because multiple bands could have been added in miRegionOp
 
660
     * at the end when one region has been exhausted.
 
661
     */
 
662
  pCurBox = &pReg->rects[curStart];
 
663
  bandY1 = pCurBox->y1;
 
664
  for (curNumRects = 0;
 
665
       (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1);
 
666
       curNumRects++)
 
667
    {
 
668
      pCurBox++;
 
669
    }
 
670
    
 
671
  if (pCurBox != pRegEnd)
 
672
    {
 
673
      /*
 
674
       * If more than one band was added, we have to find the start
 
675
       * of the last band added so the next coalescing job can start
 
676
       * at the right place... (given when multiple bands are added,
 
677
       * this may be pointless -- see above).
 
678
       */
 
679
      pRegEnd--;
 
680
      while (pRegEnd[-1].y1 == pRegEnd->y1)
 
681
        {
 
682
          pRegEnd--;
 
683
        }
 
684
      curStart = pRegEnd - pReg->rects;
 
685
      pRegEnd = pReg->rects + pReg->numRects;
 
686
    }
 
687
        
 
688
  if ((curNumRects == prevNumRects) && (curNumRects != 0)) {
 
689
    pCurBox -= curNumRects;
 
690
    /*
 
691
     * The bands may only be coalesced if the bottom of the previous
 
692
     * matches the top scanline of the current.
 
693
     */
 
694
    if (pPrevBox->y2 == pCurBox->y1)
 
695
      {
 
696
        /*
 
697
         * Make sure the bands have boxes in the same places. This
 
698
         * assumes that boxes have been added in such a way that they
 
699
         * cover the most area possible. I.e. two boxes in a band must
 
700
         * have some horizontal space between them.
 
701
         */
 
702
        do
 
703
          {
 
704
            if ((pPrevBox->x1 != pCurBox->x1) ||
 
705
                (pPrevBox->x2 != pCurBox->x2))
 
706
              {
 
707
                /*
 
708
                 * The bands don't line up so they can't be coalesced.
 
709
                 */
 
710
                return (curStart);
 
711
              }
 
712
            pPrevBox++;
 
713
            pCurBox++;
 
714
            prevNumRects -= 1;
 
715
          } while (prevNumRects != 0);
 
716
 
 
717
        pReg->numRects -= curNumRects;
 
718
        pCurBox -= curNumRects;
 
719
        pPrevBox -= curNumRects;
 
720
 
 
721
        /*
 
722
         * The bands may be merged, so set the bottom y of each box
 
723
         * in the previous band to that of the corresponding box in
 
724
         * the current band.
 
725
         */
 
726
        do
 
727
          {
 
728
            pPrevBox->y2 = pCurBox->y2;
 
729
            pPrevBox++;
 
730
            pCurBox++;
 
731
            curNumRects -= 1;
 
732
          }
 
733
        while (curNumRects != 0);
 
734
 
 
735
        /*
 
736
         * If only one band was added to the region, we have to backup
 
737
         * curStart to the start of the previous band.
 
738
         *
 
739
         * If more than one band was added to the region, copy the
 
740
         * other bands down. The assumption here is that the other bands
 
741
         * came from the same region as the current one and no further
 
742
         * coalescing can be done on them since it's all been done
 
743
         * already... curStart is already in the right place.
 
744
         */
 
745
        if (pCurBox == pRegEnd)
 
746
          {
 
747
            curStart = prevStart;
 
748
          }
 
749
        else
 
750
          {
 
751
            do
 
752
              {
 
753
                *pPrevBox++ = *pCurBox++;
 
754
              }
 
755
            while (pCurBox != pRegEnd);
 
756
          }
 
757
            
 
758
      }
 
759
  }
 
760
  return curStart;
 
761
}
 
762
 
 
763
/*-
 
764
 *-----------------------------------------------------------------------
 
765
 * miRegionOp --
 
766
 *      Apply an operation to two regions. Called by miUnion, miInverse,
 
767
 *      miSubtract, miIntersect...
 
768
 *
 
769
 * Results:
 
770
 *      None.
 
771
 *
 
772
 * Side Effects:
 
773
 *      The new region is overwritten.
 
774
 *
 
775
 * Notes:
 
776
 *      The idea behind this function is to view the two regions as sets.
 
777
 *      Together they cover a rectangle of area that this function divides
 
778
 *      into horizontal bands where points are covered only by one region
 
779
 *      or by both. For the first case, the nonOverlapFunc is called with
 
780
 *      each the band and the band's upper and lower extents. For the
 
781
 *      second, the overlapFunc is called to process the entire band. It
 
782
 *      is responsible for clipping the rectangles in the band, though
 
783
 *      this function provides the boundaries.
 
784
 *      At the end of each band, the new region is coalesced, if possible,
 
785
 *      to reduce the number of rectangles in the region.
 
786
 *
 
787
 *-----------------------------------------------------------------------
 
788
 */
 
789
/* static void*/
 
790
static void
 
791
miRegionOp(GdkRegion *newReg,
 
792
           GdkRegion *reg1,
 
793
           GdkRegion *reg2,
 
794
           overlapFunc    overlapFn,            /* Function to call for over-
 
795
                                                 * lapping bands */
 
796
           nonOverlapFunc nonOverlap1Fn,        /* Function to call for non-
 
797
                                                 * overlapping bands in region
 
798
                                                 * 1 */
 
799
           nonOverlapFunc nonOverlap2Fn)        /* Function to call for non-
 
800
                                                 * overlapping bands in region
 
801
                                                 * 2 */
 
802
{
 
803
    GdkRegionBox *r1;                   /* Pointer into first region */
 
804
    GdkRegionBox *r2;                   /* Pointer into 2d region */
 
805
    GdkRegionBox *r1End;                /* End of 1st region */
 
806
    GdkRegionBox *r2End;                /* End of 2d region */
 
807
    int           ybot;                 /* Bottom of intersection */
 
808
    int           ytop;                 /* Top of intersection */
 
809
    GdkRegionBox *oldRects;             /* Old rects for newReg */
 
810
    int           prevBand;             /* Index of start of
 
811
                                         * previous band in newReg */
 
812
    int           curBand;              /* Index of start of current
 
813
                                         * band in newReg */
 
814
    GdkRegionBox *r1BandEnd;            /* End of current band in r1 */
 
815
    GdkRegionBox *r2BandEnd;            /* End of current band in r2 */
 
816
    int           top;                  /* Top of non-overlapping
 
817
                                         * band */
 
818
    int           bot;                  /* Bottom of non-overlapping
 
819
                                         * band */
 
820
    
 
821
    /*
 
822
     * Initialization:
 
823
     *  set r1, r2, r1End and r2End appropriately, preserve the important
 
824
     * parts of the destination region until the end in case it's one of
 
825
     * the two source regions, then mark the "new" region empty, allocating
 
826
     * another array of rectangles for it to use.
 
827
     */
 
828
    r1 = reg1->rects;
 
829
    r2 = reg2->rects;
 
830
    r1End = r1 + reg1->numRects;
 
831
    r2End = r2 + reg2->numRects;
 
832
    
 
833
    oldRects = newReg->rects;
 
834
    
 
835
    EMPTY_REGION(newReg);
 
836
 
 
837
    /*
 
838
     * Allocate a reasonable number of rectangles for the new region. The idea
 
839
     * is to allocate enough so the individual functions don't need to
 
840
     * reallocate and copy the array, which is time consuming, yet we don't
 
841
     * have to worry about using too much memory. I hope to be able to
 
842
     * nuke the Xrealloc() at the end of this function eventually.
 
843
     */
 
844
    newReg->size = MAX (reg1->numRects, reg2->numRects) * 2;
 
845
    newReg->rects = g_new (GdkRegionBox, newReg->size);
 
846
    
 
847
    /*
 
848
     * Initialize ybot and ytop.
 
849
     * In the upcoming loop, ybot and ytop serve different functions depending
 
850
     * on whether the band being handled is an overlapping or non-overlapping
 
851
     * band.
 
852
     *  In the case of a non-overlapping band (only one of the regions
 
853
     * has points in the band), ybot is the bottom of the most recent
 
854
     * intersection and thus clips the top of the rectangles in that band.
 
855
     * ytop is the top of the next intersection between the two regions and
 
856
     * serves to clip the bottom of the rectangles in the current band.
 
857
     *  For an overlapping band (where the two regions intersect), ytop clips
 
858
     * the top of the rectangles of both regions and ybot clips the bottoms.
 
859
     */
 
860
    if (reg1->extents.y1 < reg2->extents.y1)
 
861
      ybot = reg1->extents.y1;
 
862
    else
 
863
      ybot = reg2->extents.y1;
 
864
    
 
865
    /*
 
866
     * prevBand serves to mark the start of the previous band so rectangles
 
867
     * can be coalesced into larger rectangles. qv. miCoalesce, above.
 
868
     * In the beginning, there is no previous band, so prevBand == curBand
 
869
     * (curBand is set later on, of course, but the first band will always
 
870
     * start at index 0). prevBand and curBand must be indices because of
 
871
     * the possible expansion, and resultant moving, of the new region's
 
872
     * array of rectangles.
 
873
     */
 
874
    prevBand = 0;
 
875
    
 
876
    do
 
877
      {
 
878
        curBand = newReg->numRects;
 
879
 
 
880
        /*
 
881
         * This algorithm proceeds one source-band (as opposed to a
 
882
         * destination band, which is determined by where the two regions
 
883
         * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the
 
884
         * rectangle after the last one in the current band for their
 
885
         * respective regions.
 
886
         */
 
887
        r1BandEnd = r1;
 
888
        while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1))
 
889
          {
 
890
            r1BandEnd++;
 
891
          }
 
892
        
 
893
        r2BandEnd = r2;
 
894
        while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1))
 
895
          {
 
896
            r2BandEnd++;
 
897
          }
 
898
        
 
899
        /*
 
900
         * First handle the band that doesn't intersect, if any.
 
901
         *
 
902
         * Note that attention is restricted to one band in the
 
903
         * non-intersecting region at once, so if a region has n
 
904
         * bands between the current position and the next place it overlaps
 
905
         * the other, this entire loop will be passed through n times.
 
906
         */
 
907
        if (r1->y1 < r2->y1)
 
908
          {
 
909
            top = MAX (r1->y1,ybot);
 
910
            bot = MIN (r1->y2,r2->y1);
 
911
 
 
912
            if ((top != bot) && (nonOverlap1Fn != (void (*)())NULL))
 
913
              {
 
914
                (* nonOverlap1Fn) (newReg, r1, r1BandEnd, top, bot);
 
915
              }
 
916
 
 
917
            ytop = r2->y1;
 
918
          }
 
919
        else if (r2->y1 < r1->y1)
 
920
          {
 
921
            top = MAX (r2->y1,ybot);
 
922
            bot = MIN (r2->y2,r1->y1);
 
923
 
 
924
            if ((top != bot) && (nonOverlap2Fn != (void (*)())NULL))
 
925
              {
 
926
                (* nonOverlap2Fn) (newReg, r2, r2BandEnd, top, bot);
 
927
              }
 
928
 
 
929
            ytop = r1->y1;
 
930
          }
 
931
        else
 
932
          {
 
933
            ytop = r1->y1;
 
934
          }
 
935
 
 
936
        /*
 
937
         * If any rectangles got added to the region, try and coalesce them
 
938
         * with rectangles from the previous band. Note we could just do
 
939
         * this test in miCoalesce, but some machines incur a not
 
940
         * inconsiderable cost for function calls, so...
 
941
         */
 
942
        if (newReg->numRects != curBand)
 
943
          {
 
944
            prevBand = miCoalesce (newReg, prevBand, curBand);
 
945
          }
 
946
 
 
947
        /*
 
948
         * Now see if we've hit an intersecting band. The two bands only
 
949
         * intersect if ybot > ytop
 
950
         */
 
951
        ybot = MIN (r1->y2, r2->y2);
 
952
        curBand = newReg->numRects;
 
953
        if (ybot > ytop)
 
954
          {
 
955
            (* overlapFn) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot);
 
956
 
 
957
          }
 
958
        
 
959
        if (newReg->numRects != curBand)
 
960
          {
 
961
            prevBand = miCoalesce (newReg, prevBand, curBand);
 
962
          }
 
963
 
 
964
        /*
 
965
         * If we've finished with a band (y2 == ybot) we skip forward
 
966
         * in the region to the next band.
 
967
         */
 
968
        if (r1->y2 == ybot)
 
969
          {
 
970
            r1 = r1BandEnd;
 
971
          }
 
972
        if (r2->y2 == ybot)
 
973
          {
 
974
            r2 = r2BandEnd;
 
975
          }
 
976
      } while ((r1 != r1End) && (r2 != r2End));
 
977
 
 
978
    /*
 
979
     * Deal with whichever region still has rectangles left.
 
980
     */
 
981
    curBand = newReg->numRects;
 
982
    if (r1 != r1End)
 
983
      {
 
984
        if (nonOverlap1Fn != (nonOverlapFunc )NULL)
 
985
          {
 
986
            do
 
987
              {
 
988
                r1BandEnd = r1;
 
989
                while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1))
 
990
                  {
 
991
                    r1BandEnd++;
 
992
                  }
 
993
                (* nonOverlap1Fn) (newReg, r1, r1BandEnd,
 
994
                                     MAX (r1->y1,ybot), r1->y2);
 
995
                r1 = r1BandEnd;
 
996
              } while (r1 != r1End);
 
997
          }
 
998
      }
 
999
    else if ((r2 != r2End) && (nonOverlap2Fn != (nonOverlapFunc) NULL))
 
1000
      {
 
1001
        do
 
1002
          {
 
1003
            r2BandEnd = r2;
 
1004
            while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1))
 
1005
              {
 
1006
                r2BandEnd++;
 
1007
              }
 
1008
            (* nonOverlap2Fn) (newReg, r2, r2BandEnd,
 
1009
                               MAX (r2->y1,ybot), r2->y2);
 
1010
            r2 = r2BandEnd;
 
1011
          } while (r2 != r2End);
 
1012
      }
 
1013
 
 
1014
    if (newReg->numRects != curBand)
 
1015
    {
 
1016
      (void) miCoalesce (newReg, prevBand, curBand);
 
1017
    }
 
1018
 
 
1019
    /*
 
1020
     * A bit of cleanup. To keep regions from growing without bound,
 
1021
     * we shrink the array of rectangles to match the new number of
 
1022
     * rectangles in the region. This never goes to 0, however...
 
1023
     *
 
1024
     * Only do this stuff if the number of rectangles allocated is more than
 
1025
     * twice the number of rectangles in the region (a simple optimization...).
 
1026
     */
 
1027
    if (newReg->numRects < (newReg->size >> 1))
 
1028
      {
 
1029
        if (REGION_NOT_EMPTY (newReg))
 
1030
          {
 
1031
            newReg->size = newReg->numRects;
 
1032
            newReg->rects = g_renew (GdkRegionBox, newReg->rects, newReg->size);
 
1033
          }
 
1034
        else
 
1035
          {
 
1036
            /*
 
1037
             * No point in doing the extra work involved in an Xrealloc if
 
1038
             * the region is empty
 
1039
             */
 
1040
            newReg->size = 1;
 
1041
            g_free (newReg->rects);
 
1042
            newReg->rects = &newReg->extents;
 
1043
          }
 
1044
      }
 
1045
 
 
1046
    if (oldRects != &newReg->extents)
 
1047
      g_free (oldRects);
 
1048
}
 
1049
 
 
1050
 
 
1051
/*======================================================================
 
1052
 *          Region Union
 
1053
 *====================================================================*/
 
1054
 
 
1055
/*-
 
1056
 *-----------------------------------------------------------------------
 
1057
 * miUnionNonO --
 
1058
 *      Handle a non-overlapping band for the union operation. Just
 
1059
 *      Adds the rectangles into the region. Doesn't have to check for
 
1060
 *      subsumption or anything.
 
1061
 *
 
1062
 * Results:
 
1063
 *      None.
 
1064
 *
 
1065
 * Side Effects:
 
1066
 *      pReg->numRects is incremented and the final rectangles overwritten
 
1067
 *      with the rectangles we're passed.
 
1068
 *
 
1069
 *-----------------------------------------------------------------------
 
1070
 */
 
1071
static void
 
1072
miUnionNonO (GdkRegion    *pReg,
 
1073
             GdkRegionBox *r,
 
1074
             GdkRegionBox *rEnd,
 
1075
             gint          y1,
 
1076
             gint          y2)
 
1077
{
 
1078
  GdkRegionBox *pNextRect;
 
1079
 
 
1080
  pNextRect = &pReg->rects[pReg->numRects];
 
1081
 
 
1082
  g_assert(y1 < y2);
 
1083
 
 
1084
  while (r != rEnd)
 
1085
    {
 
1086
      g_assert(r->x1 < r->x2);
 
1087
      MEMCHECK(pReg, pNextRect, pReg->rects);
 
1088
      pNextRect->x1 = r->x1;
 
1089
      pNextRect->y1 = y1;
 
1090
      pNextRect->x2 = r->x2;
 
1091
      pNextRect->y2 = y2;
 
1092
      pReg->numRects += 1;
 
1093
      pNextRect++;
 
1094
 
 
1095
      g_assert(pReg->numRects<=pReg->size);
 
1096
      r++;
 
1097
    }
 
1098
}
 
1099
 
 
1100
 
 
1101
/*-
 
1102
 *-----------------------------------------------------------------------
 
1103
 * miUnionO --
 
1104
 *      Handle an overlapping band for the union operation. Picks the
 
1105
 *      left-most rectangle each time and merges it into the region.
 
1106
 *
 
1107
 * Results:
 
1108
 *      None.
 
1109
 *
 
1110
 * Side Effects:
 
1111
 *      Rectangles are overwritten in pReg->rects and pReg->numRects will
 
1112
 *      be changed.
 
1113
 *
 
1114
 *-----------------------------------------------------------------------
 
1115
 */
 
1116
 
 
1117
/* static void*/
 
1118
static void
 
1119
miUnionO (GdkRegion *pReg,
 
1120
          GdkRegionBox *r1,
 
1121
          GdkRegionBox *r1End,
 
1122
          GdkRegionBox *r2,
 
1123
          GdkRegionBox *r2End,
 
1124
          gint          y1,
 
1125
          gint          y2)
 
1126
{
 
1127
  GdkRegionBox *        pNextRect;
 
1128
    
 
1129
  pNextRect = &pReg->rects[pReg->numRects];
 
1130
 
 
1131
#define MERGERECT(r)                                    \
 
1132
    if ((pReg->numRects != 0) &&                        \
 
1133
        (pNextRect[-1].y1 == y1) &&                     \
 
1134
        (pNextRect[-1].y2 == y2) &&                     \
 
1135
        (pNextRect[-1].x2 >= r->x1))                    \
 
1136
      {                                                 \
 
1137
        if (pNextRect[-1].x2 < r->x2)                   \
 
1138
          {                                             \
 
1139
            pNextRect[-1].x2 = r->x2;                   \
 
1140
            g_assert(pNextRect[-1].x1<pNextRect[-1].x2);        \
 
1141
          }                                             \
 
1142
      }                                                 \
 
1143
    else                                                \
 
1144
      {                                                 \
 
1145
        MEMCHECK(pReg, pNextRect, pReg->rects);         \
 
1146
        pNextRect->y1 = y1;                             \
 
1147
        pNextRect->y2 = y2;                             \
 
1148
        pNextRect->x1 = r->x1;                          \
 
1149
        pNextRect->x2 = r->x2;                          \
 
1150
        pReg->numRects += 1;                            \
 
1151
        pNextRect += 1;                                 \
 
1152
      }                                                 \
 
1153
    g_assert(pReg->numRects<=pReg->size);                       \
 
1154
    r++;
 
1155
    
 
1156
    g_assert (y1<y2);
 
1157
    while ((r1 != r1End) && (r2 != r2End))
 
1158
    {
 
1159
        if (r1->x1 < r2->x1)
 
1160
        {
 
1161
            MERGERECT(r1);
 
1162
        }
 
1163
        else
 
1164
        {
 
1165
            MERGERECT(r2);
 
1166
        }
 
1167
    }
 
1168
    
 
1169
    if (r1 != r1End)
 
1170
    {
 
1171
        do
 
1172
        {
 
1173
            MERGERECT(r1);
 
1174
        } while (r1 != r1End);
 
1175
    }
 
1176
    else while (r2 != r2End)
 
1177
    {
 
1178
        MERGERECT(r2);
 
1179
    }
 
1180
}
 
1181
 
 
1182
/**
 
1183
 * gdk_region_union:
 
1184
 * @source1:  a #GdkRegion
 
1185
 * @source2: a #GdkRegion 
 
1186
 * 
 
1187
 * Sets the area of @source1 to the union of the areas of @source1 and
 
1188
 * @source2. The resulting area is the set of pixels contained in
 
1189
 * either @source1 or @source2.
 
1190
 **/
 
1191
void
 
1192
gdk_region_union (GdkRegion *source1,
 
1193
                  GdkRegion *source2)
 
1194
{
 
1195
  g_return_if_fail (source1 != NULL);
 
1196
  g_return_if_fail (source2 != NULL);
 
1197
  
 
1198
  /*  checks all the simple cases */
 
1199
 
 
1200
  /*
 
1201
   * source1 and source2 are the same or source2 is empty
 
1202
   */
 
1203
  if ((source1 == source2) || (!(source2->numRects)))
 
1204
    return;
 
1205
 
 
1206
  /* 
 
1207
   * source1 is empty
 
1208
   */
 
1209
  if (!(source1->numRects))
 
1210
    {
 
1211
      miRegionCopy (source1, source2);
 
1212
      return;
 
1213
    }
 
1214
  
 
1215
  /*
 
1216
   * source1 completely subsumes source2
 
1217
   */
 
1218
  if ((source1->numRects == 1) && 
 
1219
      (source1->extents.x1 <= source2->extents.x1) &&
 
1220
      (source1->extents.y1 <= source2->extents.y1) &&
 
1221
      (source1->extents.x2 >= source2->extents.x2) &&
 
1222
      (source1->extents.y2 >= source2->extents.y2))
 
1223
    return;
 
1224
 
 
1225
  /*
 
1226
   * source2 completely subsumes source1
 
1227
   */
 
1228
  if ((source2->numRects == 1) && 
 
1229
      (source2->extents.x1 <= source1->extents.x1) &&
 
1230
      (source2->extents.y1 <= source1->extents.y1) &&
 
1231
      (source2->extents.x2 >= source1->extents.x2) &&
 
1232
      (source2->extents.y2 >= source1->extents.y2))
 
1233
    {
 
1234
      miRegionCopy(source1, source2);
 
1235
      return;
 
1236
    }
 
1237
 
 
1238
  miRegionOp (source1, source1, source2, miUnionO, 
 
1239
              miUnionNonO, miUnionNonO);
 
1240
 
 
1241
  source1->extents.x1 = MIN (source1->extents.x1, source2->extents.x1);
 
1242
  source1->extents.y1 = MIN (source1->extents.y1, source2->extents.y1);
 
1243
  source1->extents.x2 = MAX (source1->extents.x2, source2->extents.x2);
 
1244
  source1->extents.y2 = MAX (source1->extents.y2, source2->extents.y2);
 
1245
}
 
1246
 
 
1247
 
 
1248
/*======================================================================
 
1249
 *                Region Subtraction
 
1250
 *====================================================================*/
 
1251
 
 
1252
/*-
 
1253
 *-----------------------------------------------------------------------
 
1254
 * miSubtractNonO --
 
1255
 *      Deal with non-overlapping band for subtraction. Any parts from
 
1256
 *      region 2 we discard. Anything from region 1 we add to the region.
 
1257
 *
 
1258
 * Results:
 
1259
 *      None.
 
1260
 *
 
1261
 * Side Effects:
 
1262
 *      pReg may be affected.
 
1263
 *
 
1264
 *-----------------------------------------------------------------------
 
1265
 */
 
1266
/* static void*/
 
1267
static void
 
1268
miSubtractNonO1 (GdkRegion    *pReg,
 
1269
                 GdkRegionBox *r,
 
1270
                 GdkRegionBox *rEnd,
 
1271
                 gint          y1,
 
1272
                 gint          y2)
 
1273
{
 
1274
  GdkRegionBox *        pNextRect;
 
1275
        
 
1276
  pNextRect = &pReg->rects[pReg->numRects];
 
1277
        
 
1278
  g_assert(y1<y2);
 
1279
 
 
1280
  while (r != rEnd)
 
1281
    {
 
1282
      g_assert (r->x1<r->x2);
 
1283
      MEMCHECK (pReg, pNextRect, pReg->rects);
 
1284
      pNextRect->x1 = r->x1;
 
1285
      pNextRect->y1 = y1;
 
1286
      pNextRect->x2 = r->x2;
 
1287
      pNextRect->y2 = y2;
 
1288
      pReg->numRects += 1;
 
1289
      pNextRect++;
 
1290
 
 
1291
      g_assert (pReg->numRects <= pReg->size);
 
1292
 
 
1293
      r++;
 
1294
    }
 
1295
}
 
1296
 
 
1297
/*-
 
1298
 *-----------------------------------------------------------------------
 
1299
 * miSubtractO --
 
1300
 *      Overlapping band subtraction. x1 is the left-most point not yet
 
1301
 *      checked.
 
1302
 *
 
1303
 * Results:
 
1304
 *      None.
 
1305
 *
 
1306
 * Side Effects:
 
1307
 *      pReg may have rectangles added to it.
 
1308
 *
 
1309
 *-----------------------------------------------------------------------
 
1310
 */
 
1311
/* static void*/
 
1312
static void
 
1313
miSubtractO (GdkRegion    *pReg,
 
1314
             GdkRegionBox *r1,
 
1315
             GdkRegionBox *r1End,
 
1316
             GdkRegionBox *r2,
 
1317
             GdkRegionBox *r2End,
 
1318
             gint          y1,
 
1319
             gint          y2)
 
1320
{
 
1321
  GdkRegionBox *        pNextRect;
 
1322
  int   x1;
 
1323
    
 
1324
  x1 = r1->x1;
 
1325
    
 
1326
  g_assert(y1<y2);
 
1327
  pNextRect = &pReg->rects[pReg->numRects];
 
1328
 
 
1329
  while ((r1 != r1End) && (r2 != r2End))
 
1330
    {
 
1331
      if (r2->x2 <= x1)
 
1332
        {
 
1333
          /*
 
1334
           * Subtrahend missed the boat: go to next subtrahend.
 
1335
           */
 
1336
          r2++;
 
1337
        }
 
1338
      else if (r2->x1 <= x1)
 
1339
        {
 
1340
          /*
 
1341
           * Subtrahend preceeds minuend: nuke left edge of minuend.
 
1342
           */
 
1343
          x1 = r2->x2;
 
1344
          if (x1 >= r1->x2)
 
1345
            {
 
1346
              /*
 
1347
               * Minuend completely covered: advance to next minuend and
 
1348
               * reset left fence to edge of new minuend.
 
1349
               */
 
1350
              r1++;
 
1351
              if (r1 != r1End)
 
1352
                x1 = r1->x1;
 
1353
            }
 
1354
          else
 
1355
            {
 
1356
              /*
 
1357
               * Subtrahend now used up since it doesn't extend beyond
 
1358
               * minuend
 
1359
               */
 
1360
              r2++;
 
1361
            }
 
1362
        }
 
1363
      else if (r2->x1 < r1->x2)
 
1364
        {
 
1365
          /*
 
1366
           * Left part of subtrahend covers part of minuend: add uncovered
 
1367
           * part of minuend to region and skip to next subtrahend.
 
1368
           */
 
1369
          g_assert(x1<r2->x1);
 
1370
          MEMCHECK(pReg, pNextRect, pReg->rects);
 
1371
          pNextRect->x1 = x1;
 
1372
          pNextRect->y1 = y1;
 
1373
          pNextRect->x2 = r2->x1;
 
1374
          pNextRect->y2 = y2;
 
1375
          pReg->numRects += 1;
 
1376
          pNextRect++;
 
1377
 
 
1378
          g_assert(pReg->numRects<=pReg->size);
 
1379
 
 
1380
          x1 = r2->x2;
 
1381
          if (x1 >= r1->x2)
 
1382
            {
 
1383
              /*
 
1384
               * Minuend used up: advance to new...
 
1385
               */
 
1386
              r1++;
 
1387
              if (r1 != r1End)
 
1388
                x1 = r1->x1;
 
1389
            }
 
1390
          else
 
1391
            {
 
1392
              /*
 
1393
               * Subtrahend used up
 
1394
               */
 
1395
              r2++;
 
1396
            }
 
1397
        }
 
1398
      else
 
1399
        {
 
1400
          /*
 
1401
           * Minuend used up: add any remaining piece before advancing.
 
1402
           */
 
1403
          if (r1->x2 > x1)
 
1404
            {
 
1405
              MEMCHECK(pReg, pNextRect, pReg->rects);
 
1406
              pNextRect->x1 = x1;
 
1407
              pNextRect->y1 = y1;
 
1408
              pNextRect->x2 = r1->x2;
 
1409
              pNextRect->y2 = y2;
 
1410
              pReg->numRects += 1;
 
1411
              pNextRect++;
 
1412
              g_assert(pReg->numRects<=pReg->size);
 
1413
            }
 
1414
          r1++;
 
1415
          if (r1 != r1End)
 
1416
            x1 = r1->x1;
 
1417
        }
 
1418
    }
 
1419
 
 
1420
  /*
 
1421
     * Add remaining minuend rectangles to region.
 
1422
     */
 
1423
  while (r1 != r1End)
 
1424
    {
 
1425
      g_assert(x1<r1->x2);
 
1426
      MEMCHECK(pReg, pNextRect, pReg->rects);
 
1427
      pNextRect->x1 = x1;
 
1428
      pNextRect->y1 = y1;
 
1429
      pNextRect->x2 = r1->x2;
 
1430
      pNextRect->y2 = y2;
 
1431
      pReg->numRects += 1;
 
1432
      pNextRect++;
 
1433
 
 
1434
      g_assert(pReg->numRects<=pReg->size);
 
1435
 
 
1436
      r1++;
 
1437
      if (r1 != r1End)
 
1438
        {
 
1439
          x1 = r1->x1;
 
1440
        }
 
1441
    }
 
1442
}
 
1443
 
 
1444
/**
 
1445
 * gdk_region_subtract:
 
1446
 * @source1: a #GdkRegion
 
1447
 * @source2: another #GdkRegion
 
1448
 *
 
1449
 * Subtracts the area of @source2 from the area @source1. The resulting
 
1450
 * area is the set of pixels contained in @source1 but not in @source2.
 
1451
 **/
 
1452
void
 
1453
gdk_region_subtract (GdkRegion *source1,
 
1454
                     GdkRegion *source2)
 
1455
{
 
1456
  g_return_if_fail (source1 != NULL);
 
1457
  g_return_if_fail (source2 != NULL);
 
1458
  
 
1459
  /* check for trivial reject */
 
1460
  if ((!(source1->numRects)) || (!(source2->numRects)) ||
 
1461
      (!EXTENTCHECK(&source1->extents, &source2->extents)))
 
1462
    return;
 
1463
 
 
1464
  miRegionOp (source1, source1, source2, miSubtractO,
 
1465
              miSubtractNonO1, (nonOverlapFunc) NULL);
 
1466
 
 
1467
  /*
 
1468
   * Can't alter source1's extents before we call miRegionOp because miRegionOp
 
1469
   * depends on the extents of those regions being the unaltered. Besides, this
 
1470
   * way there's no checking against rectangles that will be nuked
 
1471
   * due to coalescing, so we have to examine fewer rectangles.
 
1472
   */
 
1473
  miSetExtents (source1);
 
1474
}
 
1475
 
 
1476
/**
 
1477
 * gdk_region_xor:
 
1478
 * @source1: a #GdkRegion
 
1479
 * @source2: another #GdkRegion
 
1480
 *
 
1481
 * Sets the area of @source1 to the exclusive-OR of the areas of @source1
 
1482
 * and @source2. The resulting area is the set of pixels contained in one
 
1483
 * or the other of the two sources but not in both.
 
1484
 **/
 
1485
void
 
1486
gdk_region_xor (GdkRegion *source1,
 
1487
                GdkRegion *source2)
 
1488
{
 
1489
  GdkRegion *trb;
 
1490
 
 
1491
  g_return_if_fail (source1 != NULL);
 
1492
  g_return_if_fail (source2 != NULL);
 
1493
 
 
1494
  trb = gdk_region_copy (source2);
 
1495
 
 
1496
  gdk_region_subtract (trb, source1);
 
1497
  gdk_region_subtract (source1, source2);
 
1498
 
 
1499
  gdk_region_union (source1, trb);
 
1500
  
 
1501
  gdk_region_destroy (trb);
 
1502
}
 
1503
 
 
1504
/**
 
1505
 * gdk_region_empty: 
 
1506
 * @region: a #GdkRegion
 
1507
 *
 
1508
 * Finds out if the #GdkRegion is empty.
 
1509
 *
 
1510
 * Returns: %TRUE if @region is empty.
 
1511
 */
 
1512
gboolean
 
1513
gdk_region_empty (GdkRegion *region)
 
1514
{
 
1515
  g_return_val_if_fail (region != NULL, FALSE);
 
1516
  
 
1517
  if (region->numRects == 0)
 
1518
    return TRUE;
 
1519
  else
 
1520
    return FALSE;
 
1521
}
 
1522
 
 
1523
/**
 
1524
 * gdk_region_equal:
 
1525
 * @region1: a #GdkRegion
 
1526
 * @region2: a #GdkRegion
 
1527
 *
 
1528
 * Finds out if the two regions are the same.
 
1529
 *
 
1530
 * Returns: %TRUE if @region1 and @region2 are equal.
 
1531
 */
 
1532
gboolean
 
1533
gdk_region_equal (GdkRegion *region1,
 
1534
                  GdkRegion *region2)
 
1535
{
 
1536
  int i;
 
1537
 
 
1538
  g_return_val_if_fail (region1 != NULL, FALSE);
 
1539
  g_return_val_if_fail (region2 != NULL, FALSE);
 
1540
 
 
1541
  if (region1->numRects != region2->numRects) return FALSE;
 
1542
  else if (region1->numRects == 0) return TRUE;
 
1543
  else if (region1->extents.x1 != region2->extents.x1) return FALSE;
 
1544
  else if (region1->extents.x2 != region2->extents.x2) return FALSE;
 
1545
  else if (region1->extents.y1 != region2->extents.y1) return FALSE;
 
1546
  else if (region1->extents.y2 != region2->extents.y2) return FALSE;
 
1547
  else
 
1548
    for(i = 0; i < region1->numRects; i++ )
 
1549
      {
 
1550
        if (region1->rects[i].x1 != region2->rects[i].x1) return FALSE;
 
1551
        else if (region1->rects[i].x2 != region2->rects[i].x2) return FALSE;
 
1552
        else if (region1->rects[i].y1 != region2->rects[i].y1) return FALSE;
 
1553
        else if (region1->rects[i].y2 != region2->rects[i].y2) return FALSE;
 
1554
      }
 
1555
  return TRUE;
 
1556
}
 
1557
 
 
1558
/**
 
1559
 * gdk_region_point_in:
 
1560
 * @region: a #GdkRegion
 
1561
 * @x: the x coordinate of a point
 
1562
 * @y: the y coordinate of a point
 
1563
 *
 
1564
 * Finds out if a point is in a region.
 
1565
 *
 
1566
 * Returns: %TRUE if the point is in @region.
 
1567
 */
 
1568
gboolean
 
1569
gdk_region_point_in (GdkRegion *region,
 
1570
                     int        x,
 
1571
                     int        y)
 
1572
{
 
1573
  int i;
 
1574
 
 
1575
  g_return_val_if_fail (region != NULL, FALSE);
 
1576
 
 
1577
  if (region->numRects == 0)
 
1578
    return FALSE;
 
1579
  if (!INBOX(region->extents, x, y))
 
1580
    return FALSE;
 
1581
  for (i = 0; i < region->numRects; i++)
 
1582
    {
 
1583
      if (INBOX (region->rects[i], x, y))
 
1584
        return TRUE;
 
1585
    }
 
1586
  return FALSE;
 
1587
}
 
1588
 
 
1589
/**
 
1590
 * gdk_region_rect_in: 
 
1591
 * @region: a #GdkRegion.
 
1592
 * @rectangle: a #GdkRectangle.
 
1593
 *
 
1594
 * Tests whether a rectangle is within a region.
 
1595
 *
 
1596
 * Returns: %GDK_OVERLAP_RECTANGLE_IN, %GDK_OVERLAP_RECTANGLE_OUT, or
 
1597
 *   %GDK_OVERLAP_RECTANGLE_PART, depending on whether the rectangle is inside,
 
1598
 *   outside, or partly inside the #GdkRegion, respectively.
 
1599
 */
 
1600
GdkOverlapType
 
1601
gdk_region_rect_in (GdkRegion    *region,
 
1602
                    GdkRectangle *rectangle)
 
1603
{
 
1604
  GdkRegionBox *pbox;
 
1605
  GdkRegionBox *pboxEnd;
 
1606
  GdkRegionBox  rect;
 
1607
  GdkRegionBox *prect = &rect;
 
1608
  gboolean      partIn, partOut;
 
1609
  gint rx, ry;
 
1610
 
 
1611
  g_return_val_if_fail (region != NULL, GDK_OVERLAP_RECTANGLE_OUT);
 
1612
  g_return_val_if_fail (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
 
1613
 
 
1614
  rx = rectangle->x;
 
1615
  ry = rectangle->y;
 
1616
  
 
1617
  prect->x1 = rx;
 
1618
  prect->y1 = ry;
 
1619
  prect->x2 = rx + rectangle->width;
 
1620
  prect->y2 = ry + rectangle->height;
 
1621
    
 
1622
    /* this is (just) a useful optimization */
 
1623
  if ((region->numRects == 0) || !EXTENTCHECK (&region->extents, prect))
 
1624
    return GDK_OVERLAP_RECTANGLE_OUT;
 
1625
 
 
1626
  partOut = FALSE;
 
1627
  partIn = FALSE;
 
1628
 
 
1629
    /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */
 
1630
  for (pbox = region->rects, pboxEnd = pbox + region->numRects;
 
1631
       pbox < pboxEnd;
 
1632
       pbox++)
 
1633
    {
 
1634
 
 
1635
      if (pbox->y2 <= ry)
 
1636
        continue;       /* getting up to speed or skipping remainder of band */
 
1637
 
 
1638
      if (pbox->y1 > ry)
 
1639
        {
 
1640
          partOut = TRUE;       /* missed part of rectangle above */
 
1641
          if (partIn || (pbox->y1 >= prect->y2))
 
1642
            break;
 
1643
          ry = pbox->y1;        /* x guaranteed to be == prect->x1 */
 
1644
        }
 
1645
 
 
1646
      if (pbox->x2 <= rx)
 
1647
        continue;               /* not far enough over yet */
 
1648
 
 
1649
      if (pbox->x1 > rx)
 
1650
        {
 
1651
          partOut = TRUE;       /* missed part of rectangle to left */
 
1652
          if (partIn)
 
1653
            break;
 
1654
        }
 
1655
 
 
1656
      if (pbox->x1 < prect->x2)
 
1657
        {
 
1658
          partIn = TRUE;        /* definitely overlap */
 
1659
          if (partOut)
 
1660
            break;
 
1661
        }
 
1662
 
 
1663
      if (pbox->x2 >= prect->x2)
 
1664
        {
 
1665
          ry = pbox->y2;        /* finished with this band */
 
1666
          if (ry >= prect->y2)
 
1667
            break;
 
1668
          rx = prect->x1;       /* reset x out to left again */
 
1669
        }
 
1670
      else
 
1671
        {
 
1672
          /*
 
1673
           * Because boxes in a band are maximal width, if the first box
 
1674
           * to overlap the rectangle doesn't completely cover it in that
 
1675
           * band, the rectangle must be partially out, since some of it
 
1676
           * will be uncovered in that band. partIn will have been set true
 
1677
           * by now...
 
1678
           */
 
1679
          break;
 
1680
        }
 
1681
 
 
1682
    }
 
1683
 
 
1684
  return (partIn ?
 
1685
             ((ry < prect->y2) ?
 
1686
              GDK_OVERLAP_RECTANGLE_PART : GDK_OVERLAP_RECTANGLE_IN) : 
 
1687
          GDK_OVERLAP_RECTANGLE_OUT);
 
1688
}
 
1689
 
 
1690
 
 
1691
static void
 
1692
gdk_region_unsorted_spans_intersect_foreach (GdkRegion *region,
 
1693
                                             GdkSpan   *spans,
 
1694
                                             int        n_spans,
 
1695
                                             GdkSpanFunc function,
 
1696
                                             gpointer data)
 
1697
{
 
1698
  gint i, left, right, y;
 
1699
  gint clipped_left, clipped_right;
 
1700
  GdkRegionBox *pbox;
 
1701
  GdkRegionBox *pboxEnd;
 
1702
  GdkSpan out_span;
 
1703
 
 
1704
  if (!region->numRects)
 
1705
    return;
 
1706
 
 
1707
  for (i=0;i<n_spans;i++)
 
1708
    {
 
1709
      y = spans[i].y;
 
1710
      left = spans[i].x;
 
1711
      right = left + spans[i].width; /* right is not in the span! */
 
1712
    
 
1713
      if (! ((region->extents.y1 <= y) &&
 
1714
             (region->extents.y2 > y) &&
 
1715
             (region->extents.x1 < right) &&
 
1716
             (region->extents.x2 > left)) ) 
 
1717
        continue;
 
1718
 
 
1719
      /* can stop when we passed y */
 
1720
      for (pbox = region->rects, pboxEnd = pbox + region->numRects;
 
1721
           pbox < pboxEnd;
 
1722
           pbox++)
 
1723
        {
 
1724
          if (pbox->y2 <= y)
 
1725
            continue; /* Not quite there yet */
 
1726
          
 
1727
          if (pbox->y1 > y)
 
1728
            break; /* passed the spanline */
 
1729
          
 
1730
          if ((right > pbox->x1) && (left < pbox->x2)) 
 
1731
            {
 
1732
              clipped_left = MAX (left, pbox->x1);
 
1733
              clipped_right = MIN (right, pbox->x2);
 
1734
              
 
1735
              out_span.y = y;
 
1736
              out_span.x = clipped_left;
 
1737
              out_span.width = clipped_right - clipped_left;
 
1738
              (*function) (&out_span, data);
 
1739
            }
 
1740
        }
 
1741
    }
 
1742
}
 
1743
 
 
1744
/**
 
1745
 * gdk_region_spans_intersect_foreach:
 
1746
 * @region: a #GdkRegion
 
1747
 * @spans: an array of #GdkSpans
 
1748
 * @n_spans: the length of @spans
 
1749
 * @sorted: %TRUE if @spans is sorted wrt. the y coordinate
 
1750
 * @function: function to call on each span in the intersection
 
1751
 * @data: data to pass to @function
 
1752
 *
 
1753
 * Calls a function on each span in the intersection of @region and @spans.
 
1754
 */
 
1755
void
 
1756
gdk_region_spans_intersect_foreach (GdkRegion  *region,
 
1757
                                    GdkSpan    *spans,
 
1758
                                    int         n_spans,
 
1759
                                    gboolean    sorted,
 
1760
                                    GdkSpanFunc function,
 
1761
                                    gpointer    data)
 
1762
{
 
1763
  gint left, right, y;
 
1764
  gint clipped_left, clipped_right;
 
1765
  GdkRegionBox *pbox;
 
1766
  GdkRegionBox *pboxEnd;
 
1767
  GdkSpan *span, *tmpspan;
 
1768
  GdkSpan *end_span;
 
1769
  GdkSpan out_span;
 
1770
 
 
1771
  g_return_if_fail (region != NULL);
 
1772
  g_return_if_fail (spans != NULL);
 
1773
 
 
1774
  if (!sorted)
 
1775
    {
 
1776
      gdk_region_unsorted_spans_intersect_foreach (region,
 
1777
                                                   spans,
 
1778
                                                   n_spans,
 
1779
                                                   function,
 
1780
                                                   data);
 
1781
      return;
 
1782
    }
 
1783
  
 
1784
  if ((!region->numRects) || (n_spans == 0))
 
1785
    return;
 
1786
 
 
1787
  /* The main method here is to step along the
 
1788
   * sorted rectangles and spans in lock step, and
 
1789
   * clipping the spans that are in the current
 
1790
   * rectangle before going on to the next rectangle.
 
1791
   */
 
1792
 
 
1793
  span = spans;
 
1794
  end_span = spans + n_spans;
 
1795
  pbox = region->rects;
 
1796
  pboxEnd = pbox + region->numRects;
 
1797
  while (pbox < pboxEnd)
 
1798
    {
 
1799
      while ((pbox->y2 < span->y) || (span->y < pbox->y1))
 
1800
        {
 
1801
          /* Skip any rectangles that are above the current span */
 
1802
          if (pbox->y2 < span->y)
 
1803
            {
 
1804
              pbox++;
 
1805
              if (pbox == pboxEnd)
 
1806
                return;
 
1807
            }
 
1808
          /* Skip any spans that are above the current rectangle */
 
1809
          if (span->y < pbox->y1)
 
1810
            {
 
1811
              span++;
 
1812
              if (span == end_span)
 
1813
                return;
 
1814
            }
 
1815
        }
 
1816
      
 
1817
      /* Ok, we got at least one span that might intersect this rectangle. */
 
1818
      tmpspan = span;
 
1819
      while ((tmpspan < end_span) &&
 
1820
             (tmpspan->y < pbox->y2))
 
1821
        {
 
1822
          y = tmpspan->y;
 
1823
          left = tmpspan->x;
 
1824
          right = left + tmpspan->width; /* right is not in the span! */
 
1825
          
 
1826
          if ((right > pbox->x1) && (left < pbox->x2))
 
1827
            {
 
1828
              clipped_left = MAX (left, pbox->x1);
 
1829
              clipped_right = MIN (right, pbox->x2);
 
1830
              
 
1831
              out_span.y = y;
 
1832
              out_span.x = clipped_left;
 
1833
              out_span.width = clipped_right - clipped_left;
 
1834
              (*function) (&out_span, data);
 
1835
            }
 
1836
          
 
1837
          tmpspan++;
 
1838
        }
 
1839
 
 
1840
      /* Finished this rectangle.
 
1841
       * The spans could still intersect the next one
 
1842
       */
 
1843
      pbox++;
 
1844
    }
 
1845
}
 
1846
 
 
1847
#define __GDK_REGION_GENERIC_C__
 
1848
#include "gdkaliasdef.c"