xref: /haiku/src/kits/interface/Region.cpp (revision 98057dd02a2411868fd4c35f7a48d20acfd92c23)
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:%" B_PRId32 ".0, t:%" B_PRId32
336 			".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n",
337 			i, rect->left, rect->top, rect->right - 1, rect->bottom - 1);
338 	}
339 }
340 
341 
342 // #pragma mark -
343 
344 
345 void
346 BRegion::OffsetBy(const BPoint& point)
347 {
348 	OffsetBy(point.x, point.y);
349 }
350 
351 
352 /*!	\brief Offsets all region's rects, and bounds by the given values.
353 	\param dh The horizontal offset.
354 	\param dv The vertical offset.
355 */
356 void
357 BRegion::OffsetBy(int32 x, int32 y)
358 {
359 	if (x == 0 && y == 0)
360 		return;
361 
362 	if (fCount > 0) {
363 		if (fData != &fBounds) {
364 			for (long i = 0; i < fCount; i++)
365 				offset_rect(fData[i], x, y);
366 		}
367 
368 		offset_rect(fBounds, x, y);
369 	}
370 }
371 
372 
373 /*!	\brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
374 */
375 void
376 BRegion::MakeEmpty()
377 {
378 	fBounds = (clipping_rect){ 0, 0, 0, 0 };
379 	fCount = 0;
380 }
381 
382 
383 // #pragma mark -
384 
385 
386 /*!	\brief Modifies the region, so that it includes the given BRect.
387 	\param rect The BRect to be included by the region.
388 */
389 void
390 BRegion::Include(BRect rect)
391 {
392 	Include(_Convert(rect));
393 }
394 
395 
396 /*!	\brief Modifies the region, so that it includes the given clipping_rect.
397 	\param rect The clipping_rect to be included by the region.
398 */
399 void
400 BRegion::Include(clipping_rect rect)
401 {
402 	if (!valid_rect(rect))
403 		return;
404 
405 	// convert to internal rect format
406 	rect.right ++;
407 	rect.bottom ++;
408 
409 	// use private clipping_rect constructor which avoids malloc()
410 	BRegion t(rect);
411 
412 	BRegion result;
413 	Support::XUnionRegion(this, &t, &result);
414 
415 	_AdoptRegionData(result);
416 }
417 
418 
419 /*!	\brief Modifies the region, so that it includes the area of the given region.
420 	\param region The region to be included.
421 */
422 void
423 BRegion::Include(const BRegion* region)
424 {
425 	BRegion result;
426 	Support::XUnionRegion(this, region, &result);
427 
428 	_AdoptRegionData(result);
429 }
430 
431 
432 // #pragma mark -
433 
434 
435 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
436 	\param rect The BRect to be excluded.
437 */
438 void
439 BRegion::Exclude(BRect rect)
440 {
441 	Exclude(_Convert(rect));
442 }
443 
444 
445 /*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
446 	\param rect The clipping_rect to be excluded.
447 */
448 void
449 BRegion::Exclude(clipping_rect rect)
450 {
451 	if (!valid_rect(rect))
452 		return;
453 
454 	// convert to internal rect format
455 	rect.right ++;
456 	rect.bottom ++;
457 
458 	// use private clipping_rect constructor which avoids malloc()
459 	BRegion t(rect);
460 
461 	BRegion result;
462 	Support::XSubtractRegion(this, &t, &result);
463 
464 	_AdoptRegionData(result);
465 }
466 
467 
468 /*!	\brief Modifies the region, excluding the area contained in the given
469 		BRegion.
470 	\param region The BRegion to be excluded.
471 */
472 void
473 BRegion::Exclude(const BRegion* region)
474 {
475 	BRegion result;
476 	Support::XSubtractRegion(this, region, &result);
477 
478 	_AdoptRegionData(result);
479 }
480 
481 
482 // #pragma mark -
483 
484 
485 /*!	\brief Modifies the region, so that it will contain just the area
486 		in common with the given BRegion.
487 	\param region the BRegion to intersect with.
488 */
489 void
490 BRegion::IntersectWith(const BRegion* region)
491 {
492 	BRegion result;
493 	Support::XIntersectRegion(this, region, &result);
494 
495 	_AdoptRegionData(result);
496 }
497 
498 
499 // #pragma mark -
500 
501 
502 /*!	\brief Modifies the region, so that it will contain just the area
503 		which both regions do not have in common.
504 	\param region the BRegion to exclusively include.
505 */
506 void
507 BRegion::ExclusiveInclude(const BRegion* region)
508 {
509 	BRegion result;
510 	Support::XXorRegion(this, region, &result);
511 
512 	_AdoptRegionData(result);
513 }
514 
515 
516 // #pragma mark -
517 
518 
519 /*!	\brief Takes over the data of a region and marks that region empty.
520 	\param region The region to adopt the data from.
521 */
522 void
523 BRegion::_AdoptRegionData(BRegion& region)
524 {
525 	fCount = region.fCount;
526 	fDataSize = region.fDataSize;
527 	fBounds = region.fBounds;
528 	if (fData != &fBounds)
529 		free(fData);
530 	if (region.fData != &region.fBounds)
531 		fData = region.fData;
532 	else
533 		fData = &fBounds;
534 
535 	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
536 	// called with internally allocated regions, so they don't need to
537 	// be left in a valid state.
538 	region.fData = NULL;
539 //	region.MakeEmpty();
540 }
541 
542 
543 /*!	\brief Reallocate the memory in the region.
544 	\param newSize The amount of rectangles that the region should be
545 		able to hold.
546 */
547 bool
548 BRegion::_SetSize(long newSize)
549 {
550 	// we never shrink the size
551 	newSize = max_c(fDataSize, newSize);
552 	if (newSize == fDataSize)
553 		return true;
554 
555 	// align newSize to multiple of kDataBlockSize
556 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
557 
558 	if (newSize > 0) {
559 		if (fData == &fBounds) {
560 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
561 			fData[0] = fBounds;
562 		} else if (fData) {
563 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
564 				newSize * sizeof(clipping_rect));
565 			if (!resizedData) {
566 				// failed to resize, but we cannot keep the
567 				// previous state of the object
568 				free(fData);
569 				fData = NULL;
570 			} else
571 				fData = resizedData;
572 		} else
573 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
574 	} else {
575 		// just an empty region, but no error
576 		MakeEmpty();
577 		return true;
578 	}
579 
580 	if (!fData) {
581 		// allocation actually failed
582 		fDataSize = 0;
583 		MakeEmpty();
584 		return false;
585 	}
586 
587 	fDataSize = newSize;
588 	return true;
589 }
590 
591 
592 clipping_rect
593 BRegion::_Convert(const BRect& rect) const
594 {
595 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
596 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
597 }
598 
599 
600 clipping_rect
601 BRegion::_ConvertToInternal(const BRect& rect) const
602 {
603 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
604 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
605 }
606 
607 
608 clipping_rect
609 BRegion::_ConvertToInternal(const clipping_rect& rect) const
610 {
611 	return (clipping_rect){ rect.left, rect.top,
612 		rect.right + 1, rect.bottom + 1 };
613 }
614 
615