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 // convert to internal rect format 339 rect.right ++; 340 rect.bottom ++; 341 342 // use private clipping_rect constructor which avoids malloc() 343 BRegion t(rect); 344 345 BRegion result; 346 Support::XUnionRegion(this, &t, &result); 347 348 _AdoptRegionData(result); 349 } 350 351 352 /*! \brief Modifies the region, so that it includes the area of the given region. 353 \param region The region to be included. 354 */ 355 void 356 BRegion::Include(const BRegion* region) 357 { 358 BRegion result; 359 Support::XUnionRegion(this, region, &result); 360 361 _AdoptRegionData(result); 362 } 363 364 365 // #pragma mark - 366 367 368 /*! \brief Modifies the region, excluding the area represented by the given BRect. 369 \param rect The BRect to be excluded. 370 */ 371 void 372 BRegion::Exclude(BRect rect) 373 { 374 Exclude(_Convert(rect)); 375 } 376 377 378 /*! \brief Modifies the region, excluding the area represented by the given clipping_rect. 379 \param rect The clipping_rect to be excluded. 380 */ 381 void 382 BRegion::Exclude(clipping_rect rect) 383 { 384 // convert to internal rect format 385 rect.right ++; 386 rect.bottom ++; 387 388 // use private clipping_rect constructor which avoids malloc() 389 BRegion t(rect); 390 391 BRegion result; 392 Support::XSubtractRegion(this, &t, &result); 393 394 _AdoptRegionData(result); 395 } 396 397 398 /*! \brief Modifies the region, excluding the area contained in the given 399 BRegion. 400 \param region The BRegion to be excluded. 401 */ 402 void 403 BRegion::Exclude(const BRegion* region) 404 { 405 BRegion result; 406 Support::XSubtractRegion(this, region, &result); 407 408 _AdoptRegionData(result); 409 } 410 411 412 // #pragma mark - 413 414 415 /*! \brief Modifies the region, so that it will contain just the area 416 in common with the given BRegion. 417 \param region the BRegion to intersect with. 418 */ 419 void 420 BRegion::IntersectWith(const BRegion* region) 421 { 422 BRegion result; 423 Support::XIntersectRegion(this, region, &result); 424 425 _AdoptRegionData(result); 426 } 427 428 429 // #pragma mark - 430 431 432 /*! \brief Modifies the region, so that it will contain just the area 433 which both regions do not have in common. 434 \param region the BRegion to exclusively include. 435 */ 436 void 437 BRegion::ExclusiveInclude(const BRegion* region) 438 { 439 BRegion result; 440 Support::XXorRegion(this, region, &result); 441 442 _AdoptRegionData(result); 443 } 444 445 446 // #pragma mark - 447 448 449 /*! \brief Takes over the data of a region and marks that region empty. 450 \param region The region to adopt the data from. 451 */ 452 void 453 BRegion::_AdoptRegionData(BRegion& region) 454 { 455 fCount = region.fCount; 456 fDataSize = region.fDataSize; 457 fBounds = region.fBounds; 458 if (fData != &fBounds) 459 free(fData); 460 if (region.fData != ®ion.fBounds) 461 fData = region.fData; 462 else 463 fData = &fBounds; 464 465 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 466 // called with internally allocated regions, so they don't need to 467 // be left in a valid state. 468 region.fData = NULL; 469 // region.MakeEmpty(); 470 } 471 472 473 /*! \brief Reallocate the memory in the region. 474 \param newSize The amount of rectangles that the region should be 475 able to hold. 476 */ 477 bool 478 BRegion::_SetSize(long newSize) 479 { 480 // we never shrink the size 481 newSize = max_c(fDataSize, newSize); 482 if (newSize == fDataSize) 483 return true; 484 485 // align newSize to multiple of kDataBlockSize 486 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 487 488 if (newSize > 0) { 489 if (fData == &fBounds) { 490 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 491 fData[0] = fBounds; 492 } else if (fData) { 493 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 494 newSize * sizeof(clipping_rect)); 495 if (!resizedData) { 496 // failed to resize, but we cannot keep the 497 // previous state of the object 498 free(fData); 499 fData = NULL; 500 } else 501 fData = resizedData; 502 } else 503 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 504 } else { 505 // just an empty region, but no error 506 MakeEmpty(); 507 return true; 508 } 509 510 if (!fData) { 511 // allocation actually failed 512 fDataSize = 0; 513 MakeEmpty(); 514 return false; 515 } 516 517 fDataSize = newSize; 518 return true; 519 } 520 521 522 clipping_rect 523 BRegion::_Convert(const BRect& rect) const 524 { 525 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 526 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 527 } 528 529 530 clipping_rect 531 BRegion::_ConvertToInternal(const BRect& rect) const 532 { 533 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 534 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 535 } 536 537 538 clipping_rect 539 BRegion::_ConvertToInternal(const clipping_rect& rect) const 540 { 541 return (clipping_rect){ rect.left, rect.top, 542 rect.right + 1, rect.bottom + 1 }; 543 } 544 545