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