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