Implemented the last missing function, r_sub (it could be written a bit better, though). Moved the helper methods to a separate file, cleaned up a bit the implementation, changed some things in the license.
git-svn-id: file:///srv/svn/repos/haiku/trunk/current@3965 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
910bbbc473
commit
791e557c1b
@ -1,5 +1,5 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// Copyright (c) 2001-2002, OpenBeOS
|
||||
// Copyright (c) 2003, OpenBeOS
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
@ -19,698 +19,38 @@
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// File Name: BRegion.cpp
|
||||
// File Name: Region.cpp
|
||||
// Author: Stefano Ceccherini (burton666@libero.it)
|
||||
// Description: Shape class consisting of multiple rectangles
|
||||
// Description: Region class consisting of multiple rectangles
|
||||
//
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
// Standard Includes -----------------------------------------------------------
|
||||
#include <cstdlib>
|
||||
|
||||
// System Includes -------------------------------------------------------------
|
||||
#include <Debug.h>
|
||||
#include <Region.h>
|
||||
|
||||
#include <clipping.h>
|
||||
|
||||
static const int32 kMaxPoints = 1024;
|
||||
static const int32 kMaxVerticalExtent = 0x10000000;
|
||||
#include "region_helpers.h"
|
||||
|
||||
/* friend functions */
|
||||
|
||||
/*! \brief zeroes the given region, setting its rect count to 0,
|
||||
and invalidating its bound rectangle.
|
||||
\param region The region to be zeroed.
|
||||
*/
|
||||
void
|
||||
zero_region(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
region->count = 0;
|
||||
region->bound.left = 0x7ffffffd;
|
||||
region->bound.top = 0x7ffffffd;
|
||||
region->bound.right = 0x80000003;
|
||||
region->bound.bottom = 0x80000003;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
clear_region(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
region->count = 0;
|
||||
region->bound.left = 0xfffffff;
|
||||
region->bound.top = 0xfffffff;
|
||||
region->bound.right = 0xf0000001;
|
||||
region->bound.bottom = 0xf0000001;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Cleanup the region vertically.
|
||||
\param region The region to be cleaned.
|
||||
*/
|
||||
void
|
||||
cleanup_region_1(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
clipping_rect testRect =
|
||||
{
|
||||
1, 1,
|
||||
-1, -2
|
||||
};
|
||||
|
||||
long newCount = -1;
|
||||
|
||||
if (region->count > 0) {
|
||||
for (long x = 0; x < region->count; x++) {
|
||||
clipping_rect rect = region->data[x];
|
||||
|
||||
if ((rect.left == testRect.left)
|
||||
&& (rect.right == testRect.right)
|
||||
&& (rect.top == testRect.bottom + 1)) {
|
||||
ASSERT(newCount >= 0);
|
||||
|
||||
region->data[newCount].bottom = rect.bottom;
|
||||
|
||||
} else {
|
||||
newCount++;
|
||||
region->data[newCount] = region->data[x];
|
||||
testRect = region->data[x];
|
||||
}
|
||||
}
|
||||
region->count = newCount + 1;
|
||||
|
||||
cleanup_region_horizontal(region);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Cleanup the region, by merging rects that can be merged.
|
||||
\param region The region to be cleaned.
|
||||
*/
|
||||
void
|
||||
cleanup_region(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
long oldCount;
|
||||
|
||||
do {
|
||||
oldCount = region->count;
|
||||
cleanup_region_1(region);
|
||||
} while (region->count < oldCount);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Sorts the given rects by their top value.
|
||||
\param rects A pointer to an array of clipping_rects.
|
||||
\param count The number of rectangles in the array.
|
||||
*/
|
||||
void
|
||||
sort_rects(clipping_rect *rects, long count)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
bool again; //flag that tells we changed rects positions
|
||||
|
||||
if (count == 2) {
|
||||
if (rects[0].top > rects[1].top) {
|
||||
clipping_rect tmp = rects[0];
|
||||
rects[0] = rects[1];
|
||||
rects[1] = tmp;
|
||||
}
|
||||
} else if (count > 1) {
|
||||
do {
|
||||
again = false;
|
||||
for (int c = 1; c < count; c++) {
|
||||
if (rects[c - 1].top > rects[c].top) {
|
||||
clipping_rect tmp = rects[c - 1];
|
||||
rects[c - 1] = rects[c];
|
||||
rects[c] = tmp;
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
} while (again);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sort_trans(long *lptr1, long *lptr2, long count)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
bool again; //flag that tells we changed trans positions
|
||||
|
||||
if (count == 2) {
|
||||
if (lptr1[0] > lptr1[1]) {
|
||||
int32 tmp = lptr1[0];
|
||||
lptr1[0] = lptr1[1];
|
||||
lptr1[1] = tmp;
|
||||
|
||||
tmp = lptr2[0];
|
||||
lptr2[0] = lptr2[1];
|
||||
lptr2[1] = tmp;
|
||||
}
|
||||
} else if (count > 1) {
|
||||
do {
|
||||
again = false;
|
||||
for (int c = 1; c < count; c++) {
|
||||
if (lptr1[c - 1] > lptr1[c]) {
|
||||
int32 tmp = lptr1[c - 1];
|
||||
lptr1[c - 1] = lptr1[c];
|
||||
lptr1[c] = tmp;
|
||||
|
||||
tmp = lptr2[c - 1];
|
||||
lptr2[c - 1] = lptr2[c];
|
||||
lptr2[c] = tmp;
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
} while (again);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Cleanup the region horizontally.
|
||||
\param region The region to be cleaned.
|
||||
*/
|
||||
void
|
||||
cleanup_region_horizontal(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
clipping_rect testRect =
|
||||
{
|
||||
1, 1,
|
||||
-2, -1
|
||||
};
|
||||
|
||||
long newCount = -1;
|
||||
|
||||
for (long x = 0; x < region->count; x++) {
|
||||
clipping_rect rect = region->data[x];
|
||||
if ((rect.top == testRect.top) && (rect.bottom == testRect.bottom)
|
||||
&& (rect.left == testRect.right + 1)) {
|
||||
ASSERT(newCount >= 0);
|
||||
region->data[newCount].right = rect.right;
|
||||
|
||||
} else {
|
||||
newCount++;
|
||||
region->data[newCount] = rect;
|
||||
}
|
||||
testRect = region->data[newCount];
|
||||
}
|
||||
region->count = newCount + 1;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Copy a region to another.
|
||||
\param source The region to be copied.
|
||||
\param dest The destination region.
|
||||
*/
|
||||
void
|
||||
copy_region(BRegion *source, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(source);
|
||||
ASSERT(dest);
|
||||
ASSERT(source != dest);
|
||||
|
||||
// If there is not enough memory, allocate
|
||||
if (dest->data_size < source->count) {
|
||||
free(dest->data);
|
||||
dest->data_size = source->count + 8;
|
||||
dest->data = (clipping_rect*)malloc(dest->data_size * sizeof(clipping_rect));
|
||||
}
|
||||
|
||||
dest->count = source->count;
|
||||
|
||||
// Copy rectangles and bounds.
|
||||
memcpy(dest->data, source->data, source->count * sizeof(clipping_rect));
|
||||
dest->bound = source->bound;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Copy a region to another, allocating some additional memory in the destination region.
|
||||
\param source The region to be copied.
|
||||
\param dest The destination region.
|
||||
\param count Amount of additional memory to be allocated in the destination region.
|
||||
*/
|
||||
void
|
||||
copy_region_n(BRegion *source, BRegion *dest, long count)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(source);
|
||||
ASSERT(dest);
|
||||
ASSERT(source != dest);
|
||||
|
||||
// If there is not enough memory, allocate
|
||||
if (dest->data_size < source->count) {
|
||||
free(dest->data);
|
||||
dest->data_size = source->count + count;
|
||||
dest->data = (clipping_rect*)malloc(dest->data_size * sizeof(clipping_rect));
|
||||
}
|
||||
|
||||
dest->count = source->count;
|
||||
|
||||
// Copy rectangles and bounds.
|
||||
memcpy(dest->data, source->data, source->count * sizeof(clipping_rect));
|
||||
dest->bound = source->bound;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
and_region_complex(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
zero_region(dest);
|
||||
|
||||
for (long f = 0; f < first->count; f++) {
|
||||
for (long s = 0; s < second->count; s++) {
|
||||
clipping_rect testRect = sect_rect(first->data[f],
|
||||
second->data[s]);
|
||||
if (valid_rect(testRect))
|
||||
dest->_AddRect(testRect);
|
||||
}
|
||||
}
|
||||
|
||||
if (dest->count > 1)
|
||||
sort_rects(dest->data, dest->count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
and_region_1_to_n(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
// The easy case first: We already know that the regions intersect,
|
||||
// so we check if the first region contains the second.
|
||||
// If it's the case, the intersection is exactly the second region.
|
||||
if ((first->bound.top <= second->bound.top)
|
||||
&& (first->bound.bottom >= second->bound.bottom)
|
||||
&& (first->bound.left <= second->bound.left)
|
||||
&& (first->bound.right >= second->bound.right))
|
||||
copy_region(second, dest);
|
||||
else {
|
||||
// Otherwise, we check the rect of the first region against the rects
|
||||
// of the second, and we add their intersections to the destination region
|
||||
zero_region(dest);
|
||||
for (long x = 0; x < second->count; x++) {
|
||||
clipping_rect testRect = sect_rect(first->data[0], second->data[x]);
|
||||
if (valid_rect(testRect))
|
||||
dest->_AddRect(testRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the intersection of the two given regions.
|
||||
\param first The first region to be intersected.
|
||||
\param second The second region to be intersected.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is a sort of method selector. It checks for some special
|
||||
cases, then it calls the appropriate specialized function.
|
||||
*/
|
||||
void
|
||||
and_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
clipping_rect intersection = sect_rect(first->bound, second->bound);
|
||||
|
||||
if ((first->count == 0) || (second->count == 0)
|
||||
|| (!valid_rect(intersection)))
|
||||
zero_region(dest);
|
||||
|
||||
else if ((first->count == 1) && (second->count == 1)) {
|
||||
dest->data[0] = intersection;
|
||||
dest->bound = intersection;
|
||||
dest->count = 1;
|
||||
}
|
||||
else if ((first->count > 1) && (second->count == 1))
|
||||
and_region_1_to_n(second, first, dest);
|
||||
|
||||
else if ((first->count == 1) && (second->count > 1))
|
||||
and_region_1_to_n(first, second, dest);
|
||||
|
||||
else
|
||||
and_region_complex(first, second, dest);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
append_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
copy_region(first, dest);
|
||||
|
||||
for (long c = 0; c < second->count; c++)
|
||||
dest->_AddRect(second->data[c]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
r_or(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *index1, long *index2)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
int32 lefts[kMaxPoints];
|
||||
int32 rights[kMaxPoints];
|
||||
|
||||
long i1 = *index1;
|
||||
long i2 = *index2;
|
||||
|
||||
*index1 = -1;
|
||||
*index2 = -1;
|
||||
|
||||
long foundCount = 0;
|
||||
long x = 0;
|
||||
|
||||
for (x = i1; x < first->count; x++) {
|
||||
if (first->data[x].bottom >= top && *index1 == -1)
|
||||
*index1 = x;
|
||||
|
||||
if (first->data[x].top <= top && first->data[x].bottom >= bottom) {
|
||||
lefts[foundCount] = first->data[x].left;
|
||||
rights[foundCount] = first->data[x].right;
|
||||
foundCount++;
|
||||
} else if (first->data[x].top > bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*index1 == -1)
|
||||
*index1 = i1;
|
||||
|
||||
for (x = i2; x < second->count; x++) {
|
||||
if (second->data[x].bottom >= top && *index2 == -1)
|
||||
*index2 = x;
|
||||
|
||||
if (second->data[x].top <= top && second->data[x].bottom >= bottom) {
|
||||
lefts[foundCount] = second->data[x].left;
|
||||
rights[foundCount] = second->data[x].right;
|
||||
foundCount++;
|
||||
} else if (second->data[x].top > bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*index2 == -1)
|
||||
*index2 = i2;
|
||||
|
||||
if (foundCount > 1)
|
||||
sort_trans(lefts, rights, foundCount);
|
||||
|
||||
clipping_rect rect;
|
||||
rect.top = top;
|
||||
rect.bottom = bottom;
|
||||
|
||||
long current = 0;
|
||||
while (current < foundCount) {
|
||||
long next = current + 1;
|
||||
|
||||
rect.left = lefts[current];
|
||||
rect.right = rights[current];
|
||||
|
||||
while (next < foundCount && rect.right >= lefts[next]) {
|
||||
if (rect.right < rights[next])
|
||||
rect.right = rights[next];
|
||||
next++;
|
||||
}
|
||||
|
||||
dest->_AddRect(rect);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Divides the plane into horizontal bands, then passes those bands to r_or
|
||||
which does the real work.
|
||||
\param first The first region to be or-ed.
|
||||
\param second The second region to be or-ed.
|
||||
\param dest The destination region.
|
||||
*/
|
||||
void
|
||||
or_region_complex(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
long a = 0, b = 0;
|
||||
|
||||
int32 top;
|
||||
int32 bottom = min_c(first->bound.top, second->bound.top) - 1;
|
||||
do {
|
||||
long x;
|
||||
top = bottom + 1;
|
||||
bottom = kMaxVerticalExtent;
|
||||
|
||||
for (x = a; x < first->count; x++) {
|
||||
int32 n = first->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (first->data[x].bottom >= top && first->data[x].bottom < bottom)
|
||||
bottom = first->data[x].bottom;
|
||||
}
|
||||
|
||||
for (x = b; x < second->count; x++) {
|
||||
int32 n = second->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (second->data[x].bottom >= top && second->data[x].bottom < bottom)
|
||||
bottom = second->data[x].bottom;
|
||||
}
|
||||
|
||||
// We can stand a region which extends to kMaxVerticalExtent, not more
|
||||
if (bottom >= kMaxVerticalExtent)
|
||||
break;
|
||||
PRINT(("a: %d, b: %d, top: %d, bottom: %d\n", a, b, top, bottom));
|
||||
r_or(top, bottom, first, second, dest, &a, &b);
|
||||
|
||||
} while (true);
|
||||
|
||||
cleanup_region(dest);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
or_region_1_to_n(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
// The easy case first: if the first region contains the second,
|
||||
// the union is exactly the first region, since its bound is the
|
||||
// only rectangle.
|
||||
if ((first->bound.top <= second->bound.top)
|
||||
&& (first->bound.bottom >= second->bound.bottom)
|
||||
&& (first->bound.left <= second->bound.left)
|
||||
&& (first->bound.right >= second->bound.right))
|
||||
copy_region(first, dest);
|
||||
else
|
||||
or_region_complex(first, second, dest);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
or_region_no_x(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
zero_region(dest);
|
||||
|
||||
if (first->count == 0)
|
||||
for (long x = 0; x < second->count; x++)
|
||||
dest->_AddRect(second->data[x]);
|
||||
|
||||
else if (second->count == 0)
|
||||
for (long x = 0; x < first->count; x++)
|
||||
dest->_AddRect(first->data[x]);
|
||||
|
||||
else {
|
||||
long f = 0, s = 0;
|
||||
|
||||
while ((f < first->count) && (s < second->count)) {
|
||||
|
||||
if (first->data[f].top < second->data[s].top) {
|
||||
dest->_AddRect(first->data[f]);
|
||||
f++;
|
||||
|
||||
} else {
|
||||
dest->_AddRect(second->data[s]);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
if (f == first->count)
|
||||
for (; s < second->count; s++)
|
||||
dest->_AddRect(second->data[s]);
|
||||
|
||||
else if (s == second->count)
|
||||
for (; f < first->count; f++)
|
||||
dest->_AddRect(first->data[f]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the union of the two given regions.
|
||||
\param first The first region to be merged.
|
||||
\param second The second region to be merged.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is a sort of method selector. It checks for some special
|
||||
cases, then it calls the appropriate specialized function.
|
||||
*/
|
||||
void
|
||||
or_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
BRegion *regionA, *regionB;
|
||||
|
||||
// A little trick, to save some work...
|
||||
if (first->count != 0) {
|
||||
regionA = first;
|
||||
regionB = second;
|
||||
} else {
|
||||
regionA = second;
|
||||
regionB = first;
|
||||
}
|
||||
|
||||
if (regionB->count == 0)
|
||||
copy_region(regionA, dest);
|
||||
else {
|
||||
if (regionB->bound.top > regionA->bound.bottom)
|
||||
append_region(regionA, regionB, dest);
|
||||
|
||||
else if (regionA->bound.top > regionB->bound.bottom)
|
||||
append_region(regionB, regionA, dest);
|
||||
|
||||
else if (regionA->bound.left > regionB->bound.right)
|
||||
or_region_no_x(regionB, regionA, dest);
|
||||
|
||||
else if (regionB->bound.left > regionA->bound.right)
|
||||
or_region_no_x(regionA, regionB, dest);
|
||||
|
||||
else if (regionA->count == 1)
|
||||
or_region_1_to_n(regionA, regionB, dest);
|
||||
|
||||
else if (regionB->count == 1)
|
||||
or_region_1_to_n(regionB, regionA, dest);
|
||||
|
||||
else
|
||||
or_region_complex(regionA, regionB, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Divides the plane into horizontal bands, then passes those bands to r_sub
|
||||
which does the real work.
|
||||
\param first The subtraend region.
|
||||
\param second The minuend region.
|
||||
\param dest The destination region.
|
||||
*/
|
||||
void
|
||||
sub_region_complex(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
long a = 0, b = 0;
|
||||
|
||||
int32 top;
|
||||
int32 bottom = min_c(first->bound.top, second->bound.top) - 1;
|
||||
|
||||
do {
|
||||
long x;
|
||||
top = bottom + 1;
|
||||
bottom = kMaxVerticalExtent;
|
||||
|
||||
for (x = a; x < first->count; x++) {
|
||||
int32 n = first->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (first->data[x].bottom >= top && first->data[x].bottom < bottom)
|
||||
bottom = first->data[x].bottom;
|
||||
}
|
||||
|
||||
for (x = b; x < second->count; x++) {
|
||||
int32 n = second->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (second->data[x].bottom >= top && second->data[x].bottom < bottom)
|
||||
bottom = second->data[x].bottom;
|
||||
}
|
||||
|
||||
if (bottom >= kMaxVerticalExtent)
|
||||
break;
|
||||
|
||||
r_sub(top, bottom, first, second, dest, &a, &b);
|
||||
|
||||
} while (true);
|
||||
|
||||
cleanup_region(dest);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the difference of the two given regions.
|
||||
\param first The subtraend region.
|
||||
\param second The minuend region.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is a sort of method selector. It checks for some special
|
||||
cases, then it calls the appropriate specialized function.
|
||||
*/
|
||||
void
|
||||
sub_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
if (first->count == 0)
|
||||
zero_region(dest);
|
||||
|
||||
else if ((second->count == 0)
|
||||
|| (!valid_rect(sect_rect(first->bound, second->bound))))
|
||||
copy_region(first, dest);
|
||||
else
|
||||
sub_region_complex(second, first, dest);
|
||||
}
|
||||
|
||||
|
||||
/* BRegion class */
|
||||
|
||||
/*! \brief Initializes a region. The region will have no rects, and its bound will be invalid.
|
||||
/*! \brief Initializes a region. The region will have no rects,
|
||||
and its bound will be invalid.
|
||||
*/
|
||||
BRegion::BRegion()
|
||||
:data(NULL)
|
||||
:data_size(8),
|
||||
data(NULL)
|
||||
{
|
||||
data_size = 8;
|
||||
data = (clipping_rect*)malloc(data_size * sizeof(clipping_rect));
|
||||
|
||||
zero_region(this);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Initializes a region to be a copy of another.
|
||||
/*! \brief Initializes a region to be a copy of another.
|
||||
\param region The region to copy.
|
||||
*/
|
||||
BRegion::BRegion(const BRegion ®ion)
|
||||
@ -735,9 +75,9 @@ BRegion::BRegion(const BRegion ®ion)
|
||||
\param rect The BRect to set the region to.
|
||||
*/
|
||||
BRegion::BRegion(const BRect rect)
|
||||
:data(NULL)
|
||||
:data_size(8),
|
||||
data(NULL)
|
||||
{
|
||||
data_size = 8;
|
||||
data = (clipping_rect*)malloc(data_size * sizeof(clipping_rect));
|
||||
|
||||
Set(rect);
|
||||
@ -880,7 +220,7 @@ bool
|
||||
BRegion::Contains(BPoint pt) const
|
||||
{
|
||||
// If the point doesn't lie within the region's bounds,
|
||||
// don't ever try it against the region's rects.
|
||||
// don't even try it against the region's rects.
|
||||
if (!point_in(bound, pt))
|
||||
return false;
|
||||
|
||||
@ -1097,18 +437,20 @@ BRegion::_AddRect(clipping_rect rect)
|
||||
// Wait! We could merge "rect" with one of the
|
||||
// existing rectangles...
|
||||
long last = count - 1;
|
||||
if ((rect.top == data[last].bottom + 1)
|
||||
&& (rect.left == data[last].left)
|
||||
&& (rect.right == data[last].right)) {
|
||||
if (rect.top == data[last].bottom + 1
|
||||
&& rect.left == data[last].left
|
||||
&& rect.right == data[last].right) {
|
||||
|
||||
data[last].bottom = rect.bottom;
|
||||
|
||||
} else if ((rect.top == data[last].top) && (rect.bottom = data[last].bottom)) {
|
||||
} else if (rect.top == data[last].top && rect.bottom == data[last].bottom) {
|
||||
|
||||
if (rect.left == data[last].right + 1)
|
||||
data[last].right = rect.right;
|
||||
|
||||
else if (rect.right == data[last].left - 1)
|
||||
data[last].left = rect.left;
|
||||
|
||||
else
|
||||
add = true; //no luck... just add the rect
|
||||
|
||||
|
839
src/kits/interface/region_helpers.cpp
Normal file
839
src/kits/interface/region_helpers.cpp
Normal file
@ -0,0 +1,839 @@
|
||||
#include <Debug.h>
|
||||
|
||||
#include <clipping.h>
|
||||
#include "region_helpers.h"
|
||||
|
||||
static const int32 kMaxPoints = 1024;
|
||||
static const int32 kMaxVerticalExtent = 0x10000000;
|
||||
|
||||
/* friend functions */
|
||||
|
||||
/*! \brief zeroes the given region, setting its rect count to 0,
|
||||
and invalidating its bound rectangle.
|
||||
\param region The region to be zeroed.
|
||||
*/
|
||||
void
|
||||
zero_region(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
region->count = 0;
|
||||
region->bound.left = 0x7ffffffd;
|
||||
region->bound.top = 0x7ffffffd;
|
||||
region->bound.right = 0x80000003;
|
||||
region->bound.bottom = 0x80000003;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
clear_region(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
region->count = 0;
|
||||
region->bound.left = 0xfffffff;
|
||||
region->bound.top = 0xfffffff;
|
||||
region->bound.right = 0xf0000001;
|
||||
region->bound.bottom = 0xf0000001;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Cleanup the region vertically.
|
||||
\param region The region to be cleaned.
|
||||
*/
|
||||
void
|
||||
cleanup_region_1(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
clipping_rect testRect =
|
||||
{
|
||||
1, 1,
|
||||
-1, -2
|
||||
};
|
||||
|
||||
long newCount = -1;
|
||||
|
||||
if (region->count > 0) {
|
||||
for (long x = 0; x < region->count; x++) {
|
||||
clipping_rect rect = region->data[x];
|
||||
|
||||
if (rect.left == testRect.left && rect.right == testRect.right
|
||||
&& rect.top == testRect.bottom + 1) {
|
||||
|
||||
ASSERT(newCount >= 0);
|
||||
region->data[newCount].bottom = rect.bottom;
|
||||
|
||||
} else {
|
||||
newCount++;
|
||||
region->data[newCount] = region->data[x];
|
||||
testRect = region->data[x];
|
||||
}
|
||||
}
|
||||
region->count = newCount + 1;
|
||||
|
||||
cleanup_region_horizontal(region);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Cleanup the region, by merging rects that can be merged.
|
||||
\param region The region to be cleaned.
|
||||
*/
|
||||
void
|
||||
cleanup_region(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
long oldCount;
|
||||
|
||||
do {
|
||||
oldCount = region->count;
|
||||
cleanup_region_1(region);
|
||||
} while (region->count < oldCount);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Sorts the given rects by their top value.
|
||||
\param rects A pointer to an array of clipping_rects.
|
||||
\param count The number of rectangles in the array.
|
||||
*/
|
||||
void
|
||||
sort_rects(clipping_rect *rects, long count)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
bool again; //flag that tells we changed rects positions
|
||||
|
||||
if (count == 2) {
|
||||
if (rects[0].top > rects[1].top) {
|
||||
clipping_rect tmp = rects[0];
|
||||
rects[0] = rects[1];
|
||||
rects[1] = tmp;
|
||||
}
|
||||
} else if (count > 2) {
|
||||
do {
|
||||
again = false;
|
||||
for (long c = 1; c < count; c++) {
|
||||
if (rects[c - 1].top > rects[c].top) {
|
||||
clipping_rect tmp = rects[c - 1];
|
||||
rects[c - 1] = rects[c];
|
||||
rects[c] = tmp;
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
} while (again);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
sort_trans(long *lptr1, long *lptr2, long count)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
bool again; //flag that tells we changed trans positions
|
||||
|
||||
if (count == 2) {
|
||||
if (lptr1[0] > lptr1[1]) {
|
||||
int32 tmp = lptr1[0];
|
||||
lptr1[0] = lptr1[1];
|
||||
lptr1[1] = tmp;
|
||||
|
||||
tmp = lptr2[0];
|
||||
lptr2[0] = lptr2[1];
|
||||
lptr2[1] = tmp;
|
||||
}
|
||||
} else if (count > 2) {
|
||||
do {
|
||||
again = false;
|
||||
for (long c = 1; c < count; c++) {
|
||||
if (lptr1[c - 1] > lptr1[c]) {
|
||||
int32 tmp = lptr1[c - 1];
|
||||
lptr1[c - 1] = lptr1[c];
|
||||
lptr1[c] = tmp;
|
||||
|
||||
tmp = lptr2[c - 1];
|
||||
lptr2[c - 1] = lptr2[c];
|
||||
lptr2[c] = tmp;
|
||||
again = true;
|
||||
}
|
||||
}
|
||||
} while (again);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Cleanup the region horizontally.
|
||||
\param region The region to be cleaned.
|
||||
*/
|
||||
void
|
||||
cleanup_region_horizontal(BRegion *region)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
clipping_rect testRect =
|
||||
{
|
||||
1, 1,
|
||||
-2, -1
|
||||
};
|
||||
|
||||
long newCount = -1;
|
||||
|
||||
for (long x = 0; x < region->count; x++) {
|
||||
clipping_rect rect = region->data[x];
|
||||
if (rect.top == testRect.top && rect.bottom == testRect.bottom
|
||||
&& rect.left == testRect.right + 1) {
|
||||
|
||||
ASSERT(newCount >= 0);
|
||||
region->data[newCount].right = rect.right;
|
||||
|
||||
} else {
|
||||
newCount++;
|
||||
region->data[newCount] = rect;
|
||||
}
|
||||
testRect = region->data[newCount];
|
||||
}
|
||||
region->count = newCount + 1;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Copy a region to another.
|
||||
\param source The region to be copied.
|
||||
\param dest The destination region.
|
||||
*/
|
||||
void
|
||||
copy_region(BRegion *source, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(source);
|
||||
ASSERT(dest);
|
||||
ASSERT(source != dest);
|
||||
|
||||
// If there is not enough memory, allocate
|
||||
if (dest->data_size < source->count) {
|
||||
free(dest->data);
|
||||
dest->data_size = source->count + 8;
|
||||
dest->data = (clipping_rect*)malloc(dest->data_size * sizeof(clipping_rect));
|
||||
}
|
||||
|
||||
dest->count = source->count;
|
||||
|
||||
// Copy rectangles and bounds.
|
||||
memcpy(dest->data, source->data, source->count * sizeof(clipping_rect));
|
||||
dest->bound = source->bound;
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Copy a region to another, allocating some additional memory in the destination region.
|
||||
\param source The region to be copied.
|
||||
\param dest The destination region.
|
||||
\param count Amount of additional memory to be allocated in the destination region.
|
||||
*/
|
||||
void
|
||||
copy_region_n(BRegion *source, BRegion *dest, long count)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(source);
|
||||
ASSERT(dest);
|
||||
ASSERT(source != dest);
|
||||
|
||||
// If there is not enough memory, allocate
|
||||
if (dest->data_size < source->count) {
|
||||
free(dest->data);
|
||||
dest->data_size = source->count + count;
|
||||
dest->data = (clipping_rect*)malloc(dest->data_size * sizeof(clipping_rect));
|
||||
}
|
||||
|
||||
dest->count = source->count;
|
||||
|
||||
// Copy rectangles and bounds.
|
||||
memcpy(dest->data, source->data, source->count * sizeof(clipping_rect));
|
||||
dest->bound = source->bound;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
and_region_complex(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
zero_region(dest);
|
||||
|
||||
for (long f = 0; f < first->count; f++) {
|
||||
for (long s = 0; s < second->count; s++) {
|
||||
clipping_rect testRect = sect_rect(first->data[f], second->data[s]);
|
||||
if (valid_rect(testRect))
|
||||
dest->_AddRect(testRect);
|
||||
}
|
||||
}
|
||||
|
||||
if (dest->count > 1)
|
||||
sort_rects(dest->data, dest->count);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
and_region_1_to_n(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
// The easy case first: We already know that the regions intersect,
|
||||
// so we check if the first region contains the second.
|
||||
// If it's the case, the intersection is exactly the second region.
|
||||
if (first->bound.top <= second->bound.top
|
||||
&& first->bound.bottom >= second->bound.bottom
|
||||
&& first->bound.left <= second->bound.left
|
||||
&& first->bound.right >= second->bound.right)
|
||||
copy_region(second, dest);
|
||||
else {
|
||||
// Otherwise, we check the rect of the first region against the rects
|
||||
// of the second, and we add their intersections to the destination region
|
||||
zero_region(dest);
|
||||
for (long x = 0; x < second->count; x++) {
|
||||
clipping_rect testRect = sect_rect(first->data[0], second->data[x]);
|
||||
if (valid_rect(testRect))
|
||||
dest->_AddRect(testRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the intersection of the two given regions.
|
||||
\param first The first region to be intersected.
|
||||
\param second The second region to be intersected.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is a sort of method selector. It checks for some special
|
||||
cases, then it calls the appropriate specialized function.
|
||||
*/
|
||||
void
|
||||
and_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
clipping_rect intersection = sect_rect(first->bound, second->bound);
|
||||
|
||||
if (first->count == 0 || second->count == 0 || !valid_rect(intersection))
|
||||
zero_region(dest);
|
||||
|
||||
else if (first->count == 1 && second->count == 1) {
|
||||
dest->data[0] = intersection;
|
||||
dest->bound = intersection;
|
||||
dest->count = 1;
|
||||
}
|
||||
else if (first->count > 1 && second->count == 1)
|
||||
and_region_1_to_n(second, first, dest);
|
||||
|
||||
else if (first->count == 1 && second->count > 1)
|
||||
and_region_1_to_n(first, second, dest);
|
||||
|
||||
else
|
||||
and_region_complex(first, second, dest);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the union of the two given regions.
|
||||
\param first The first region to be or-ed.
|
||||
\param second The second region to be or-ed.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is called by or_region when the two regions don't intersect,
|
||||
and when the second region top coordinate is bigger than first region's bottom
|
||||
coordinate.
|
||||
*/
|
||||
void
|
||||
append_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
copy_region(first, dest);
|
||||
|
||||
for (long c = 0; c < second->count; c++)
|
||||
dest->_AddRect(second->data[c]);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
r_or(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *indexA, long *indexB)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
int32 lefts[kMaxPoints];
|
||||
int32 rights[kMaxPoints];
|
||||
|
||||
long i1 = *indexA;
|
||||
long i2 = *indexB;
|
||||
|
||||
*indexA = -1;
|
||||
*indexB = -1;
|
||||
|
||||
long foundCount = 0;
|
||||
long x = 0;
|
||||
|
||||
// Store left and right points to the appropriate array
|
||||
for (x = i1; x < first->count; x++) {
|
||||
|
||||
// Look if this rect can be used next time we are called,
|
||||
// thus correctly maintaining the "index" parameters.
|
||||
if (first->data[x].bottom >= top && *indexA == -1)
|
||||
*indexA = x;
|
||||
|
||||
if (first->data[x].top <= top && first->data[x].bottom >= bottom) {
|
||||
lefts[foundCount] = first->data[x].left;
|
||||
rights[foundCount] = first->data[x].right;
|
||||
foundCount++;
|
||||
} else if (first->data[x].top > bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*indexA == -1)
|
||||
*indexA = i1;
|
||||
|
||||
for (x = i2; x < second->count; x++) {
|
||||
if (second->data[x].bottom >= top && *indexB == -1)
|
||||
*indexB = x;
|
||||
|
||||
if (second->data[x].top <= top && second->data[x].bottom >= bottom) {
|
||||
lefts[foundCount] = second->data[x].left;
|
||||
rights[foundCount] = second->data[x].right;
|
||||
foundCount++;
|
||||
} else if (second->data[x].top > bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*indexB == -1)
|
||||
*indexB = i2;
|
||||
|
||||
if (foundCount > 1)
|
||||
sort_trans(lefts, rights, foundCount);
|
||||
|
||||
ASSERT(foundCount > 0);
|
||||
|
||||
clipping_rect rect;
|
||||
rect.top = top;
|
||||
rect.bottom = bottom;
|
||||
|
||||
// Check if a rect intersects with the next one.
|
||||
// If so, merge the two rects, if not, just add the rect.
|
||||
long current = 0;
|
||||
while (current < foundCount) {
|
||||
long next = current + 1;
|
||||
|
||||
rect.left = lefts[current];
|
||||
rect.right = rights[current];
|
||||
|
||||
while (next < foundCount && rect.right >= lefts[next]) {
|
||||
if (rect.right < rights[next])
|
||||
rect.right = rights[next];
|
||||
next++;
|
||||
}
|
||||
|
||||
dest->_AddRect(rect);
|
||||
current = next;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Divides the plane into horizontal bands, then passes those bands to r_or
|
||||
which does the real work.
|
||||
\param first The first region to be or-ed.
|
||||
\param second The second region to be or-ed.
|
||||
\param dest The destination region.
|
||||
*/
|
||||
void
|
||||
or_region_complex(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
long a = 0, b = 0;
|
||||
|
||||
int32 top;
|
||||
int32 bottom = min_c(first->bound.top, second->bound.top) - 1;
|
||||
do {
|
||||
long x;
|
||||
top = bottom + 1;
|
||||
bottom = kMaxVerticalExtent;
|
||||
|
||||
for (x = a; x < first->count; x++) {
|
||||
int32 n = first->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (first->data[x].bottom >= top && first->data[x].bottom < bottom)
|
||||
bottom = first->data[x].bottom;
|
||||
}
|
||||
|
||||
for (x = b; x < second->count; x++) {
|
||||
int32 n = second->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (second->data[x].bottom >= top && second->data[x].bottom < bottom)
|
||||
bottom = second->data[x].bottom;
|
||||
}
|
||||
|
||||
// We can stand a region which extends to kMaxVerticalExtent, not more
|
||||
if (bottom >= kMaxVerticalExtent)
|
||||
break;
|
||||
|
||||
r_or(top, bottom, first, second, dest, &a, &b);
|
||||
|
||||
} while (true);
|
||||
|
||||
cleanup_region(dest);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the union of the two given regions.
|
||||
\param first The first region to be or-ed.
|
||||
\param second The second region to be or-ed.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is called by or_region when one of the two regions contains just
|
||||
one rect.
|
||||
*/
|
||||
void
|
||||
or_region_1_to_n(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
// The easy case first: if the first region contains the second,
|
||||
// the union is exactly the first region, since its bound is the
|
||||
// only rectangle.
|
||||
if (first->bound.top <= second->bound.top
|
||||
&& first->bound.bottom >= second->bound.bottom
|
||||
&& first->bound.left <= second->bound.left
|
||||
&& first->bound.right >= second->bound.right)
|
||||
copy_region(first, dest);
|
||||
else
|
||||
or_region_complex(first, second, dest);
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the union of the two given regions.
|
||||
\param first The first region to be or-ed.
|
||||
\param second The second region to be or-ed.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is called by or_region when the two regions don't intersect.
|
||||
*/
|
||||
void
|
||||
or_region_no_x(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
zero_region(dest);
|
||||
|
||||
long x;
|
||||
|
||||
if (first->count == 0)
|
||||
for (x = 0; x < second->count; x++)
|
||||
dest->_AddRect(second->data[x]);
|
||||
|
||||
else if (second->count == 0)
|
||||
for (x = 0; x < first->count; x++)
|
||||
dest->_AddRect(first->data[x]);
|
||||
|
||||
else {
|
||||
long f = 0, s = 0;
|
||||
|
||||
while (f < first->count && s < second->count) {
|
||||
|
||||
if (first->data[f].top < second->data[s].top) {
|
||||
dest->_AddRect(first->data[f]);
|
||||
f++;
|
||||
|
||||
} else {
|
||||
dest->_AddRect(second->data[s]);
|
||||
s++;
|
||||
}
|
||||
}
|
||||
|
||||
if (f == first->count)
|
||||
for (; s < second->count; s++)
|
||||
dest->_AddRect(second->data[s]);
|
||||
|
||||
else if (s == second->count)
|
||||
for (; f < first->count; f++)
|
||||
dest->_AddRect(first->data[f]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the union of the two given regions.
|
||||
\param first The first region to be merged.
|
||||
\param second The second region to be merged.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is a sort of method selector. It checks for some special
|
||||
cases, then it calls the appropriate specialized function.
|
||||
*/
|
||||
void
|
||||
or_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
BRegion *regionA, *regionB;
|
||||
|
||||
// A little trick, to save some work...
|
||||
if (first->count != 0) {
|
||||
regionA = first;
|
||||
regionB = second;
|
||||
} else {
|
||||
regionA = second;
|
||||
regionB = first;
|
||||
}
|
||||
|
||||
if (regionB->count == 0)
|
||||
copy_region(regionA, dest);
|
||||
else {
|
||||
if (regionB->bound.top > regionA->bound.bottom)
|
||||
append_region(regionA, regionB, dest);
|
||||
|
||||
else if (regionA->bound.top > regionB->bound.bottom)
|
||||
append_region(regionB, regionA, dest);
|
||||
|
||||
else if (regionA->bound.left > regionB->bound.right)
|
||||
or_region_no_x(regionB, regionA, dest);
|
||||
|
||||
else if (regionB->bound.left > regionA->bound.right)
|
||||
or_region_no_x(regionA, regionB, dest);
|
||||
|
||||
else if (regionA->count == 1)
|
||||
or_region_1_to_n(regionA, regionB, dest);
|
||||
|
||||
else if (regionB->count == 1)
|
||||
or_region_1_to_n(regionB, regionA, dest);
|
||||
|
||||
else
|
||||
or_region_complex(regionA, regionB, dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Divides the plane into horizontal bands, then passes those bands to r_sub
|
||||
which does the real work.
|
||||
\param first The subtraend region.
|
||||
\param second The minuend region.
|
||||
\param dest The destination region.
|
||||
*/
|
||||
void
|
||||
sub_region_complex(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
long a = 0, b = 0;
|
||||
|
||||
int32 top;
|
||||
int32 bottom = min_c(first->bound.top, second->bound.top) - 1;
|
||||
|
||||
do {
|
||||
long x;
|
||||
top = bottom + 1;
|
||||
bottom = kMaxVerticalExtent;
|
||||
|
||||
for (x = a; x < first->count; x++) {
|
||||
int32 n = first->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (first->data[x].bottom >= top && first->data[x].bottom < bottom)
|
||||
bottom = first->data[x].bottom;
|
||||
}
|
||||
|
||||
for (x = b; x < second->count; x++) {
|
||||
int32 n = second->data[x].top - 1;
|
||||
if (n >= top && n < bottom)
|
||||
bottom = n;
|
||||
if (second->data[x].bottom >= top && second->data[x].bottom < bottom)
|
||||
bottom = second->data[x].bottom;
|
||||
}
|
||||
|
||||
if (bottom >= kMaxVerticalExtent)
|
||||
break;
|
||||
|
||||
r_sub(top, bottom, first, second, dest, &a, &b);
|
||||
|
||||
} while (true);
|
||||
|
||||
cleanup_region(dest);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
r_sub(long top, long bottom, BRegion *first, BRegion *second, BRegion *dest, long *indexA, long *indexB)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
|
||||
int32 leftsA[kMaxPoints / 2];
|
||||
int32 rightsA[kMaxPoints / 2];
|
||||
int32 leftsB[kMaxPoints / 2];
|
||||
int32 rightsB[kMaxPoints / 2];
|
||||
|
||||
long i1 = *indexA;
|
||||
long i2 = *indexB;
|
||||
|
||||
*indexA = -1;
|
||||
*indexB = -1;
|
||||
|
||||
long foundA = 0;
|
||||
long foundB = 0;
|
||||
long x = 0;
|
||||
|
||||
// Store left and right points to the appropriate array
|
||||
for (x = i1; x < first->count; x++) {
|
||||
|
||||
// Look if this rect can be used next time we are called,
|
||||
// thus correctly maintaining the "index" parameters.
|
||||
if (first->data[x].bottom >= top && *indexA == -1)
|
||||
*indexA = x;
|
||||
|
||||
if (first->data[x].top <= top && first->data[x].bottom >= bottom) {
|
||||
leftsA[foundA] = first->data[x].left;
|
||||
rightsA[foundA] = first->data[x].right;
|
||||
foundA++;
|
||||
} else if (first->data[x].top > bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*indexA == -1)
|
||||
*indexA = i1;
|
||||
|
||||
if (foundA > 1)
|
||||
sort_trans(leftsA, rightsA, foundA);
|
||||
|
||||
for (x = i2; x < second->count; x++) {
|
||||
if (second->data[x].bottom >= top && *indexB == -1)
|
||||
*indexB = x;
|
||||
|
||||
if (second->data[x].top <= top && second->data[x].bottom >= bottom) {
|
||||
leftsB[foundB] = second->data[x].left;
|
||||
rightsB[foundB] = second->data[x].right;
|
||||
foundB++;
|
||||
} else if (second->data[x].top > bottom)
|
||||
break;
|
||||
}
|
||||
|
||||
if (*indexB == -1)
|
||||
*indexB = i2;
|
||||
|
||||
if (foundB > 1)
|
||||
sort_trans(leftsB, rightsB, foundB);
|
||||
|
||||
// No minuend's rect, just add all the subtraend's rects.
|
||||
if (foundA == 0)
|
||||
for (x = 0; x < foundB; x++) {
|
||||
clipping_rect rect = { leftsB[x], top, rightsB[x], bottom };
|
||||
dest->_AddRect(rect);
|
||||
}
|
||||
else if (foundB > 0) {
|
||||
long f = 0, s = 0;
|
||||
|
||||
clipping_rect minuendRect;
|
||||
minuendRect.top = top;
|
||||
minuendRect.bottom = bottom;
|
||||
minuendRect.left = 0x80000003;
|
||||
|
||||
clipping_rect subRect;
|
||||
subRect.top = top;
|
||||
subRect.bottom = bottom;
|
||||
|
||||
// We take the empty spaces between the minuend rects, and intersect
|
||||
// these with the subtraend rects. We then add their intersection
|
||||
// to the destination region.
|
||||
do {
|
||||
subRect.left = leftsB[s];
|
||||
subRect.right = rightsB[s];
|
||||
|
||||
if (f != 0)
|
||||
minuendRect.left = rightsA[f - 1] + 1;
|
||||
|
||||
minuendRect.right = leftsA[f] - 1;
|
||||
|
||||
if (leftsB[s] > minuendRect.right) {
|
||||
if (++f > foundA)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
|
||||
clipping_rect intersection = sect_rect(minuendRect, subRect);
|
||||
|
||||
if (valid_rect(intersection))
|
||||
dest->_AddRect(intersection);
|
||||
|
||||
if (rightsB[s] < minuendRect.left)
|
||||
s++;
|
||||
|
||||
if (s >= foundB)
|
||||
break;
|
||||
|
||||
f++;
|
||||
|
||||
} while (f < foundA);
|
||||
|
||||
//Last rect: we take the right coordinate of the last minuend rect
|
||||
//as left coordinate of the rect to intersect, and the maximum possible
|
||||
//value as the right coordinate.
|
||||
minuendRect.left = rightsA[foundA - 1] + 1;
|
||||
minuendRect.right = 0x7ffffffd;
|
||||
|
||||
// Skip the subtraend rects that could never intersect.
|
||||
while (s < foundB && rightsB[s] < minuendRect.left)
|
||||
s++;
|
||||
|
||||
for (long c = s; c < foundB; c++) {
|
||||
subRect.left = leftsB[c];
|
||||
subRect.right = rightsB[c];
|
||||
|
||||
clipping_rect intersection = sect_rect(minuendRect, subRect);
|
||||
|
||||
if (valid_rect(intersection))
|
||||
dest->_AddRect(intersection);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/*! \brief Modify the destination region to be the difference of the two given regions.
|
||||
\param first The subtraend region.
|
||||
\param second The minuend region.
|
||||
\param dest The destination region.
|
||||
|
||||
This function is a sort of method selector. It checks for some special
|
||||
cases, then it calls the appropriate specialized function.
|
||||
*/
|
||||
void
|
||||
sub_region(BRegion *first, BRegion *second, BRegion *dest)
|
||||
{
|
||||
PRINT(("%s\n", __PRETTY_FUNCTION__));
|
||||
ASSERT(first);
|
||||
ASSERT(second);
|
||||
ASSERT(dest);
|
||||
|
||||
if (first->count == 0)
|
||||
zero_region(dest);
|
||||
|
||||
else if (second->count == 0 || !valid_rect(sect_rect(first->bound, second->bound)))
|
||||
copy_region(first, dest);
|
||||
|
||||
else
|
||||
sub_region_complex(second, first, dest);
|
||||
}
|
||||
|
27
src/kits/interface/region_helpers.h
Normal file
27
src/kits/interface/region_helpers.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef __REGION_HELPERS_H
|
||||
#define __REGION_HELPERS_H
|
||||
|
||||
void zero_region(BRegion *a_region);
|
||||
void clear_region(BRegion *a_region);
|
||||
void cleanup_region_1(BRegion *region_in);
|
||||
void cleanup_region(BRegion *region_in);
|
||||
void sort_rects(clipping_rect *rects, long count);
|
||||
void sort_trans(long *lptr1, long *lptr2, long count);
|
||||
void cleanup_region_horizontal(BRegion *region_in);
|
||||
void copy_region(BRegion *src_region, BRegion *dst_region);
|
||||
void copy_region_n(BRegion*, BRegion*, long);
|
||||
void and_region_complex(BRegion*, BRegion*, BRegion*);
|
||||
void and_region_1_to_n(BRegion*, BRegion*, BRegion*);
|
||||
void and_region(BRegion*, BRegion*, BRegion*);
|
||||
void append_region(BRegion*, BRegion*, BRegion*);
|
||||
void r_or(long, long, BRegion*, BRegion*, BRegion*, long*, long *);
|
||||
void or_region_complex(BRegion*, BRegion*, BRegion*);
|
||||
void or_region_1_to_n(BRegion*, BRegion*, BRegion*);
|
||||
void or_region_no_x(BRegion*, BRegion*, BRegion*);
|
||||
void or_region(BRegion*, BRegion*, BRegion*);
|
||||
//void sub(long, long, BRegion*, BRegion*, BRegion*, long*, long*);
|
||||
void sub_region_complex(BRegion*, BRegion*, BRegion*);
|
||||
void r_sub(long , long, BRegion*, BRegion*, BRegion*, long*, long*);
|
||||
void sub_region(BRegion*, BRegion*, BRegion*);
|
||||
|
||||
#endif // __REGION_HELPERS_H
|
Loading…
Reference in New Issue
Block a user