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