xref: /haiku/src/kits/interface/RegionSupport.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2003-2004, OpenBeOS
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		RegionSupport.cpp
23 //	Author:			Stefano Ceccherini (burton666@libero.it)
24 //	Description:	Class that does the dirty work for BRegion.
25 //
26 //------------------------------------------------------------------------------
27 
28 // TODO: check for possible performance issue in ROr() and RSub().
29 // Check if inlining some methods can make us be faster.
30 
31 // Standard Includes -----------------------------------------------------------
32 #include <cstring>
33 #include <new>
34 
35 // System Includes -------------------------------------------------------------
36 #include <Debug.h>
37 #include <Region.h>
38 
39 // Private Includes -------------------------------------------------------------
40 #include <clipping.h>
41 #include <RegionSupport.h>
42 
43 // Constants --------------------------------------------------------------------
44 const int32 kMaxPoints = 1024;
45 const int32 kMaxVerticalExtent = 0x10000000;
46 
47 
48 #define TRACE_REGION 0
49 #define ARGS (const char *, ...)
50 #if TRACE_REGION
51 	#define RTRACE(ARGS) printf ARGS
52 	#define CALLED() printf("%s\n", __PRETTY_FUNCTION__)
53 #else
54 	#define RTRACE(ARGS) ;
55  	#define CALLED()
56 #endif
57 
58 using namespace std;
59 
60 /*!	\brief zeroes the given region, setting its rect count to 0,
61 	and invalidating its bound rectangle.
62 	\param region The region to be zeroed.
63 */
64 void
65 BRegion::Support::ZeroRegion(BRegion *region)
66 {
67 	CALLED();
68 
69 	region->count = 0;
70 	region->bound.left = 0x7ffffffd;
71 	region->bound.top = 0x7ffffffd;
72 	region->bound.right = 0x80000003;
73 	region->bound.bottom = 0x80000003;
74 }
75 
76 
77 /*!	\brief clear the given region, setting its rect count to 0,
78 	and setting its bound rectangle to 0xFFFFFFF, 0xFFFFFFF, 0xF0000001, 0xF0000001.
79 	\param region The region to be cleared.
80 */
81 void
82 BRegion::Support::ClearRegion(BRegion *region)
83 {
84 	CALLED();
85 
86 	// XXX: What is it used for ?
87 	// Could be that a cleared region represents an infinite one ?
88 
89 	region->count = 0;
90 	region->bound.left = 0xfffffff;
91 	region->bound.top = 0xfffffff;
92 	region->bound.right = 0xf0000001;
93 	region->bound.bottom = 0xf0000001;
94 }
95 
96 
97 /*!	\brief Copy a region to another.
98 	\param source The region to be copied.
99 	\param dest The destination region.
100 */
101 void
102 BRegion::Support::CopyRegion(BRegion *source, BRegion *dest)
103 {
104 	CALLED();
105 	ASSERT(source);
106 	ASSERT(dest);
107 	ASSERT(source != dest);
108 
109 	// If there is not enough memory, allocate
110 	if (dest->data_size < source->count) {
111 		free(dest->data);
112 		dest->data_size = source->count + 8;
113 		dest->data = (clipping_rect *)malloc(dest->data_size * sizeof(clipping_rect));
114 	}
115 
116 	dest->count = source->count;
117 
118 	// Copy rectangles and bounds.
119 	memcpy(dest->data, source->data, source->count * sizeof(clipping_rect));
120 	dest->bound = source->bound;
121 }
122 
123 
124 /*!	\brief Modify the destination region to be the intersection of the two given regions.
125 	\param first The first region to be intersected.
126 	\param second The second region to be intersected.
127 	\param dest The destination region.
128 
129 	This function is a sort of method selector. It checks for some special
130 	cases, then it calls the appropriate specialized function.
131 */
132 void
133 BRegion::Support::AndRegion(BRegion *first, BRegion *second, BRegion *dest)
134 {
135 	CALLED();
136 	ASSERT(first);
137 	ASSERT(second);
138 	ASSERT(dest);
139 
140 	clipping_rect intersection = sect_rect(first->bound, second->bound);
141 
142 	if (first->count == 0 || second->count == 0	|| !valid_rect(intersection))
143 		ZeroRegion(dest);
144 
145 	else if (first->count == 1 && second->count == 1) {
146 		dest->data[0] = intersection;
147 		dest->bound = intersection;
148 		dest->count = 1;
149 	}
150 	else if (first->count > 1 && second->count == 1)
151 		AndRegion1ToN(second, first, dest);
152 
153 	else if (first->count == 1 && second->count > 1)
154 		AndRegion1ToN(first, second, dest);
155 
156 	else
157 		AndRegionComplex(first, second, dest);
158 }
159 
160 
161 /*!	\brief Modify the destination region to be the union of the two given regions.
162 	\param first The first region to be merged.
163 	\param second The second region to be merged.
164 	\param dest The destination region.
165 
166 	This function is a sort of method selector. It checks for some special
167 	cases, then it calls the appropriate specialized function.
168 */
169 void
170 BRegion::Support::OrRegion(BRegion *first, BRegion *second, BRegion *dest)
171 {
172 	CALLED();
173 	ASSERT(first);
174 	ASSERT(second);
175 	ASSERT(dest);
176 
177 	BRegion *regionA, *regionB;
178 
179 	// A little trick, to save some work...
180 	if (first->count != 0) {
181 		regionA = first;
182 		regionB = second;
183 	} else {
184 		regionA = second;
185 		regionB = first;
186 	}
187 
188 	if (regionB->count == 0)
189 		CopyRegion(regionA, dest);
190 	else {
191 		if (regionB->bound.top > regionA->bound.bottom)
192 			AppendRegion(regionA, regionB, dest);
193 
194 		else if (regionA->bound.top > regionB->bound.bottom)
195 			AppendRegion(regionB, regionA, dest);
196 
197 		else if (regionA->bound.left > regionB->bound.right)
198 			OrRegionNoX(regionB, regionA, dest);
199 
200 		else if (regionB->bound.left > regionA->bound.right)
201 			OrRegionNoX(regionA, regionB, dest);
202 
203 		else if (regionA->count == 1)
204 			OrRegion1ToN(regionA, regionB, dest);
205 
206 		else if (regionB->count == 1)
207 			OrRegion1ToN(regionB, regionA, dest);
208 
209 		else
210 			OrRegionComplex(regionA, regionB, dest);
211 	}
212 }
213 
214 
215 /*!	\brief Modify the destination region to be the difference of the two given regions.
216 	\param first The subtraend region.
217 	\param second The minuend region.
218 	\param dest The destination region.
219 
220 	This function is a sort of method selector. It checks for some special
221 	cases, then it calls the appropriate specialized function.
222 */
223 void
224 BRegion::Support::SubRegion(BRegion *first, BRegion *second, BRegion *dest)
225 {
226 	CALLED();
227 	ASSERT(first);
228 	ASSERT(second);
229 	ASSERT(dest);
230 
231 	if (first->count == 0)
232 		ZeroRegion(dest);
233 
234 	else if (second->count == 0	|| !rects_intersect(first->bound, second->bound))
235 		CopyRegion(first, dest);
236 
237 	else
238 		SubRegionComplex(second, first, dest);
239 }
240 
241 
242 /*!	\brief Cleanup the region, by merging rects that can be merged.
243 	\param region The region to be cleaned.
244 */
245 void
246 BRegion::Support::CleanupRegion(BRegion *region)
247 {
248 	CALLED();
249 
250 	long oldCount;
251 
252 	do {
253 		oldCount = region->count;
254 		CleanupRegionVertical(region);
255 		CleanupRegionHorizontal(region);
256 	} while (region->count < oldCount);
257 }
258 
259 
260 /*!	\brief Cleanup the region vertically.
261 	\param region The region to be cleaned.
262 */
263 void
264 BRegion::Support::CleanupRegionVertical(BRegion *region)
265 {
266 	CALLED();
267 
268 	clipping_rect testRect = { 1, 1, -1, -2 };
269 	long newCount = -1;
270 
271 	for (long x = 0; x < region->count; x++) {
272 		clipping_rect &rect = region->data[x];
273 
274 		if (rect.left == testRect.left && rect.right == testRect.right
275 				&& rect.top == testRect.bottom + 1) {
276 
277 			ASSERT(newCount >= 0);
278 			region->data[newCount].bottom = rect.bottom;
279 
280 		} else {
281 			newCount++;
282 			region->data[newCount] = region->data[x];
283 			testRect = region->data[x];
284 		}
285 	}
286 	region->count = newCount + 1;
287 }
288 
289 
290 /*!	\brief Cleanup the region horizontally.
291 	\param region The region to be cleaned.
292 */
293 void
294 BRegion::Support::CleanupRegionHorizontal(BRegion *region)
295 {
296 	CALLED();
297 
298 	clipping_rect testRect = { 1, 1, -2, -1 };
299 	long newCount = -1;
300 
301 	for (long x = 0; x < region->count; x++) {
302 		clipping_rect &rect = region->data[x];
303 
304 		if (rect.top == testRect.top && rect.bottom == testRect.bottom
305 					&& rect.left == testRect.right + 1) {
306 
307 			ASSERT(newCount >= 0);
308 			region->data[newCount].right = rect.right;
309 
310 		} else {
311 			newCount++;
312 			region->data[newCount] = rect;
313 		}
314 		testRect = region->data[newCount];
315 	}
316 	region->count = newCount + 1;
317 }
318 
319 
320 // Helper method to swap two rects
321 static inline void
322 SwapRects(clipping_rect &rect, clipping_rect &anotherRect)
323 {
324 	clipping_rect tmpRect;
325 	tmpRect = rect;
326 	rect = anotherRect;
327 	anotherRect = tmpRect;
328 }
329 
330 
331 /*!	\brief Sorts the given rects by their top value.
332 	\param rects A pointer to an array of clipping_rects.
333 	\param count The number of rectangles in the array.
334 */
335 void
336 BRegion::Support::SortRects(clipping_rect *rects, long count)
337 {
338 	CALLED();
339 
340 	bool again; //flag that tells we changed rects positions
341 
342 	if (count == 2) {
343 		if (rects[0].top > rects[1].top)
344 			SwapRects(rects[0], rects[1]);
345 
346 	} else if (count > 2) {
347 		do {
348 			again = false;
349 			for (long c = 1; c < count; c++) {
350 				if (rects[c - 1].top > rects[c].top) {
351 					SwapRects(rects[c - 1], rects[c]);
352 					again = true;
353 				}
354 			}
355 		} while (again);
356 	}
357 }
358 
359 
360 // Helper methods to swap transition points in two given arrays
361 static inline void
362 SwapTrans(long *leftPoints, long *rightPoints, long index1, long index2)
363 {
364 	// First, swap the left points
365 	long tmp = leftPoints[index1];
366 	leftPoints[index1] = leftPoints[index2];
367 	leftPoints[index2] = tmp;
368 
369 	// then the right points
370 	tmp = rightPoints[index1];
371 	rightPoints[index1] = rightPoints[index2];
372 	rightPoints[index2] = tmp;
373 }
374 
375 
376 void
377 BRegion::Support::SortTrans(long *lptr1, long *lptr2, long count)
378 {
379 	CALLED();
380 
381 	bool again; //flag that tells we changed trans positions
382 
383 	if (count == 2) {
384 		if (lptr1[0] > lptr1[1])
385 			SwapTrans(lptr1, lptr2, 0, 1);
386 
387 	} else if (count > 2) {
388 		do {
389 			again = false;
390 			for (long c = 1; c < count; c++) {
391 				if (lptr1[c - 1] > lptr1[c]) {
392 					SwapTrans(lptr1, lptr2, c - 1, c);
393 					again = true;
394 				}
395 			}
396 		} while (again);
397 	}
398 }
399 
400 
401 /*!	\brief Copy a region to another, allocating some additional memory in the destination region.
402 	\param source The region to be copied.
403 	\param dest The destination region.
404 	\param count Amount of additional memory to be allocated in the destination region.
405 */
406 void
407 BRegion::Support::CopyRegionMore(BRegion *source, BRegion *dest, long count)
408 {
409 	CALLED();
410 	ASSERT(source);
411 	ASSERT(dest);
412 	ASSERT(source != dest);
413 
414 	// If there is not enough memory, allocate
415 	if (dest->data_size < source->count) {
416 		free(dest->data);
417 		dest->data_size = source->count + count;
418 		dest->data = (clipping_rect *)malloc(dest->data_size * sizeof(clipping_rect));
419 	}
420 
421 	dest->count = source->count;
422 
423 	// Copy rectangles and bounds.
424 	memcpy(dest->data, source->data, source->count * sizeof(clipping_rect));
425 	dest->bound = source->bound;
426 }
427 
428 
429 /*!	\brief Modify the destination region to be the intersection of the two given regions.
430 	\param first The first region to be intersected.
431 	\param second The second region to be intersected.
432 	\param dest The destination region.
433 
434 	Called by and_region() when the intersection is complex.
435 */
436 void
437 BRegion::Support::AndRegionComplex(BRegion *first, BRegion *second, BRegion *dest)
438 {
439 	CALLED();
440 	ASSERT(first);
441 	ASSERT(second);
442 	ASSERT(dest);
443 
444 	ZeroRegion(dest);
445 
446 	for (long f = 0; f < first->count; f++) {
447 		for (long s = 0; s < second->count; s++) {
448 			clipping_rect testRect = sect_rect(first->data[f], second->data[s]);
449 			if (valid_rect(testRect))
450 				dest->_AddRect(testRect);
451 		}
452 	}
453 
454 	if (dest->count > 1)
455 		SortRects(dest->data, dest->count);
456 }
457 
458 
459 /*!	\brief Modify the destination region to be the intersection of the two given regions.
460 	\param first The first region to be intersected.
461 	\param second The second region to be intersected.
462 	\param dest The destination region.
463 
464 	Called by and_region() when one of the two region contains just one rect.
465 */
466 void
467 BRegion::Support::AndRegion1ToN(BRegion *first, BRegion *second, BRegion *dest)
468 {
469 	CALLED();
470 	ASSERT(first);
471 	ASSERT(second);
472 	ASSERT(dest);
473 
474 	// The easy case first: We already know that the regions intersect,
475 	// so we check if the first region contains the second.
476 	// If it's the case, the intersection is exactly the second region.
477 	if (first->bound.top <= second->bound.top
478 			&& first->bound.bottom >= second->bound.bottom
479 			&& first->bound.left <= second->bound.left
480 			&& first->bound.right >= second->bound.right)
481 		CopyRegion(second, dest);
482 	else {
483 	// Otherwise, we check the rect of the first region against the rects
484 	// of the second, and we add their intersections to the destination region
485 		ZeroRegion(dest);
486 		for (long x = 0; x < second->count; x++) {
487 			clipping_rect testRect = sect_rect(first->data[0], second->data[x]);
488 			if (valid_rect(testRect))
489 				dest->_AddRect(testRect);
490 		}
491 	}
492 }
493 
494 
495 /*!	\brief Modify the destination region to be the union of the two given regions.
496 	\param first The first region to be or-ed.
497 	\param second The second region to be or-ed.
498 	\param dest The destination region.
499 
500 	This function is called by or_region when the two regions don't intersect,
501 	and when the second region top coordinate is bigger than first region's bottom
502 	coordinate.
503 */
504 void
505 BRegion::Support::AppendRegion(BRegion *first, BRegion *second, BRegion *dest)
506 {
507 	CALLED();
508 	ASSERT(first);
509 	ASSERT(second);
510 	ASSERT(dest);
511 
512 	CopyRegion(first, dest);
513 
514 	for (long c = 0; c < second->count; c++)
515 		dest->_AddRect(second->data[c]);
516 }
517 
518 
519 void
520 BRegion::Support::ROr(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *indexA, long *indexB)
521 {
522 	CALLED();
523 
524 	int32 stackLefts[kMaxPoints];
525 	int32 stackRights[kMaxPoints];
526 
527 	int32 *lefts = stackLefts;
528 	int32 *rights = stackRights;
529 
530 	long i1 = *indexA;
531 	long i2 = *indexB;
532 
533 	*indexA = -1;
534 	*indexB = -1;
535 
536 	long foundCount = 0;
537 	long x = 0;
538 
539 	// allocate arrays on the heap, if the ones one the stack are too small
540 	int32 *allocatedBuffer = NULL;
541 	int32 maxCount = first->count - i1 + second->count - i2;
542 
543 	if (maxCount > kMaxPoints) {
544 		RTRACE(("Stack space isn't sufficient. Allocating %ld bytes on the heap...\n",
545 				2 * maxCount));
546 		lefts = allocatedBuffer = new(nothrow) int32[2 * maxCount];
547 		if (!allocatedBuffer)
548 			return;
549 		rights = allocatedBuffer + maxCount;
550 	}
551 
552 	// Store left and right points to the appropriate array
553 	for (x = i1; x < first->count; x++) {
554 
555 		// Look if this rect can be used next time we are called,
556 		// thus correctly maintaining the "index" parameters.
557 		if (first->data[x].bottom >= top && *indexA == -1)
558 			*indexA = x;
559 
560 		if (first->data[x].top <= top && first->data[x].bottom >= bottom) {
561 			lefts[foundCount] = first->data[x].left;
562 			rights[foundCount] = first->data[x].right;
563 			foundCount++;
564 		} else if (first->data[x].top > bottom)
565 			break;
566 	}
567 
568 	if (*indexA == -1)
569 		*indexA = i1;
570 
571 	for (x = i2; x < second->count; x++) {
572 		if (second->data[x].bottom >= top && *indexB == -1)
573 			*indexB = x;
574 
575 		if (second->data[x].top <= top && second->data[x].bottom >= bottom) {
576 			lefts[foundCount] = second->data[x].left;
577 			rights[foundCount] = second->data[x].right;
578 			foundCount++;
579 		} else if (second->data[x].top > bottom)
580 			break;
581 	}
582 
583 	if (*indexB == -1)
584 		*indexB = i2;
585 
586 	if (foundCount > 1)
587 		SortTrans(lefts, rights, foundCount);
588 
589 	ASSERT(foundCount > 0);
590 
591 	clipping_rect rect;
592 	rect.top = top;
593 	rect.bottom = bottom;
594 
595 	// Check if a rect intersects with the next one.
596 	// If so, merge the two rects, if not, just add the rect.
597 	long current = 0;
598 	while (current < foundCount) {
599 		long next = current + 1;
600 
601 		rect.left = lefts[current];
602 		rect.right = rights[current];
603 
604 		while (next < foundCount && rect.right >= lefts[next]) {
605 			if (rect.right < rights[next])
606 				rect.right = rights[next];
607 			next++;
608 		}
609 
610 		dest->_AddRect(rect);
611 		current = next;
612 	}
613 
614 	if (allocatedBuffer) {
615 		RTRACE(("Freeing heap...\n"));
616 		delete[] allocatedBuffer;
617 	}
618 }
619 
620 
621 /*! \brief Divides the plane into horizontal bands, then passes those bands to r_or
622 	which does the real work.
623 	\param first The first region to be or-ed.
624 	\param second The second region to be or-ed.
625 	\param dest The destination region.
626 */
627 void
628 BRegion::Support::OrRegionComplex(BRegion *first, BRegion *second, BRegion *dest)
629 {
630 	CALLED();
631 	long a = 0, b = 0;
632 
633 	int32 top;
634 	int32 bottom = min_c(first->bound.top, second->bound.top) - 1;
635 	do {
636 		long x;
637 		top = bottom + 1;
638 		bottom = kMaxVerticalExtent;
639 
640 		for (x = a; x < first->count; x++) {
641 			int32 n = first->data[x].top - 1;
642 			if (n >= top && n < bottom)
643 				bottom = n;
644 			if (first->data[x].bottom >= top && first->data[x].bottom < bottom)
645 				bottom = first->data[x].bottom;
646 		}
647 
648 		for (x = b; x < second->count; x++) {
649 			int32 n = second->data[x].top - 1;
650 			if (n >= top && n < bottom)
651 				bottom = n;
652 			if (second->data[x].bottom >= top && second->data[x].bottom < bottom)
653 				bottom = second->data[x].bottom;
654 		}
655 
656 		// We can stand a region which extends to kMaxVerticalExtent, not more
657 		if (bottom >= kMaxVerticalExtent)
658 			break;
659 
660 		ROr(top, bottom, first, second, dest, &a, &b);
661 
662 	} while (true);
663 
664 	CleanupRegion(dest);
665 }
666 
667 
668 /*!	\brief Modify the destination region to be the union of the two given regions.
669 	\param first The first region to be or-ed.
670 	\param second The second region to be or-ed.
671 	\param dest The destination region.
672 
673 	This function is called by or_region when one of the two regions contains just
674 	one rect.
675 */
676 void
677 BRegion::Support::OrRegion1ToN(BRegion *first, BRegion *second, BRegion *dest)
678 {
679 	CALLED();
680 	ASSERT(first);
681 	ASSERT(second);
682 	ASSERT(dest);
683 
684 	// The easy case first: if the first region contains the second,
685 	// the union is exactly the first region, since its bound is the
686 	// only rectangle.
687 	if (first->bound.top <= second->bound.top
688 			&& first->bound.bottom >= second->bound.bottom
689 			&& first->bound.left <= second->bound.left
690 			&& first->bound.right >= second->bound.right)
691 		CopyRegion(first, dest);
692 	else
693 		OrRegionComplex(first, second, dest);
694 }
695 
696 
697 /*!	\brief Modify the destination region to be the union of the two given regions.
698 	\param first The first region to be or-ed.
699 	\param second The second region to be or-ed.
700 	\param dest The destination region.
701 
702 	This function is called by or_region when the two regions don't intersect.
703 */
704 void
705 BRegion::Support::OrRegionNoX(BRegion *first, BRegion *second, BRegion *dest)
706 {
707 	CALLED();
708 	ASSERT(first);
709 	ASSERT(second);
710 	ASSERT(dest);
711 
712 	ZeroRegion(dest);
713 
714 	long x;
715 
716 	if (first->count == 0)
717 		for (x = 0; x < second->count; x++)
718 			dest->_AddRect(second->data[x]);
719 
720 	else if (second->count == 0)
721 		for (x = 0; x < first->count; x++)
722 			dest->_AddRect(first->data[x]);
723 
724 	else {
725 		long f = 0, s = 0;
726 
727 		while (f < first->count && s < second->count) {
728 
729 			if (first->data[f].top < second->data[s].top) {
730 				dest->_AddRect(first->data[f]);
731 				f++;
732 
733 			} else {
734 				dest->_AddRect(second->data[s]);
735 				s++;
736 			}
737 		}
738 
739 		if (f == first->count)
740 			for (; s < second->count; s++)
741 				dest->_AddRect(second->data[s]);
742 
743 		else if (s == second->count)
744 			for (; f < first->count; f++)
745 				dest->_AddRect(first->data[f]);
746 	}
747 }
748 
749 
750 /*! \brief Divides the plane into horizontal bands, then passes those bands to r_sub
751 	which does the real work.
752 	\param first The subtraend region.
753 	\param second The minuend region.
754 	\param dest The destination region.
755 */
756 void
757 BRegion::Support::SubRegionComplex(BRegion *first, BRegion *second, BRegion *dest)
758 {
759 	CALLED();
760 	long a = 0, b = 0;
761 
762 	int32 top;
763 	int32 bottom = min_c(first->bound.top, second->bound.top) - 1;
764 
765 	do {
766 		long x;
767 		top = bottom + 1;
768 		bottom = kMaxVerticalExtent;
769 
770 		for (x = a; x < first->count; x++) {
771 			int32 n = first->data[x].top - 1;
772 			if (n >= top && n < bottom)
773 				bottom = n;
774 			if (first->data[x].bottom >= top && first->data[x].bottom < bottom)
775 				bottom = first->data[x].bottom;
776 		}
777 
778 		for (x = b; x < second->count; x++) {
779 			int32 n = second->data[x].top - 1;
780 			if (n >= top && n < bottom)
781 				bottom = n;
782 			if (second->data[x].bottom >= top && second->data[x].bottom < bottom)
783 				bottom = second->data[x].bottom;
784 		}
785 
786 		if (bottom >= kMaxVerticalExtent)
787 			break;
788 
789 		RSub(top, bottom, first, second, dest, &a, &b);
790 
791 	} while (true);
792 
793 	CleanupRegion(dest);
794 }
795 
796 
797 void
798 BRegion::Support::RSub(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *indexA, long *indexB)
799 {
800 	CALLED();
801 
802 	int32 stackLeftsA[kMaxPoints / 2];
803 	int32 stackLeftsB[kMaxPoints / 2];
804 	int32 stackRightsA[kMaxPoints / 2];
805 	int32 stackRightsB[kMaxPoints / 2];
806 
807 	int32 *leftsA = stackLeftsA;
808 	int32 *leftsB = stackLeftsB;
809 	int32 *rightsA = stackRightsA;
810 	int32 *rightsB = stackRightsB;
811 
812 	long i1 = *indexA;
813 	long i2 = *indexB;
814 
815 	*indexA = -1;
816 	*indexB = -1;
817 
818 	long foundA = 0;
819 	long foundB = 0;
820 	long x = 0;
821 
822 	// allocate arrays on the heap, if the ones one the stack are too small
823 	int32 *allocatedBuffer = NULL;
824 	int32 maxCountA = first->count - i1;
825 	int32 maxCountB = second->count - i2;
826 
827 	if (maxCountA + maxCountB > kMaxPoints) {
828 		RTRACE(("Stack space isn't sufficient. Allocating %ld bytes on the heap...\n",
829 				2 * (maxCountA + maxCountB)));
830 		leftsA = allocatedBuffer = new(nothrow) int32[2 * (maxCountA + maxCountB)];
831 		if (!allocatedBuffer)
832 			return;
833 		rightsA = allocatedBuffer + maxCountA;
834 		leftsB = rightsA + maxCountA;
835 		rightsB = leftsB + maxCountB;
836 	}
837 
838 	// Store left and right points to the appropriate array
839 	for (x = i1; x < first->count; x++) {
840 
841 		// Look if this rect can be used next time we are called,
842 		// thus correctly maintaining the "index" parameters.
843 		if (first->data[x].bottom >= top && *indexA == -1)
844 			*indexA = x;
845 
846 		if (first->data[x].top <= top && first->data[x].bottom >= bottom) {
847 			leftsA[foundA] = first->data[x].left;
848 			rightsA[foundA] = first->data[x].right;
849 			foundA++;
850 		} else if (first->data[x].top > bottom)
851 			break;
852 	}
853 
854 	if (*indexA == -1)
855 		*indexA = i1;
856 
857 	if (foundA > 1)
858 		SortTrans(leftsA, rightsA, foundA);
859 
860 	for (x = i2; x < second->count; x++) {
861 		if (second->data[x].bottom >= top && *indexB == -1)
862 			*indexB = x;
863 
864 		if (second->data[x].top <= top && second->data[x].bottom >= bottom) {
865 			leftsB[foundB] = second->data[x].left;
866 			rightsB[foundB] = second->data[x].right;
867 			foundB++;
868 		} else if (second->data[x].top > bottom)
869 			break;
870 	}
871 
872 	if (*indexB == -1)
873 		*indexB = i2;
874 
875 	if (foundB > 1)
876 		SortTrans(leftsB, rightsB, foundB);
877 
878 	// No minuend's rect, just add all the subtraend's rects.
879 	if (foundA == 0)
880 		for (x = 0; x < foundB; x++) {
881 			clipping_rect rect = { leftsB[x], top, rightsB[x], bottom };
882 			dest->_AddRect(rect);
883 		}
884 	else if (foundB > 0) {
885 		long f = 0, s = 0;
886 
887 		clipping_rect minuendRect;
888 		minuendRect.top = top;
889 		minuendRect.bottom = bottom;
890 		minuendRect.left = 0x80000003;
891 
892 		clipping_rect subRect;
893 		subRect.top = top;
894 		subRect.bottom = bottom;
895 
896 		// We take the empty spaces between the minuend rects, and intersect
897 		// these with the subtraend rects. We then add their intersection
898 		// to the destination region.
899 		do {
900 			subRect.left = leftsB[s];
901 			subRect.right = rightsB[s];
902 
903 			if (f != 0)
904 				minuendRect.left = rightsA[f - 1] + 1;
905 
906 			minuendRect.right = leftsA[f] - 1;
907 
908 			if (leftsB[s] > minuendRect.right) {
909 				if (++f > foundA)
910 					break;
911 				else
912 					continue;
913 			}
914 
915 			clipping_rect intersection = sect_rect(minuendRect, subRect);
916 
917 			if (valid_rect(intersection))
918 				dest->_AddRect(intersection);
919 
920 			if (rightsB[s] < minuendRect.left)
921 				s++;
922 
923 			if (s >= foundB)
924 				break;
925 
926 			f++;
927 
928 		} while (f < foundA);
929 
930 		// Last rect: we take the right coordinate of the last minuend rect
931 		// as left coordinate of the rect to intersect, and the maximum possible
932 		// value as the right coordinate.
933 		minuendRect.left = rightsA[foundA - 1] + 1;
934 		minuendRect.right = 0x7ffffffd;
935 
936 		// Skip the subtraend rects that could never intersect.
937 		while (s < foundB && rightsB[s] < minuendRect.left)
938 			s++;
939 
940 		for (long c = s; c < foundB; c++) {
941 			subRect.left = leftsB[c];
942 			subRect.right = rightsB[c];
943 
944 			clipping_rect intersection = sect_rect(minuendRect, subRect);
945 
946 			if (valid_rect(intersection))
947 				dest->_AddRect(intersection);
948 		}
949 	}
950 
951 	if (allocatedBuffer) {
952 		RTRACE(("Freeing heap...\n"));
953 		delete[] allocatedBuffer;
954 	}
955 }
956 
957 
958 #undef TRACE_REGION
959