xref: /haiku/src/kits/interface/Region.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
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 newBounds)
121 {
122 	Set(_Convert(newBounds));
123 }
124 
125 
126 //Set the region to contain just the given clipping_rect.
127 void
128 BRegion::Set(clipping_rect newBounds)
129 {
130 	_SetSize(1);
131 
132 	if (valid_rect(newBounds) && fData) {
133 		fCount = 1;
134 		// cheap convert to internal rect format
135 		newBounds.right++;
136 		newBounds.bottom++;
137 		fData[0] = fBounds = newBounds;
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 // Empties the region, so that it doesn't include any rect, and invalidates
310 // its bounds.
311 void
312 BRegion::MakeEmpty()
313 {
314 	fBounds = (clipping_rect){ 0, 0, 0, 0 };
315 	fCount = 0;
316 }
317 
318 
319 // Modifies the region, so that it includes the given BRect.
320 void
321 BRegion::Include(BRect rect)
322 {
323 	Include(_Convert(rect));
324 }
325 
326 
327 // Modifies the region, so that it includes the given clipping_rect.
328 void
329 BRegion::Include(clipping_rect clipping)
330 {
331 	if (!valid_rect(clipping))
332 		return;
333 
334 	// convert to internal clipping format
335 	clipping.right++;
336 	clipping.bottom++;
337 
338 	// use private clipping_rect constructor which avoids malloc()
339 	BRegion temp(clipping);
340 
341 	BRegion result;
342 	Support::XUnionRegion(this, &temp, &result);
343 
344 	_AdoptRegionData(result);
345 }
346 
347 
348 // Modifies the region, so that it includes the area of the given region.
349 void
350 BRegion::Include(const BRegion* region)
351 {
352 	BRegion result;
353 	Support::XUnionRegion(this, region, &result);
354 
355 	_AdoptRegionData(result);
356 }
357 
358 
359 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
360 	\param rect The BRect to be excluded.
361 */
362 void
363 BRegion::Exclude(BRect rect)
364 {
365 	Exclude(_Convert(rect));
366 }
367 
368 
369 // Modifies the region, excluding the area represented by the given clipping_rect.
370 void
371 BRegion::Exclude(clipping_rect clipping)
372 {
373 	if (!valid_rect(clipping))
374 		return;
375 
376 	// convert to internal clipping format
377 	clipping.right++;
378 	clipping.bottom++;
379 
380 	// use private clipping_rect constructor which avoids malloc()
381 	BRegion temp(clipping);
382 
383 	BRegion result;
384 	Support::XSubtractRegion(this, &temp, &result);
385 
386 	_AdoptRegionData(result);
387 }
388 
389 
390 // Modifies the region, excluding the area contained in the given BRegion.
391 void
392 BRegion::Exclude(const BRegion* region)
393 {
394 	BRegion result;
395 	Support::XSubtractRegion(this, region, &result);
396 
397 	_AdoptRegionData(result);
398 }
399 
400 
401 // Modifies the region, so that it will contain only the area in common
402 // with the given BRegion.
403 void
404 BRegion::IntersectWith(const BRegion* region)
405 {
406 	BRegion result;
407 	Support::XIntersectRegion(this, region, &result);
408 
409 	_AdoptRegionData(result);
410 }
411 
412 
413 // Modifies the region, so that it will contain just the area which both
414 // regions do not have in common.
415 void
416 BRegion::ExclusiveInclude(const BRegion* region)
417 {
418 	BRegion result;
419 	Support::XXorRegion(this, region, &result);
420 
421 	_AdoptRegionData(result);
422 }
423 
424 
425 //	#pragma mark - BRegion private methods
426 
427 
428 // Takes over the data of a region and marks that region empty.
429 void
430 BRegion::_AdoptRegionData(BRegion& region)
431 {
432 	fCount = region.fCount;
433 	fDataSize = region.fDataSize;
434 	fBounds = region.fBounds;
435 	if (fData != &fBounds)
436 		free(fData);
437 	if (region.fData != &region.fBounds)
438 		fData = region.fData;
439 	else
440 		fData = &fBounds;
441 
442 	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
443 	// called with internally allocated regions, so they don't need to
444 	// be left in a valid state.
445 	region.fData = NULL;
446 //	region.MakeEmpty();
447 }
448 
449 
450 // Reallocate the memory in the region.
451 bool
452 BRegion::_SetSize(int32 newSize)
453 {
454 	// we never shrink the size
455 	newSize = max_c(fDataSize, newSize);
456 		// The amount of rectangles that the region should be able to hold.
457 	if (newSize == fDataSize)
458 		return true;
459 
460 	// align newSize to multiple of kDataBlockSize
461 	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
462 
463 	if (newSize > 0) {
464 		if (fData == &fBounds) {
465 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
466 			fData[0] = fBounds;
467 		} else if (fData) {
468 			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
469 				newSize * sizeof(clipping_rect));
470 			if (!resizedData) {
471 				// failed to resize, but we cannot keep the
472 				// previous state of the object
473 				free(fData);
474 				fData = NULL;
475 			} else
476 				fData = resizedData;
477 		} else
478 			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
479 	} else {
480 		// just an empty region, but no error
481 		MakeEmpty();
482 		return true;
483 	}
484 
485 	if (!fData) {
486 		// allocation actually failed
487 		fDataSize = 0;
488 		MakeEmpty();
489 		return false;
490 	}
491 
492 	fDataSize = newSize;
493 	return true;
494 }
495 
496 
497 clipping_rect
498 BRegion::_Convert(const BRect& rect) const
499 {
500 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
501 		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
502 }
503 
504 
505 clipping_rect
506 BRegion::_ConvertToInternal(const BRect& rect) const
507 {
508 	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
509 		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
510 }
511 
512 
513 clipping_rect
514 BRegion::_ConvertToInternal(const clipping_rect& rect) const
515 {
516 	return (clipping_rect){ rect.left, rect.top,
517 		rect.right + 1, rect.bottom + 1 };
518 }
519