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