xref: /haiku/src/kits/interface/Region.cpp (revision adb0d19d561947362090081e81d90dde59142026)
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 	:
30 	fCount(0),
31 	fDataSize(0),
32 	fBounds((clipping_rect){ 0, 0, 0, 0 }),
33 	fData(NULL)
34 {
35 	_SetSize(kDataBlockSize);
36 }
37 
38 
39 /*! \brief Initializes a region to be a copy of another.
40 	\param region The region to copy.
41 */
42 BRegion::BRegion(const BRegion& region)
43 	:
44 	fCount(0),
45 	fDataSize(0),
46 	fBounds((clipping_rect){ 0, 0, 0, 0 }),
47 	fData(NULL)
48 {
49 	*this = region;
50 }
51 
52 
53 /*!	\brief Initializes a region to contain a BRect.
54 	\param rect The BRect to set the region to.
55 */
56 BRegion::BRegion(const BRect rect)
57 	:
58 	fCount(0),
59 	fDataSize(1),
60 	fBounds((clipping_rect){ 0, 0, 0, 0 }),
61 	fData(&fBounds)
62 {
63 	if (!rect.IsValid())
64 		return;
65 
66 	fBounds = _ConvertToInternal(rect);
67 	fCount = 1;
68 }
69 
70 // NOTE: private constructor
71 /*!	\brief Initializes a region to contain a clipping_rect.
72 	\param rect The clipping_rect to set the region to, already in
73 	internal rect format.
74 */
75 BRegion::BRegion(const clipping_rect& rect)
76 	: fCount(1)
77 	, fDataSize(1)
78 	, fBounds(rect)
79 	, fData(&fBounds)
80 {
81 }
82 
83 
84 /*!	\brief Frees the allocated memory.
85 */
86 BRegion::~BRegion()
87 {
88 	if (fData != &fBounds)
89 		free(fData);
90 }
91 
92 
93 // #pragma mark -
94 
95 
96 /*!	\brief Modifies the region to be a copy of the given BRegion.
97 	\param region the BRegion to copy.
98 	\return This function always returns \c *this.
99 */
100 BRegion&
101 BRegion::operator=(const BRegion& region)
102 {
103 	if (&region == this)
104 		return *this;
105 
106 	// handle reallocation if we're too small to contain
107 	// the other region
108 	if (_SetSize(region.fDataSize)) {
109 		memcpy(fData, region.fData, region.fCount * sizeof(clipping_rect));
110 
111 		fBounds = region.fBounds;
112 		fCount = region.fCount;
113 	}
114 
115 	return *this;
116 }
117 
118 
119 /*!	\brief Compares this region to another (by value).
120 	\param other the BRegion to compare to.
121 	\return \ctrue if the two regions are the same, \cfalse otherwise.
122 */
123 bool
124 BRegion::operator==(const BRegion& other) const
125 {
126 	if (&other == this)
127 		return true;
128 
129 	if (fCount != other.fCount)
130 		return false;
131 
132 	return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0;
133 }
134 
135 
136 /*!	\brief Set the region to contain just the given BRect.
137 	\param newBounds A BRect.
138 */
139 void
140 BRegion::Set(BRect newBounds)
141 {
142 	Set(_Convert(newBounds));
143 }
144 
145 
146 /*!	\brief Set the region to contain just the given clipping_rect.
147 	\param newBounds A clipping_rect.
148 */
149 void
150 BRegion::Set(clipping_rect newBounds)
151 {
152 	_SetSize(1);
153 
154 	if (valid_rect(newBounds) && fData) {
155 		fCount = 1;
156 		// cheap convert to internal rect format
157 		newBounds.right++;
158 		newBounds.bottom++;
159 		fData[0] = fBounds = newBounds;
160 	} else
161 		MakeEmpty();
162 }
163 
164 
165 // #pragma mark -
166 
167 
168 /*! \brief Returns the bounds of the region.
169 	\return A BRect which represents the bounds of the region.
170 */
171 BRect
172 BRegion::Frame() const
173 {
174 	return BRect(fBounds.left, fBounds.top,
175 		fBounds.right - 1, fBounds.bottom - 1);
176 }
177 
178 
179 /*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates).
180 	\return A clipping_rect which represents the bounds of the region.
181 */
182 clipping_rect
183 BRegion::FrameInt() const
184 {
185 	return (clipping_rect){ fBounds.left, fBounds.top,
186 		fBounds.right - 1, fBounds.bottom - 1 };
187 }
188 
189 
190 BRect
191 BRegion::RectAt(int32 index)
192 {
193 	return const_cast<const BRegion*>(this)->RectAt(index);
194 }
195 
196 
197 /*! \brief Returns the regions's BRect at the given index.
198 	\param index The index (zero based) of the wanted rectangle.
199 	\return If the given index is valid, it returns the BRect at that index,
200 		otherwise, it returns an invalid BRect.
201 */
202 BRect
203 BRegion::RectAt(int32 index) const
204 {
205 	if (index >= 0 && index < fCount) {
206 		const clipping_rect& r = fData[index];
207 		return BRect(r.left, r.top, r.right - 1, r.bottom - 1);
208 	}
209 
210 	return BRect();
211 		// an invalid BRect
212 }
213 
214 
215 clipping_rect
216 BRegion::RectAtInt(int32 index)
217 {
218 	return const_cast<const BRegion*>(this)->RectAtInt(index);
219 }
220 
221 
222 /*! \brief Returns the regions's clipping_rect at the given index.
223 	\param index The index (zero based) of the wanted rectangle.
224 	\return If the given index is valid, it returns the clipping_rect at that index,
225 		otherwise, it returns an invalid clipping_rect.
226 */
227 clipping_rect
228 BRegion::RectAtInt(int32 index) const
229 {
230 	if (index >= 0 && index < fCount) {
231 		const clipping_rect& r = fData[index];
232 		return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 };
233 	}
234 
235 	return (clipping_rect){ 1, 1, 0, 0 };
236 		// an invalid clipping_rect
237 }
238 
239 
240 /*!	\brief Counts the region rects.
241 	\return An int32 which is the total number of rects in the region.
242 */
243 int32
244 BRegion::CountRects()
245 {
246 	return fCount;
247 }
248 
249 
250 /*!	\brief Counts the region rects.
251 	\return An int32 which is the total number of rects in the region.
252 */
253 int32
254 BRegion::CountRects() const
255 {
256 	return fCount;
257 }
258 
259 
260 // #pragma mark -
261 
262 
263 /*!	\brief Check if the region has any area in common with the given BRect.
264 	\param rect The BRect to check the region against to.
265 	\return \ctrue if the region has any area in common with the BRect, \cfalse if not.
266 */
267 bool
268 BRegion::Intersects(BRect rect) const
269 {
270 	return Intersects(_Convert(rect));
271 }
272 
273 
274 /*!	\brief Check if the region has any area in common with the given clipping_rect.
275 	\param rect The clipping_rect to check the region against to.
276 	\return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not.
277 */
278 bool
279 BRegion::Intersects(clipping_rect rect) const
280 {
281 	// cheap convert to internal rect format
282 	rect.right ++;
283 	rect.bottom ++;
284 
285 	int result = Support::XRectInRegion(this, rect);
286 
287 	return result > Support::RectangleOut;
288 }
289 
290 
291 /*!	\brief Check if the region contains the given BPoint.
292 	\param pt The BPoint to be checked.
293 	\return \ctrue if the region contains the BPoint, \cfalse if not.
294 */
295 bool
296 BRegion::Contains(BPoint point) const
297 {
298 	return Support::XPointInRegion(this, (int)point.x, (int)point.y);
299 }
300 
301 
302 /*!	\brief Check if the region contains the given coordinates.
303 	\param x The \cx coordinate of the point to be checked.
304 	\param y The \cy coordinate of the point to be checked.
305 	\return \ctrue if the region contains the point, \cfalse if not.
306 */
307 bool
308 BRegion::Contains(int32 x, int32 y)
309 {
310 	return Support::XPointInRegion(this, x, y);
311 }
312 
313 
314 /*!	\brief Check if the region contains the given coordinates.
315 	\param x The \cx coordinate of the point to be checked.
316 	\param y The \cy coordinate of the point to be checked.
317 	\return \ctrue if the region contains the point, \cfalse if not.
318 */
319 bool
320 BRegion::Contains(int32 x, int32 y) const
321 {
322 	return Support::XPointInRegion(this, x, y);
323 }
324 
325 
326 /*!	\brief Prints the BRegion to stdout.
327 */
328 void
329 BRegion::PrintToStream() const
330 {
331 	Frame().PrintToStream();
332 
333 	for (long i = 0; i < fCount; i++) {
334 		clipping_rect *rect = &fData[i];
335 		printf("data[%ld] = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n",
336 			i, rect->left, rect->top, rect->right - 1, rect->bottom - 1);
337 	}
338 }
339 
340 
341 // #pragma mark -
342 
343 
344 /*!	\brief Offsets all region's rects, and bounds by the given values.
345 	\param dh The horizontal offset.
346 	\param dv The vertical offset.
347 */
348 void
349 BRegion::OffsetBy(int32 x, int32 y)
350 {
351 	if (x == 0 && y == 0)
352 		return;
353 
354 	if (fCount > 0) {
355 		if (fData != &fBounds) {
356 			for (long i = 0; i < fCount; i++)
357 				offset_rect(fData[i], x, y);
358 		}
359 
360 		offset_rect(fBounds, x, y);
361 	}
362 }
363 
364 
365 /*!	\brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
366 */
367 void
368 BRegion::MakeEmpty()
369 {
370 	fBounds = (clipping_rect){ 0, 0, 0, 0 };
371 	fCount = 0;
372 }
373 
374 
375 // #pragma mark -
376 
377 
378 /*!	\brief Modifies the region, so that it includes the given BRect.
379 	\param rect The BRect to be included by the region.
380 */
381 void
382 BRegion::Include(BRect rect)
383 {
384 	Include(_Convert(rect));
385 }
386 
387 
388 /*!	\brief Modifies the region, so that it includes the given clipping_rect.
389 	\param rect The clipping_rect to be included by the region.
390 */
391 void
392 BRegion::Include(clipping_rect rect)
393 {
394 	if (!valid_rect(rect))
395 		return;
396 
397 	// convert to internal rect format
398 	rect.right ++;
399 	rect.bottom ++;
400 
401 	// use private clipping_rect constructor which avoids malloc()
402 	BRegion t(rect);
403 
404 	BRegion result;
405 	Support::XUnionRegion(this, &t, &result);
406 
407 	_AdoptRegionData(result);
408 }
409 
410 
411 /*!	\brief Modifies the region, so that it includes the area of the given region.
412 	\param region The region to be included.
413 */
414 void
415 BRegion::Include(const BRegion* region)
416 {
417 	BRegion result;
418 	Support::XUnionRegion(this, region, &result);
419 
420 	_AdoptRegionData(result);
421 }
422 
423 
424 // #pragma mark -
425 
426 
427 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
428 	\param rect The BRect to be excluded.
429 */
430 void
431 BRegion::Exclude(BRect rect)
432 {
433 	Exclude(_Convert(rect));
434 }
435 
436 
437 /*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
438 	\param rect The clipping_rect to be excluded.
439 */
440 void
441 BRegion::Exclude(clipping_rect rect)
442 {
443 	if (!valid_rect(rect))
444 		return;
445 
446 	// convert to internal rect format
447 	rect.right ++;
448 	rect.bottom ++;
449 
450 	// use private clipping_rect constructor which avoids malloc()
451 	BRegion t(rect);
452 
453 	BRegion result;
454 	Support::XSubtractRegion(this, &t, &result);
455 
456 	_AdoptRegionData(result);
457 }
458 
459 
460 /*!	\brief Modifies the region, excluding the area contained in the given
461 		BRegion.
462 	\param region The BRegion to be excluded.
463 */
464 void
465 BRegion::Exclude(const BRegion* region)
466 {
467 	BRegion result;
468 	Support::XSubtractRegion(this, region, &result);
469 
470 	_AdoptRegionData(result);
471 }
472 
473 
474 // #pragma mark -
475 
476 
477 /*!	\brief Modifies the region, so that it will contain just the area
478 		in common with the given BRegion.
479 	\param region the BRegion to intersect with.
480 */
481 void
482 BRegion::IntersectWith(const BRegion* region)
483 {
484 	BRegion result;
485 	Support::XIntersectRegion(this, region, &result);
486 
487 	_AdoptRegionData(result);
488 }
489 
490 
491 // #pragma mark -
492 
493 
494 /*!	\brief Modifies the region, so that it will contain just the area
495 		which both regions do not have in common.
496 	\param region the BRegion to exclusively include.
497 */
498 void
499 BRegion::ExclusiveInclude(const BRegion* region)
500 {
501 	BRegion result;
502 	Support::XXorRegion(this, region, &result);
503 
504 	_AdoptRegionData(result);
505 }
506 
507 
508 // #pragma mark -
509 
510 
511 /*!	\brief Takes over the data of a region and marks that region empty.
512 	\param region The region to adopt the data from.
513 */
514 void
515 BRegion::_AdoptRegionData(BRegion& region)
516 {
517 	fCount = region.fCount;
518 	fDataSize = region.fDataSize;
519 	fBounds = region.fBounds;
520 	if (fData != &fBounds)
521 		free(fData);
522 	if (region.fData != &region.fBounds)
523 		fData = region.fData;
524 	else
525 		fData = &fBounds;
526 
527 	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
528 	// called with internally allocated regions, so they don't need to
529 	// be left in a valid state.
530 	region.fData = NULL;
531 //	region.MakeEmpty();
532 }
533 
534 
535 /*!	\brief Reallocate the memory in the region.
536 	\param newSize The amount of rectangles that the region should be
537 		able to hold.
538 */
539 bool
540 BRegion::_SetSize(long newSize)
541 {
542 	// we never shrink the size
543 	newSize = max_c(fDataSize, newSize);
544 	if (newSize == fDataSize)
545 		return true;
546 
547 	// align newSize to multiple of kDataBlockSize
548 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
549 
550 	if (newSize > 0) {
551 		if (fData == &fBounds) {
552 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
553 			fData[0] = fBounds;
554 		} else if (fData) {
555 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
556 				newSize * sizeof(clipping_rect));
557 			if (!resizedData) {
558 				// failed to resize, but we cannot keep the
559 				// previous state of the object
560 				free(fData);
561 				fData = NULL;
562 			} else
563 				fData = resizedData;
564 		} else
565 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
566 	} else {
567 		// just an empty region, but no error
568 		MakeEmpty();
569 		return true;
570 	}
571 
572 	if (!fData) {
573 		// allocation actually failed
574 		fDataSize = 0;
575 		MakeEmpty();
576 		return false;
577 	}
578 
579 	fDataSize = newSize;
580 	return true;
581 }
582 
583 
584 clipping_rect
585 BRegion::_Convert(const BRect& rect) const
586 {
587 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
588 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
589 }
590 
591 
592 clipping_rect
593 BRegion::_ConvertToInternal(const BRect& rect) const
594 {
595 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
596 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
597 }
598 
599 
600 clipping_rect
601 BRegion::_ConvertToInternal(const clipping_rect& rect) const
602 {
603 	return (clipping_rect){ rect.left, rect.top,
604 		rect.right + 1, rect.bottom + 1 };
605 }
606 
607