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