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