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