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
25BRegion::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
36BRegion::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
47BRegion::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
63BRegion::BRegion(const clipping_rect& clipping)
64	:
65	fCount(1),
66	fDataSize(1),
67	fBounds(clipping),
68	fData(&fBounds)
69{
70}
71
72
73BRegion::~BRegion()
74{
75	if (fData != &fBounds)
76		free(fData);
77}
78
79
80BRegion&
81BRegion::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
98bool
99BRegion::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
111void
112BRegion::Set(BRect rect)
113{
114	Set(_Convert(rect));
115}
116
117
118void
119BRegion::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
131BRect
132BRegion::Frame() const
133{
134	return BRect(fBounds.left, fBounds.top,
135		fBounds.right - 1, fBounds.bottom - 1);
136}
137
138
139clipping_rect
140BRegion::FrameInt() const
141{
142	return (clipping_rect){ fBounds.left, fBounds.top,
143		fBounds.right - 1, fBounds.bottom - 1 };
144}
145
146
147BRect
148BRegion::RectAt(int32 index)
149{
150	return const_cast<const BRegion*>(this)->RectAt(index);
151}
152
153
154BRect
155BRegion::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
167clipping_rect
168BRegion::RectAtInt(int32 index)
169{
170	return const_cast<const BRegion*>(this)->RectAtInt(index);
171}
172
173
174clipping_rect
175BRegion::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
187int32
188BRegion::CountRects()
189{
190	return fCount;
191}
192
193
194int32
195BRegion::CountRects() const
196{
197	return fCount;
198}
199
200
201bool
202BRegion::Intersects(BRect rect) const
203{
204	return Intersects(_Convert(rect));
205}
206
207
208bool
209BRegion::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
219bool
220BRegion::Contains(BPoint point) const
221{
222	return Support::XPointInRegion(this, (int)point.x, (int)point.y);
223}
224
225
226bool
227BRegion::Contains(int32 x, int32 y)
228{
229	return Support::XPointInRegion(this, x, y);
230}
231
232
233bool
234BRegion::Contains(int32 x, int32 y) const
235{
236	return Support::XPointInRegion(this, x, y);
237}
238
239
240// Prints the BRegion to stdout.
241void
242BRegion::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
255void
256BRegion::OffsetBy(const BPoint& point)
257{
258	OffsetBy(point.x, point.y);
259}
260
261
262void
263BRegion::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
279void
280BRegion::ScaleBy(BSize scale)
281{
282	ScaleBy(scale.Width(), scale.Height());
283}
284
285
286void
287BRegion::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
303void
304BRegion::MakeEmpty()
305{
306	fBounds = (clipping_rect){ 0, 0, 0, 0 };
307	fCount = 0;
308}
309
310
311void
312BRegion::Include(BRect rect)
313{
314	Include(_Convert(rect));
315}
316
317
318void
319BRegion::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
338void
339BRegion::Include(const BRegion* region)
340{
341	BRegion result;
342	Support::XUnionRegion(this, region, &result);
343
344	_AdoptRegionData(result);
345}
346
347
348void
349BRegion::Exclude(BRect rect)
350{
351	Exclude(_Convert(rect));
352}
353
354
355void
356BRegion::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
375void
376BRegion::Exclude(const BRegion* region)
377{
378	BRegion result;
379	Support::XSubtractRegion(this, region, &result);
380
381	_AdoptRegionData(result);
382}
383
384
385void
386BRegion::IntersectWith(const BRegion* region)
387{
388	BRegion result;
389	Support::XIntersectRegion(this, region, &result);
390
391	_AdoptRegionData(result);
392}
393
394
395void
396BRegion::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*/
414void
415BRegion::_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*/
442bool
443BRegion::_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
488clipping_rect
489BRegion::_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
496clipping_rect
497BRegion::_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
504clipping_rect
505BRegion::_ConvertToInternal(const clipping_rect& rect) const
506{
507	return (clipping_rect){ rect.left, rect.top,
508		rect.right + 1, rect.bottom + 1 };
509}
510