gdkregion-generic.c 12.7 KB
Newer Older
1
#include "config.h"
Elliot Lee's avatar
Elliot Lee committed
2 3
#include <stdlib.h>
#include <string.h>
4
#include <gdkregion.h>
5
#include "gdkalias.h"
6

7 8 9 10 11 12 13
/**
 * gdk_region_new:
 *
 * Creates a new empty #GdkRegion.
 *
 * Returns: a new empty #GdkRegion
 */
14
GdkRegion *
Matthias Clasen's avatar
Matthias Clasen committed
15
gdk_region_new (void)
16
{
17
  return cairo_region_create ();
18 19
}

Havoc Pennington's avatar
Havoc Pennington committed
20 21 22 23 24 25 26 27
/**
 * gdk_region_rectangle:
 * @rectangle: a #GdkRectangle
 * 
 * Creates a new region containing the area @rectangle.
 * 
 * Return value: a new region
 **/
28
GdkRegion *
29
gdk_region_rectangle (const GdkRectangle *rectangle)
30
{
31 32
  g_return_val_if_fail (rectangle != NULL, NULL);

33 34 35
  if (rectangle->width <= 0 || rectangle->height <= 0)
    return gdk_region_new();

36
  return cairo_region_create_rectangle (rectangle);
37 38
}

Havoc Pennington's avatar
Havoc Pennington committed
39 40 41 42 43 44 45 46
/**
 * gdk_region_copy:
 * @region: a #GdkRegion
 * 
 * Copies @region, creating an identical new region.
 * 
 * Return value: a new region identical to @region
 **/
47
GdkRegion *
48
gdk_region_copy (const GdkRegion *region)
49
{
50
  return cairo_region_copy (region);
51 52
}

53 54 55 56 57
/**
 * gdk_region_get_clipbox:
 * @region: a #GdkRegion
 * @rectangle: return location for the clipbox
 *
Matthias Clasen's avatar
Matthias Clasen committed
58 59
 * Obtains the smallest rectangle which includes the entire #GdkRegion.
 *
60
 */
61
void
62 63
gdk_region_get_clipbox (const GdkRegion *region,
			GdkRectangle    *rectangle)
64
{
65 66
  g_return_if_fail (region != NULL);
  g_return_if_fail (rectangle != NULL);
67
  
68
  cairo_region_get_extents (region, rectangle);
69 70
}

71 72 73 74

/**
 * gdk_region_get_rectangles:
 * @region: a #GdkRegion
75
 * @rectangles: (array length=n_rectangles) (transfer container): return location for an array of rectangles
76 77 78 79 80 81
 * @n_rectangles: length of returned array
 *
 * Obtains the area covered by the region as a list of rectangles.
 * The array returned in @rectangles must be freed with g_free().
 **/
void
82 83 84
gdk_region_get_rectangles (const GdkRegion  *region,
                           GdkRectangle    **rectangles,
                           gint             *n_rectangles)
85
{
86 87
  gint i, n;
  GdkRectangle *rects;
88 89 90 91 92
  
  g_return_if_fail (region != NULL);
  g_return_if_fail (rectangles != NULL);
  g_return_if_fail (n_rectangles != NULL);
  
93 94
  n = cairo_region_num_rectangles (region);
  rects = g_new (GdkRectangle, n);
95

96
  for (i = 0; i < n; i++)
97
    {
98
      cairo_region_get_rectangle (region, i, &rects[i]);
99
    }
100 101 102

  *n_rectangles = n;
  *rectangles = rects;
103 104
}

105 106 107 108 109 110 111 112 113
/**
 * gdk_region_union_with_rect:
 * @region: a #GdkRegion.
 * @rect: a #GdkRectangle.
 * 
 * Sets the area of @region to the union of the areas of @region and
 * @rect. The resulting area is the set of pixels contained in
 * either @region or @rect.
 **/
114
void
115 116
gdk_region_union_with_rect (GdkRegion          *region,
			    const GdkRectangle *rect)
117
{
118 119 120
  g_return_if_fail (region != NULL);
  g_return_if_fail (rect != NULL);

121
  if (rect->width <= 0 || rect->height <= 0)
122 123
    return;
    
124
  cairo_region_union_rectangle (region, rect);
125 126
}

127 128 129 130 131 132
/**
 * gdk_region_destroy:
 * @region: a #GdkRegion
 *
 * Destroys a #GdkRegion.
 */
133
void
134
gdk_region_destroy (GdkRegion *region)
135
{
136
  g_return_if_fail (region != NULL);
137

138
  cairo_region_destroy (region);
139
}
140 141


