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 to. 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 Takes over the data of a region and marks that region empty. 433 \param region The region to adopt the data from. 434 */ 435 void 436 BRegion::_AdoptRegionData(BRegion& region) 437 { 438 fCount = region.fCount; 439 fDataSize = region.fDataSize; 440 fBounds = region.fBounds; 441 if (fData != &fBounds) 442 free(fData); 443 if (region.fData != ®ion.fBounds) 444 fData = region.fData; 445 else 446 fData = &fBounds; 447 448 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 449 // called with internally allocated regions, so they don't need to 450 // be left in a valid state. 451 region.fData = NULL; 452 // region.MakeEmpty(); 453 } 454 455 456 /*! \brief Reallocate the memory in the region. 457 \param newSize The amount of rectangles that the region should be 458 able to hold. 459 */ 460 bool 461 BRegion::_SetSize(long newSize) 462 { 463 // we never shrink the size 464 newSize = max_c(fDataSize, newSize); 465 if (newSize == fDataSize) 466 return true; 467 468 // align newSize to multiple of kDataBlockSize 469 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 470 471 if (newSize > 0) { 472 if (fData == &fBounds) { 473 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 474 fData[0] = fBounds; 475 } else if (fData) { 476 clipping_rect* resizedData = (clipping_rect*)realloc(fData, 477 newSize * sizeof(clipping_rect)); 478 if (!resizedData) { 479 // failed to resize, but we cannot keep the 480 // previous state of the object 481 free(fData); 482 fData = NULL; 483 } else 484 fData = resizedData; 485 } else 486 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 487 } else { 488 // just an empty region, but no error 489 MakeEmpty(); 490 return true; 491 } 492 493 if (!fData) { 494 // allocation actually failed 495 fDataSize = 0; 496 MakeEmpty(); 497 return false; 498 } 499 500 fDataSize = newSize; 501 return true; 502 } 503 504 clipping_rect 505 BRegion::_Convert(const BRect& rect) const 506 { 507 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 508 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 509 } 510 511 512 clipping_rect 513 BRegion::_ConvertToInternal(const BRect& rect) const 514 { 515 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 516 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 517 } 518 519 520 clipping_rect 521 BRegion::_ConvertToInternal(const clipping_rect& rect) const 522 { 523 return (clipping_rect){ rect.left, rect.top, 524 rect.right + 1, rect.bottom + 1 }; 525 } 526 527