xref: /haiku/src/kits/interface/Region.cpp (revision 56eb8e78cc702792e3b032e3f5f45da9e5dbea9e)
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 to.
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 Takes over the data of a region and marks that region empty.
433 	\param region The region to adopt the data from.
434 */
435 void
436 BRegion::_AdoptRegionData(BRegion& region)
437 {
438 	fCount = region.fCount;
439 	fDataSize = region.fDataSize;
440 	fBounds = region.fBounds;
441 	if (fData != &fBounds)
442 		free(fData);
443 	if (region.fData != &region.fBounds)
444 		fData = region.fData;
445 	else
446 		fData = &fBounds;
447 
448 	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
449 	// called with internally allocated regions, so they don't need to
450 	// be left in a valid state.
451 	region.fData = NULL;
452 //	region.MakeEmpty();
453 }
454 
455 
456 /*!	\brief Reallocate the memory in the region.
457 	\param newSize The amount of rectangles that the region should be
458 		able to hold.
459 */
460 bool
461 BRegion::_SetSize(long newSize)
462 {
463 	// we never shrink the size
464 	newSize = max_c(fDataSize, newSize);
465 	if (newSize == fDataSize)
466 		return true;
467 
468 	// align newSize to multiple of kDataBlockSize
469 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
470 
471 	if (newSize > 0) {
472 		if (fData == &fBounds) {
473 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
474 			fData[0] = fBounds;
475 		} else if (fData) {
476 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
477 				newSize * sizeof(clipping_rect));
478 			if (!resizedData) {
479 				// failed to resize, but we cannot keep the
480 				// previous state of the object
481 				free(fData);
482 				fData = NULL;
483 			} else
484 				fData = resizedData;
485 		} else
486 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
487 	} else {
488 		// just an empty region, but no error
489 		MakeEmpty();
490 		return true;
491 	}
492 
493 	if (!fData) {
494 		// allocation actually failed
495 		fDataSize = 0;
496 		MakeEmpty();
497 		return false;
498 	}
499 
500 	fDataSize = newSize;
501 	return true;
502 }
503 
504 clipping_rect
505 BRegion::_Convert(const BRect& rect) const
506 {
507 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
508 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
509 }
510 
511 
512 clipping_rect
513 BRegion::_ConvertToInternal(const BRect& rect) const
514 {
515 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
516 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
517 }
518 
519 
520 clipping_rect
521 BRegion::_ConvertToInternal(const clipping_rect& rect) const
522 {
523 	return (clipping_rect){ rect.left, rect.top,
524 		rect.right + 1, rect.bottom + 1 };
525 }
526 
527