xref: /haiku/src/kits/interface/Region.cpp (revision e6b30aee0fd7a23d6a6baab9f3718945a0cd838a)
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 	// convert to internal rect format
339 	rect.right ++;
340 	rect.bottom ++;
341 
342 	// use private clipping_rect constructor which avoids malloc()
343 	BRegion t(rect);
344 
345 	BRegion result;
346 	Support::XUnionRegion(this, &t, &result);
347 
348 	_AdoptRegionData(result);
349 }
350 
351 
352 /*!	\brief Modifies the region, so that it includes the area of the given region.
353 	\param region The region to be included.
354 */
355 void
356 BRegion::Include(const BRegion* region)
357 {
358 	BRegion result;
359 	Support::XUnionRegion(this, region, &result);
360 
361 	_AdoptRegionData(result);
362 }
363 
364 
365 // #pragma mark -
366 
367 
368 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
369 	\param rect The BRect to be excluded.
370 */
371 void
372 BRegion::Exclude(BRect rect)
373 {
374 	Exclude(_Convert(rect));
375 }
376 
377 
378 /*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
379 	\param rect The clipping_rect to be excluded.
380 */
381 void
382 BRegion::Exclude(clipping_rect rect)
383 {
384 	// convert to internal rect format
385 	rect.right ++;
386 	rect.bottom ++;
387 
388 	// use private clipping_rect constructor which avoids malloc()
389 	BRegion t(rect);
390 
391 	BRegion result;
392 	Support::XSubtractRegion(this, &t, &result);
393 
394 	_AdoptRegionData(result);
395 }
396 
397 
398 /*!	\brief Modifies the region, excluding the area contained in the given
399 		BRegion.
400 	\param region The BRegion to be excluded.
401 */
402 void
403 BRegion::Exclude(const BRegion* region)
404 {
405 	BRegion result;
406 	Support::XSubtractRegion(this, region, &result);
407 
408 	_AdoptRegionData(result);
409 }
410 
411 
412 // #pragma mark -
413 
414 
415 /*!	\brief Modifies the region, so that it will contain just the area
416 		in common with the given BRegion.
417 	\param region the BRegion to intersect with.
418 */
419 void
420 BRegion::IntersectWith(const BRegion* region)
421 {
422 	BRegion result;
423 	Support::XIntersectRegion(this, region, &result);
424 
425 	_AdoptRegionData(result);
426 }
427 
428 
429 // #pragma mark -
430 
431 
432 /*!	\brief Modifies the region, so that it will contain just the area
433 		which both regions do not have in common.
434 	\param region the BRegion to exclusively include.
435 */
436 void
437 BRegion::ExclusiveInclude(const BRegion* region)
438 {
439 	BRegion result;
440 	Support::XXorRegion(this, region, &result);
441 
442 	_AdoptRegionData(result);
443 }
444 
445 
446 // #pragma mark -
447 
448 
449 /*!	\brief Takes over the data of a region and marks that region empty.
450 	\param region The region to adopt the data from.
451 */
452 void
453 BRegion::_AdoptRegionData(BRegion& region)
454 {
455 	fCount = region.fCount;
456 	fDataSize = region.fDataSize;
457 	fBounds = region.fBounds;
458 	if (fData != &fBounds)
459 		free(fData);
460 	if (region.fData != &region.fBounds)
461 		fData = region.fData;
462 	else
463 		fData = &fBounds;
464 
465 	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
466 	// called with internally allocated regions, so they don't need to
467 	// be left in a valid state.
468 	region.fData = NULL;
469 //	region.MakeEmpty();
470 }
471 
472 
473 /*!	\brief Reallocate the memory in the region.
474 	\param newSize The amount of rectangles that the region should be
475 		able to hold.
476 */
477 bool
478 BRegion::_SetSize(long newSize)
479 {
480 	// we never shrink the size
481 	newSize = max_c(fDataSize, newSize);
482 	if (newSize == fDataSize)
483 		return true;
484 
485 	// align newSize to multiple of kDataBlockSize
486 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
487 
488 	if (newSize > 0) {
489 		if (fData == &fBounds) {
490 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
491 			fData[0] = fBounds;
492 		} else if (fData) {
493 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
494 				newSize * sizeof(clipping_rect));
495 			if (!resizedData) {
496 				// failed to resize, but we cannot keep the
497 				// previous state of the object
498 				free(fData);
499 				fData = NULL;
500 			} else
501 				fData = resizedData;
502 		} else
503 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
504 	} else {
505 		// just an empty region, but no error
506 		MakeEmpty();
507 		return true;
508 	}
509 
510 	if (!fData) {
511 		// allocation actually failed
512 		fDataSize = 0;
513 		MakeEmpty();
514 		return false;
515 	}
516 
517 	fDataSize = newSize;
518 	return true;
519 }
520 
521 
522 clipping_rect
523 BRegion::_Convert(const BRect& rect) const
524 {
525 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
526 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
527 }
528 
529 
530 clipping_rect
531 BRegion::_ConvertToInternal(const BRect& rect) const
532 {
533 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
534 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
535 }
536 
537 
538 clipping_rect
539 BRegion::_ConvertToInternal(const clipping_rect& rect) const
540 {
541 	return (clipping_rect){ rect.left, rect.top,
542 		rect.right + 1, rect.bottom + 1 };
543 }
544 
545