xref: /haiku/src/kits/interface/Region.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 //------------------------------------------------------------------------------
2 //	Copyright (c) 2003-2005, Haiku, Inc.
3 //
4 //	Permission is hereby granted, free of charge, to any person obtaining a
5 //	copy of this software and associated documentation files (the "Software"),
6 //	to deal in the Software without restriction, including without limitation
7 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 //	and/or sell copies of the Software, and to permit persons to whom the
9 //	Software is furnished to do so, subject to the following conditions:
10 //
11 //	The above copyright notice and this permission notice shall be included in
12 //	all copies or substantial portions of the Software.
13 //
14 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 //	DEALINGS IN THE SOFTWARE.
21 //
22 //	File Name:		Region.cpp
23 //	Author:			Stefano Ceccherini (burton666@libero.it)
24 //	Description:	Region class consisting of multiple rectangles
25 //
26 //------------------------------------------------------------------------------
27 
28 //	Notes: As now, memory is always allocated and never freed (except on destruction,
29 //	or sometimes when a copy is made).
30 //  This let us be a bit faster since we don't do many reallocations.
31 //	But that means that even an empty region could "waste" much space, if it contained
32 //	many rects before being emptied.
33 //	I.E: a region which contains 24 rects allocates more than 24 * 4 * sizeof(int32)
34 //  = 96 * sizeof(int32) bytes. If we call MakeEmpty(), that region will contain no rects,
35 //  but it will still keep the allocated memory.
36 //	This shouldnt' be an issue, since usually BRegions are just used for calculations,
37 //	and don't last so long.
38 //	Anyway, we can change that behaviour if we want, but BeOS's BRegion seems to behave exactly
39 //	like this.
40 
41 #include <cstdlib>
42 #include <cstring>
43 
44 #include <Debug.h>
45 #include <Region.h>
46 
47 #include <clipping.h>
48 #include <RegionSupport.h>
49 
50 
51 const static int32 kInitialDataSize = 8;
52 
53 
54 /*! \brief Initializes a region. The region will have no rects,
55 	and its bound will be invalid.
56 */
57 BRegion::BRegion()
58 	:
59 	data_size(0),
60 	data(NULL)
61 {
62 	data = (clipping_rect *)malloc(kInitialDataSize * sizeof(clipping_rect));
63 	if (data != NULL)
64 		data_size = kInitialDataSize;
65 
66 	Support::ZeroRegion(*this);
67 }
68 
69 
70 /*! \brief Initializes a region to be a copy of another.
71 	\param region The region to copy.
72 */
73 BRegion::BRegion(const BRegion &region)
74 	:
75 	data_size(0),
76 	data(NULL)
77 {
78 	const int32 size = region.data_size > 0 ? region.data_size : 1;
79 
80 	data = (clipping_rect *)malloc(size * sizeof(clipping_rect));
81 	if (data != NULL) {
82 		data_size = size;
83 		bound = region.bound;
84 		count = region.count;
85 		memcpy(data, region.data, count * sizeof(clipping_rect));
86 	} else
87 		Support::ZeroRegion(*this);
88 }
89 
90 
91 /*!	\brief Initializes a region to contain a BRect.
92 	\param rect The BRect to set the region to.
93 */
94 BRegion::BRegion(const BRect rect)
95 	:
96 	data_size(0),
97 	data(NULL)
98 {
99 	data = (clipping_rect *)malloc(kInitialDataSize * sizeof(clipping_rect));
100 	if (data != NULL) {
101 		data_size = kInitialDataSize;
102 		Set(rect);
103 	} else
104 		Support::ZeroRegion(*this);
105 }
106 
107 
108 /*!	\brief Frees the allocated memory.
109 */
110 BRegion::~BRegion()
111 {
112 	free(data);
113 }
114 
115 
116 /*! \brief Returns the bounds of the region.
117 	\return A BRect which represents the bounds of the region.
118 */
119 BRect
120 BRegion::Frame() const
121 {
122 	return to_BRect(bound);
123 }
124 
125 
126 /*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates).
127 	\return A clipping_rect which represents the bounds of the region.
128 */
129 clipping_rect
130 BRegion::FrameInt() const
131 {
132 	return bound;
133 }
134 
135 
136 /*! \brief Returns the regions's BRect at the given index.
137 	\param index The index (zero based) of the wanted rectangle.
138 	\return If the given index is valid, it returns the BRect at that index,
139 		otherwise, it returns an invalid BRect.
140 */
141 BRect
142 BRegion::RectAt(int32 index)
143 {
144 	if (index >= 0 && index < count)
145 		return to_BRect(data[index]);
146 
147 	return BRect(); //An invalid BRect
148 }
149 
150 
151 /*! \brief Returns the regions's clipping_rect at the given index.
152 	\param index The index (zero based) of the wanted rectangle.
153 	\return If the given index is valid, it returns the clipping_rect at that index,
154 		otherwise, it returns an invalid clipping_rect.
155 */
156 clipping_rect
157 BRegion::RectAtInt(int32 index)
158 {
159 	if (index >= 0 && index < count)
160 		return data[index];
161 
162 	clipping_rect rect = { 1, 1, 0, 0 };
163 	return rect;
164 }
165 
166 
167 /*!	\brief Counts the region rects.
168 	\return An int32 which is the total number of rects in the region.
169 */
170 int32
171 BRegion::CountRects()
172 {
173 	return count;
174 }
175 
176 
177 /*!	\brief Set the region to contain just the given BRect.
178 	\param newBounds A BRect.
179 */
180 void
181 BRegion::Set(BRect newBounds)
182 {
183 	Set(to_clipping_rect(newBounds));
184 }
185 
186 
187 /*!	\brief Set the region to contain just the given clipping_rect.
188 	\param newBounds A clipping_rect.
189 */
190 void
191 BRegion::Set(clipping_rect newBounds)
192 {
193 	ASSERT(data_size > 0);
194 
195 	if (valid_rect(newBounds)) {
196 		count = 1;
197 		data[0] = newBounds;
198 		bound = newBounds;
199 	} else
200 		Support::ZeroRegion(*this);
201 }
202 
203 
204 /*!	\brief Check if the region has any area in common with the given BRect.
205 	\param rect The BRect to check the region against to.
206 	\return \ctrue if the region has any area in common with the BRect, \cfalse if not.
207 */
208 bool
209 BRegion::Intersects(BRect rect) const
210 {
211 	return Intersects(to_clipping_rect(rect));
212 }
213 
214 
215 /*!	\brief Check if the region has any area in common with the given clipping_rect.
216 	\param rect The clipping_rect to check the region against to.
217 	\return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not.
218 */
219 bool
220 BRegion::Intersects(clipping_rect rect) const
221 {
222 	if (!rects_intersect(rect, bound))
223 		return false;
224 
225 	for (long c = 0; c < count; c++) {
226 		if (rects_intersect(data[c], rect))
227 			return true;
228 	}
229 
230 	return false;
231 }
232 
233 
234 /*!	\brief Check if the region contains the given BPoint.
235 	\param pt The BPoint to be checked.
236 	\return \ctrue if the region contains the BPoint, \cfalse if not.
237 */
238 bool
239 BRegion::Contains(BPoint pt) const
240 {
241 	// If the point doesn't lie within the region's bounds,
242 	// don't even try it against the region's rects.
243 	if (!point_in(bound, pt))
244 		return false;
245 
246 	for (long c = 0; c < count; c++) {
247 		if (point_in(data[c], pt))
248 			return true;
249 	}
250 	return false;
251 }
252 
253 
254 /*!	\brief Check if the region contains the given coordinates.
255 	\param x The \cx coordinate of the point to be checked.
256 	\param y The \cy coordinate of the point to be checked.
257 	\return \ctrue if the region contains the point, \cfalse if not.
258 */
259 bool
260 BRegion::Contains(int32 x, int32 y)
261 {
262 	// see above
263 	if (!point_in(bound, x, y))
264 		return false;
265 
266 	for (long c = 0; c < count; c++) {
267 		if (point_in(data[c], x, y))
268 			return true;
269 	}
270 	return false;
271 }
272 
273 
274 /*!	\brief Prints the BRegion to stdout.
275 */
276 void
277 BRegion::PrintToStream() const
278 {
279 	Frame().PrintToStream();
280 
281 	for (long c = 0; c < count; c++) {
282 		clipping_rect *rect = &data[c];
283 		printf("data = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n",
284 			rect->left, rect->top, rect->right, rect->bottom);
285 	}
286 }
287 
288 
289 /*!	\brief Offsets all region's rects, and bounds by the given values.
290 	\param dh The horizontal offset.
291 	\param dv The vertical offset.
292 */
293 void
294 BRegion::OffsetBy(int32 dh, int32 dv)
295 {
296 	if (dh == 0 && dv == 0)
297 		return;
298 
299 	if (count > 0) {
300 		for (long c = 0; c < count; c++)
301 			offset_rect(data[c], dh, dv);
302 
303 		offset_rect(bound, dh, dv);
304 	}
305 }
306 
307 
308 /*!	\brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
309 */
310 void
311 BRegion::MakeEmpty()
312 {
313 	Support::ZeroRegion(*this);
314 }
315 
316 
317 /*!	\brief Modifies the region, so that it includes the given BRect.
318 	\param rect The BRect to be included by the region.
319 */
320 void
321 BRegion::Include(BRect rect)
322 {
323 	Include(to_clipping_rect(rect));
324 }
325 
326 
327 /*!	\brief Modifies the region, so that it includes the given clipping_rect.
328 	\param rect The clipping_rect to be included by the region.
329 */
330 void
331 BRegion::Include(clipping_rect rect)
332 {
333 	BRegion region;
334 	BRegion newRegion;
335 
336 	region.Set(rect);
337 
338 	Support::OrRegion(*this, region, newRegion);
339 	Support::CopyRegion(newRegion, *this);
340 }
341 
342 
343 /*!	\brief Modifies the region, so that it includes the area of the given region.
344 	\param region The region to be included.
345 */
346 void
347 BRegion::Include(const BRegion *region)
348 {
349 	BRegion newRegion;
350 
351 	Support::OrRegion(*this, *region, newRegion);
352 	Support::CopyRegion(newRegion, *this);
353 }
354 
355 
356 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
357 	\param rect The BRect to be excluded.
358 */
359 void
360 BRegion::Exclude(BRect rect)
361 {
362 	Exclude(to_clipping_rect(rect));
363 }
364 
365 
366 /*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
367 	\param rect The clipping_rect to be excluded.
368 */
369 void
370 BRegion::Exclude(clipping_rect rect)
371 {
372 	BRegion region;
373 	BRegion newRegion;
374 
375 	region.Set(rect);
376 
377 	Support::SubRegion(*this, region, newRegion);
378 	Support::CopyRegion(newRegion, *this);
379 }
380 
381 
382 /*!	\brief Modifies the region, excluding the area contained in the given BRegion.
383 	\param region The BRegion to be excluded.
384 */
385 void
386 BRegion::Exclude(const BRegion *region)
387 {
388 	BRegion newRegion;
389 
390 	Support::SubRegion(*this, *region, newRegion);
391 	Support::CopyRegion(newRegion, *this);
392 }
393 
394 
395 /*!	\brief Modifies the region, so that it will contain just the area in common with the given BRegion.
396 	\param region the BRegion to intersect to.
397 */
398 void
399 BRegion::IntersectWith(const BRegion *region)
400 {
401 	BRegion newRegion;
402 
403 	Support::AndRegion(*this, *region, newRegion);
404 	Support::CopyRegion(newRegion, *this);
405 }
406 
407 
408 /*!	\brief Modifies the region to be a copy of the given BRegion.
409 	\param region the BRegion to copy.
410 	\return This function always returns \c *this.
411 */
412 BRegion &
413 BRegion::operator=(const BRegion &region)
414 {
415 	if (&region != this) {
416 		bound = region.bound;
417 		count = region.count;
418 
419 		// handle reallocation if we're too small to contain
420 		// the other region
421 		set_size(region.data_size);
422 
423 		// TODO: what is this supposed to do??
424 		if (data_size <= 0)
425 			data_size = 1;
426 
427 		if (data)
428 			memcpy(data, region.data, count * sizeof(clipping_rect));
429 	}
430 
431 	return *this;
432 }
433 
434 
435 /*!	\brief Adds a rect to the region.
436 	\param rect The clipping_rect to be added.
437 
438 	Adds the given rect to the region, merging it with another already contained in the region,
439 	if possible. Recalculate the region's bounds if needed.
440 */
441 void
442 BRegion::_AddRect(clipping_rect rect)
443 {
444 	ASSERT(count >= 0);
445 	ASSERT(data_size >= 0);
446 	ASSERT(valid_rect(rect));
447 
448 	// Should we just reallocate the memory and
449 	// copy the rect ?
450 	bool addRect = true;
451 
452 	if (count > 0) {
453 		// Wait! We could merge the rect with one of the
454 		// existing rectangles, if it's adiacent.
455 		// We just check it against the last rectangle, since
456 		// we are keeping them sorted by their "top" coordinates.
457 		long last = count - 1;
458 		if (rect.left == data[last].left && rect.right == data[last].right
459 				&& rect.top == data[last].bottom + 1) {
460 
461 			data[last].bottom = rect.bottom;
462 			addRect = false;
463 
464 		} else if (rect.top == data[last].top && rect.bottom == data[last].bottom) {
465 			if (rect.left == data[last].right + 1) {
466 
467 				data[last].right = rect.right;
468 				addRect = false;
469 
470 			} else if (rect.right == data[last].left - 1) {
471 
472 				data[last].left = rect.left;
473 				addRect = false;
474 			}
475 		}
476 	}
477 
478 	// We weren't lucky.... just add the rect as a new one
479 	if (addRect) {
480 		if (data_size <= count)
481 			set_size(count + 16);
482 
483 		data[count] = rect;
484 
485 		count++;
486 	}
487 
488 	// Recalculate bounds
489 	if (rect.top < bound.top)
490 		bound.top = rect.top;
491 
492 	if (rect.left < bound.left)
493 		bound.left = rect.left;
494 
495 	if (rect.right > bound.right)
496 		bound.right = rect.right;
497 
498 	if (rect.bottom > bound.bottom)
499 		bound.bottom = rect.bottom;
500 }
501 
502 
503 /*!	\brief Reallocate the memory in the region.
504 	\param new_size The amount of rectangles that the region could contain.
505 */
506 void
507 BRegion::set_size(long new_size)
508 {
509 	if (new_size <= 0)
510 		new_size = data_size + 16;
511 
512 	data = (clipping_rect *)realloc(data, new_size * sizeof(clipping_rect));
513 
514 	if (data == NULL)
515 		debugger("BRegion::set_size realloc error\n");
516 
517 	data_size = new_size;
518 
519 	ASSERT(count <= data_size);
520 }
521