142 143 144 145 146 147 148 149
/**
 * gdk_region_offset:
 * @region: a #GdkRegion
 * @dx: the distance to move the region horizontally
 * @dy: the distance to move the region vertically
 *
 * Moves a region the specified distance.
 */
150 151 152 153 154
void
gdk_region_offset (GdkRegion *region,
		   gint       x,
		   gint       y)
{
155
  cairo_region_translate (region, x, y);
156 157
}

158 159 160 161 162 163 164 165 166
/**
 * gdk_region_shrink:
 * @region: a #GdkRegion
 * @dx: the number of pixels to shrink the region horizontally
 * @dy: the number of pixels to shrink the region vertically
 *
 * Resizes a region by the specified amount.
 * Positive values shrink the region. Negative values expand it.
 */
167
void
168
gdk_region_shrink (GdkRegion *region,
169 170 171
		   int        dx,
		   int        dy)
{
172 173
  GdkRectangle *rects;
  int i, n_rects;
174

175 176 177
  gdk_region_get_rectangles (region, &rects, &n_rects);
  /* clear region */
  gdk_region_subtract (region, region);
178

179
  for (i = 0; i < n_rects; i++)
180
    {
181 182 183 184 185 186 187 188 189
      if (rects[i].width <= 2 * dx ||
          rects[i].height <= 2 * dy)
        continue;

      rects[i].x += dx;
      rects[i].y += dy;
      rects[i].width -= 2 * dx;
      rects[i].height -= 2 * dy;
      cairo_region_union_rectangle (region, &rects[i]);
190 191 192
    }
}

Havoc Pennington's avatar
Havoc Pennington committed
193 194 195 196 197
/**
 * gdk_region_intersect:
 * @source1: a #GdkRegion
 * @source2: another #GdkRegion
 *
198 199 200
 * Sets the area of @source1 to the intersection of the areas of @source1
 * and @source2. The resulting area is the set of pixels contained in
 * both @source1 and @source2.
Havoc Pennington's avatar
Havoc Pennington committed
201
 **/
202
void
203 204
gdk_region_intersect (GdkRegion       *source1,
		      const GdkRegion *source2)
205
{
206 207
  g_return_if_fail (source1 != NULL);
  g_return_if_fail (source2 != NULL);
208
  
209
  cairo_region_intersect (source1, (cairo_region_t *) source2);
210 211
}

212 213 214 215 216 217 218 219 220
/**
 * gdk_region_union:
 * @source1:  a #GdkRegion
 * @source2: a #GdkRegion 
 * 
 * Sets the area of @source1 to the union of the areas of @source1 and
 * @source2. The resulting area is the set of pixels contained in
 * either @source1 or @source2.
 **/
221
void
222 223
gdk_region_union (GdkRegion       *source1,
		  const GdkRegion *source2)
224
{
225 226
  g_return_if_fail (source1 != NULL);
  g_return_if_fail (source2 != NULL);
227
  
228
  cairo_region_union (source1, (cairo_region_t *) source2);
229 230
}

Havoc Pennington's avatar
Havoc Pennington committed
231 232 233 234 235
/**
 * gdk_region_subtract:
 * @source1: a #GdkRegion
 * @source2: another #GdkRegion
 *
236 237
 * Subtracts the area of @source2 from the area @source1. The resulting
 * area is the set of pixels contained in @source1 but not in @source2.
Havoc Pennington's avatar
Havoc Pennington committed
238
 **/
239
void
240 241
gdk_region_subtract (GdkRegion       *source1,
		     const GdkRegion *source2)
242
{
243 244
  g_return_if_fail (source1 != NULL);
  g_return_if_fail (source2 != NULL);
245
  
246
  cairo_region_subtract (source1, source2);
247 248
}

Havoc Pennington's avatar
Havoc Pennington committed
249 250 251 252 253
/**
 * gdk_region_xor:
 * @source1: a #GdkRegion
 * @source2: another #GdkRegion
 *
254 255 256
 * Sets the area of @source1 to the exclusive-OR of the areas of @source1
 * and @source2. The resulting area is the set of pixels contained in one
 * or the other of the two sources but not in both.
Havoc Pennington's avatar
Havoc Pennington committed
257
 **/
258
void
259 260
gdk_region_xor (GdkRegion       *source1,
		const GdkRegion *source2)
261 262 263
{
  GdkRegion *trb;

264 265
  g_return_if_fail (source1 != NULL);
  g_return_if_fail (source2 != NULL);
266

267
  trb = gdk_region_copy (source2);
268

269 270
  gdk_region_subtract (trb, source1);
  gdk_region_subtract (source1, source2);
271

272
  gdk_region_union (source1, trb);
273 274 275 276
  
  gdk_region_destroy (trb);
}

