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 newBounds) 121 { 122 Set(_Convert(newBounds)); 123 } 124 125 126 //Set the region to contain just the given clipping_rect. 127 void 128 BRegion::Set(clipping_rect newBounds) 129 { 130 _SetSize(1); 131 132 if (valid_rect(newBounds) && fData) { 133 fCount = 1; 134 // cheap convert to internal rect format 135 newBounds.right++; 136 newBounds.bottom++; 137 fData[0] = fBounds = newBounds; 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 // Empties the region, so that it doesn't include any rect, and invalidates 310 // its bounds. 311 void 312 BRegion::MakeEmpty() 313 { 314 fBounds = (clipping_rect){ 0, 0, 0, 0 }; 315 fCount = 0; 316 } 317 318 319 // Modifies the region, so that it includes the given BRect. 320 void 321 BRegion::Include(BRect rect) 322 { 323 Include(_Convert(rect)); 324 } 325 326 327 // Modifies the region, so that it includes the given clipping_rect. 328 void 329 BRegion::Include(clipping_rect clipping) 330 { 331 if (!valid_rect(clipping)) 332 return; 333 334 // convert to internal clipping format 335 clipping.right++; 336 clipping.bottom++; 337 338 // use private clipping_rect constructor which avoids malloc() 339 BRegion temp(clipping); 340 341 BRegion result; 342 Support::XUnionRegion(this, &temp, &result); 343 344 _AdoptRegionData(result); 345 } 346 347 348 // Modifies the region, so that it includes the area of the given region. 349 void 350 BRegion::Include(const BRegion* region) 351 { 352 BRegion result; 353 Support::XUnionRegion(this, region, &result); 354 355 _AdoptRegionData(result); 356 } 357 358 359 /*! \brief Modifies the region, excluding the area represented by the given BRect. 360 \param rect The BRect to be excluded. 361 */ 362 void 363 BRegion::Exclude(BRect rect) 364 { 365 Exclude(_Convert(rect)); 366 } 367 368 369 // Modifies the region, excluding the area represented by the given clipping_rect. 370 void 371 BRegion::Exclude(clipping_rect clipping) 372 { 373 if (!valid_rect(clipping)) 374 return; 375 376 // convert to internal clipping format 377 clipping.right++; 378 clipping.bottom++; 379 380 // use private clipping_rect constructor which avoids malloc() 381 BRegion temp(clipping); 382 383 BRegion result; 384 Support::XSubtractRegion(this, &temp, &result); 385 386 _AdoptRegionData(result); 387 } 388 389 390 // Modifies the region, excluding the area contained in the given BRegion. 391 void 392 BRegion::Exclude(const BRegion* region) 393 { 394 BRegion result; 395 Support::XSubtractRegion(this, region, &result); 396 397 _AdoptRegionData(result); 398 } 399 400 401 // Modifies the region, so that it will contain only the area in common 402 // with the given BRegion. 403 void 404 BRegion::IntersectWith(const BRegion* region) 405 { 406 BRegion result; 407 Support::XIntersectRegion(this, region, &result); 408 409 _AdoptRegionData(result); 410 } 411 412 413 // Modifies the region, so that it will contain just the area which both 414 // regions do not have in common. 415 void 416 BRegion::ExclusiveInclude(const BRegion* region) 417 { 418 BRegion result; 419 Support::XXorRegion(this, region, &result); 420 421 _AdoptRegionData(result); 422 } 423 424 425 // #pragma mark - BRegion private methods 426 427 428 // Takes over the data of a region and marks that region empty. 429 void 430 BRegion::_AdoptRegionData(BRegion& region) 431 { 432 fCount = region.fCount; 433 fDataSize = region.fDataSize; 434 fBounds = region.fBounds; 435 if (fData != &fBounds) 436 free(fData); 437 if (region.fData != ®ion.fBounds) 438 fData = region.fData; 439 else 440 fData = &fBounds; 441 442 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 443 // called with internally allocated regions, so they don't need to 444 // be left in a valid state. 445 region.fData = NULL; 446 // region.MakeEmpty(); 447 } 448 449 450 // Reallocate the memory in the region. 451 bool 452 BRegion::_SetSize(int32 newSize) 453 { 454 // we never shrink the size 455 newSize = max_c(fDataSize, newSize); 456 // The amount of rectangles that the region should be able to hold. 457 if (newSize == fDataSize) 458 return true; 459 460 // align newSize to multiple of kDataBlockSize 461 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 462 463 if (newSize > 0) { 464 if (fData == &fBounds) { 465 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 466 fData[0] = fBounds; 467 } else if (fData) { 468 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 469 newSize * sizeof(clipping_rect)); 470 if (!resizedData) { 471 // failed to resize, but we cannot keep the 472 // previous state of the object 473 free(fData); 474 fData = NULL; 475 } else 476 fData = resizedData; 477 } else 478 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 479 } else { 480 // just an empty region, but no error 481 MakeEmpty(); 482 return true; 483 } 484 485 if (!fData) { 486 // allocation actually failed 487 fDataSize = 0; 488 MakeEmpty(); 489 return false; 490 } 491 492 fDataSize = newSize; 493 return true; 494 } 495 496 497 clipping_rect 498 BRegion::_Convert(const BRect& rect) const 499 { 500 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 501 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 502 } 503 504 505 clipping_rect 506 BRegion::_ConvertToInternal(const BRect& rect) const 507 { 508 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 509 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 510 } 511 512 513 clipping_rect 514 BRegion::_ConvertToInternal(const clipping_rect& rect) const 515 { 516 return (clipping_rect){ rect.left, rect.top, 517 rect.right + 1, rect.bottom + 1 }; 518 } 519