xref: /haiku/src/kits/interface/Region.cpp (revision 548149fe48f59cf27f6071e9629b8bbe2ad32c1f)
1 /*
2  * Copyright 2003-2014 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  *	Authors:
6  *		Stephan Aßmus, superstippi@gmx.de
7  *		Stefano Ceccherini, burton666@libero.it
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 // Initializes an empty region.
26 BRegion::BRegion()
27 	:
28 	fCount(0),
29 	fDataSize(0),
30 	fBounds((clipping_rect){ 0, 0, 0, 0 }),
31 	fData(NULL)
32 {
33 	_SetSize(kDataBlockSize);
34 }
35 
36 
37 // Initializes a region to be a copy of another.
38 BRegion::BRegion(const BRegion& other)
39 	:
40 	fCount(0),
41 	fDataSize(0),
42 	fBounds((clipping_rect){ 0, 0, 0, 0 }),
43 	fData(NULL)
44 {
45 	*this = other;
46 }
47 
48 
49 // Initializes a region to contain a BRect.
50 BRegion::BRegion(const BRect rect)
51 	:
52 	fCount(0),
53 	fDataSize(1),
54 	fBounds((clipping_rect){ 0, 0, 0, 0 }),
55 	fData(&fBounds)
56 {
57 	if (!rect.IsValid())
58 		return;
59 
60 	fBounds = _ConvertToInternal(rect);
61 	fCount = 1;
62 }
63 
64 
65 // Initializes a region to contain a clipping_rect.
66 // NOTE: private constructor
67 BRegion::BRegion(const clipping_rect& clipping)
68 	:
69 	fCount(1),
70 	fDataSize(1),
71 	fBounds(clipping),
72 	fData(&fBounds)
73 {
74 }
75 
76 
77 BRegion::~BRegion()
78 {
79 	if (fData != &fBounds)
80 		free(fData);
81 }
82 
83 
84 // Modifies the region to be a copy of the given BRegion.
85 BRegion&
86 BRegion::operator=(const BRegion& other)
87 {
88 	if (&other == this)
89 		return *this;
90 
91 	// handle reallocation if we're too small to contain
92 	// the other other
93 	if (_SetSize(other.fDataSize)) {
94 		memcpy(fData, other.fData, other.fCount * sizeof(clipping_rect));
95 
96 		fBounds = other.fBounds;
97 		fCount = other.fCount;
98 	}
99 
100 	return *this;
101 }
102 
103 
104 // Compares this region to another (by value).
105 bool
106 BRegion::operator==(const BRegion& other) const
107 {
108 	if (&other == this)
109 		return true;
110 
111 	if (fCount != other.fCount)
112 		return false;
113 
114 	return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0;
115 }
116 
117 
118 // Set the region to contain just the given BRect.
119 void
120 BRegion::Set(BRect rect)
121 {
122 	Set(_Convert(rect));
123 }
124 
125 
126 //Set the region to contain just the given clipping_rect.
127 void
128 BRegion::Set(clipping_rect clipping)
129 {
130 	_SetSize(1);
131 
132 	if (valid_rect(clipping) && fData != NULL) {
133 		fCount = 1;
134 		// cheap convert to internal rect format
135 		clipping.right++;
136 		clipping.bottom++;
137 		fData[0] = fBounds = clipping;
138 	} else
139 		MakeEmpty();
140 }
141 
142 
143 // Returns the bounds of the region.
144 BRect
145 BRegion::Frame() const
146 {
147 	return BRect(fBounds.left, fBounds.top,
148 		fBounds.right - 1, fBounds.bottom - 1);
149 }
150 
151 
152 // Returns the bounds of the region as a clipping_rect
153 // (which has integer coordinates).
154 clipping_rect
155 BRegion::FrameInt() const
156 {
157 	return (clipping_rect){ fBounds.left, fBounds.top,
158 		fBounds.right - 1, fBounds.bottom - 1 };
159 }
160 
161 
162 // Returns the rect contained in the region at the given index.
163 BRect
164 BRegion::RectAt(int32 index)
165 {
166 	return const_cast<const BRegion*>(this)->RectAt(index);
167 }
168 
169 
170 // Returns the rect contained in the region at the given index. (const)
171 BRect
172 BRegion::RectAt(int32 index) const
173 {
174 	if (index >= 0 && index < fCount) {
175 		const clipping_rect& r = fData[index];
176 		return BRect(r.left, r.top, r.right - 1, r.bottom - 1);
177 	}
178 
179 	return BRect();
180 		// an invalid BRect
181 }
182 
183 
184 // Returns the clipping_rect contained in the region at the given index.
185 clipping_rect
186 BRegion::RectAtInt(int32 index)
187 {
188 	return const_cast<const BRegion*>(this)->RectAtInt(index);
189 }
190 
191 
192 // Returns the clipping_rect contained in the region at the given index.
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 // Returns the number of rects contained in the region.
207 int32
208 BRegion::CountRects()
209 {
210 	return fCount;
211 }
212 
213 
214 // Returns the number of rects contained in the region.
215 int32
216 BRegion::CountRects() const
217 {
218 	return fCount;
219 }
220 
221 
222 // Check if the region has any area in common with the given BRect.
223 bool
224 BRegion::Intersects(BRect rect) const
225 {
226 	return Intersects(_Convert(rect));
227 }
228 
229 
230 // Check if the region has any area in common with the given clipping_rect.
231 bool
232 BRegion::Intersects(clipping_rect clipping) const
233 {
234 	// cheap convert to internal rect format
235 	clipping.right++;
236 	clipping.bottom++;
237 
238 	int result = Support::XRectInRegion(this, clipping);
239 
240 	return result > Support::RectangleOut;
241 }
242 
243 
244 // Check if the region contains the given BPoint.
245 bool
246 BRegion::Contains(BPoint point) const
247 {
248 	return Support::XPointInRegion(this, (int)point.x, (int)point.y);
249 }
250 
251 
252 // Check if the region contains the given coordinates.
253 bool
254 BRegion::Contains(int32 x, int32 y)
255 {
256 	return Support::XPointInRegion(this, x, y);
257 }
258 
259 
260 // Check if the region contains the given coordinates.
261 bool
262 BRegion::Contains(int32 x, int32 y) const
263 {
264 	return Support::XPointInRegion(this, x, y);
265 }
266 
267 
268 // Prints the BRegion to stdout.
269 void
270 BRegion::PrintToStream() const
271 {
272 	Frame().PrintToStream();
273 
274 	for (int32 i = 0; i < fCount; i++) {
275 		clipping_rect *rect = &fData[i];
276 		printf("data[%" B_PRId32 "] = BRect(l:%" B_PRId32 ".0, t:%" B_PRId32
277 			".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n",
278 			i, rect->left, rect->top, rect->right - 1, rect->bottom - 1);
279 	}
280 }
281 
282 
283 void
284 BRegion::OffsetBy(const BPoint& point)
285 {
286 	OffsetBy(point.x, point.y);
287 }
288 
289 
290 // Applies the given x and y offsets to each rect contained by
291 // the region and recalculates the region's bounds.
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 (int32 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 void
310 BRegion::ScaleBy(BSize scale)
311 {
312 	ScaleBy(scale.Width(), scale.Height());
313 }
314 
315 
316 void
317 BRegion::ScaleBy(float x, float y)
318 {
319 	if (x == 1.0 && y == 1.0)
320 		return;
321 
322 	if (fCount > 0) {
323 		if (fData != &fBounds) {
324 			for (int32 i = 0; i < fCount; i++)
325 				scale_rect(fData[i], x, y);
326 		}
327 
328 		scale_rect(fBounds, x, y);
329 	}
330 }
331 
332 
333 // Empties the region, so that it doesn't include any rect, and invalidates
334 // its bounds.
335 void
336 BRegion::MakeEmpty()
337 {
338 	fBounds = (clipping_rect){ 0, 0, 0, 0 };
339 	fCount = 0;
340 }
341 
342 
343 // Modifies the region, so that it includes the given BRect.
344 void
345 BRegion::Include(BRect rect)
346 {
347 	Include(_Convert(rect));
348 }
349 
350 
351 // Modifies the region, so that it includes the given clipping_rect.
352 void
353 BRegion::Include(clipping_rect clipping)
354 {
355 	if (!valid_rect(clipping))
356 		return;
357 
358 	// convert to internal clipping format
359 	clipping.right++;
360 	clipping.bottom++;
361 
362 	// use private clipping_rect constructor which avoids malloc()
363 	BRegion temp(clipping);
364 
365 	BRegion result;
366 	Support::XUnionRegion(this, &temp, &result);
367 
368 	_AdoptRegionData(result);
369 }
370 
371 
372 // Modifies the region, so that it includes the area of the given region.
373 void
374 BRegion::Include(const BRegion* region)
375 {
376 	BRegion result;
377 	Support::XUnionRegion(this, region, &result);
378 
379 	_AdoptRegionData(result);
380 }
381 
382 
383 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
384 	\param rect The BRect to be excluded.
385 */
386 void
387 BRegion::Exclude(BRect rect)
388 {
389 	Exclude(_Convert(rect));
390 }
391 
392 
393 // Modifies the region, excluding the area represented by the given clipping_rect.
394 void
395 BRegion::Exclude(clipping_rect clipping)
396 {
397 	if (!valid_rect(clipping))
398 		return;
399 
400 	// convert to internal clipping format
401 	clipping.right++;
402 	clipping.bottom++;
403 
404 	// use private clipping_rect constructor which avoids malloc()
405 	BRegion temp(clipping);
406 
407 	BRegion result;
408 	Support::XSubtractRegion(this, &temp, &result);
409 
410 	_AdoptRegionData(result);
411 }
412 
413 
414 // Modifies the region, excluding the area contained in the given BRegion.
415 void
416 BRegion::Exclude(const BRegion* region)
417 {
418 	BRegion result;
419 	Support::XSubtractRegion(this, region, &result);
420 
421 	_AdoptRegionData(result);
422 }
423 
424 
425 // Modifies the region, so that it will contain only the area in common
426 // with the given BRegion.
427 void
428 BRegion::IntersectWith(const BRegion* region)
429 {
430 	BRegion result;
431 	Support::XIntersectRegion(this, region, &result);
432 
433 	_AdoptRegionData(result);
434 }
435 
436 
437 // Modifies the region, so that it will contain just the area which both
438 // regions do not have in common.
439 void
440 BRegion::ExclusiveInclude(const BRegion* region)
441 {
442 	BRegion result;
443 	Support::XXorRegion(this, region, &result);
444 
445 	_AdoptRegionData(result);
446 }
447 
448 
449 //	#pragma mark - BRegion private methods
450 
451 
452 /*!
453 	\fn void BRegion::_AdoptRegionData(BRegion& region)
454 	\brief Takes over the data of \a region and empties it.
455 
456 	\param region The \a region to adopt 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 /*!
480 	\fn bool BRegion::_SetSize(int32 newSize)
481 	\brief Reallocate the memory in the region.
482 
483 	\param newSize The amount of rectangles that the region should be
484 		able to hold.
485 */
486 bool
487 BRegion::_SetSize(int32 newSize)
488 {
489 	// we never shrink the size
490 	newSize = max_c(fDataSize, newSize);
491 		// The amount of rectangles that the region should be able to hold.
492 	if (newSize == fDataSize)
493 		return true;
494 
495 	// align newSize to multiple of kDataBlockSize
496 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
497 
498 	if (newSize > 0) {
499 		if (fData == &fBounds) {
500 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
501 			fData[0] = fBounds;
502 		} else if (fData) {
503 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
504 				newSize * sizeof(clipping_rect));
505 			if (!resizedData) {
506 				// failed to resize, but we cannot keep the
507 				// previous state of the object
508 				free(fData);
509 				fData = NULL;
510 			} else
511 				fData = resizedData;
512 		} else
513 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
514 	} else {
515 		// just an empty region, but no error
516 		MakeEmpty();
517 		return true;
518 	}
519 
520 	if (!fData) {
521 		// allocation actually failed
522 		fDataSize = 0;
523 		MakeEmpty();
524 		return false;
525 	}
526 
527 	fDataSize = newSize;
528 	return true;
529 }
530 
531 
532 clipping_rect
533 BRegion::_Convert(const BRect& rect) const
534 {
535 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
536 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
537 }
538 
539 
540 clipping_rect
541 BRegion::_ConvertToInternal(const BRect& rect) const
542 {
543 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
544 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
545 }
546 
547 
548 clipping_rect
549 BRegion::_ConvertToInternal(const clipping_rect& rect) const
550 {
551 	return (clipping_rect){ rect.left, rect.top,
552 		rect.right + 1, rect.bottom + 1 };
553 }
554