277 278 279 280
/**
 * gdk_region_empty: 
 * @region: a #GdkRegion
 *
Matthias Clasen's avatar
Matthias Clasen committed
281
 * Finds out if the #GdkRegion is empty.
282 283
 *
 * Returns: %TRUE if @region is empty.
284 285
 */
gboolean
286
gdk_region_empty (const GdkRegion *region)
287
{
288
  g_return_val_if_fail (region != NULL, FALSE);
289
  
290
  return cairo_region_is_empty (region);
291 292
}

293 294 295 296 297
/**
 * gdk_region_equal:
 * @region1: a #GdkRegion
 * @region2: a #GdkRegion
 *
Matthias Clasen's avatar
Matthias Clasen committed
298
 * Finds out if the two regions are the same.
299 300
 *
 * Returns: %TRUE if @region1 and @region2 are equal.
301 302
 */
gboolean
303 304
gdk_region_equal (const GdkRegion *region1,
		  const GdkRegion *region2)
305
{
306 307
  g_return_val_if_fail (region1 != NULL, FALSE);
  g_return_val_if_fail (region2 != NULL, FALSE);
308

309
  return cairo_region_equal (region1, region2);
310 311
}

312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
/**
 * gdk_region_rect_equal:
 * @region: a #GdkRegion
 * @rectangle: a #GdkRectangle
 *
 * Finds out if a regions is the same as a rectangle.
 *
 * Returns: %TRUE if @region and @rectangle are equal.
 *
 * Since: 2.18
 */
gboolean
gdk_region_rect_equal (const GdkRegion    *region,
		       const GdkRectangle *rectangle)
{
327 328
  cairo_rectangle_int_t extents;

329 330 331
  g_return_val_if_fail (region != NULL, FALSE);
  g_return_val_if_fail (rectangle != NULL, FALSE);

332 333 334 335 336 337
  if (cairo_region_num_rectangles (region) != 1) return FALSE;
  cairo_region_get_extents (region, &extents);
  if (extents.x != rectangle->x) return FALSE;
  else if (extents.y != rectangle->y) return FALSE;
  else if (extents.width != rectangle->width) return FALSE;
  else if (extents.height != rectangle->height) return FALSE;
338 339 340
  return TRUE;
}

341 342 343 344 345 346
/**
 * gdk_region_point_in:
 * @region: a #GdkRegion
 * @x: the x coordinate of a point
 * @y: the y coordinate of a point
 *
Matthias Clasen's avatar
Matthias Clasen committed
347
 * Finds out if a point is in a region.
348 349 350
 *
 * Returns: %TRUE if the point is in @region.
 */
351
gboolean
352 353 354
gdk_region_point_in (const GdkRegion *region,
		     int              x,
		     int              y)
355
{
356 357
  g_return_val_if_fail (region != NULL, FALSE);

358
  return cairo_region_contains_point (region, x, y);
359 360
}

361 362 363 364 365 366 367 368 369 370 371
/**
 * gdk_region_rect_in: 
 * @region: a #GdkRegion.
 * @rectangle: a #GdkRectangle.
 *
 * Tests whether a rectangle is within a region.
 *
 * Returns: %GDK_OVERLAP_RECTANGLE_IN, %GDK_OVERLAP_RECTANGLE_OUT, or
 *   %GDK_OVERLAP_RECTANGLE_PART, depending on whether the rectangle is inside,
 *   outside, or partly inside the #GdkRegion, respectively.
 */
372
GdkOverlapType
373 374
gdk_region_rect_in (const GdkRegion    *region,
		    const GdkRectangle *rectangle)
375
{
376 377
  g_return_val_if_fail (region != NULL, GDK_OVERLAP_RECTANGLE_OUT);
  g_return_val_if_fail (rectangle != NULL, GDK_OVERLAP_RECTANGLE_OUT);
378

379
  return cairo_region_contains_rectangle (region, rectangle);
380
}
381 382 383


static void
384 385 386 387 388
gdk_region_unsorted_spans_intersect_foreach (GdkRegion     *region,
					     const GdkSpan *spans,
					     int            n_spans,
					     GdkSpanFunc    function,
					     gpointer       data)
