1 /* 2 * Copyright 2003-2007, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini (burton666@libero.it) 7 * Stephan Aßmus <superstippi@gmx.de> 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 22 const static int32 kDataBlockSize = 8; 23 24 25 /*! \brief Initializes a region. The region will have no rects, 26 and its fBounds will be invalid. 27 */ 28 BRegion::BRegion() 29 : 30 fCount(0), 31 fDataSize(0), 32 fBounds((clipping_rect){ 0, 0, 0, 0 }), 33 fData(NULL) 34 { 35 _SetSize(kDataBlockSize); 36 } 37 38 39 /*! \brief Initializes a region to be a copy of another. 40 \param region The region to copy. 41 */ 42 BRegion::BRegion(const BRegion& region) 43 : 44 fCount(0), 45 fDataSize(0), 46 fBounds((clipping_rect){ 0, 0, 0, 0 }), 47 fData(NULL) 48 { 49 *this = region; 50 } 51 52 53 /*! \brief Initializes a region to contain a BRect. 54 \param rect The BRect to set the region to. 55 */ 56 BRegion::BRegion(const BRect rect) 57 : 58 fCount(0), 59 fDataSize(1), 60 fBounds((clipping_rect){ 0, 0, 0, 0 }), 61 fData(&fBounds) 62 { 63 if (!rect.IsValid()) 64 return; 65 66 fBounds = _ConvertToInternal(rect); 67 fCount = 1; 68 } 69 70 // NOTE: private constructor 71 /*! \brief Initializes a region to contain a clipping_rect. 72 \param rect The clipping_rect to set the region to, already in 73 internal rect format. 74 */ 75 BRegion::BRegion(const clipping_rect& rect) 76 : fCount(1) 77 , fDataSize(1) 78 , fBounds(rect) 79 , fData(&fBounds) 80 { 81 } 82 83 84 /*! \brief Frees the allocated memory. 85 */ 86 BRegion::~BRegion() 87 { 88 if (fData != &fBounds) 89 free(fData); 90 } 91 92 93 // #pragma mark - 94 95 96 /*! \brief Modifies the region to be a copy of the given BRegion. 97 \param region the BRegion to copy. 98 \return This function always returns \c *this. 99 */ 100 BRegion& 101 BRegion::operator=(const BRegion& region) 102 { 103 if (®ion == this) 104 return *this; 105 106 // handle reallocation if we're too small to contain 107 // the other region 108 if (_SetSize(region.fDataSize)) { 109 memcpy(fData, region.fData, region.fCount * sizeof(clipping_rect)); 110 111 fBounds = region.fBounds; 112 fCount = region.fCount; 113 } 114 115 return *this; 116 } 117 118 119 /*! \brief Compares this region to another (by value). 120 \param other the BRegion to compare to. 121 \return \ctrue if the two regions are the same, \cfalse otherwise. 122 */ 123 bool 124 BRegion::operator==(const BRegion& other) const 125 { 126 if (&other == this) 127 return true; 128 129 if (fCount != other.fCount) 130 return false; 131 132 return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0; 133 } 134 135 136 /*! \brief Set the region to contain just the given BRect. 137 \param newBounds A BRect. 138 */ 139 void 140 BRegion::Set(BRect newBounds) 141 { 142 Set(_Convert(newBounds)); 143 } 144 145 146 /*! \brief Set the region to contain just the given clipping_rect. 147 \param newBounds A clipping_rect. 148 */ 149 void 150 BRegion::Set(clipping_rect newBounds) 151 { 152 _SetSize(1); 153 154 if (valid_rect(newBounds) && fData) { 155 fCount = 1; 156 // cheap convert to internal rect format 157 newBounds.right++; 158 newBounds.bottom++; 159 fData[0] = fBounds = newBounds; 160 } else 161 MakeEmpty(); 162 } 163 164 165 // #pragma mark - 166 167 168 /*! \brief Returns the bounds of the region. 169 \return A BRect which represents the bounds of the region. 170 */ 171 BRect 172 BRegion::Frame() const 173 { 174 return BRect(fBounds.left, fBounds.top, 175 fBounds.right - 1, fBounds.bottom - 1); 176 } 177 178 179 /*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates). 180 \return A clipping_rect which represents the bounds of the region. 181 */ 182 clipping_rect 183 BRegion::FrameInt() const 184 { 185 return (clipping_rect){ fBounds.left, fBounds.top, 186 fBounds.right - 1, fBounds.bottom - 1 }; 187 } 188 189 190 BRect 191 BRegion::RectAt(int32 index) 192 { 193 return const_cast<const BRegion*>(this)->RectAt(index); 194 } 195 196 197 /*! \brief Returns the regions's BRect at the given index. 198 \param index The index (zero based) of the wanted rectangle. 199 \return If the given index is valid, it returns the BRect at that index, 200 otherwise, it returns an invalid BRect. 201 */ 202 BRect 203 BRegion::RectAt(int32 index) const 204 { 205 if (index >= 0 && index < fCount) { 206 const clipping_rect& r = fData[index]; 207 return BRect(r.left, r.top, r.right - 1, r.bottom - 1); 208 } 209 210 return BRect(); 211 // an invalid BRect 212 } 213 214 215 clipping_rect 216 BRegion::RectAtInt(int32 index) 217 { 218 return const_cast<const BRegion*>(this)->RectAtInt(index); 219 } 220 221 222 /*! \brief Returns the regions's clipping_rect at the given index. 223 \param index The index (zero based) of the wanted rectangle. 224 \return If the given index is valid, it returns the clipping_rect at that index, 225 otherwise, it returns an invalid clipping_rect. 226 */ 227 clipping_rect 228 BRegion::RectAtInt(int32 index) const 229 { 230 if (index >= 0 && index < fCount) { 231 const clipping_rect& r = fData[index]; 232 return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 }; 233 } 234 235 return (clipping_rect){ 1, 1, 0, 0 }; 236 // an invalid clipping_rect 237 } 238 239 240 /*! \brief Counts the region rects. 241 \return An int32 which is the total number of rects in the region. 242 */ 243 int32 244 BRegion::CountRects() 245 { 246 return fCount; 247 } 248 249 250 /*! \brief Counts the region rects. 251 \return An int32 which is the total number of rects in the region. 252 */ 253 int32 254 BRegion::CountRects() const 255 { 256 return fCount; 257 } 258 259 260 // #pragma mark - 261 262 263 /*! \brief Check if the region has any area in common with the given BRect. 264 \param rect The BRect to check the region against to. 265 \return \ctrue if the region has any area in common with the BRect, \cfalse if not. 266 */ 267 bool 268 BRegion::Intersects(BRect rect) const 269 { 270 return Intersects(_Convert(rect)); 271 } 272 273 274 /*! \brief Check if the region has any area in common with the given clipping_rect. 275 \param rect The clipping_rect to check the region against to. 276 \return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not. 277 */ 278 bool 279 BRegion::Intersects(clipping_rect rect) const 280 { 281 // cheap convert to internal rect format 282 rect.right ++; 283 rect.bottom ++; 284 285 int result = Support::XRectInRegion(this, rect); 286 287 return result > Support::RectangleOut; 288 } 289 290 291 /*! \brief Check if the region contains the given BPoint. 292 \param pt The BPoint to be checked. 293 \return \ctrue if the region contains the BPoint, \cfalse if not. 294 */ 295 bool 296 BRegion::Contains(BPoint point) const 297 { 298 return Support::XPointInRegion(this, (int)point.x, (int)point.y); 299 } 300 301 302 /*! \brief Check if the region contains the given coordinates. 303 \param x The \cx coordinate of the point to be checked. 304 \param y The \cy coordinate of the point to be checked. 305 \return \ctrue if the region contains the point, \cfalse if not. 306 */ 307 bool 308 BRegion::Contains(int32 x, int32 y) 309 { 310 return Support::XPointInRegion(this, x, y); 311 } 312 313 314 /*! \brief Check if the region contains the given coordinates. 315 \param x The \cx coordinate of the point to be checked. 316 \param y The \cy coordinate of the point to be checked. 317 \return \ctrue if the region contains the point, \cfalse if not. 318 */ 319 bool 320 BRegion::Contains(int32 x, int32 y) const 321 { 322 return Support::XPointInRegion(this, x, y); 323 } 324 325 326 /*! \brief Prints the BRegion to stdout. 327 */ 328 void 329 BRegion::PrintToStream() const 330 { 331 Frame().PrintToStream(); 332 333 for (long i = 0; i < fCount; i++) { 334 clipping_rect *rect = &fData[i]; 335 printf("data[%ld] = BRect(l:%" B_PRId32 ".0, t:%" B_PRId32 336 ".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n", 337 i, rect->left, rect->top, rect->right - 1, rect->bottom - 1); 338 } 339 } 340 341 342 // #pragma mark - 343 344 345 void 346 BRegion::OffsetBy(const BPoint& point) 347 { 348 OffsetBy(point.x, point.y); 349 } 350 351 352 /*! \brief Offsets all region's rects, and bounds by the given values. 353 \param dh The horizontal offset. 354 \param dv The vertical offset. 355 */ 356 void 357 BRegion::OffsetBy(int32 x, int32 y) 358 { 359 if (x == 0 && y == 0) 360 return; 361 362 if (fCount > 0) { 363 if (fData != &fBounds) { 364 for (long i = 0; i < fCount; i++) 365 offset_rect(fData[i], x, y); 366 } 367 368 offset_rect(fBounds, x, y); 369 } 370 } 371 372 373 /*! \brief Empties the region, so that it doesn't include any rect, and invalidates its bounds. 374 */ 375 void 376 BRegion::MakeEmpty() 377 { 378 fBounds = (clipping_rect){ 0, 0, 0, 0 }; 379 fCount = 0; 380 } 381 382 383 // #pragma mark - 384 385 386 /*! \brief Modifies the region, so that it includes the given BRect. 387 \param rect The BRect to be included by the region. 388 */ 389 void 390 BRegion::Include(BRect rect) 391 { 392 Include(_Convert(rect)); 393 } 394 395 396 /*! \brief Modifies the region, so that it includes the given clipping_rect. 397 \param rect The clipping_rect to be included by the region. 398 */ 399 void 400 BRegion::Include(clipping_rect rect) 401 { 402 if (!valid_rect(rect)) 403 return; 404 405 // convert to internal rect format 406 rect.right ++; 407 rect.bottom ++; 408 409 // use private clipping_rect constructor which avoids malloc() 410 BRegion t(rect); 411 412 BRegion result; 413 Support::XUnionRegion(this, &t, &result); 414 415 _AdoptRegionData(result); 416 } 417 418 419 /*! \brief Modifies the region, so that it includes the area of the given region. 420 \param region The region to be included. 421 */ 422 void 423 BRegion::Include(const BRegion* region) 424 { 425 BRegion result; 426 Support::XUnionRegion(this, region, &result); 427 428 _AdoptRegionData(result); 429 } 430 431 432 // #pragma mark - 433 434 435 /*! \brief Modifies the region, excluding the area represented by the given BRect. 436 \param rect The BRect to be excluded. 437 */ 438 void 439 BRegion::Exclude(BRect rect) 440 { 441 Exclude(_Convert(rect)); 442 } 443 444 445 /*! \brief Modifies the region, excluding the area represented by the given clipping_rect. 446 \param rect The clipping_rect to be excluded. 447 */ 448 void 449 BRegion::Exclude(clipping_rect rect) 450 { 451 if (!valid_rect(rect)) 452 return; 453 454 // convert to internal rect format 455 rect.right ++; 456 rect.bottom ++; 457 458 // use private clipping_rect constructor which avoids malloc() 459 BRegion t(rect); 460 461 BRegion result; 462 Support::XSubtractRegion(this, &t, &result); 463 464 _AdoptRegionData(result); 465 } 466 467 468 /*! \brief Modifies the region, excluding the area contained in the given 469 BRegion. 470 \param region The BRegion to be excluded. 471 */ 472 void 473 BRegion::Exclude(const BRegion* region) 474 { 475 BRegion result; 476 Support::XSubtractRegion(this, region, &result); 477 478 _AdoptRegionData(result); 479 } 480 481 482 // #pragma mark - 483 484 485 /*! \brief Modifies the region, so that it will contain just the area 486 in common with the given BRegion. 487 \param region the BRegion to intersect with. 488 */ 489 void 490 BRegion::IntersectWith(const BRegion* region) 491 { 492 BRegion result; 493 Support::XIntersectRegion(this, region, &result); 494 495 _AdoptRegionData(result); 496 } 497 498 499 // #pragma mark - 500 501 502 /*! \brief Modifies the region, so that it will contain just the area 503 which both regions do not have in common. 504 \param region the BRegion to exclusively include. 505 */ 506 void 507 BRegion::ExclusiveInclude(const BRegion* region) 508 { 509 BRegion result; 510 Support::XXorRegion(this, region, &result); 511 512 _AdoptRegionData(result); 513 } 514 515 516 // #pragma mark - 517 518 519 /*! \brief Takes over the data of a region and marks that region empty. 520 \param region The region to adopt the data from. 521 */ 522 void 523 BRegion::_AdoptRegionData(BRegion& region) 524 { 525 fCount = region.fCount; 526 fDataSize = region.fDataSize; 527 fBounds = region.fBounds; 528 if (fData != &fBounds) 529 free(fData); 530 if (region.fData != ®ion.fBounds) 531 fData = region.fData; 532 else 533 fData = &fBounds; 534 535 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 536 // called with internally allocated regions, so they don't need to 537 // be left in a valid state. 538 region.fData = NULL; 539 // region.MakeEmpty(); 540 } 541 542 543 /*! \brief Reallocate the memory in the region. 544 \param newSize The amount of rectangles that the region should be 545 able to hold. 546 */ 547 bool 548 BRegion::_SetSize(long newSize) 549 { 550 // we never shrink the size 551 newSize = max_c(fDataSize, newSize); 552 if (newSize == fDataSize) 553 return true; 554 555 // align newSize to multiple of kDataBlockSize 556 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 557 558 if (newSize > 0) { 559 if (fData == &fBounds) { 560 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 561 fData[0] = fBounds; 562 } else if (fData) { 563 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 564 newSize * sizeof(clipping_rect)); 565 if (!resizedData) { 566 // failed to resize, but we cannot keep the 567 // previous state of the object 568 free(fData); 569 fData = NULL; 570 } else 571 fData = resizedData; 572 } else 573 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 574 } else { 575 // just an empty region, but no error 576 MakeEmpty(); 577 return true; 578 } 579 580 if (!fData) { 581 // allocation actually failed 582 fDataSize = 0; 583 MakeEmpty(); 584 return false; 585 } 586 587 fDataSize = newSize; 588 return true; 589 } 590 591 592 clipping_rect 593 BRegion::_Convert(const BRect& rect) const 594 { 595 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 596 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 597 } 598 599 600 clipping_rect 601 BRegion::_ConvertToInternal(const BRect& rect) const 602 { 603 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 604 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 605 } 606 607 608 clipping_rect 609 BRegion::_ConvertToInternal(const clipping_rect& rect) const 610 { 611 return (clipping_rect){ rect.left, rect.top, 612 rect.right + 1, rect.bottom + 1 }; 613 } 614 615