* Copyright 2003-2014 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Stephan Aßmus, superstippi@gmx.de
* Stefano Ceccherini, burton666@libero.it
*/
#include <Region.h>
#include <stdlib.h>
#include <string.h>
#include <Debug.h>
#include "clipping.h"
#include "RegionSupport.h"
const static int32 kDataBlockSize = 8;
BRegion::BRegion()
:
fCount(0),
fDataSize(0),
fBounds((clipping_rect){ 0, 0, 0, 0 }),
fData(NULL)
{
_SetSize(kDataBlockSize);
}
BRegion::BRegion(const BRegion& other)
:
fCount(0),
fDataSize(0),
fBounds((clipping_rect){ 0, 0, 0, 0 }),
fData(NULL)
{
*this = other;
}
BRegion::BRegion(const BRect rect)
:
fCount(0),
fDataSize(1),
fBounds((clipping_rect){ 0, 0, 0, 0 }),
fData(&fBounds)
{
if (!rect.IsValid())
return;
fBounds = _ConvertToInternal(rect);
fCount = 1;
}
#if defined(__cplusplus) && __cplusplus >= 201103L
BRegion::BRegion(BRegion&& other)
:
fCount(0),
fDataSize(0),
fBounds((clipping_rect){ 0, 0, 0, 0 }),
fData(NULL)
{
MoveFrom(other);
}
#endif
BRegion::BRegion(const clipping_rect& clipping)
:
fCount(1),
fDataSize(1),
fBounds(clipping),
fData(&fBounds)
{
}
BRegion::~BRegion()
{
if (fData != &fBounds)
free(fData);
}
BRegion&
BRegion::operator=(const BRegion& other)
{
if (&other == this)
return *this;
if (_SetSize(other.fDataSize)) {
memcpy(fData, other.fData, other.fCount * sizeof(clipping_rect));
fBounds = other.fBounds;
fCount = other.fCount;
}
return *this;
}
#if defined(__cplusplus) && __cplusplus >= 201103L
BRegion&
BRegion::operator=(BRegion&& other)
{
MoveFrom(other);
return *this;
}
#endif
bool
BRegion::operator==(const BRegion& other) const
{
if (&other == this)
return true;
if (fCount != other.fCount)
return false;
return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0;
}
void
BRegion::Set(BRect rect)
{
Set(_Convert(rect));
}
void
BRegion::Set(clipping_rect clipping)
{
_SetSize(1);
if (valid_rect(clipping) && fData != NULL) {
fCount = 1;
fData[0] = fBounds = _ConvertToInternal(clipping);
} else
MakeEmpty();
}
void
BRegion::MoveFrom(BRegion& other)
{
if (other.CountRects() <= 0) {
MakeEmpty();
return;
}
if (other.CountRects() == 1) {
Set(other.FrameInt());
other.MakeEmpty();
return;
}
fCount = other.fCount;
fDataSize = other.fDataSize;
fBounds = other.fBounds;
fData = other.fData;
other.fCount = 0;
other.fDataSize = 0;
other.fBounds = (clipping_rect){ 0, 0, 0, 0 };
other.fData = NULL;
}
BRect
BRegion::Frame() const
{
return BRect(fBounds.left, fBounds.top,
fBounds.right - 1, fBounds.bottom - 1);
}
clipping_rect
BRegion::FrameInt() const
{
return (clipping_rect){ fBounds.left, fBounds.top,
fBounds.right - 1, fBounds.bottom - 1 };
}
BRect
BRegion::RectAt(int32 index)
{
return const_cast<const BRegion*>(this)->RectAt(index);
}
BRect
BRegion::RectAt(int32 index) const
{
if (index >= 0 && index < fCount) {
const clipping_rect& r = fData[index];
return BRect(r.left, r.top, r.right - 1, r.bottom - 1);
}
return BRect();
}
clipping_rect
BRegion::RectAtInt(int32 index)
{
return const_cast<const BRegion*>(this)->RectAtInt(index);
}
clipping_rect
BRegion::RectAtInt(int32 index) const
{
if (index >= 0 && index < fCount) {
const clipping_rect& r = fData[index];
return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 };
}
return (clipping_rect){ 1, 1, 0, 0 };
}
int32
BRegion::CountRects()
{
return fCount;
}
int32
BRegion::CountRects() const
{
return fCount;
}
bool
BRegion::Intersects(BRect rect) const
{
return Intersects(_Convert(rect));
}
bool
BRegion::Intersects(clipping_rect clipping) const
{
clipping = _ConvertToInternal(clipping);
int result = Support::XRectInRegion(this, clipping);
return result > Support::RectangleOut;
}
bool
BRegion::Contains(BPoint point) const
{
return Support::XPointInRegion(this, (int)point.x, (int)point.y);
}
bool
BRegion::Contains(int32 x, int32 y)
{
return Support::XPointInRegion(this, x, y);
}
bool
BRegion::Contains(int32 x, int32 y) const
{
return Support::XPointInRegion(this, x, y);
}
void
BRegion::PrintToStream() const
{
Frame().PrintToStream();
for (int32 i = 0; i < fCount; i++) {
clipping_rect *rect = &fData[i];
printf("data[%" B_PRId32 "] = BRect(l:%" B_PRId32 ".0, t:%" B_PRId32
".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n",
i, rect->left, rect->top, rect->right - 1, rect->bottom - 1);
}
}
void
BRegion::OffsetBy(const BPoint& point)
{
OffsetBy(point.x, point.y);
}
void
BRegion::OffsetBy(int32 x, int32 y)
{
if (x == 0 && y == 0)
return;
if (fCount > 0) {
if (fData != &fBounds) {
for (int32 i = 0; i < fCount; i++)
offset_rect(fData[i], x, y);
}
offset_rect(fBounds, x, y);
}
}
void
BRegion::ScaleBy(BSize scale)
{
ScaleBy(scale.Width(), scale.Height());
}
void
BRegion::ScaleBy(float x, float y)
{
if (x == 1.0 && y == 1.0)
return;
if (fCount > 0) {
if (fData != &fBounds) {
for (int32 i = 0; i < fCount; i++)
scale_rect(fData[i], x, y);
}
scale_rect(fBounds, x, y);
}
}
void
BRegion::MakeEmpty()
{
fBounds = (clipping_rect){ 0, 0, 0, 0 };
fCount = 0;
}
void
BRegion::Include(BRect rect)
{
Include(_Convert(rect));
}
void
BRegion::Include(clipping_rect clipping)
{
if (!valid_rect(clipping))
return;
clipping.right++;
clipping.bottom++;
BRegion temp(clipping);
BRegion result;
Support::XUnionRegion(this, &temp, &result);
_AdoptRegionData(result);
}
void
BRegion::Include(const BRegion* region)
{
BRegion result;
Support::XUnionRegion(this, region, &result);
_AdoptRegionData(result);
}
void
BRegion::Exclude(BRect rect)
{
Exclude(_Convert(rect));
}
void
BRegion::Exclude(clipping_rect clipping)
{
if (!valid_rect(clipping))
return;
clipping.right++;
clipping.bottom++;
BRegion temp(clipping);
BRegion result;
Support::XSubtractRegion(this, &temp, &result);
_AdoptRegionData(result);
}
void
BRegion::Exclude(const BRegion* region)
{
BRegion result;
Support::XSubtractRegion(this, region, &result);
_AdoptRegionData(result);
}
void
BRegion::IntersectWith(const BRegion* region)
{
BRegion result;
Support::XIntersectRegion(this, region, &result);
_AdoptRegionData(result);
}
void
BRegion::ExclusiveInclude(const BRegion* region)
{
BRegion result;
Support::XXorRegion(this, region, &result);
_AdoptRegionData(result);
}
\fn void BRegion::_AdoptRegionData(BRegion& region)
\brief Takes over the data of \a region and empties it.
\param region The \a region to adopt data from.
*/
void
BRegion::_AdoptRegionData(BRegion& region)
{
fCount = region.fCount;
fDataSize = region.fDataSize;
fBounds = region.fBounds;
if (fData != &fBounds)
free(fData);
if (region.fData != ®ion.fBounds)
fData = region.fData;
else
fData = &fBounds;
region.fData = NULL;
}
\fn bool BRegion::_SetSize(int32 newSize)
\brief Reallocate the memory in the region.
\param newSize The amount of rectangles that the region should be
able to hold.
*/
bool
BRegion::_SetSize(int32 newSize)
{
newSize = max_c(fDataSize, newSize);
if (newSize == fDataSize)
return true;
newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
if (newSize > 0) {
if (fData == &fBounds) {
fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
fData[0] = fBounds;
} else if (fData) {
clipping_rect* resizedData = (clipping_rect*)realloc(fData,
newSize * sizeof(clipping_rect));
if (!resizedData) {
free(fData);
fData = NULL;
} else
fData = resizedData;
} else
fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
} else {
MakeEmpty();
return true;
}
if (!fData) {
fDataSize = 0;
MakeEmpty();
return false;
}
fDataSize = newSize;
return true;
}
clipping_rect
BRegion::_Convert(const BRect& rect) const
{
return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
}
clipping_rect
BRegion::_ConvertToInternal(const BRect& rect) const
{
return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
}
clipping_rect
BRegion::_ConvertToInternal(const clipping_rect& rect) const
{
return (clipping_rect){ rect.left, rect.top,
rect.right + 1, rect.bottom + 1 };
}