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
22const static int32 kDataBlockSize = 8;
23
24
25// Initializes an empty region.
26BRegion::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.
38BRegion::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.
50BRegion::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
67BRegion::BRegion(const clipping_rect& clipping)
68	:
69	fCount(1),
70	fDataSize(1),
71	fBounds(clipping),
72	fData(&fBounds)
73{
74}
75
76
77BRegion::~BRegion()
78{
79	if (fData != &fBounds)
80		free(fData);
81}
82
83
84// Modifies the region to be a copy of the given BRegion.
85BRegion&
86BRegion::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).
105bool
106BRegion::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.
119void
120BRegion::Set(BRect rect)
121{
122	Set(_Convert(rect));
123}
124
125
126//Set the region to contain just the given clipping_rect.
127void
128BRegion::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.
144BRect
145BRegion::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).
154clipping_rect
155BRegion::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.
163BRect
164BRegion::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)
171BRect
172BRegion::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.
185clipping_rect
186BRegion::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.
193clipping_rect
194BRegion::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.
207int32
208BRegion::CountRects()
209{
210	return fCount;
211}
212
213
214// Returns the number of rects contained in the region.
215int32
216BRegion::CountRects() const
217{
218	return fCount;
219}
220
221
222// Check if the region has any area in common with the given BRect.
223bool
224BRegion::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.
231bool
232BRegion::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.
245bool
246BRegion::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.
253bool
254BRegion::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.
261bool
262BRegion::Contains(int32 x, int32 y) const
263{
264	return Support::XPointInRegion(this, x, y);
265}
266
267
268// Prints the BRegion to stdout.
269void
270BRegion::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
283void
284BRegion::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.
292void
293BRegion::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
309void
310BRegion::ScaleBy(BSize scale)
311{
312	ScaleBy(scale.Width(), scale.Height());
313}
314
315
316void
317BRegion::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.
335void
336BRegion::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.
344void
345BRegion::Include(BRect rect)
346{
347	Include(_Convert(rect));
348}
349
350
351// Modifies the region, so that it includes the given clipping_rect.
352void
353BRegion::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.
373void
374BRegion::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*/
386void
387BRegion::Exclude(BRect rect)
388{
389	Exclude(_Convert(rect));
390}
391
392
393// Modifies the region, excluding the area represented by the given clipping_rect.
394void
395BRegion::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.
415void
416BRegion::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.
427void
428BRegion::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.
439void
440BRegion::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*/
458void
459BRegion::_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*/
486bool
487BRegion::_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
532clipping_rect
533BRegion::_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
540clipping_rect
541BRegion::_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
548clipping_rect
549BRegion::_ConvertToInternal(const clipping_rect& rect) const
550{
551	return (clipping_rect){ rect.left, rect.top,
552		rect.right + 1, rect.bottom + 1 };
553}
554