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:
Stefano Ceccherini 2003-07-14 06:22:47 +00:00
parent 910bbbc473
commit 791e557c1b
3 changed files with 890 additions and 682 deletions

View File

@ -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 &region)
@ -735,9 +75,9 @@ BRegion::BRegion(const BRegion &region)
\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

View 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);
}

View 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