xref: /haiku/src/kits/interface/Region.cpp (revision fef6144999c2fa611f59ee6ffe6dd7999501385c)
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 
42 #include <cstdlib>
43 #include <cstring>
44 
45 #include <Debug.h>
46 #include <Region.h>
47 
48 #include <clipping.h>
49 #include <RegionSupport.h>
50 
51 
52 /*! \brief Initializes a region. The region will have no rects,
53 	and its bound will be invalid.
54 */
55 BRegion::BRegion()
56 	:
57 	data_size(8),
58 	data(NULL)
59 {
60 	data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
61 
62 	Support::ZeroRegion(*this);
63 }
64 
65 
66 /*! \brief Initializes a region to be a copy of another.
67 	\param region The region to copy.
68 */
69 BRegion::BRegion(const BRegion &region)
70 	:
71 	data(NULL)
72 {
73 	bound = region.bound;
74 	count = region.count;
75 	data_size = region.data_size;
76 
77 	if (data_size <= 0)
78 		data_size = 1;
79 
80 	data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
81 
82 	memcpy(data, region.data, count * sizeof(clipping_rect));
83 }
84 
85 
86 /*!	\brief Initializes a region to contain a BRect.
87 	\param rect The BRect to set the region to.
88 */
89 BRegion::BRegion(const BRect rect)
90 	:
91 	data_size(8),
92 	data(NULL)
93 {
94 	data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
95 
96 	Set(rect);
97 }
98 
99 
100 /*!	\brief Frees the allocated memory.
101 */
102 BRegion::~BRegion()
103 {
104 	free(data);
105 }
106 
107 
108 /*! \brief Returns the bounds of the region.
109 	\return A BRect which represents the bounds of the region.
110 */
111 BRect
112 BRegion::Frame() const
113 {
114 	return to_BRect(bound);
115 }
116 
117 
118 /*! \brief Returns the bounds of the region as a clipping_rect (which has integer coordinates).
119 	\return A clipping_rect which represents the bounds of the region.
120 */
121 clipping_rect
122 BRegion::FrameInt() const
123 {
124 	return bound;
125 }
126 
127 
128 /*! \brief Returns the regions's BRect at the given index.
129 	\param index The index (zero based) of the wanted rectangle.
130 	\return If the given index is valid, it returns the BRect at that index,
131 		otherwise, it returns an invalid BRect.
132 */
133 BRect
134 BRegion::RectAt(int32 index)
135 {
136 	if (index >= 0 && index < count)
137 		return to_BRect(data[index]);
138 
139 	return BRect(); //An invalid BRect
140 }
141 
142 
143 /*! \brief Returns the regions's clipping_rect at the given index.
144 	\param index The index (zero based) of the wanted rectangle.
145 	\return If the given index is valid, it returns the clipping_rect at that index,
146 		otherwise, it returns an invalid clipping_rect.
147 */
148 clipping_rect
149 BRegion::RectAtInt(int32 index)
150 {
151 	if (index >= 0 && index < count)
152 		return data[index];
153 
154 	clipping_rect rect = { 1, 1, 0, 0 };
155 	return rect;
156 }
157 
158 
159 /*!	\brief Counts the region rects.
160 	\return An int32 which is the total number of rects in the region.
161 */
162 int32
163 BRegion::CountRects()
164 {
165 	return count;
166 }
167 
168 
169 /*!	\brief Set the region to contain just the given BRect.
170 	\param newBounds A BRect.
171 */
172 void
173 BRegion::Set(BRect newBounds)
174 {
175 	Set(to_clipping_rect(newBounds));
176 }
177 
178 
179 /*!	\brief Set the region to contain just the given clipping_rect.
180 	\param newBounds A clipping_rect.
181 */
182 void
183 BRegion::Set(clipping_rect newBounds)
184 {
185 	ASSERT(data_size > 0);
186 
187 	if (valid_rect(newBounds)) {
188 		count = 1;
189 		data[0] = newBounds;
190 		bound = newBounds;
191 	} else
192 		Support::ZeroRegion(*this);
193 }
194 
195 
196 /*!	\brief Check if the region has any area in common with the given BRect.
197 	\param rect The BRect to check the region against to.
198 	\return \ctrue if the region has any area in common with the BRect, \cfalse if not.
199 */
200 bool
201 BRegion::Intersects(BRect rect) const
202 {
203 	return Intersects(to_clipping_rect(rect));
204 }
205 
206 
207 /*!	\brief Check if the region has any area in common with the given clipping_rect.
208 	\param rect The clipping_rect to check the region against to.
209 	\return \ctrue if the region has any area in common with the clipping_rect, \cfalse if not.
210 */
211 bool
212 BRegion::Intersects(clipping_rect rect) const
213 {
214 	if (!rects_intersect(rect, bound))
215 		return false;
216 
217 	for (long c = 0; c < count; c++) {
218 		if (rects_intersect(data[c], rect))
219 			return true;
220 	}
221 
222 	return false;
223 }
224 
225 
226 /*!	\brief Check if the region contains the given BPoint.
227 	\param pt The BPoint to be checked.
228 	\return \ctrue if the region contains the BPoint, \cfalse if not.
229 */
230 bool
231 BRegion::Contains(BPoint pt) const
232 {
233 	// If the point doesn't lie within the region's bounds,
234 	// don't even try it against the region's rects.
235 	if (!point_in(bound, pt))
236 		return false;
237 
238 	for (long c = 0; c < count; c++) {
239 		if (point_in(data[c], pt))
240 			return true;
241 	}
242 	return false;
243 }
244 
245 
246 /*!	\brief Check if the region contains the given coordinates.
247 	\param x The \cx coordinate of the point to be checked.
248 	\param y The \cy coordinate of the point to be checked.
249 	\return \ctrue if the region contains the point, \cfalse if not.
250 */
251 bool
252 BRegion::Contains(int32 x, int32 y)
253 {
254 	// see above
255 	if (!point_in(bound, x, y))
256 		return false;
257 
258 	for (long c = 0; c < count; c++) {
259 		if (point_in(data[c], x, y))
260 			return true;
261 	}
262 	return false;
263 }
264 
265 
266 /*!	\brief Prints the BRegion to stdout.
267 */
268 void
269 BRegion::PrintToStream() const
270 {
271 	Frame().PrintToStream();
272 
273 	for (long c = 0; c < count; c++) {
274 		clipping_rect *rect = &data[c];
275 		printf("data = BRect(l:%ld.0, t:%ld.0, r:%ld.0, b:%ld.0)\n",
276 			rect->left, rect->top, rect->right, rect->bottom);
277 	}
278 }
279 
280 
281 /*!	\brief Offsets all region's rects, and bounds by the given values.
282 	\param dh The horizontal offset.
283 	\param dv The vertical offset.
284 */
285 void
286 BRegion::OffsetBy(int32 dh, int32 dv)
287 {
288 	if (count > 0) {
289 		for (long c = 0; c < count; c++)
290 			offset_rect(data[c], dh, dv);
291 
292 		offset_rect(bound, dh, dv);
293 	}
294 }
295 
296 
297 /*!	\brief Empties the region, so that it doesn't include any rect, and invalidates its bounds.
298 */
299 void
300 BRegion::MakeEmpty()
301 {
302 	Support::ZeroRegion(*this);
303 }
304 
305 
306 /*!	\brief Modifies the region, so that it includes the given BRect.
307 	\param rect The BRect to be included by the region.
308 */
309 void
310 BRegion::Include(BRect rect)
311 {
312 	Include(to_clipping_rect(rect));
313 }
314 
315 
316 /*!	\brief Modifies the region, so that it includes the given clipping_rect.
317 	\param rect The clipping_rect to be included by the region.
318 */
319 void
320 BRegion::Include(clipping_rect rect)
321 {
322 	BRegion region;
323 	BRegion newRegion;
324 
325 	region.Set(rect);
326 
327 	Support::OrRegion(*this, region, newRegion);
328 	Support::CopyRegion(newRegion, *this);
329 }
330 
331 
332 /*!	\brief Modifies the region, so that it includes the area of the given region.
333 	\param region The region to be included.
334 */
335 void
336 BRegion::Include(const BRegion *region)
337 {
338 	BRegion newRegion;
339 
340 	Support::OrRegion(*this, *region, newRegion);
341 	Support::CopyRegion(newRegion, *this);
342 }
343 
344 
345 /*!	\brief Modifies the region, excluding the area represented by the given BRect.
346 	\param rect The BRect to be excluded.
347 */
348 void
349 BRegion::Exclude(BRect rect)
350 {
351 	Exclude(to_clipping_rect(rect));
352 }
353 
354 
355 /*!	\brief Modifies the region, excluding the area represented by the given clipping_rect.
356 	\param rect The clipping_rect to be excluded.
357 */
358 void
359 BRegion::Exclude(clipping_rect rect)
360 {
361 	BRegion region;
362 	BRegion newRegion;
363 
364 	region.Set(rect);
365 
366 	Support::SubRegion(*this, region, newRegion);
367 	Support::CopyRegion(newRegion, *this);
368 }
369 
370 
371 /*!	\brief Modifies the region, excluding the area contained in the given BRegion.
372 	\param region The BRegion to be excluded.
373 */
374 void
375 BRegion::Exclude(const BRegion *region)
376 {
377 	BRegion newRegion;
378 
379 	Support::SubRegion(*this, *region, newRegion);
380 	Support::CopyRegion(newRegion, *this);
381 }
382 
383 
384 /*!	\brief Modifies the region, so that it will contain just the area in common with the given BRegion.
385 	\param region the BRegion to intersect to.
386 */
387 void
388 BRegion::IntersectWith(const BRegion *region)
389 {
390 	BRegion newRegion;
391 
392 	Support::AndRegion(*this, *region, newRegion);
393 	Support::CopyRegion(newRegion, *this);
394 }
395 
396 
397 /*!	\brief Modifies the region to be a copy of the given BRegion.
398 	\param region the BRegion to copy.
399 	\return This function always returns \c *this.
400 */
401 BRegion &
402 BRegion::operator=(const BRegion &region)
403 {
404 	if (&region != this) {
405 		free(data);
406 		bound = region.bound;
407 		count = region.count;
408 		data_size = region.data_size;
409 
410 		if (data_size <= 0)
411 			data_size = 1;
412 
413 		data = (clipping_rect *)malloc(data_size * sizeof(clipping_rect));
414 
415 		memcpy(data, region.data, count * sizeof(clipping_rect));
416 	}
417 
418 	return *this;
419 }
420 
421 
422 /*!	\brief Adds a rect to the region.
423 	\param rect The clipping_rect to be added.
424 
425 	Adds the given rect to the region, merging it with another already contained in the region,
426 	if possible. Recalculate the region's bounds if needed.
427 */
428 void
429 BRegion::_AddRect(clipping_rect rect)
430 {
431 	ASSERT(count >= 0);
432 	ASSERT(data_size >= 0);
433 	ASSERT(valid_rect(rect));
434 
435 	// Should we just reallocate the memory and
436 	// copy the rect ?
437 	bool addRect = true;
438 
439 	if (count > 0) {
440 		// Wait! We could merge the rect with one of the
441 		// existing rectangles, if it's adiacent.
442 		// We just check it against the last rectangle, since
443 		// we are keeping them sorted by their "top" coordinates.
444 		long last = count - 1;
445 		if (rect.left == data[last].left && rect.right == data[last].right
446 				&& rect.top == data[last].bottom + 1) {
447 
448 			data[last].bottom = rect.bottom;
449 			addRect = false;
450 
451 		} else if (rect.top == data[last].top && rect.bottom == data[last].bottom) {
452 			if (rect.left == data[last].right + 1) {
453 
454 				data[last].right = rect.right;
455 				addRect = false;
456 
457 			} else if (rect.right == data[last].left - 1) {
458 
459 				data[last].left = rect.left;
460 				addRect = false;
461 			}
462 		}
463 	}
464 
465 	// We weren't lucky.... just add the rect as a new one
466 	if (addRect) {
467 		if (data_size <= count)
468 			set_size(count + 16);
469 
470 		data[count] = rect;
471 
472 		count++;
473 	}
474 
475 	// Recalculate bounds
476 	if (rect.top < bound.top)
477 		bound.top = rect.top;
478 
479 	if (rect.left < bound.left)
480 		bound.left = rect.left;
481 
482 	if (rect.right > bound.right)
483 		bound.right = rect.right;
484 
485 	if (rect.bottom > bound.bottom)
486 		bound.bottom = rect.bottom;
487 }
488 
489 
490 /*!	\brief Reallocate the memory in the region.
491 	\param new_size The amount of rectangles that the region could contain.
492 */
493 void
494 BRegion::set_size(long new_size)
495 {
496 	if (new_size <= 0)
497 		new_size = data_size + 16;
498 
499 	data = (clipping_rect *)realloc(data, new_size * sizeof(clipping_rect));
500 
501 	if (data == NULL)
502 		debugger("BRegion::set_size realloc error\n");
503 
504 	data_size = new_size;
505 
506 	ASSERT(count <= data_size);
507 }
508