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