xref: /haiku/src/kits/interface/Region.cpp (revision 4b3b81da9e459443d75329cfd08bc9a57ad02653)
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 /*! \brief Returns the regions's BRect at the given index.
171 	\param index The index (zero based) of the wanted rectangle.
172 	\return If the given index is valid, it returns the BRect at that index,
173 		otherwise, it returns an invalid BRect.
174 */
175 BRect
176 BRegion::RectAt(int32 index) /*const*/
177 {
178 	if (index >= 0 && index < fCount) {
179 		const clipping_rect& r = fData[index];
180 		return BRect(r.left, r.top, r.right - 1, r.bottom - 1);
181 	}
182 
183 	return BRect();
184 		// an invalid BRect
185 }
186 
187 
188 /*! \brief Returns the regions's clipping_rect at the given index.
189 	\param index The index (zero based) of the wanted rectangle.
190 	\return If the given index is valid, it returns the clipping_rect at that index,
191 		otherwise, it returns an invalid clipping_rect.
192 */
193 clipping_rect
194 BRegion::RectAtInt(int32 index) /*const*/
195 {
196 	if (index >= 0 && index < fCount) {
197 		const clipping_rect& r = fData[index];
198 		return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 };
199 	}
200 
201 	return (clipping_rect){ 1, 1, 0, 0 };
202 		// an invalid clipping_rect
203 }
204 
205 
206 /*!	\brief Counts the region rects.
207 	\return An int32 which is the total number of rects in the region.
208 */
209 int32
210 BRegion::CountRects() /*const*/
211 {
212 	return fCount;
213 }
214 
215 
216 // #pragma mark -
217 
218 
219 /*!	\brief Check if the region has any area in common with the given BRect.
220 	\param rect The BRect to check the region against to.
221 	\return \ctrue if the region has any area in common with the BRect, \cfalse if not.
222 */
223 bool
224 BRegion::Intersects(BRect rect) const
225 {
226 	return Intersects(_Convert(rect));
227 }
228 
229 
230 /*!	\brief Check if the region has any area in common with the given clipping_rect.
231 	\param rect The clipping_rect to check the region against to.
232 	\return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not.
233 */
234 bool
235 BRegion::Intersects(clipping_rect rect) const
236 {
237 	// cheap convert to internal rect format
238 	rect.right ++;
239 	rect.bottom ++;
240 
241 	int result = Support::XRectInRegion(this, rect);
242 
243 	return result > Support::RectangleOut;
244 }
245 
246 
247 /*!	\brief Check if the region contains the given BPoint.
248 	\param pt The BPoint to be checked.
249 	\return \ctrue if the region contains the BPoint, \cfalse if not.
250 */
251 bool
252 BRegion::Contains(BPoint point) const
253 {
254 	return Support::XPointInRegion(this, (int)point.x, (int)point.y);
255 }
256 
257 
258 /*!	\brief Check if the region contains the given coordinates.
259 	\param x The \cx coordinate of the point to be checked.
260 	\param y The \cy coordinate of the point to be checked.
261 	\return \ctrue if the region contains the point, \cfalse if not.
262 */
263 bool
264 BRegion::Contains(int32 x, int32 y) /*const*/
265 {
266 	return Support::XPointInRegion(this, x, y);
267 }
268 
269 
270 /*!	\brief Prints the BRegion to stdout.
271 */
272 void
273 BRegion::PrintToStream() const
274 {
275 	Frame().PrintToStream();
276 
277 	for (long i = 0; i < fCount; i++) {
278 		clipping_rect *rect = &fData[i];
279 		printf("data[%ld] = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n",
280 			i, rect->left, rect->top, rect->right - 1, rect->bottom - 1);
281 	}
282 }
283 
284 
285 // #pragma mark -
286 
287 
288 /*!	\brief Offsets all region's rects, and bounds by the given values.
289 	\param dh The horizontal offset.
290 	\param dv The vertical offset.
291 */
292 void
293 BRegion::OffsetBy(int32 x, int32 y)
294 {
295 	if (x == 0 && y == 0)
296 		return;
297 
298 	if (fCount > 0) {
299 		if (fData != &fBounds) {
300 			for (long i = 0; i < fCount; i++)
301 				offset_rect(fData[i], x, y);
302 		}
303 
304 		offset_rect(fBounds, x, y);
305 	}
306 }
307 
308 
309 /*!	\brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
310 */
311 void
312 BRegion::MakeEmpty()
313 {
314 	fBounds = (clipping_rect){ 0, 0, 0, 0 };
315 	fCount = 0;
316 }
317 
318 
319 // #pragma mark -
320 
321 
322 /*!	\brief Modifies the region, so that it includes the given BRect.
323 	\param rect The BRect to be included by the region.
324 */
325 void
326 BRegion::Include(BRect rect)
327 {
328 	Include(_Convert(rect));
329 }
330 
331 
332 /*!	\brief Modifies the region, so that it includes the given clipping_rect.
333 	\param rect The clipping_rect to be included by the region.
334 */
335 void
336 BRegion::Include(clipping_rect rect)
337 {
338 	if (!valid_rect(rect))
339 		return;
340 
341 	// convert to internal rect format
342 	rect.right ++;
343 	rect.bottom ++;
344 
345 	// use private clipping_rect constructor which avoids malloc()
346 	BRegion t(rect);
347 
348 	BRegion result;
349 	Support::XUnionRegion(this, &t, &result);
350 
351 	_AdoptRegionData(result);
352 }
353 
354 
355 /*!	\brief Modifies the region, so that it includes the area of the given region.
356 	\param region The region to be included.
357 */
358 void
359 BRegion::Include(const BRegion* region)
360 {
361 	BRegion result;
362 	Support::XUnionRegion(this, region, &result);
363 
364 	_AdoptRegionData(result);
365 }
366 
367 
368 // #pragma mark -
369 
370 
371 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
372 	\param rect The BRect to be excluded.
373 */
374 void
375 BRegion::Exclude(BRect rect)
376 {
377 	Exclude(_Convert(rect));
378 }
379 
380 
381 /*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
382 	\param rect The clipping_rect to be excluded.
383 */
384 void
385 BRegion::Exclude(clipping_rect rect)
386 {
387 	if (!valid_rect(rect))
388 		return;
389 
390 	// convert to internal rect format
391 	rect.right ++;
392 	rect.bottom ++;
393 
394 	// use private clipping_rect constructor which avoids malloc()
395 	BRegion t(rect);
396 
397 	BRegion result;
398 	Support::XSubtractRegion(this, &t, &result);
399 
400 	_AdoptRegionData(result);
401 }
402 
403 
404 /*!	\brief Modifies the region, excluding the area contained in the given
405 		BRegion.
406 	\param region The BRegion to be excluded.
407 */
408 void
409 BRegion::Exclude(const BRegion* region)
410 {
411 	BRegion result;
412 	Support::XSubtractRegion(this, region, &result);
413 
414 	_AdoptRegionData(result);
415 }
416 
417 
418 // #pragma mark -
419 
420 
421 /*!	\brief Modifies the region, so that it will contain just the area
422 		in common with the given BRegion.
423 	\param region the BRegion to intersect with.
424 */
425 void
426 BRegion::IntersectWith(const BRegion* region)
427 {
428 	BRegion result;
429 	Support::XIntersectRegion(this, region, &result);
430 
431 	_AdoptRegionData(result);
432 }
433 
434 
435 // #pragma mark -
436 
437 
438 /*!	\brief Modifies the region, so that it will contain just the area
439 		which both regions do not have in common.
440 	\param region the BRegion to exclusively include.
441 */
442 void
443 BRegion::ExclusiveInclude(const BRegion* region)
444 {
445 	BRegion result;
446 	Support::XXorRegion(this, region, &result);
447 
448 	_AdoptRegionData(result);
449 }
450 
451 
452 // #pragma mark -
453 
454 
455 /*!	\brief Takes over the data of a region and marks that region empty.
456 	\param region The region to adopt the data from.
457 */
458 void
459 BRegion::_AdoptRegionData(BRegion& region)
460 {
461 	fCount = region.fCount;
462 	fDataSize = region.fDataSize;
463 	fBounds = region.fBounds;
464 	if (fData != &fBounds)
465 		free(fData);
466 	if (region.fData != &region.fBounds)
467 		fData = region.fData;
468 	else
469 		fData = &fBounds;
470 
471 	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
472 	// called with internally allocated regions, so they don't need to
473 	// be left in a valid state.
474 	region.fData = NULL;
475 //	region.MakeEmpty();
476 }
477 
478 
479 /*!	\brief Reallocate the memory in the region.
480 	\param newSize The amount of rectangles that the region should be
481 		able to hold.
482 */
483 bool
484 BRegion::_SetSize(long newSize)
485 {
486 	// we never shrink the size
487 	newSize = max_c(fDataSize, newSize);
488 	if (newSize == fDataSize)
489 		return true;
490 
491 	// align newSize to multiple of kDataBlockSize
492 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
493 
494 	if (newSize > 0) {
495 		if (fData == &fBounds) {
496 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
497 			fData[0] = fBounds;
498 		} else if (fData) {
499 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
500 				newSize * sizeof(clipping_rect));
501 			if (!resizedData) {
502 				// failed to resize, but we cannot keep the
503 				// previous state of the object
504 				free(fData);
505 				fData = NULL;
506 			} else
507 				fData = resizedData;
508 		} else
509 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
510 	} else {
511 		// just an empty region, but no error
512 		MakeEmpty();
513 		return true;
514 	}
515 
516 	if (!fData) {
517 		// allocation actually failed
518 		fDataSize = 0;
519 		MakeEmpty();
520 		return false;
521 	}
522 
523 	fDataSize = newSize;
524 	return true;
525 }
526 
527 
528 clipping_rect
529 BRegion::_Convert(const BRect& rect) const
530 {
531 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
532 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
533 }
534 
535 
536 clipping_rect
537 BRegion::_ConvertToInternal(const BRect& rect) const
538 {
539 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
540 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
541 }
542 
543 
544 clipping_rect
545 BRegion::_ConvertToInternal(const clipping_rect& rect) const
546 {
547 	return (clipping_rect){ rect.left, rect.top,
548 		rect.right + 1, rect.bottom + 1 };
549 }
550 
551