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