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