git-svn-id: file:///srv/svn/repos/haiku/trunk/current@5302 a95241bf-73f2-0310-859d-f6bbb57e9c96
512 lines
12 KiB
C++
512 lines
12 KiB
C++
//------------------------------------------------------------------------------
|
|
// 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"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
// DEALINGS IN THE SOFTWARE.
|
|
//
|
|
// File Name: Region.cpp
|
|
// Author: Stefano Ceccherini ([email protected])
|
|
// Description: Region class consisting of multiple rectangles
|
|
//
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Notes: As now, memory is always allocated and never freed (except on destruction,
|
|
// or sometimes when a copy is made).
|
|
// This let us be a bit faster since we don't do many reallocations.
|
|
// But that means that even an empty region could "waste" much space, if it contained
|
|
// many rects before being emptied.
|
|
// I.E: a region which contains 24 rects allocates more than 24 * 4 * sizeof(int32)
|
|
// = 96 * sizeof(int32) bytes. If we call MakeEmpty(), that region will contain no rects,
|
|
// but it will still keep the allocated memory.
|
|
// This shouldnt' be an issue, since usually BRegions are just used for calculations,
|
|
// and don't last so long.
|
|
// Anyway, we can change that behaviour if we want, but BeOS's BRegion seems to behave exactly
|
|
// like this.
|
|
|
|
|
|
// Standard Includes -----------------------------------------------------------
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
// System Includes -------------------------------------------------------------
|
|
#include <Debug.h>
|
|
#include <Region.h>
|
|
|
|
// Private Includes -------------------------------------------------------------
|
|
#include <clipping.h>
|
|
#include <RegionSupport.h>
|
|
|
|
|
|
/*! \brief Initializes a region. The region will have no rects,
|
|
and its bound will be invalid.
|
|
*/
|
|
BRegion::BRegion()
|
|
:
|
|
data_size(8),
|
|
data(NULL)
|
|
{
|
|
data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
|
|
|
|
Support::ZeroRegion(this);
|
|
}
|
|
|
|
|
|
/*! \brief Initializes a region to be a copy of another.
|
|
\param region The region to copy.
|
|
*/
|
|
BRegion::BRegion(const BRegion ®ion)
|
|
:
|
|
data(NULL)
|
|
{
|
|
bound = region.bound;
|
|
count = region.count;
|
|
data_size = region.data_size;
|
|
|
|
if (data_size <= 0)
|
|
data_size = 1;
|
|
|
|
data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
|
|
|
|
memcpy(data, region.data, count * sizeof(clipping_rect));
|
|
}
|
|
|
|
|
|
/*! \brief Initializes a region to contain a BRect.
|
|
\param rect The BRect to set the region to.
|
|
*/
|
|
BRegion::BRegion(const BRect rect)
|
|
:
|
|
data_size(8),
|
|
data(NULL)
|
|
{
|
|
data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
|
|
|
|
Set(rect);
|
|
}
|
|
|
|
|
|
/*! \brief Frees the allocated memory.
|
|
*/
|
|
BRegion::~BRegion()
|
|
{
|
|
free(data);
|
|
}
|
|
|
|
|
|
/*! \brief Returns the bounds of the region.
|
|
\return A BRect which represents the bounds of the region.
|
|
*/
|
|
BRect
|
|
BRegion::Frame() const
|
|
{
|
|
return to_BRect(bound);
|
|
}
|
|
|
|
|
|
/*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates).
|
|
\return A clipping_rect which represents the bounds of the region.
|
|
*/
|
|
clipping_rect
|
|
BRegion::FrameInt() const
|
|
{
|
|
return bound;
|
|
}
|
|
|
|
|
|
/*! \brief Returns the regions's BRect at the given index.
|
|
\param index The index (zero based) of the wanted rectangle.
|
|
\return If the given index is valid, it returns the BRect at that index,
|
|
otherwise, it returns an invalid BRect.
|
|
*/
|
|
BRect
|
|
BRegion::RectAt(int32 index)
|
|
{
|
|
if (index >= 0 && index < count)
|
|
return to_BRect(data[index]);
|
|
|
|
return BRect(); //An invalid BRect
|
|
}
|
|
|
|
|
|
/*! \brief Returns the regions's clipping_rect at the given index.
|
|
\param index The index (zero based) of the wanted rectangle.
|
|
\return If the given index is valid, it returns the clipping_rect at that index,
|
|
otherwise, it returns an invalid clipping_rect.
|
|
*/
|
|
clipping_rect
|
|
BRegion::RectAtInt(int32 index)
|
|
{
|
|
clipping_rect rect = { 1, 1, 0, 0 };
|
|
|
|
if (index >= 0 && index < count)
|
|
return data[index];
|
|
|
|
return rect;
|
|
}
|
|
|
|
|
|
/*! \brief Counts the region rects.
|
|
\return An int32 which is the total number of rects in the region.
|
|
*/
|
|
int32
|
|
BRegion::CountRects()
|
|
{
|
|
return count;
|
|
}
|
|
|
|
|
|
/*! \brief Set the region to contain just the given BRect.
|
|
\param newBounds A BRect.
|
|
*/
|
|
void
|
|
BRegion::Set(BRect newBounds)
|
|
{
|
|
Set(to_clipping_rect(newBounds));
|
|
}
|
|
|
|
|
|
/*! \brief Set the region to contain just the given clipping_rect.
|
|
\param newBounds A clipping_rect.
|
|
*/
|
|
void
|
|
BRegion::Set(clipping_rect newBounds)
|
|
{
|
|
ASSERT(data_size > 0);
|
|
|
|
if (valid_rect(newBounds)) {
|
|
count = 1;
|
|
data[0] = newBounds;
|
|
bound = newBounds;
|
|
}
|
|
else
|
|
Support::ZeroRegion(this);
|
|
}
|
|
|
|
|
|
/*! \brief Check if the region has any area in common with the given BRect.
|
|
\param rect The BRect to check the region against to.
|
|
\return \ctrue if the region has any area in common with the BRect, \cfalse if not.
|
|
*/
|
|
bool
|
|
BRegion::Intersects(BRect rect) const
|
|
{
|
|
return Intersects(to_clipping_rect(rect));
|
|
}
|
|
|
|
|
|
/*! \brief Check if the region has any area in common with the given clipping_rect.
|
|
\param rect The clipping_rect to check the region against to.
|
|
\return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not.
|
|
*/
|
|
bool
|
|
BRegion::Intersects(clipping_rect rect) const
|
|
{
|
|
if (!rects_intersect(rect, bound))
|
|
return false;
|
|
|
|
for (long c = 0; c < count; c++) {
|
|
if (rects_intersect(data[c], rect))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*! \brief Check if the region contains the given BPoint.
|
|
\param pt The BPoint to be checked.
|
|
\return \ctrue if the region contains the BPoint, \cfalse if not.
|
|
*/
|
|
bool
|
|
BRegion::Contains(BPoint pt) const
|
|
{
|
|
// If the point doesn't lie within the region's bounds,
|
|
// don't even try it against the region's rects.
|
|
if (!point_in(bound, pt))
|
|
return false;
|
|
|
|
for (long c = 0; c < count; c++) {
|
|
if (point_in(data[c], pt))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*! \brief Check if the region contains the given coordinates.
|
|
\param x The \cx coordinate of the point to be checked.
|
|
\param y The \cy coordinate of the point to be checked.
|
|
\return \ctrue if the region contains the point, \cfalse if not.
|
|
*/
|
|
bool
|
|
BRegion::Contains(int32 x, int32 y)
|
|
{
|
|
// see above
|
|
if (!point_in(bound, x, y))
|
|
return false;
|
|
|
|
for (long c = 0; c < count; c++) {
|
|
if (point_in(data[c], x, y))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*! \brief Prints the BRegion to stdout.
|
|
*/
|
|
void
|
|
BRegion::PrintToStream() const
|
|
{
|
|
Frame().PrintToStream();
|
|
|
|
for (long c = 0; c < count; c++) {
|
|
clipping_rect *rect = &data[c];
|
|
printf("data = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n",
|
|
rect->left, rect->top, rect->right, rect->bottom);
|
|
}
|
|
}
|
|
|
|
|
|
/*! \brief Offsets all region's rects, and bounds by the given values.
|
|
\param dh The horizontal offset.
|
|
\param dv The vertical offset.
|
|
*/
|
|
void
|
|
BRegion::OffsetBy(int32 dh, int32 dv)
|
|
{
|
|
if (count > 0) {
|
|
for (long c = 0; c < count; c++)
|
|
offset_rect(data[c], dh, dv);
|
|
|
|
offset_rect(bound, dh, dv);
|
|
}
|
|
}
|
|
|
|
|
|
/*! \brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
|
|
*/
|
|
void
|
|
BRegion::MakeEmpty()
|
|
{
|
|
Support::ZeroRegion(this);
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, so that it includes the given BRect.
|
|
\param rect The BRect to be included by the region.
|
|
*/
|
|
void
|
|
BRegion::Include(BRect rect)
|
|
{
|
|
Include(to_clipping_rect(rect));
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, so that it includes the given clipping_rect.
|
|
\param rect The clipping_rect to be included by the region.
|
|
*/
|
|
void
|
|
BRegion::Include(clipping_rect rect)
|
|
{
|
|
BRegion region;
|
|
BRegion newRegion;
|
|
|
|
region.Set(rect);
|
|
|
|
Support::OrRegion(this, ®ion, &newRegion);
|
|
Support::CopyRegion(&newRegion, this);
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, so that it includes the area of the given region.
|
|
\param region The region to be included.
|
|
*/
|
|
void
|
|
BRegion::Include(const BRegion *region)
|
|
{
|
|
BRegion newRegion;
|
|
|
|
Support::OrRegion(this, const_cast<BRegion *>(region), &newRegion);
|
|
Support::CopyRegion(&newRegion, this);
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, excluding the area represented by the given BRect.
|
|
\param rect The BRect to be excluded.
|
|
*/
|
|
void
|
|
BRegion::Exclude(BRect rect)
|
|
{
|
|
Exclude(to_clipping_rect(rect));
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, excluding the area represented by the given clipping_rect.
|
|
\param rect The clipping_rect to be excluded.
|
|
*/
|
|
void
|
|
BRegion::Exclude(clipping_rect rect)
|
|
{
|
|
BRegion region;
|
|
BRegion newRegion;
|
|
|
|
region.Set(rect);
|
|
|
|
Support::SubRegion(this, ®ion, &newRegion);
|
|
Support::CopyRegion(&newRegion, this);
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, excluding the area contained in the given BRegion.
|
|
\param region The BRegion to be excluded.
|
|
*/
|
|
void
|
|
BRegion::Exclude(const BRegion *region)
|
|
{
|
|
BRegion newRegion;
|
|
|
|
Support::SubRegion(this, const_cast<BRegion *>(region), &newRegion);
|
|
Support::CopyRegion(&newRegion, this);
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region, so that it will contain just the area in common with the given BRegion.
|
|
\param region the BRegion to intersect to.
|
|
*/
|
|
void
|
|
BRegion::IntersectWith(const BRegion *region)
|
|
{
|
|
BRegion newRegion;
|
|
|
|
Support::AndRegion(this, const_cast<BRegion *>(region), &newRegion);
|
|
Support::CopyRegion(&newRegion, this);
|
|
}
|
|
|
|
|
|
/*! \brief Modifies the region to be a copy of the given BRegion.
|
|
\param region the BRegion to copy.
|
|
\return This function always returns \c *this.
|
|
*/
|
|
BRegion &
|
|
BRegion::operator=(const BRegion ®ion)
|
|
{
|
|
if (®ion != this) {
|
|
free(data);
|
|
bound = region.bound;
|
|
count = region.count;
|
|
data_size = region.data_size;
|
|
|
|
if (data_size <= 0)
|
|
data_size = 1;
|
|
|
|
data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
|
|
|
|
memcpy(data, region.data, count * sizeof(clipping_rect));
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
/*! \brief Adds a rect to the region.
|
|
\param rect The clipping_rect to be added.
|
|
|
|
Adds the given rect to the region, merging it with another already contained in the region,
|
|
if possible. Recalculate the region's bounds if needed.
|
|
*/
|
|
void
|
|
BRegion::_AddRect(clipping_rect rect)
|
|
{
|
|
ASSERT(count >= 0);
|
|
ASSERT(data_size >= 0);
|
|
|
|
// Should we just reallocate the memory and
|
|
// copy the rect ?
|
|
bool addRect = true;
|
|
|
|
if (count > 0) {
|
|
// Wait! We could merge the rect with one of the
|
|
// existing rectangles, if it's adiacent.
|
|
// We just check it against the last rectangle, since
|
|
// we are keeping them sorted by their "top" coordinates.
|
|
long last = count - 1;
|
|
if (rect.left == data[last].left && rect.right == data[last].right
|
|
&& rect.top == data[last].bottom + 1) {
|
|
|
|
data[last].bottom = rect.bottom;
|
|
addRect = false;
|
|
|
|
} else if (rect.top == data[last].top && rect.bottom == data[last].bottom) {
|
|
if (rect.left == data[last].right + 1) {
|
|
|
|
data[last].right = rect.right;
|
|
addRect = false;
|
|
|
|
} else if (rect.right == data[last].left - 1) {
|
|
|
|
data[last].left = rect.left;
|
|
addRect = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We weren't lucky.... just add the rect as a new one
|
|
if (addRect) {
|
|
if (data_size <= count)
|
|
set_size(count + 16);
|
|
|
|
data[count] = rect;
|
|
|
|
count++;
|
|
}
|
|
|
|
// Recalculate bounds
|
|
if (rect.top < bound.top)
|
|
bound.top = rect.top;
|
|
|
|
if (rect.left < bound.left)
|
|
bound.left = rect.left;
|
|
|
|
if (rect.right > bound.right)
|
|
bound.right = rect.right;
|
|
|
|
if (rect.bottom > bound.bottom)
|
|
bound.bottom = rect.bottom;
|
|
}
|
|
|
|
|
|
/*! \brief Reallocate the memory in the region.
|
|
\param new_size The amount of rectangles that the region could contain.
|
|
*/
|
|
void
|
|
BRegion::set_size(long new_size)
|
|
{
|
|
if (new_size <= 0)
|
|
new_size = data_size + 16;
|
|
|
|
data = (clipping_rect *)realloc(data, new_size * sizeof(clipping_rect));
|
|
|
|
if (data == NULL)
|
|
debugger("BRegion::set_size realloc error\n");
|
|
|
|
data_size = new_size;
|
|
|
|
ASSERT(count <= data_size);
|
|
}
|