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 <malloc.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 BRect to set the region to. 70 */ 71 BRegion::BRegion(const clipping_rect& rect) 72 : fCount(1) 73 , fDataSize(1) 74 , fBounds(rect) 75 , fData(&fBounds) 76 { 77 } 78 79 80 /*! \brief Frees the allocated memory. 81 */ 82 BRegion::~BRegion() 83 { 84 if (fData != &fBounds) 85 free(fData); 86 } 87 88 89 // #pragma mark - 90 91 92 /*! \brief Modifies the region to be a copy of the given BRegion. 93 \param region the BRegion to copy. 94 \return This function always returns \c *this. 95 */ 96 BRegion & 97 BRegion::operator=(const BRegion ®ion) 98 { 99 if (®ion == this) 100 return *this; 101 102 // handle reallocation if we're too small to contain 103 // the other region 104 if (_SetSize(region.fDataSize)) { 105 memcpy(fData, region.fData, region.fCount * sizeof(clipping_rect)); 106 107 fBounds = region.fBounds; 108 fCount = region.fCount; 109 } 110 111 return *this; 112 } 113 114 115 /*! \brief Set the region to contain just the given BRect. 116 \param newBounds A BRect. 117 */ 118 void 119 BRegion::Set(BRect newBounds) 120 { 121 Set(_Convert(newBounds)); 122 } 123 124 125 /*! \brief Set the region to contain just the given clipping_rect. 126 \param newBounds A clipping_rect. 127 */ 128 void 129 BRegion::Set(clipping_rect newBounds) 130 { 131 _SetSize(1); 132 133 if (valid_rect(newBounds) && fData) { 134 fCount = 1; 135 // cheap convert to internal rect format 136 newBounds.right++; 137 newBounds.bottom++; 138 fData[0] = fBounds = newBounds; 139 } else 140 MakeEmpty(); 141 } 142 143 144 // #pragma mark - 145 146 147 /*! \brief Returns the bounds of the region. 148 \return A BRect which represents the bounds of the region. 149 */ 150 BRect 151 BRegion::Frame() const 152 { 153 return BRect(fBounds.left, fBounds.top, 154 fBounds.right - 1, fBounds.bottom - 1); 155 } 156 157 158 /*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates). 159 \return A clipping_rect which represents the bounds of the region. 160 */ 161 clipping_rect 162 BRegion::FrameInt() const 163 { 164 return (clipping_rect){ fBounds.left, fBounds.top, 165 fBounds.right - 1, fBounds.bottom - 1 }; 166 } 167 168 169 /*! \brief Returns the regions's BRect at the given index. 170 \param index The index (zero based) of the wanted rectangle. 171 \return If the given index is valid, it returns the BRect at that index, 172 otherwise, it returns an invalid BRect. 173 */ 174 BRect 175 BRegion::RectAt(int32 index) /*const*/ 176 { 177 if (index >= 0 && index < fCount) { 178 const clipping_rect& r = fData[index]; 179 return BRect(r.left, r.top, r.right - 1, r.bottom - 1); 180 } 181 182 return BRect(); 183 // an invalid BRect 184 } 185 186 187 /*! \brief Returns the regions's clipping_rect at the given index. 188 \param index The index (zero based) of the wanted rectangle. 189 \return If the given index is valid, it returns the clipping_rect at that index, 190 otherwise, it returns an invalid clipping_rect. 191 */ 192 clipping_rect 193 BRegion::RectAtInt(int32 index) /*const*/ 194 { 195 if (index >= 0 && index < fCount) { 196 const clipping_rect& r = fData[index]; 197 return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 }; 198 } 199 200 return (clipping_rect){ 1, 1, 0, 0 }; 201 // an invalid clipping_rect 202 } 203 204 205 /*! \brief Counts the region rects. 206 \return An int32 which is the total number of rects in the region. 207 */ 208 int32 209 BRegion::CountRects() /*const*/ 210 { 211 return fCount; 212 } 213 214 215 // #pragma mark - 216 217 218 /*! \brief Check if the region has any area in common with the given BRect. 219 \param rect The BRect to check the region against to. 220 \return \ctrue if the region has any area in common with the BRect, \cfalse if not. 221 */ 222 bool 223 BRegion::Intersects(BRect rect) const 224 { 225 return Intersects(_Convert(rect)); 226 } 227 228 229 /*! \brief Check if the region has any area in common with the given clipping_rect. 230 \param rect The clipping_rect to check the region against to. 231 \return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not. 232 */ 233 bool 234 BRegion::Intersects(clipping_rect rect) const 235 { 236 // cheap convert to internal rect format 237 rect.right ++; 238 rect.bottom ++; 239 240 int result = Support::XRectInRegion(this, rect); 241 242 return result > Support::RectangleOut; 243 } 244 245 246 /*! \brief Check if the region contains the given BPoint. 247 \param pt The BPoint to be checked. 248 \return \ctrue if the region contains the BPoint, \cfalse if not. 249 */ 250 bool 251 BRegion::Contains(BPoint point) const 252 { 253 return Support::XPointInRegion(this, (int)point.x, (int)point.y); 254 } 255 256 257 /*! \brief Check if the region contains the given coordinates. 258 \param x The \cx coordinate of the point to be checked. 259 \param y The \cy coordinate of the point to be checked. 260 \return \ctrue if the region contains the point, \cfalse if not. 261 */ 262 bool 263 BRegion::Contains(int32 x, int32 y) /*const*/ 264 { 265 return Support::XPointInRegion(this, x, y); 266 } 267 268 269 /*! \brief Prints the BRegion to stdout. 270 */ 271 void 272 BRegion::PrintToStream() const 273 { 274 Frame().PrintToStream(); 275 276 for (long i = 0; i < fCount; i++) { 277 clipping_rect *rect = &fData[i]; 278 printf("data[%ld] = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n", 279 i, rect->left, rect->top, rect->right - 1, rect->bottom - 1); 280 } 281 } 282 283 284 // #pragma mark - 285 286 287 /*! \brief Offsets all region's rects, and bounds by the given values. 288 \param dh The horizontal offset. 289 \param dv The vertical offset. 290 */ 291 void 292 BRegion::OffsetBy(int32 x, int32 y) 293 { 294 if (x == 0 && y == 0) 295 return; 296 297 if (fCount > 0) { 298 if (fData != &fBounds) { 299 for (long i = 0; i < fCount; i++) 300 offset_rect(fData[i], x, y); 301 } 302 303 offset_rect(fBounds, x, y); 304 } 305 } 306 307 308 /*! \brief Empties the region, so that it doesn't include any rect, and invalidates its bounds. 309 */ 310 void 311 BRegion::MakeEmpty() 312 { 313 fBounds= (clipping_rect){ 0, 0, 0, 0 }; 314 fCount = 0; 315 } 316 317 318 // #pragma mark - 319 320 321 /*! \brief Modifies the region, so that it includes the given BRect. 322 \param rect The BRect to be included by the region. 323 */ 324 void 325 BRegion::Include(BRect rect) 326 { 327 Include(_Convert(rect)); 328 } 329 330 331 /*! \brief Modifies the region, so that it includes the given clipping_rect. 332 \param rect The clipping_rect to be included by the region. 333 */ 334 void 335 BRegion::Include(clipping_rect rect) 336 { 337 // convert to internal rect format 338 rect.right ++; 339 rect.bottom ++; 340 341 // use private clipping_rect constructor which avoids malloc() 342 BRegion t(rect); 343 344 BRegion result; 345 Support::XUnionRegion(this, &t, &result); 346 347 _AdoptRegionData(result); 348 } 349 350 351 /*! \brief Modifies the region, so that it includes the area of the given region. 352 \param region The region to be included. 353 */ 354 void 355 BRegion::Include(const BRegion* region) 356 { 357 BRegion result; 358 Support::XUnionRegion(this, region, &result); 359 360 _AdoptRegionData(result); 361 } 362 363 364 // #pragma mark - 365 366 367 /*! \brief Modifies the region, excluding the area represented by the given BRect. 368 \param rect The BRect to be excluded. 369 */ 370 void 371 BRegion::Exclude(BRect rect) 372 { 373 Exclude(_Convert(rect)); 374 } 375 376 377 /*! \brief Modifies the region, excluding the area represented by the given clipping_rect. 378 \param rect The clipping_rect to be excluded. 379 */ 380 void 381 BRegion::Exclude(clipping_rect rect) 382 { 383 // convert to internal rect format 384 rect.right ++; 385 rect.bottom ++; 386 387 // use private clipping_rect constructor which avoids malloc() 388 BRegion t(rect); 389 390 BRegion result; 391 Support::XSubtractRegion(this, &t, &result); 392 393 _AdoptRegionData(result); 394 } 395 396 397 /*! \brief Modifies the region, excluding the area contained in the given 398 BRegion. 399 \param region The BRegion to be excluded. 400 */ 401 void 402 BRegion::Exclude(const BRegion* region) 403 { 404 BRegion result; 405 Support::XSubtractRegion(this, region, &result); 406 407 _AdoptRegionData(result); 408 } 409 410 411 // #pragma mark - 412 413 414 /*! \brief Modifies the region, so that it will contain just the area 415 in common with the given BRegion. 416 \param region the BRegion to intersect to. 417 */ 418 void 419 BRegion::IntersectWith(const BRegion* region) 420 { 421 BRegion result; 422 Support::XIntersectRegion(this, region, &result); 423 424 _AdoptRegionData(result); 425 } 426 427 428 // #pragma mark - 429 430 431 /*! \brief Takes over the data of a region and marks that region empty. 432 \param region The region to adopt the data from. 433 */ 434 void 435 BRegion::_AdoptRegionData(BRegion& region) 436 { 437 fCount = region.fCount; 438 fDataSize = region.fDataSize; 439 fBounds = region.fBounds; 440 if (fData != &fBounds) 441 free(fData); 442 if (region.fData != ®ion.fBounds) 443 fData = region.fData; 444 else 445 fData = &fBounds; 446 447 // NOTE: MakeEmpty() is not called since _AdoptRegionData is only 448 // called with internally allocated regions, so they don't need to 449 // be left in a valid state. 450 region.fData = NULL; 451 // region.MakeEmpty(); 452 } 453 454 455 /*! \brief Reallocate the memory in the region. 456 \param newSize The amount of rectangles that the region should be 457 able to hold. 458 */ 459 bool 460 BRegion::_SetSize(long newSize) 461 { 462 // we never shrink the size 463 newSize = max_c(fDataSize, newSize); 464 if (newSize == fDataSize) 465 return true; 466 467 // align newSize to multiple of kDataBlockSize 468 newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize; 469 470 if (newSize > 0) { 471 if (fData == &fBounds) { 472 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 473 fData[0] = fBounds; 474 } else if (fData) 475 fData = (clipping_rect*)realloc(fData, newSize * sizeof(clipping_rect)); 476 else 477 fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect)); 478 } 479 480 if (!fData || newSize <= 0) { 481 fDataSize = 0; 482 MakeEmpty(); 483 return false; 484 } 485 486 fDataSize = newSize; 487 return true; 488 } 489 490 clipping_rect 491 BRegion::_Convert(const BRect& rect) const 492 { 493 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 494 (int)ceilf(rect.right), (int)ceilf(rect.bottom) }; 495 } 496 497 498 clipping_rect 499 BRegion::_ConvertToInternal(const BRect& rect) const 500 { 501 return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top), 502 (int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 }; 503 } 504 505 506 clipping_rect 507 BRegion::_ConvertToInternal(const clipping_rect& rect) const 508 { 509 return (clipping_rect){ rect.left, rect.top, 510 rect.right + 1, rect.bottom + 1 }; 511 } 512 513