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 /*! \brief Offsets all region's rects, and bounds by the given values. 346 \param dh The horizontal offset. 347 \param dv The vertical offset. 348 */ 349 void 350 BRegion::OffsetBy(int32 x, int32 y) 351 { 352 if (x == 0 && y == 0) 353 return; 354 355 if (fCount > 0) { 356 if (fData != &fBounds) { 357 for (long i = 0; i < fCount; i++) 358 offset_rect(fData[i], x, y); 359 } 360 361 offset_rect(fBounds, x, y); 362 } 363 } 364 365 366 /*! \brief Empties the region, so that it doesn't include any rect, and invalidates its bounds. 367 */ 368 void 369 BRegion::MakeEmpty() 370 { 371 fBounds = (clipping_rect){ 0, 0, 0, 0 }; 372 fCount = 0; 373 } 374 375 376 // #pragma mark - 377 378 379 /*! \brief Modifies the region, so that it includes the given BRect. 380 \param rect The BRect to be included by the region. 381 */ 382 void 383 BRegion::Include(BRect rect) 384 { 385 Include(_Convert(rect)); 386 } 387 388 389 /*! \brief Modifies the region, so that it includes the given clipping_rect. 390 \param rect The clipping_rect to be included by the region. 391 */ 392 void 393 BRegion::Include(clipping_rect rect) 394 { 395 if (!valid_rect(rect)) 396 return; 397 398 // convert to internal rect format 399 rect.right ++; 400 rect.bottom ++; 401 402 // use private clipping_rect constructor which avoids malloc() 403 BRegion t(rect); 404 405 BRegion result; 406 Support::XUnionRegion(this, &t, &result); 407 408 _AdoptRegionData(result); 409 } 410 411 412 /*! \brief Modifies the region, so that it includes the area of the given region. 413 \param region The region to be included. 414 */ 415 void 416 BRegion::Include(const BRegion* region) 417 { 418 BRegion result; 419 Support::XUnionRegion(this, region, &result); 420 421 _AdoptRegionData(result); 422 } 423 424 425 // #pragma mark - 426 427 428 /*! \brief Modifies the region, excluding the area represented by the given BRect. 429 \param rect The BRect to be excluded. 430 */ 431 void 432 BRegion::Exclude(BRect rect) 433 { 434 Exclude(_Convert(rect)); 435 } 436 437 438 /*! \brief Modifies the region, excluding the area represented by the given clipping_rect. 439 \param rect The clipping_rect to be excluded. 440 */ 441 void 442 BRegion::Exclude(clipping_rect rect) 443 { 444 if (!valid_rect(rect)) 445 return; 446 447 // convert to internal rect format 448 rect.right ++; 449 rect.bottom ++; 450 451 // use private clipping_rect constructor which avoids malloc() 452 BRegion t(rect); 453 454 BRegion result; 455 Support::XSubtractRegion(this, &t, &result); 456 457 _AdoptRegionData(result); 458 } 459 460 461 /*! \brief Modifies the region, excluding the area contained in the given 462 BRegion. 463 \param region The BRegion to be excluded. 464 */ 465 void 466 BRegion::Exclude(const BRegion* region) 467 { 468 BRegion result; 469 Support::XSubtractRegion(this, region, &result); 470 471 _AdoptRegionData(result); 472 } 473 474 475 // #pragma mark - 476 477 478 /*! \brief Modifies the region, so that it will contain just the area 479 in common with the given BRegion. 480 \param region the BRegion to intersect with. 481 */ 482 void 483 BRegion::IntersectWith(const BRegion* region) 484 { 485 BRegion result; 486 Support::XIntersectRegion(this, region, &result); 487 488 _AdoptRegionData(result); 489 } 490 491 492 // #pragma mark - 493 494 495 /*! \brief Modifies the region, so that it will contain just the area 496 which both regions do not have in common. 497 \param region the BRegion to exclusively include. 498 */ 499 void 500 BRegion::ExclusiveInclude(const BRegion* region) 501 { 502 BRegion result; 503 Support::XXorRegion(this, region, &result); 504 505 _AdoptRegionData(result); 506 } 507 508 509 // #pragma mark - 510 511 512 /*! \brief Takes over the data of a region and marks that region empty. 513 \param region The region to adopt the data from. 514 */ 515 void 516 BRegion::_AdoptRegionData(BRegion& region) 517 { 518 fCount = region.fCount; 519 fDataSize = region.fDataSize; 520 fBounds = region.fBounds; 521 if (fData != &fBounds) 522 free(fData); 523 if (region.fData != ®ion.fBounds) 524 fData = region.fData; 525 else 526 fData = &fBounds; 527 528 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 529 // called with internally allocated regions, so they don't need to 530 // be left in a valid state. 531 region.fData = NULL; 532 // region.MakeEmpty(); 533 } 534 535 536 /*! \brief Reallocate the memory in the region. 537 \param newSize The amount of rectangles that the region should be 538 able to hold. 539 */ 540 bool 541 BRegion::_SetSize(long newSize) 542 { 543 // we never shrink the size 544 newSize = max_c(fDataSize, newSize); 545 if (newSize == fDataSize) 546 return true; 547 548 // align newSize to multiple of kDataBlockSize 549 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 550 551 if (newSize > 0) { 552 if (fData == &fBounds) { 553 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 554 fData[0] = fBounds; 555 } else if (fData) { 556 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 557 newSize * sizeof(clipping_rect)); 558 if (!resizedData) { 559 // failed to resize, but we cannot keep the 560 // previous state of the object 561 free(fData); 562 fData = NULL; 563 } else 564 fData = resizedData; 565 } else 566 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 567 } else { 568 // just an empty region, but no error 569 MakeEmpty(); 570 return true; 571 } 572 573 if (!fData) { 574 // allocation actually failed 575 fDataSize = 0; 576 MakeEmpty(); 577 return false; 578 } 579 580 fDataSize = newSize; 581 return true; 582 } 583 584 585 clipping_rect 586 BRegion::_Convert(const BRect& rect) const 587 { 588 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 589 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 590 } 591 592 593 clipping_rect 594 BRegion::_ConvertToInternal(const BRect& rect) const 595 { 596 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 597 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 598 } 599 600 601 clipping_rect 602 BRegion::_ConvertToInternal(const clipping_rect& rect) const 603 { 604 return (clipping_rect){ rect.left, rect.top, 605 rect.right + 1, rect.bottom + 1 }; 606 } 607 608