389
{
390
  gint i, j, left, right, y, n_rects;
391
  gint clipped_left, clipped_right;
392
  cairo_rectangle_int_t pbox, extents;
393

394 395
  n_rects = cairo_region_num_rectangles (region);
  if (!n_rects)
396 397
    return;

398
  cairo_region_get_extents (region, &extents);
399 400 401 402 403 404
  for (i=0;i<n_spans;i++)
    {
      y = spans[i].y;
      left = spans[i].x;
      right = left + spans[i].width; /* right is not in the span! */
    
405 406 407 408
      if (! ((extents.y <= y) &&
	     (extents.y + extents.height > y) &&
	     (extents.x < right) &&
	     (extents.x + extents.width > left)) ) 
409 410 411
	continue;

      /* can stop when we passed y */
412
      for (j = 0; j < n_rects; j++)
413
	{
414 415 416
          cairo_region_get_rectangle (region, j, &pbox);

	  if (pbox.y + pbox.height <= y)
417 418
	    continue; /* Not quite there yet */
	  
419
	  if (pbox.y > y)
420 421
	    break; /* passed the spanline */
	  
422
	  if ((right > pbox.x) && (left < pbox.x + pbox.width)) 
423
	    {
424 425
              GdkSpan out_span;

426 427
	      clipped_left = MAX (left, pbox.x);
	      clipped_right = MIN (right, pbox.x + pbox.width);
428 429 430 431 432 433 434 435 436 437
	      
	      out_span.y = y;
	      out_span.x = clipped_left;
	      out_span.width = clipped_right - clipped_left;
	      (*function) (&out_span, data);
	    }
	}
    }
}

438 439 440 441 442 443 444 445 446 447 448
/**
 * gdk_region_spans_intersect_foreach:
 * @region: a #GdkRegion
 * @spans: an array of #GdkSpans
 * @n_spans: the length of @spans
 * @sorted: %TRUE if @spans is sorted wrt. the y coordinate
 * @function: function to call on each span in the intersection
 * @data: data to pass to @function
 *
 * Calls a function on each span in the intersection of @region and @spans.
 */
449
void
450 451 452 453 454 455
gdk_region_spans_intersect_foreach (GdkRegion     *region,
				    const GdkSpan *spans,
				    int            n_spans,
				    gboolean       sorted,
				    GdkSpanFunc    function,
				    gpointer       data)
456
{
457
  gint i, left, right, y, n_rects;
458
  gint clipped_left, clipped_right;
459
  GdkRectangle pbox;
460 461
  const GdkSpan *span, *tmpspan;
  const GdkSpan *end_span;
462

463 464 465
  g_return_if_fail (region != NULL);
  g_return_if_fail (spans != NULL);

466 467 468 469 470 471 472 473 474 475
  if (!sorted)
    {
      gdk_region_unsorted_spans_intersect_foreach (region,
						   spans,
						   n_spans,
						   function,
						   data);
      return;
    }
  
476 477
  n_rects = cairo_region_num_rectangles (region);
  if (n_rects == 0 || n_spans == 0)
478 479 480 481 482 483 484 485 486 487
    return;

  /* The main method here is to step along the
   * sorted rectangles and spans in lock step, and
   * clipping the spans that are in the current
   * rectangle before going on to the next rectangle.
   */

  span = spans;
  end_span = spans + n_spans;
488
  for (i = 0; i < n_rects; i++)
489
    {
490 491 492
      cairo_region_get_rectangle (region, i, &pbox);

      while ((pbox.y + pbox.height < span->y) || (span->y < pbox.y))
493 494
	{
	  /* Skip any rectangles that are above the current span */
495
	  if (pbox.y + pbox.height < span->y)
496
	    {
497 498
	      i++;
	      if (i == n_rects)
499
		return;
500
              cairo_region_get_rectangle (region, i, &pbox);
501 502
	    }
	  /* Skip any spans that are above the current rectangle */
503
	  if (span->y < pbox.y)
504 505 506 507 508 509 510 511 512 513
	    {
	      span++;
	      if (span == end_span)
		return;
	    }
	}
      
      /* Ok, we got at least one span that might intersect this rectangle. */
      tmpspan = span;
      while ((tmpspan < end_span) &&
514
	     (tmpspan->y < pbox.y + pbox.height))
515 516 517 518 519
	{
	  y = tmpspan->y;
	  left = tmpspan->x;
	  right = left + tmpspan->width; /* right is not in the span! */
	  
520
	  if ((right > pbox.x) && (left < pbox.x + pbox.width))
521
	    {
522 523
              GdkSpan out_span;

524 525
	      clipped_left = MAX (left, pbox.x);
	      clipped_right = MIN (right, pbox.x + pbox.width);
526 527 528 529 530 531 532 533 534 535 536
	      
	      out_span.y = y;
	      out_span.x = clipped_left;
	      out_span.width = clipped_right - clipped_left;
	      (*function) (&out_span, data);
	    }
	  
	  tmpspan++;
	}
    }
}
537 538 539

#define __GDK_REGION_GENERIC_C__
#include "gdkaliasdef.c"