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 /*! \brief Returns the regions's BRect at the given index. 171 \param index The index (zero based) of the wanted rectangle. 172 \return If the given index is valid, it returns the BRect at that index, 173 otherwise, it returns an invalid BRect. 174 */ 175 BRect 176 BRegion::RectAt(int32 index) /*const*/ 177 { 178 if (index >= 0 && index < fCount) { 179 const clipping_rect& r = fData[index]; 180 return BRect(r.left, r.top, r.right - 1, r.bottom - 1); 181 } 182 183 return BRect(); 184 // an invalid BRect 185 } 186 187 188 /*! \brief Returns the regions's clipping_rect at the given index. 189 \param index The index (zero based) of the wanted rectangle. 190 \return If the given index is valid, it returns the clipping_rect at that index, 191 otherwise, it returns an invalid clipping_rect. 192 */ 193 clipping_rect 194 BRegion::RectAtInt(int32 index) /*const*/ 195 { 196 if (index >= 0 && index < fCount) { 197 const clipping_rect& r = fData[index]; 198 return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 }; 199 } 200 201 return (clipping_rect){ 1, 1, 0, 0 }; 202 // an invalid clipping_rect 203 } 204 205 206 /*! \brief Counts the region rects. 207 \return An int32 which is the total number of rects in the region. 208 */ 209 int32 210 BRegion::CountRects() /*const*/ 211 { 212 return fCount; 213 } 214 215 216 // #pragma mark - 217 218 219 /*! \brief Check if the region has any area in common with the given BRect. 220 \param rect The BRect to check the region against to. 221 \return \ctrue if the region has any area in common with the BRect, \cfalse if not. 222 */ 223 bool 224 BRegion::Intersects(BRect rect) const 225 { 226 return Intersects(_Convert(rect)); 227 } 228 229 230 /*! \brief Check if the region has any area in common with the given clipping_rect. 231 \param rect The clipping_rect to check the region against to. 232 \return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not. 233 */ 234 bool 235 BRegion::Intersects(clipping_rect rect) const 236 { 237 // cheap convert to internal rect format 238 rect.right ++; 239 rect.bottom ++; 240 241 int result = Support::XRectInRegion(this, rect); 242 243 return result > Support::RectangleOut; 244 } 245 246 247 /*! \brief Check if the region contains the given BPoint. 248 \param pt The BPoint to be checked. 249 \return \ctrue if the region contains the BPoint, \cfalse if not. 250 */ 251 bool 252 BRegion::Contains(BPoint point) const 253 { 254 return Support::XPointInRegion(this, (int)point.x, (int)point.y); 255 } 256 257 258 /*! \brief Check if the region contains the given coordinates. 259 \param x The \cx coordinate of the point to be checked. 260 \param y The \cy coordinate of the point to be checked. 261 \return \ctrue if the region contains the point, \cfalse if not. 262 */ 263 bool 264 BRegion::Contains(int32 x, int32 y) /*const*/ 265 { 266 return Support::XPointInRegion(this, x, y); 267 } 268 269 270 /*! \brief Prints the BRegion to stdout. 271 */ 272 void 273 BRegion::PrintToStream() const 274 { 275 Frame().PrintToStream(); 276 277 for (long i = 0; i < fCount; i++) { 278 clipping_rect *rect = &fData[i]; 279 printf("data[%ld] = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n", 280 i, rect->left, rect->top, rect->right - 1, rect->bottom - 1); 281 } 282 } 283 284 285 // #pragma mark - 286 287 288 /*! \brief Offsets all region's rects, and bounds by the given values. 289 \param dh The horizontal offset. 290 \param dv The vertical offset. 291 */ 292 void 293 BRegion::OffsetBy(int32 x, int32 y) 294 { 295 if (x == 0 && y == 0) 296 return; 297 298 if (fCount > 0) { 299 if (fData != &fBounds) { 300 for (long i = 0; i < fCount; i++) 301 offset_rect(fData[i], x, y); 302 } 303 304 offset_rect(fBounds, x, y); 305 } 306 } 307 308 309 /*! \brief Empties the region, so that it doesn't include any rect, and invalidates its bounds. 310 */ 311 void 312 BRegion::MakeEmpty() 313 { 314 fBounds = (clipping_rect){ 0, 0, 0, 0 }; 315 fCount = 0; 316 } 317 318 319 // #pragma mark - 320 321 322 /*! \brief Modifies the region, so that it includes the given BRect. 323 \param rect The BRect to be included by the region. 324 */ 325 void 326 BRegion::Include(BRect rect) 327 { 328 Include(_Convert(rect)); 329 } 330 331 332 /*! \brief Modifies the region, so that it includes the given clipping_rect. 333 \param rect The clipping_rect to be included by the region. 334 */ 335 void 336 BRegion::Include(clipping_rect rect) 337 { 338 if (!valid_rect(rect)) 339 return; 340 341 // convert to internal rect format 342 rect.right ++; 343 rect.bottom ++; 344 345 // use private clipping_rect constructor which avoids malloc() 346 BRegion t(rect); 347 348 BRegion result; 349 Support::XUnionRegion(this, &t, &result); 350 351 _AdoptRegionData(result); 352 } 353 354 355 /*! \brief Modifies the region, so that it includes the area of the given region. 356 \param region The region to be included. 357 */ 358 void 359 BRegion::Include(const BRegion* region) 360 { 361 BRegion result; 362 Support::XUnionRegion(this, region, &result); 363 364 _AdoptRegionData(result); 365 } 366 367 368 // #pragma mark - 369 370 371 /*! \brief Modifies the region, excluding the area represented by the given BRect. 372 \param rect The BRect to be excluded. 373 */ 374 void 375 BRegion::Exclude(BRect rect) 376 { 377 Exclude(_Convert(rect)); 378 } 379 380 381 /*! \brief Modifies the region, excluding the area represented by the given clipping_rect. 382 \param rect The clipping_rect to be excluded. 383 */ 384 void 385 BRegion::Exclude(clipping_rect rect) 386 { 387 if (!valid_rect(rect)) 388 return; 389 390 // convert to internal rect format 391 rect.right ++; 392 rect.bottom ++; 393 394 // use private clipping_rect constructor which avoids malloc() 395 BRegion t(rect); 396 397 BRegion result; 398 Support::XSubtractRegion(this, &t, &result); 399 400 _AdoptRegionData(result); 401 } 402 403 404 /*! \brief Modifies the region, excluding the area contained in the given 405 BRegion. 406 \param region The BRegion to be excluded. 407 */ 408 void 409 BRegion::Exclude(const BRegion* region) 410 { 411 BRegion result; 412 Support::XSubtractRegion(this, region, &result); 413 414 _AdoptRegionData(result); 415 } 416 417 418 // #pragma mark - 419 420 421 /*! \brief Modifies the region, so that it will contain just the area 422 in common with the given BRegion. 423 \param region the BRegion to intersect with. 424 */ 425 void 426 BRegion::IntersectWith(const BRegion* region) 427 { 428 BRegion result; 429 Support::XIntersectRegion(this, region, &result); 430 431 _AdoptRegionData(result); 432 } 433 434 435 // #pragma mark - 436 437 438 /*! \brief Modifies the region, so that it will contain just the area 439 which both regions do not have in common. 440 \param region the BRegion to exclusively include. 441 */ 442 void 443 BRegion::ExclusiveInclude(const BRegion* region) 444 { 445 BRegion result; 446 Support::XXorRegion(this, region, &result); 447 448 _AdoptRegionData(result); 449 } 450 451 452 // #pragma mark - 453 454 455 /*! \brief Takes over the data of a region and marks that region empty. 456 \param region The region to adopt the data from. 457 */ 458 void 459 BRegion::_AdoptRegionData(BRegion& region) 460 { 461 fCount = region.fCount; 462 fDataSize = region.fDataSize; 463 fBounds = region.fBounds; 464 if (fData != &fBounds) 465 free(fData); 466 if (region.fData != ®ion.fBounds) 467 fData = region.fData; 468 else 469 fData = &fBounds; 470 471 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 472 // called with internally allocated regions, so they don't need to 473 // be left in a valid state. 474 region.fData = NULL; 475 // region.MakeEmpty(); 476 } 477 478 479 /*! \brief Reallocate the memory in the region. 480 \param newSize The amount of rectangles that the region should be 481 able to hold. 482 */ 483 bool 484 BRegion::_SetSize(long newSize) 485 { 486 // we never shrink the size 487 newSize = max_c(fDataSize, newSize); 488 if (newSize == fDataSize) 489 return true; 490 491 // align newSize to multiple of kDataBlockSize 492 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 493 494 if (newSize > 0) { 495 if (fData == &fBounds) { 496 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 497 fData[0] = fBounds; 498 } else if (fData) { 499 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 500 newSize * sizeof(clipping_rect)); 501 if (!resizedData) { 502 // failed to resize, but we cannot keep the 503 // previous state of the object 504 free(fData); 505 fData = NULL; 506 } else 507 fData = resizedData; 508 } else 509 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 510 } else { 511 // just an empty region, but no error 512 MakeEmpty(); 513 return true; 514 } 515 516 if (!fData) { 517 // allocation actually failed 518 fDataSize = 0; 519 MakeEmpty(); 520 return false; 521 } 522 523 fDataSize = newSize; 524 return true; 525 } 526 527 528 clipping_rect 529 BRegion::_Convert(const BRect& rect) const 530 { 531 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 532 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 533 } 534 535 536 clipping_rect 537 BRegion::_ConvertToInternal(const BRect& rect) const 538 { 539 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 540 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 541 } 542 543 544 clipping_rect 545 BRegion::_ConvertToInternal(const clipping_rect& rect) const 546 { 547 return (clipping_rect){ rect.left, rect.top, 548 rect.right + 1, rect.bottom + 1 }; 549 } 550 551