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 <stdlib.h>
14#include <string.h>
15
16#include <Debug.h>
17
18#include "clipping.h"
19#include "RegionSupport.h"
20
21
22const 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*/
28BRegion::BRegion()
29	:
30	fCount(0),
31	fDataSize(0),
32	fBounds((clipping_rect){ 0, 0, 0, 0 }),
33	fData(NULL)
34{
35	_SetSize(kDataBlockSize);
36}
37
38
39/*! \brief Initializes a region to be a copy of another.
40	\param region The region to copy.
41*/
42BRegion::BRegion(const BRegion& region)
43	:
44	fCount(0),
45	fDataSize(0),
46	fBounds((clipping_rect){ 0, 0, 0, 0 }),
47	fData(NULL)
48{
49	*this = region;
50}
51
52
53/*!	\brief Initializes a region to contain a BRect.
54	\param rect The BRect to set the region to.
55*/
56BRegion::BRegion(const BRect rect)
57	:
58	fCount(0),
59	fDataSize(1),
60	fBounds((clipping_rect){ 0, 0, 0, 0 }),
61	fData(&fBounds)
62{
63	if (!rect.IsValid())
64		return;
65
66	fBounds = _ConvertToInternal(rect);
67	fCount = 1;
68}
69
70// NOTE: private constructor
71/*!	\brief Initializes a region to contain a clipping_rect.
72	\param rect The clipping_rect to set the region to, already in
73	internal rect format.
74*/
75BRegion::BRegion(const clipping_rect& rect)
76	: fCount(1)
77	, fDataSize(1)
78	, fBounds(rect)
79	, fData(&fBounds)
80{
81}
82
83
84/*!	\brief Frees the allocated memory.
85*/
86BRegion::~BRegion()
87{
88	if (fData != &fBounds)
89		free(fData);
90}
91
92
93// #pragma mark -
94
95
96/*!	\brief Modifies the region to be a copy of the given BRegion.
97	\param region the BRegion to copy.
98	\return This function always returns \c *this.
99*/
100BRegion&
101BRegion::operator=(const BRegion& region)
102{
103	if (&region == this)
104		return *this;
105
106	// handle reallocation if we're too small to contain
107	// the other region
108	if (_SetSize(region.fDataSize)) {
109		memcpy(fData, region.fData, region.fCount * sizeof(clipping_rect));
110
111		fBounds = region.fBounds;
112		fCount = region.fCount;
113	}
114
115	return *this;
116}
117
118
119/*!	\brief Compares this region to another (by value).
120	\param other the BRegion to compare to.
121	\return \ctrue if the two regions are the same, \cfalse otherwise.
122*/
123bool
124BRegion::operator==(const BRegion& other) const
125{
126	if (&other == this)
127		return true;
128
129	if (fCount != other.fCount)
130		return false;
131
132	return memcmp(fData, other.fData, fCount * sizeof(clipping_rect)) == 0;
133}
134
135
136/*!	\brief Set the region to contain just the given BRect.
137	\param newBounds A BRect.
138*/
139void
140BRegion::Set(BRect newBounds)
141{
142	Set(_Convert(newBounds));
143}
144
145
146/*!	\brief Set the region to contain just the given clipping_rect.
147	\param newBounds A clipping_rect.
148*/
149void
150BRegion::Set(clipping_rect newBounds)
151{
152	_SetSize(1);
153
154	if (valid_rect(newBounds) && fData) {
155		fCount = 1;
156		// cheap convert to internal rect format
157		newBounds.right++;
158		newBounds.bottom++;
159		fData[0] = fBounds = newBounds;
160	} else
161		MakeEmpty();
162}
163
164
165// #pragma mark -
166
167
168/*! \brief Returns the bounds of the region.
169	\return A BRect which represents the bounds of the region.
170*/
171BRect
172BRegion::Frame() const
173{
174	return BRect(fBounds.left, fBounds.top,
175		fBounds.right - 1, fBounds.bottom - 1);
176}
177
178
179/*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates).
180	\return A clipping_rect which represents the bounds of the region.
181*/
182clipping_rect
183BRegion::FrameInt() const
184{
185	return (clipping_rect){ fBounds.left, fBounds.top,
186		fBounds.right - 1, fBounds.bottom - 1 };
187}
188
189
190BRect
191BRegion::RectAt(int32 index)
192{
193	return const_cast<const BRegion*>(this)->RectAt(index);
194}
195
196
197/*! \brief Returns the regions's BRect at the given index.
198	\param index The index (zero based) of the wanted rectangle.
199	\return If the given index is valid, it returns the BRect at that index,
200		otherwise, it returns an invalid BRect.
201*/
202BRect
203BRegion::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
215clipping_rect
216BRegion::RectAtInt(int32 index)
217{
218	return const_cast<const BRegion*>(this)->RectAtInt(index);
219}
220
221
222/*! \brief Returns the regions's clipping_rect at the given index.
223	\param index The index (zero based) of the wanted rectangle.
224	\return If the given index is valid, it returns the clipping_rect at that index,
225		otherwise, it returns an invalid clipping_rect.
226*/
227clipping_rect
228BRegion::RectAtInt(int32 index) const
229{
230	if (index >= 0 && index < fCount) {
231		const clipping_rect& r = fData[index];
232		return (clipping_rect){ r.left, r.top, r.right - 1, r.bottom - 1 };
233	}
234
235	return (clipping_rect){ 1, 1, 0, 0 };
236		// an invalid clipping_rect
237}
238
239
240/*!	\brief Counts the region rects.
241	\return An int32 which is the total number of rects in the region.
242*/
243int32
244BRegion::CountRects()
245{
246	return fCount;
247}
248
249
250/*!	\brief Counts the region rects.
251	\return An int32 which is the total number of rects in the region.
252*/
253int32
254BRegion::CountRects() const
255{
256	return fCount;
257}
258
259
260// #pragma mark -
261
262
263/*!	\brief Check if the region has any area in common with the given BRect.
264	\param rect The BRect to check the region against to.
265	\return \ctrue if the region has any area in common with the BRect, \cfalse if not.
266*/
267bool
268BRegion::Intersects(BRect rect) const
269{
270	return Intersects(_Convert(rect));
271}
272
273
274/*!	\brief Check if the region has any area in common with the given clipping_rect.
275	\param rect The clipping_rect to check the region against to.
276	\return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not.
277*/
278bool
279BRegion::Intersects(clipping_rect rect) const
280{
281	// cheap convert to internal rect format
282	rect.right ++;
283	rect.bottom ++;
284
285	int result = Support::XRectInRegion(this, rect);
286
287	return result > Support::RectangleOut;
288}
289
290
291/*!	\brief Check if the region contains the given BPoint.
292	\param pt The BPoint to be checked.
293	\return \ctrue if the region contains the BPoint, \cfalse if not.
294*/
295bool
296BRegion::Contains(BPoint point) const
297{
298	return Support::XPointInRegion(this, (int)point.x, (int)point.y);
299}
300
301
302/*!	\brief Check if the region contains the given coordinates.
303	\param x The \cx coordinate of the point to be checked.
304	\param y The \cy coordinate of the point to be checked.
305	\return \ctrue if the region contains the point, \cfalse if not.
306*/
307bool
308BRegion::Contains(int32 x, int32 y)
309{
310	return Support::XPointInRegion(this, x, y);
311}
312
313
314/*!	\brief Check if the region contains the given coordinates.
315	\param x The \cx coordinate of the point to be checked.
316	\param y The \cy coordinate of the point to be checked.
317	\return \ctrue if the region contains the point, \cfalse if not.
318*/
319bool
320BRegion::Contains(int32 x, int32 y) const
321{
322	return Support::XPointInRegion(this, x, y);
323}
324
325
326/*!	\brief Prints the BRegion to stdout.
327*/
328void
329BRegion::PrintToStream() const
330{
331	Frame().PrintToStream();
332
333	for (int32 i = 0; i < fCount; i++) {
334		clipping_rect *rect = &fData[i];
335		printf("data[%" B_PRId32 "] = BRect(l:%" B_PRId32 ".0, t:%" B_PRId32
336			".0, r:%" B_PRId32 ".0, b:%" B_PRId32 ".0)\n",
337			i, rect->left, rect->top, rect->right - 1, rect->bottom - 1);
338	}
339}
340
341
342// #pragma mark -
343
344
345void
346BRegion::OffsetBy(const BPoint& point)
347{
348	OffsetBy(point.x, point.y);
349}
350
351
352/*!	\brief Offsets all region's rects, and bounds by the given values.
353	\param dh The horizontal offset.
354	\param dv The vertical offset.
355*/
356void
357BRegion::OffsetBy(int32 x, int32 y)
358{
359	if (x == 0 && y == 0)
360		return;
361
362	if (fCount > 0) {
363		if (fData != &fBounds) {
364			for (int32 i = 0; i < fCount; i++)
365				offset_rect(fData[i], x, y);
366		}
367
368		offset_rect(fBounds, x, y);
369	}
370}
371
372
373/*!	\brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
374*/
375void
376BRegion::MakeEmpty()
377{
378	fBounds = (clipping_rect){ 0, 0, 0, 0 };
379	fCount = 0;
380}
381
382
383// #pragma mark -
384
385
386/*!	\brief Modifies the region, so that it includes the given BRect.
387	\param rect The BRect to be included by the region.
388*/
389void
390BRegion::Include(BRect rect)
391{
392	Include(_Convert(rect));
393}
394
395
396/*!	\brief Modifies the region, so that it includes the given clipping_rect.
397	\param rect The clipping_rect to be included by the region.
398*/
399void
400BRegion::Include(clipping_rect rect)
401{
402	if (!valid_rect(rect))
403		return;
404
405	// convert to internal rect format
406	rect.right ++;
407	rect.bottom ++;
408
409	// use private clipping_rect constructor which avoids malloc()
410	BRegion t(rect);
411
412	BRegion result;
413	Support::XUnionRegion(this, &t, &result);
414
415	_AdoptRegionData(result);
416}
417
418
419/*!	\brief Modifies the region, so that it includes the area of the given region.
420	\param region The region to be included.
421*/
422void
423BRegion::Include(const BRegion* region)
424{
425	BRegion result;
426	Support::XUnionRegion(this, region, &result);
427
428	_AdoptRegionData(result);
429}
430
431
432// #pragma mark -
433
434
435/*!	\brief Modifies the region, excluding the area represented by the given BRect.
436	\param rect The BRect to be excluded.
437*/
438void
439BRegion::Exclude(BRect rect)
440{
441	Exclude(_Convert(rect));
442}
443
444
445/*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
446	\param rect The clipping_rect to be excluded.
447*/
448void
449BRegion::Exclude(clipping_rect rect)
450{
451	if (!valid_rect(rect))
452		return;
453
454	// convert to internal rect format
455	rect.right ++;
456	rect.bottom ++;
457
458	// use private clipping_rect constructor which avoids malloc()
459	BRegion t(rect);
460
461	BRegion result;
462	Support::XSubtractRegion(this, &t, &result);
463
464	_AdoptRegionData(result);
465}
466
467
468/*!	\brief Modifies the region, excluding the area contained in the given
469		BRegion.
470	\param region The BRegion to be excluded.
471*/
472void
473BRegion::Exclude(const BRegion* region)
474{
475	BRegion result;
476	Support::XSubtractRegion(this, region, &result);
477
478	_AdoptRegionData(result);
479}
480
481
482// #pragma mark -
483
484
485/*!	\brief Modifies the region, so that it will contain just the area
486		in common with the given BRegion.
487	\param region the BRegion to intersect with.
488*/
489void
490BRegion::IntersectWith(const BRegion* region)
491{
492	BRegion result;
493	Support::XIntersectRegion(this, region, &result);
494
495	_AdoptRegionData(result);
496}
497
498
499// #pragma mark -
500
501
502/*!	\brief Modifies the region, so that it will contain just the area
503		which both regions do not have in common.
504	\param region the BRegion to exclusively include.
505*/
506void
507BRegion::ExclusiveInclude(const BRegion* region)
508{
509	BRegion result;
510	Support::XXorRegion(this, region, &result);
511
512	_AdoptRegionData(result);
513}
514
515
516// #pragma mark -
517
518
519/*!	\brief Takes over the data of a region and marks that region empty.
520	\param region The region to adopt the data from.
521*/
522void
523BRegion::_AdoptRegionData(BRegion& region)
524{
525	fCount = region.fCount;
526	fDataSize = region.fDataSize;
527	fBounds = region.fBounds;
528	if (fData != &fBounds)
529		free(fData);
530	if (region.fData != &region.fBounds)
531		fData = region.fData;
532	else
533		fData = &fBounds;
534
535	// NOTE: MakeEmpty() is not called since _AdoptRegionData is only
536	// called with internally allocated regions, so they don't need to
537	// be left in a valid state.
538	region.fData = NULL;
539//	region.MakeEmpty();
540}
541
542
543/*!	\brief Reallocate the memory in the region.
544	\param newSize The amount of rectangles that the region should be
545		able to hold.
546*/
547bool
548BRegion::_SetSize(int32 newSize)
549{
550	// we never shrink the size
551	newSize = max_c(fDataSize, newSize);
552	if (newSize == fDataSize)
553		return true;
554
555	// align newSize to multiple of kDataBlockSize
556	newSize = ((newSize + kDataBlockSize - 1) / kDataBlockSize) * kDataBlockSize;
557
558	if (newSize > 0) {
559		if (fData == &fBounds) {
560			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
561			fData[0] = fBounds;
562		} else if (fData) {
563			clipping_rect* resizedData = (clipping_rect*)realloc(fData,
564				newSize * sizeof(clipping_rect));
565			if (!resizedData) {
566				// failed to resize, but we cannot keep the
567				// previous state of the object
568				free(fData);
569				fData = NULL;
570			} else
571				fData = resizedData;
572		} else
573			fData = (clipping_rect*)malloc(newSize * sizeof(clipping_rect));
574	} else {
575		// just an empty region, but no error
576		MakeEmpty();
577		return true;
578	}
579
580	if (!fData) {
581		// allocation actually failed
582		fDataSize = 0;
583		MakeEmpty();
584		return false;
585	}
586
587	fDataSize = newSize;
588	return true;
589}
590
591
592clipping_rect
593BRegion::_Convert(const BRect& rect) const
594{
595	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
596		(int)ceilf(rect.right), (int)ceilf(rect.bottom) };
597}
598
599
600clipping_rect
601BRegion::_ConvertToInternal(const BRect& rect) const
602{
603	return (clipping_rect){ (int)floorf(rect.left), (int)floorf(rect.top),
604		(int)ceilf(rect.right) + 1, (int)ceilf(rect.bottom) + 1 };
605}
606
607
608clipping_rect
609BRegion::_ConvertToInternal(const clipping_rect& rect) const
610{
611	return (clipping_rect){ rect.left, rect.top,
612		rect.right + 1, rect.bottom + 1 };
613}
614
615