1/* 2 * Copyright 2003-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan A��mus, superstippi@gmx.de 7 * Stefano Ceccherini, burton666@libero.it 8 */ 9 10 11#include <Region.h> 12 13#include <stdlib.h> 14#include <string.h> 15 16#include <Debug.h> 17 18#include "clipping.h" 19#include "RegionSupport.h" 20 21 22const static int32 kDataBlockSize = 8; 23 24 25BRegion::BRegion() 26 : 27 fCount(0), 28 fDataSize(0), 29 fBounds((clipping_rect){ 0, 0, 0, 0 }), 30 fData(NULL) 31{ 32 _SetSize(kDataBlockSize); 33} 34 35 36BRegion::BRegion(const BRegion& other) 37 : 38 fCount(0), 39 fDataSize(0), 40 fBounds((clipping_rect){ 0, 0, 0, 0 }), 41 fData(NULL) 42{ 43 *this = other; 44} 45 46 47BRegion::BRegion(const BRect rect) 48 : 49 fCount(0), 50 fDataSize(1), 51 fBounds((clipping_rect){ 0, 0, 0, 0 }), 52 fData(&fBounds) 53{ 54 if (!rect.IsValid()) 55 return; 56 57 fBounds = _ConvertToInternal(rect); 58 fCount = 1; 59} 60 61 62// NOTE: private constructor 63BRegion::BRegion(const clipping_rect& clipping) 64 : 65 fCount(1), 66 fDataSize(1), 67 fBounds(clipping), 68 fData(&fBounds) 69{ 70} 71 72 73BRegion::~BRegion() 74{ 75 if (fData != &fBounds) 76 free(fData); 77} 78 79 80BRegion& 81BRegion::operator=(const BRegion& other) 82{ 83 if (&other == this) 84 return *this; 85 86 // handle reallocation if we're too small to contain the other's data 87 if (_SetSize(other.fDataSize)) { 88 memcpy(fData, other.fData, other.fCount * sizeof(clipping_rect)); 89 90 fBounds = other.fBounds; 91 fCount = other.fCount; 92 } 93 94 return *this; 95} 96 97 98bool 99BRegion::operator==(const BRegion& other) const 100{ 101 if (&other == this) 102 return true; 103 104 if (fCount != other.fCount) 105 return false; 106 107 return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0; 108} 109 110 111void 112BRegion::Set(BRect rect) 113{ 114 Set(_Convert(rect)); 115} 116 117 118void 119BRegion::Set(clipping_rect clipping) 120{ 121 _SetSize(1); 122 123 if (valid_rect(clipping) && fData != NULL) { 124 fCount = 1; 125 fData[0] = fBounds = _ConvertToInternal(clipping); 126 } else 127 MakeEmpty(); 128} 129 130 131BRect 132BRegion::Frame() const 133{ 134 return BRect(fBounds.left, fBounds.top, 135 fBounds.right - 1, fBounds.bottom - 1); 136} 137 138 139clipping_rect 140BRegion::FrameInt() const 141{ 142 return (clipping_rect){ fBounds.left, fBounds.top, 143 fBounds.right - 1, fBounds.bottom - 1 }; 144} 145 146 147BRect 148BRegion::RectAt(int32 index) 149{ 150 return const_cast<const BRegion*>(this)->RectAt(index); 151} 152 153 154BRect 155BRegion::RectAt(int32 index) const 156{ 157 if (index >= 0 && index < fCount) { 158 const clipping_rect& r = fData[index]; 159 return BRect(r.left, r.top, r.right - 1, r.bottom - 1); 160 } 161 162 return BRect(); 163 // an invalid BRect 164} 165 166 167clipping_rect 168BRegion::RectAtInt(int32 index) 169{ 170 return const_cast<const BRegion*>(this)->RectAtInt(index); 171} 172 173 174clipping_rect 175BRegion::RectAtInt(int32 index) const 176{ 177 if (index >= 0 && index < fCount) { 178 const clipping_rect& r = fData[index]; 179 return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 }; 180 } 181 182 return (clipping_rect){ 1, 1, 0, 0 }; 183 // an invalid clipping_rect 184} 185 186 187int32 188BRegion::CountRects() 189{ 190 return fCount; 191} 192 193 194int32 195BRegion::CountRects() const 196{ 197 return fCount; 198} 199 200 201bool 202BRegion::Intersects(BRect rect) const 203{ 204 return Intersects(_Convert(rect)); 205} 206 207 208bool 209BRegion::Intersects(clipping_rect clipping) const 210{ 211 clipping = _ConvertToInternal(clipping); 212 213 int result = Support::XRectInRegion(this, clipping); 214 215 return result > Support::RectangleOut; 216} 217 218 219bool 220BRegion::Contains(BPoint point) const 221{ 222 return Support::XPointInRegion(this, (int)point.x, (int)point.y); 223} 224 225 226bool 227BRegion::Contains(int32 x, int32 y) 228{ 229 return Support::XPointInRegion(this, x, y); 230} 231 232 233bool 234BRegion::Contains(int32 x, int32 y) const 235{ 236 return Support::XPointInRegion(this, x, y); 237} 238 239 240// Prints the BRegion to stdout. 241void 242BRegion::PrintToStream() const 243{ 244 Frame().PrintToStream(); 245 246 for (int32 i = 0; i < fCount; i++) { 247 clipping_rect *rect = &fData[i]; 248 printf("data[%" B_PRId32 "] = BRect(l:%" B_PRId32 ".0, t:%" B_PRId32 249 ".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n", 250 i, rect->left, rect->top, rect->right - 1, rect->bottom - 1); 251 } 252} 253 254 255void 256BRegion::OffsetBy(const BPoint& point) 257{ 258 OffsetBy(point.x, point.y); 259} 260 261 262void 263BRegion::OffsetBy(int32 x, int32 y) 264{ 265 if (x == 0 && y == 0) 266 return; 267 268 if (fCount > 0) { 269 if (fData != &fBounds) { 270 for (int32 i = 0; i < fCount; i++) 271 offset_rect(fData[i], x, y); 272 } 273 274 offset_rect(fBounds, x, y); 275 } 276} 277 278 279void 280BRegion::ScaleBy(BSize scale) 281{ 282 ScaleBy(scale.Width(), scale.Height()); 283} 284 285 286void 287BRegion::ScaleBy(float x, float y) 288{ 289 if (x == 1.0 && y == 1.0) 290 return; 291 292 if (fCount > 0) { 293 if (fData != &fBounds) { 294 for (int32 i = 0; i < fCount; i++) 295 scale_rect(fData[i], x, y); 296 } 297 298 scale_rect(fBounds, x, y); 299 } 300} 301 302 303void 304BRegion::MakeEmpty() 305{ 306 fBounds = (clipping_rect){ 0, 0, 0, 0 }; 307 fCount = 0; 308} 309 310 311void 312BRegion::Include(BRect rect) 313{ 314 Include(_Convert(rect)); 315} 316 317 318void 319BRegion::Include(clipping_rect clipping) 320{ 321 if (!valid_rect(clipping)) 322 return; 323 324 // convert to internal clipping format 325 clipping.right++; 326 clipping.bottom++; 327 328 // use private clipping_rect constructor which avoids malloc() 329 BRegion temp(clipping); 330 331 BRegion result; 332 Support::XUnionRegion(this, &temp, &result); 333 334 _AdoptRegionData(result); 335} 336 337 338void 339BRegion::Include(const BRegion* region) 340{ 341 BRegion result; 342 Support::XUnionRegion(this, region, &result); 343 344 _AdoptRegionData(result); 345} 346 347 348void 349BRegion::Exclude(BRect rect) 350{ 351 Exclude(_Convert(rect)); 352} 353 354 355void 356BRegion::Exclude(clipping_rect clipping) 357{ 358 if (!valid_rect(clipping)) 359 return; 360 361 // convert to internal clipping format 362 clipping.right++; 363 clipping.bottom++; 364 365 // use private clipping_rect constructor which avoids malloc() 366 BRegion temp(clipping); 367 368 BRegion result; 369 Support::XSubtractRegion(this, &temp, &result); 370 371 _AdoptRegionData(result); 372} 373 374 375void 376BRegion::Exclude(const BRegion* region) 377{ 378 BRegion result; 379 Support::XSubtractRegion(this, region, &result); 380 381 _AdoptRegionData(result); 382} 383 384 385void 386BRegion::IntersectWith(const BRegion* region) 387{ 388 BRegion result; 389 Support::XIntersectRegion(this, region, &result); 390 391 _AdoptRegionData(result); 392} 393 394 395void 396BRegion::ExclusiveInclude(const BRegion* region) 397{ 398 BRegion result; 399 Support::XXorRegion(this, region, &result); 400 401 _AdoptRegionData(result); 402} 403 404 405// #pragma mark - BRegion private methods 406 407 408/*! 409 \fn void BRegion::_AdoptRegionData(BRegion& region) 410 \brief Takes over the data of \a region and empties it. 411 412 \param region The \a region to adopt data from. 413*/ 414void 415BRegion::_AdoptRegionData(BRegion& region) 416{ 417 fCount = region.fCount; 418 fDataSize = region.fDataSize; 419 fBounds = region.fBounds; 420 if (fData != &fBounds) 421 free(fData); 422 if (region.fData != ®ion.fBounds) 423 fData = region.fData; 424 else 425 fData = &fBounds; 426 427 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 428 // called with internally allocated regions, so they don't need to 429 // be left in a valid state. 430 region.fData = NULL; 431// region.MakeEmpty(); 432} 433 434 435/*! 436 \fn bool BRegion::_SetSize(int32 newSize) 437 \brief Reallocate the memory in the region. 438 439 \param newSize The amount of rectangles that the region should be 440 able to hold. 441*/ 442bool 443BRegion::_SetSize(int32 newSize) 444{ 445 // we never shrink the size 446 newSize = max_c(fDataSize, newSize); 447 // The amount of rectangles that the region should be able to hold. 448 if (newSize == fDataSize) 449 return true; 450 451 // align newSize to multiple of kDataBlockSize 452 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 453 454 if (newSize > 0) { 455 if (fData == &fBounds) { 456 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 457 fData[0] = fBounds; 458 } else if (fData) { 459 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 460 newSize * sizeof(clipping_rect)); 461 if (!resizedData) { 462 // failed to resize, but we cannot keep the 463 // previous state of the object 464 free(fData); 465 fData = NULL; 466 } else 467 fData = resizedData; 468 } else 469 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 470 } else { 471 // just an empty region, but no error 472 MakeEmpty(); 473 return true; 474 } 475 476 if (!fData) { 477 // allocation actually failed 478 fDataSize = 0; 479 MakeEmpty(); 480 return false; 481 } 482 483 fDataSize = newSize; 484 return true; 485} 486 487 488clipping_rect 489BRegion::_Convert(const BRect& rect) const 490{ 491 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 492 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 493} 494 495 496clipping_rect 497BRegion::_ConvertToInternal(const BRect& rect) const 498{ 499 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 500 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 501} 502 503 504clipping_rect 505BRegion::_ConvertToInternal(const clipping_rect& rect) const 506{ 507 return (clipping_rect){ rect.left, rect.top, 508 rect.right + 1, rect.bottom + 1 }; 509} 510