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