1 /* 2 * Copyright 2003-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus, superstippi@gmx.de 7 * Stefano Ceccherini, burton666@libero.it 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 // Initializes an empty region. 26 BRegion::BRegion() 27 : 28 fCount(0), 29 fDataSize(0), 30 fBounds((clipping_rect){ 0, 0, 0, 0 }), 31 fData(NULL) 32 { 33 _SetSize(kDataBlockSize); 34 } 35 36 37 // Initializes a region to be a copy of another. 38 BRegion::BRegion(const BRegion& other) 39 : 40 fCount(0), 41 fDataSize(0), 42 fBounds((clipping_rect){ 0, 0, 0, 0 }), 43 fData(NULL) 44 { 45 *this = other; 46 } 47 48 49 // Initializes a region to contain a BRect. 50 BRegion::BRegion(const BRect rect) 51 : 52 fCount(0), 53 fDataSize(1), 54 fBounds((clipping_rect){ 0, 0, 0, 0 }), 55 fData(&fBounds) 56 { 57 if (!rect.IsValid()) 58 return; 59 60 fBounds = _ConvertToInternal(rect); 61 fCount = 1; 62 } 63 64 65 // Initializes a region to contain a clipping_rect. 66 // NOTE: private constructor 67 BRegion::BRegion(const clipping_rect& clipping) 68 : 69 fCount(1), 70 fDataSize(1), 71 fBounds(clipping), 72 fData(&fBounds) 73 { 74 } 75 76 77 BRegion::~BRegion() 78 { 79 if (fData != &fBounds) 80 free(fData); 81 } 82 83 84 // Modifies the region to be a copy of the given BRegion. 85 BRegion& 86 BRegion::operator=(const BRegion& other) 87 { 88 if (&other == this) 89 return *this; 90 91 // handle reallocation if we're too small to contain 92 // the other other 93 if (_SetSize(other.fDataSize)) { 94 memcpy(fData, other.fData, other.fCount * sizeof(clipping_rect)); 95 96 fBounds = other.fBounds; 97 fCount = other.fCount; 98 } 99 100 return *this; 101 } 102 103 104 // Compares this region to another (by value). 105 bool 106 BRegion::operator==(const BRegion& other) const 107 { 108 if (&other == this) 109 return true; 110 111 if (fCount != other.fCount) 112 return false; 113 114 return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0; 115 } 116 117 118 // Set the region to contain just the given BRect. 119 void 120 BRegion::Set(BRect rect) 121 { 122 Set(_Convert(rect)); 123 } 124 125 126 //Set the region to contain just the given clipping_rect. 127 void 128 BRegion::Set(clipping_rect clipping) 129 { 130 _SetSize(1); 131 132 if (valid_rect(clipping) && fData != NULL) { 133 fCount = 1; 134 // cheap convert to internal rect format 135 clipping.right++; 136 clipping.bottom++; 137 fData[0] = fBounds = clipping; 138 } else 139 MakeEmpty(); 140 } 141 142 143 // Returns the bounds of the region. 144 BRect 145 BRegion::Frame() const 146 { 147 return BRect(fBounds.left, fBounds.top, 148 fBounds.right - 1, fBounds.bottom - 1); 149 } 150 151 152 // Returns the bounds of the region as a clipping_rect 153 // (which has integer coordinates). 154 clipping_rect 155 BRegion::FrameInt() const 156 { 157 return (clipping_rect){ fBounds.left, fBounds.top, 158 fBounds.right - 1, fBounds.bottom - 1 }; 159 } 160 161 162 // Returns the rect contained in the region at the given index. 163 BRect 164 BRegion::RectAt(int32 index) 165 { 166 return const_cast<const BRegion*>(this)->RectAt(index); 167 } 168 169 170 // Returns the rect contained in the region at the given index. (const) 171 BRect 172 BRegion::RectAt(int32 index) const 173 { 174 if (index >= 0 && index < fCount) { 175 const clipping_rect& r = fData[index]; 176 return BRect(r.left, r.top, r.right - 1, r.bottom - 1); 177 } 178 179 return BRect(); 180 // an invalid BRect 181 } 182 183 184 // Returns the clipping_rect contained in the region at the given index. 185 clipping_rect 186 BRegion::RectAtInt(int32 index) 187 { 188 return const_cast<const BRegion*>(this)->RectAtInt(index); 189 } 190 191 192 // Returns the clipping_rect contained in the region at the given index. 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 // Returns the number of rects contained in the region. 207 int32 208 BRegion::CountRects() 209 { 210 return fCount; 211 } 212 213 214 // Returns the number of rects contained in the region. 215 int32 216 BRegion::CountRects() const 217 { 218 return fCount; 219 } 220 221 222 // Check if the region has any area in common with the given BRect. 223 bool 224 BRegion::Intersects(BRect rect) const 225 { 226 return Intersects(_Convert(rect)); 227 } 228 229 230 // Check if the region has any area in common with the given clipping_rect. 231 bool 232 BRegion::Intersects(clipping_rect clipping) const 233 { 234 // cheap convert to internal rect format 235 clipping.right++; 236 clipping.bottom++; 237 238 int result = Support::XRectInRegion(this, clipping); 239 240 return result > Support::RectangleOut; 241 } 242 243 244 // Check if the region contains the given BPoint. 245 bool 246 BRegion::Contains(BPoint point) const 247 { 248 return Support::XPointInRegion(this, (int)point.x, (int)point.y); 249 } 250 251 252 // Check if the region contains the given coordinates. 253 bool 254 BRegion::Contains(int32 x, int32 y) 255 { 256 return Support::XPointInRegion(this, x, y); 257 } 258 259 260 // Check if the region contains the given coordinates. 261 bool 262 BRegion::Contains(int32 x, int32 y) const 263 { 264 return Support::XPointInRegion(this, x, y); 265 } 266 267 268 // Prints the BRegion to stdout. 269 void 270 BRegion::PrintToStream() const 271 { 272 Frame().PrintToStream(); 273 274 for (int32 i = 0; i < fCount; i++) { 275 clipping_rect *rect = &fData[i]; 276 printf("data[%" B_PRId32 "] = BRect(l:%" B_PRId32 ".0, t:%" B_PRId32 277 ".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n", 278 i, rect->left, rect->top, rect->right - 1, rect->bottom - 1); 279 } 280 } 281 282 283 void 284 BRegion::OffsetBy(const BPoint& point) 285 { 286 OffsetBy(point.x, point.y); 287 } 288 289 290 // Applies the given x and y offsets to each rect contained by 291 // the region and recalculates the region's bounds. 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 (int32 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 void 310 BRegion::ScaleBy(BSize scale) 311 { 312 ScaleBy(scale.Width(), scale.Height()); 313 } 314 315 316 void 317 BRegion::ScaleBy(float x, float y) 318 { 319 if (x == 1.0 && y == 1.0) 320 return; 321 322 if (fCount > 0) { 323 if (fData != &fBounds) { 324 for (int32 i = 0; i < fCount; i++) 325 scale_rect(fData[i], x, y); 326 } 327 328 scale_rect(fBounds, x, y); 329 } 330 } 331 332 333 // Empties the region, so that it doesn't include any rect, and invalidates 334 // its bounds. 335 void 336 BRegion::MakeEmpty() 337 { 338 fBounds = (clipping_rect){ 0, 0, 0, 0 }; 339 fCount = 0; 340 } 341 342 343 // Modifies the region, so that it includes the given BRect. 344 void 345 BRegion::Include(BRect rect) 346 { 347 Include(_Convert(rect)); 348 } 349 350 351 // Modifies the region, so that it includes the given clipping_rect. 352 void 353 BRegion::Include(clipping_rect clipping) 354 { 355 if (!valid_rect(clipping)) 356 return; 357 358 // convert to internal clipping format 359 clipping.right++; 360 clipping.bottom++; 361 362 // use private clipping_rect constructor which avoids malloc() 363 BRegion temp(clipping); 364 365 BRegion result; 366 Support::XUnionRegion(this, &temp, &result); 367 368 _AdoptRegionData(result); 369 } 370 371 372 // Modifies the region, so that it includes the area of the given region. 373 void 374 BRegion::Include(const BRegion* region) 375 { 376 BRegion result; 377 Support::XUnionRegion(this, region, &result); 378 379 _AdoptRegionData(result); 380 } 381 382 383 /*! \brief Modifies the region, excluding the area represented by the given BRect. 384 \param rect The BRect to be excluded. 385 */ 386 void 387 BRegion::Exclude(BRect rect) 388 { 389 Exclude(_Convert(rect)); 390 } 391 392 393 // Modifies the region, excluding the area represented by the given clipping_rect. 394 void 395 BRegion::Exclude(clipping_rect clipping) 396 { 397 if (!valid_rect(clipping)) 398 return; 399 400 // convert to internal clipping format 401 clipping.right++; 402 clipping.bottom++; 403 404 // use private clipping_rect constructor which avoids malloc() 405 BRegion temp(clipping); 406 407 BRegion result; 408 Support::XSubtractRegion(this, &temp, &result); 409 410 _AdoptRegionData(result); 411 } 412 413 414 // Modifies the region, excluding the area contained in the given BRegion. 415 void 416 BRegion::Exclude(const BRegion* region) 417 { 418 BRegion result; 419 Support::XSubtractRegion(this, region, &result); 420 421 _AdoptRegionData(result); 422 } 423 424 425 // Modifies the region, so that it will contain only the area in common 426 // with the given BRegion. 427 void 428 BRegion::IntersectWith(const BRegion* region) 429 { 430 BRegion result; 431 Support::XIntersectRegion(this, region, &result); 432 433 _AdoptRegionData(result); 434 } 435 436 437 // Modifies the region, so that it will contain just the area which both 438 // regions do not have in common. 439 void 440 BRegion::ExclusiveInclude(const BRegion* region) 441 { 442 BRegion result; 443 Support::XXorRegion(this, region, &result); 444 445 _AdoptRegionData(result); 446 } 447 448 449 // #pragma mark - BRegion private methods 450 451 452 /*! 453 \fn void BRegion::_AdoptRegionData(BRegion& region) 454 \brief Takes over the data of \a region and empties it. 455 456 \param region The \a region to adopt 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 /*! 480 \fn bool BRegion::_SetSize(int32 newSize) 481 \brief Reallocate the memory in the region. 482 483 \param newSize The amount of rectangles that the region should be 484 able to hold. 485 */ 486 bool 487 BRegion::_SetSize(int32 newSize) 488 { 489 // we never shrink the size 490 newSize = max_c(fDataSize, newSize); 491 // The amount of rectangles that the region should be able to hold. 492 if (newSize == fDataSize) 493 return true; 494 495 // align newSize to multiple of kDataBlockSize 496 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 497 498 if (newSize > 0) { 499 if (fData == &fBounds) { 500 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 501 fData[0] = fBounds; 502 } else if (fData) { 503 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 504 newSize * sizeof(clipping_rect)); 505 if (!resizedData) { 506 // failed to resize, but we cannot keep the 507 // previous state of the object 508 free(fData); 509 fData = NULL; 510 } else 511 fData = resizedData; 512 } else 513 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 514 } else { 515 // just an empty region, but no error 516 MakeEmpty(); 517 return true; 518 } 519 520 if (!fData) { 521 // allocation actually failed 522 fDataSize = 0; 523 MakeEmpty(); 524 return false; 525 } 526 527 fDataSize = newSize; 528 return true; 529 } 530 531 532 clipping_rect 533 BRegion::_Convert(const BRect& rect) const 534 { 535 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 536 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 537 } 538 539 540 clipping_rect 541 BRegion::_ConvertToInternal(const BRect& rect) const 542 { 543 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 544 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 545 } 546 547 548 clipping_rect 549 BRegion::_ConvertToInternal(const clipping_rect& rect) const 550 { 551 return (clipping_rect){ rect.left, rect.top, 552 rect.right + 1, rect.bottom + 1 }; 553 } 554