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