1 //------------------------------------------------------------------------------ 2 // Copyright (c) 2003-2005, Haiku, Inc. 3 // 4 // Permission is hereby granted, free of charge, to any person obtaining a 5 // copy of this software and associated documentation files (the "Software"), 6 // to deal in the Software without restriction, including without limitation 7 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 // and/or sell copies of the Software, and to permit persons to whom the 9 // Software is furnished to do so, subject to the following conditions: 10 // 11 // The above copyright notice and this permission notice shall be included in 12 // all copies or substantial portions of the Software. 13 // 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 // DEALINGS IN THE SOFTWARE. 21 // 22 // File Name: Region.cpp 23 // Author: Stefano Ceccherini (burton666@libero.it) 24 // Description: Region class consisting of multiple rectangles 25 // 26 //------------------------------------------------------------------------------ 27 28 // Notes: As now, memory is always allocated and never freed (except on destruction, 29 // or sometimes when a copy is made). 30 // This let us be a bit faster since we don't do many reallocations. 31 // But that means that even an empty region could "waste" much space, if it contained 32 // many rects before being emptied. 33 // I.E: a region which contains 24 rects allocates more than 24 * 4 * sizeof(int32) 34 // = 96 * sizeof(int32) bytes. If we call MakeEmpty(), that region will contain no rects, 35 // but it will still keep the allocated memory. 36 // This shouldnt' be an issue, since usually BRegions are just used for calculations, 37 // and don't last so long. 38 // Anyway, we can change that behaviour if we want, but BeOS's BRegion seems to behave exactly 39 // like this. 40 41 #include <cstdlib> 42 #include <cstring> 43 44 #include <Debug.h> 45 #include <Region.h> 46 47 #include <clipping.h> 48 #include <RegionSupport.h> 49 50 51 const static int32 kInitialDataSize = 8; 52 53 54 /*! \brief Initializes a region. The region will have no rects, 55 and its bound will be invalid. 56 */ 57 BRegion::BRegion() 58 : 59 data_size(0), 60 data(NULL) 61 { 62 data = (clipping_rect *)malloc(kInitialDataSize * sizeof(clipping_rect)); 63 if (data != NULL) 64 data_size = kInitialDataSize; 65 66 Support::ZeroRegion(*this); 67 } 68 69 70 /*! \brief Initializes a region to be a copy of another. 71 \param region The region to copy. 72 */ 73 BRegion::BRegion(const BRegion ®ion) 74 : 75 data_size(0), 76 data(NULL) 77 { 78 const int32 size = region.data_size > 0 ? region.data_size : 1; 79 80 data = (clipping_rect *)malloc(size * sizeof(clipping_rect)); 81 if (data != NULL) { 82 data_size = size; 83 bound = region.bound; 84 count = region.count; 85 memcpy(data, region.data, count * sizeof(clipping_rect)); 86 } else 87 Support::ZeroRegion(*this); 88 } 89 90 91 /*! \brief Initializes a region to contain a BRect. 92 \param rect The BRect to set the region to. 93 */ 94 BRegion::BRegion(const BRect rect) 95 : 96 data_size(0), 97 data(NULL) 98 { 99 data = (clipping_rect *)malloc(kInitialDataSize * sizeof(clipping_rect)); 100 if (data != NULL) { 101 data_size = kInitialDataSize; 102 Set(rect); 103 } else 104 Support::ZeroRegion(*this); 105 } 106 107 108 /*! \brief Frees the allocated memory. 109 */ 110 BRegion::~BRegion() 111 { 112 free(data); 113 } 114 115 116 /*! \brief Returns the bounds of the region. 117 \return A BRect which represents the bounds of the region. 118 */ 119 BRect 120 BRegion::Frame() const 121 { 122 return to_BRect(bound); 123 } 124 125 126 /*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates). 127 \return A clipping_rect which represents the bounds of the region. 128 */ 129 clipping_rect 130 BRegion::FrameInt() const 131 { 132 return bound; 133 } 134 135 136 /*! \brief Returns the regions's BRect at the given index. 137 \param index The index (zero based) of the wanted rectangle. 138 \return If the given index is valid, it returns the BRect at that index, 139 otherwise, it returns an invalid BRect. 140 */ 141 BRect 142 BRegion::RectAt(int32 index) 143 { 144 if (index >= 0 && index < count) 145 return to_BRect(data[index]); 146 147 return BRect(); //An invalid BRect 148 } 149 150 151 /*! \brief Returns the regions's clipping_rect at the given index. 152 \param index The index (zero based) of the wanted rectangle. 153 \return If the given index is valid, it returns the clipping_rect at that index, 154 otherwise, it returns an invalid clipping_rect. 155 */ 156 clipping_rect 157 BRegion::RectAtInt(int32 index) 158 { 159 if (index >= 0 && index < count) 160 return data[index]; 161 162 clipping_rect rect = { 1, 1, 0, 0 }; 163 return rect; 164 } 165 166 167 /*! \brief Counts the region rects. 168 \return An int32 which is the total number of rects in the region. 169 */ 170 int32 171 BRegion::CountRects() 172 { 173 return count; 174 } 175 176 177 /*! \brief Set the region to contain just the given BRect. 178 \param newBounds A BRect. 179 */ 180 void 181 BRegion::Set(BRect newBounds) 182 { 183 Set(to_clipping_rect(newBounds)); 184 } 185 186 187 /*! \brief Set the region to contain just the given clipping_rect. 188 \param newBounds A clipping_rect. 189 */ 190 void 191 BRegion::Set(clipping_rect newBounds) 192 { 193 ASSERT(data_size > 0); 194 195 if (valid_rect(newBounds)) { 196 count = 1; 197 data[0] = newBounds; 198 bound = newBounds; 199 } else 200 Support::ZeroRegion(*this); 201 } 202 203 204 /*! \brief Check if the region has any area in common with the given BRect. 205 \param rect The BRect to check the region against to. 206 \return \ctrue if the region has any area in common with the BRect, \cfalse if not. 207 */ 208 bool 209 BRegion::Intersects(BRect rect) const 210 { 211 return Intersects(to_clipping_rect(rect)); 212 } 213 214 215 /*! \brief Check if the region has any area in common with the given clipping_rect. 216 \param rect The clipping_rect to check the region against to. 217 \return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not. 218 */ 219 bool 220 BRegion::Intersects(clipping_rect rect) const 221 { 222 if (!rects_intersect(rect, bound)) 223 return false; 224 225 for (long c = 0; c < count; c++) { 226 if (rects_intersect(data[c], rect)) 227 return true; 228 } 229 230 return false; 231 } 232 233 234 /*! \brief Check if the region contains the given BPoint. 235 \param pt The BPoint to be checked. 236 \return \ctrue if the region contains the BPoint, \cfalse if not. 237 */ 238 bool 239 BRegion::Contains(BPoint pt) const 240 { 241 // If the point doesn't lie within the region's bounds, 242 // don't even try it against the region's rects. 243 if (!point_in(bound, pt)) 244 return false; 245 246 for (long c = 0; c < count; c++) { 247 if (point_in(data[c], pt)) 248 return true; 249 } 250 return false; 251 } 252 253 254 /*! \brief Check if the region contains the given coordinates. 255 \param x The \cx coordinate of the point to be checked. 256 \param y The \cy coordinate of the point to be checked. 257 \return \ctrue if the region contains the point, \cfalse if not. 258 */ 259 bool 260 BRegion::Contains(int32 x, int32 y) 261 { 262 // see above 263 if (!point_in(bound, x, y)) 264 return false; 265 266 for (long c = 0; c < count; c++) { 267 if (point_in(data[c], x, y)) 268 return true; 269 } 270 return false; 271 } 272 273 274 /*! \brief Prints the BRegion to stdout. 275 */ 276 void 277 BRegion::PrintToStream() const 278 { 279 Frame().PrintToStream(); 280 281 for (long c = 0; c < count; c++) { 282 clipping_rect *rect = &data[c]; 283 printf("data = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n", 284 rect->left, rect->top, rect->right, rect->bottom); 285 } 286 } 287 288 289 /*! \brief Offsets all region's rects, and bounds by the given values. 290 \param dh The horizontal offset. 291 \param dv The vertical offset. 292 */ 293 void 294 BRegion::OffsetBy(int32 dh, int32 dv) 295 { 296 if (dh == 0 && dv == 0) 297 return; 298 299 if (count > 0) { 300 for (long c = 0; c < count; c++) 301 offset_rect(data[c], dh, dv); 302 303 offset_rect(bound, dh, dv); 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 Support::ZeroRegion(*this); 314 } 315 316 317 /*! \brief Modifies the region, so that it includes the given BRect. 318 \param rect The BRect to be included by the region. 319 */ 320 void 321 BRegion::Include(BRect rect) 322 { 323 Include(to_clipping_rect(rect)); 324 } 325 326 327 /*! \brief Modifies the region, so that it includes the given clipping_rect. 328 \param rect The clipping_rect to be included by the region. 329 */ 330 void 331 BRegion::Include(clipping_rect rect) 332 { 333 BRegion region; 334 BRegion newRegion; 335 336 region.Set(rect); 337 338 Support::OrRegion(*this, region, newRegion); 339 Support::CopyRegion(newRegion, *this); 340 } 341 342 343 /*! \brief Modifies the region, so that it includes the area of the given region. 344 \param region The region to be included. 345 */ 346 void 347 BRegion::Include(const BRegion *region) 348 { 349 BRegion newRegion; 350 351 Support::OrRegion(*this, *region, newRegion); 352 Support::CopyRegion(newRegion, *this); 353 } 354 355 356 /*! \brief Modifies the region, excluding the area represented by the given BRect. 357 \param rect The BRect to be excluded. 358 */ 359 void 360 BRegion::Exclude(BRect rect) 361 { 362 Exclude(to_clipping_rect(rect)); 363 } 364 365 366 /*! \brief Modifies the region, excluding the area represented by the given clipping_rect. 367 \param rect The clipping_rect to be excluded. 368 */ 369 void 370 BRegion::Exclude(clipping_rect rect) 371 { 372 BRegion region; 373 BRegion newRegion; 374 375 region.Set(rect); 376 377 Support::SubRegion(*this, region, newRegion); 378 Support::CopyRegion(newRegion, *this); 379 } 380 381 382 /*! \brief Modifies the region, excluding the area contained in the given BRegion. 383 \param region The BRegion to be excluded. 384 */ 385 void 386 BRegion::Exclude(const BRegion *region) 387 { 388 BRegion newRegion; 389 390 Support::SubRegion(*this, *region, newRegion); 391 Support::CopyRegion(newRegion, *this); 392 } 393 394 395 /*! \brief Modifies the region, so that it will contain just the area in common with the given BRegion. 396 \param region the BRegion to intersect to. 397 */ 398 void 399 BRegion::IntersectWith(const BRegion *region) 400 { 401 BRegion newRegion; 402 403 Support::AndRegion(*this, *region, newRegion); 404 Support::CopyRegion(newRegion, *this); 405 } 406 407 408 /*! \brief Modifies the region to be a copy of the given BRegion. 409 \param region the BRegion to copy. 410 \return This function always returns \c *this. 411 */ 412 BRegion & 413 BRegion::operator=(const BRegion ®ion) 414 { 415 if (®ion != this) { 416 bound = region.bound; 417 count = region.count; 418 419 // handle reallocation if we're too small to contain 420 // the other region 421 set_size(region.data_size); 422 423 // TODO: what is this supposed to do?? 424 if (data_size <= 0) 425 data_size = 1; 426 427 if (data) 428 memcpy(data, region.data, count * sizeof(clipping_rect)); 429 } 430 431 return *this; 432 } 433 434 435 /*! \brief Adds a rect to the region. 436 \param rect The clipping_rect to be added. 437 438 Adds the given rect to the region, merging it with another already contained in the region, 439 if possible. Recalculate the region's bounds if needed. 440 */ 441 void 442 BRegion::_AddRect(clipping_rect rect) 443 { 444 ASSERT(count >= 0); 445 ASSERT(data_size >= 0); 446 ASSERT(valid_rect(rect)); 447 448 // Should we just reallocate the memory and 449 // copy the rect ? 450 bool addRect = true; 451 452 if (count > 0) { 453 // Wait! We could merge the rect with one of the 454 // existing rectangles, if it's adiacent. 455 // We just check it against the last rectangle, since 456 // we are keeping them sorted by their "top" coordinates. 457 long last = count - 1; 458 if (rect.left == data[last].left && rect.right == data[last].right 459 && rect.top == data[last].bottom + 1) { 460 461 data[last].bottom = rect.bottom; 462 addRect = false; 463 464 } else if (rect.top == data[last].top && rect.bottom == data[last].bottom) { 465 if (rect.left == data[last].right + 1) { 466 467 data[last].right = rect.right; 468 addRect = false; 469 470 } else if (rect.right == data[last].left - 1) { 471 472 data[last].left = rect.left; 473 addRect = false; 474 } 475 } 476 } 477 478 // We weren't lucky.... just add the rect as a new one 479 if (addRect) { 480 if (data_size <= count) 481 set_size(count + 16); 482 483 data[count] = rect; 484 485 count++; 486 } 487 488 // Recalculate bounds 489 if (rect.top < bound.top) 490 bound.top = rect.top; 491 492 if (rect.left < bound.left) 493 bound.left = rect.left; 494 495 if (rect.right > bound.right) 496 bound.right = rect.right; 497 498 if (rect.bottom > bound.bottom) 499 bound.bottom = rect.bottom; 500 } 501 502 503 /*! \brief Reallocate the memory in the region. 504 \param new_size The amount of rectangles that the region could contain. 505 */ 506 void 507 BRegion::set_size(long new_size) 508 { 509 if (new_size <= 0) 510 new_size = data_size + 16; 511 512 data = (clipping_rect *)realloc(data, new_size * sizeof(clipping_rect)); 513 514 if (data == NULL) 515 debugger("BRegion::set_size realloc error\n"); 516 517 data_size = new_size; 518 519 ASSERT(count <= data_size); 520 } 521