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