xref: /haiku/src/kits/interface/Shape.cpp (revision 0754c319592cd8a523959d85fb06ab23c64a98a6)
1 /*
2  * Copyright 2003-2010 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  *		Marc Flerackers, mflerackers@androme.be
8  *		Michael Lotz, mmlr@mlotz.ch
9  *		Marcus Overhagen, marcus@overhagen.de
10  */
11 
12 
13 #include <Shape.h>
14 
15 #include <Message.h>
16 #include <Point.h>
17 #include <Rect.h>
18 
19 #include <ShapePrivate.h>
20 
21 #include <new>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 
26 //	#pragma mark - BShapeIterator
27 
28 
29 BShapeIterator::BShapeIterator()
30 {
31 }
32 
33 
34 BShapeIterator::~BShapeIterator()
35 {
36 }
37 
38 
39 status_t
40 BShapeIterator::Iterate(BShape* shape)
41 {
42 	shape_data* data = (shape_data*)shape->fPrivateData;
43 	BPoint* points = data->ptList;
44 
45 	for (int32 i = 0; i < data->opCount; i++) {
46 		int32 op = data->opList[i] & 0xFF000000;
47 
48 		if ((op & OP_MOVETO) != 0) {
49 			IterateMoveTo(points);
50 			points++;
51 		}
52 
53 		if ((op & OP_LINETO) != 0) {
54 			int32 count = data->opList[i] & 0x00FFFFFF;
55 			IterateLineTo(count, points);
56 			points += count;
57 		}
58 
59 		if ((op & OP_BEZIERTO) != 0) {
60 			int32 count = data->opList[i] & 0x00FFFFFF;
61 			IterateBezierTo(count / 3, points);
62 			points += count;
63 		}
64 
65 		if ((op & OP_LARGE_ARC_TO_CW) != 0 || (op & OP_LARGE_ARC_TO_CCW) != 0
66 			|| (op & OP_SMALL_ARC_TO_CW) != 0
67 			|| (op & OP_SMALL_ARC_TO_CCW) != 0) {
68 			int32 count = data->opList[i] & 0x00FFFFFF;
69 			for (int32 i = 0; i < count / 3; i++) {
70 				IterateArcTo(points[0].x, points[0].y, points[1].x,
71 					op & (OP_LARGE_ARC_TO_CW | OP_LARGE_ARC_TO_CCW),
72 					op & (OP_SMALL_ARC_TO_CCW | OP_LARGE_ARC_TO_CCW),
73 					points[2]);
74 				points += 3;
75 			}
76 		}
77 
78 		if ((op & OP_CLOSE) != 0)
79 			IterateClose();
80 	}
81 
82 	return B_OK;
83 }
84 
85 
86 status_t
87 BShapeIterator::IterateMoveTo(BPoint* point)
88 {
89 	return B_OK;
90 }
91 
92 
93 status_t
94 BShapeIterator::IterateLineTo(int32 lineCount, BPoint* linePoints)
95 {
96 	return B_OK;
97 }
98 
99 
100 status_t
101 BShapeIterator::IterateBezierTo(int32 bezierCount, BPoint* bezierPoints)
102 {
103 	return B_OK;
104 }
105 
106 
107 status_t
108 BShapeIterator::IterateClose()
109 {
110 	return B_OK;
111 }
112 
113 
114 status_t
115 BShapeIterator::IterateArcTo(float& rx, float& ry, float& angle, bool largeArc,
116 	bool counterClockWise, BPoint& point)
117 {
118 	return B_OK;
119 }
120 
121 
122 // #pragma mark - BShapeIterator FBC padding
123 
124 
125 void BShapeIterator::_ReservedShapeIterator2() {}
126 void BShapeIterator::_ReservedShapeIterator3() {}
127 void BShapeIterator::_ReservedShapeIterator4() {}
128 
129 
130 // #pragma mark - BShape
131 
132 
133 BShape::BShape()
134 {
135 	InitData();
136 }
137 
138 
139 BShape::BShape(const BShape& other)
140 {
141 	InitData();
142 	AddShape(&other);
143 }
144 
145 
146 BShape::BShape(BMessage* archive)
147 	:
148 	BArchivable(archive)
149 {
150 	InitData();
151 
152 	shape_data* data = (shape_data*)fPrivateData;
153 
154 	ssize_t size = 0;
155 	int32 count = 0;
156 	type_code type = 0;
157 	archive->GetInfo("ops", &type, &count);
158 	if (!AllocateOps(count))
159 		return;
160 
161 	int32 i = 0;
162 	const uint32* opPtr;
163 	while (archive->FindData("ops", B_INT32_TYPE, i++,
164 			(const void**)&opPtr, &size) == B_OK) {
165 		data->opList[data->opCount++] = *opPtr;
166 	}
167 
168 	archive->GetInfo("pts", &type, &count);
169 	if (!AllocatePts(count)) {
170 		Clear();
171 		return;
172 	}
173 
174 	i = 0;
175 	const BPoint* ptPtr;
176 	while (archive->FindData("pts", B_POINT_TYPE, i++,
177 			(const void**)&ptPtr, &size) == B_OK) {
178 		data->ptList[data->ptCount++] = *ptPtr;
179 	}
180 }
181 
182 
183 BShape::~BShape()
184 {
185 	shape_data* data = (shape_data*)fPrivateData;
186 
187 	free(data->opList);
188 	free(data->ptList);
189 
190 	delete (shape_data*)fPrivateData;
191 }
192 
193 
194 status_t
195 BShape::Archive(BMessage* archive, bool deep) const
196 {
197 	status_t result = BArchivable::Archive(archive, deep);
198 
199 	if (result != B_OK)
200 		return result;
201 
202 	shape_data* data = (shape_data*)fPrivateData;
203 
204 	// If no valid shape data, return
205 	if (data->opCount == 0 || data->ptCount == 0)
206 		return result;
207 
208 	// Avoids allocation for each point
209 	result = archive->AddData("pts", B_POINT_TYPE, data->ptList,
210 		sizeof(BPoint), true, data->ptCount);
211 	if (result != B_OK)
212 		return result;
213 
214 	for (int32 i = 1; i < data->ptCount && result == B_OK; i++)
215 		result = archive->AddPoint("pts", data->ptList[i]);
216 
217 	// Avoids allocation for each op
218 	if (result == B_OK) {
219 		result = archive->AddData("ops", B_INT32_TYPE, data->opList,
220 			sizeof(int32), true, data->opCount);
221 	}
222 
223 	for (int32 i = 1; i < data->opCount && result == B_OK; i++)
224 		result = archive->AddInt32("ops", data->opList[i]);
225 
226 	return result;
227 }
228 
229 
230 BArchivable*
231 BShape::Instantiate(BMessage* archive)
232 {
233 	if (validate_instantiation(archive, "BShape"))
234 		return new BShape(archive);
235 	else
236 		return NULL;
237 }
238 
239 
240 BShape&
241 BShape::operator=(const BShape& other)
242 {
243 	if (this != &other) {
244 		Clear();
245 		AddShape(&other);
246 	}
247 
248 	return *this;
249 }
250 
251 
252 bool
253 BShape::operator==(const BShape& other) const
254 {
255 	if (this == &other)
256 		return true;
257 
258 	shape_data* data = (shape_data*)fPrivateData;
259 	shape_data* otherData = (shape_data*)other.fPrivateData;
260 
261 	if (data->opCount != otherData->opCount)
262 		return false;
263 
264 	if (data->ptCount != otherData->ptCount)
265 		return false;
266 
267 	return memcmp(data->opList, otherData->opList,
268 			data->opCount * sizeof(uint32)) == 0
269 		&& memcmp(data->ptList, otherData->ptList,
270 			data->ptCount * sizeof(BPoint)) == 0;
271 }
272 
273 
274 bool
275 BShape::operator!=(const BShape& other) const
276 {
277 	return !(*this == other);
278 }
279 
280 
281 void
282 BShape::Clear()
283 {
284 	shape_data* data = (shape_data*)fPrivateData;
285 
286 	data->opCount = 0;
287 	data->opSize = 0;
288 	if (data->opList) {
289 		free(data->opList);
290 		data->opList = NULL;
291 	}
292 
293 	data->ptCount = 0;
294 	data->ptSize = 0;
295 	if (data->ptList) {
296 		free(data->ptList);
297 		data->ptList = NULL;
298 	}
299 
300 	fState = 0;
301 	fBuildingOp = 0;
302 }
303 
304 
305 BRect
306 BShape::Bounds() const
307 {
308 	shape_data* data = (shape_data*)fPrivateData;
309 	BRect bounds;
310 
311 	if (data->ptCount == 0)
312 		return bounds;
313 
314 	// TODO: This implementation doesn't take into account curves at all.
315 	bounds.left = data->ptList[0].x;
316 	bounds.top = data->ptList[0].y;
317 	bounds.right = data->ptList[0].x;
318 	bounds.bottom = data->ptList[0].y;
319 
320 	for (int32 i = 1; i < data->ptCount; i++) {
321 		if (bounds.left > data->ptList[i].x)
322 			bounds.left = data->ptList[i].x;
323 
324 		if (bounds.top > data->ptList[i].y)
325 			bounds.top = data->ptList[i].y;
326 
327 		if (bounds.right < data->ptList[i].x)
328 			bounds.right = data->ptList[i].x;
329 
330 		if (bounds.bottom < data->ptList[i].y)
331 			bounds.bottom = data->ptList[i].y;
332 	}
333 
334 	return bounds;
335 }
336 
337 
338 BPoint
339 BShape::CurrentPosition() const
340 {
341 	shape_data* data = (shape_data*)fPrivateData;
342 
343 	if (data->ptCount == 0)
344 		return B_ORIGIN;
345 
346 	return data->ptList[data->ptCount - 1];
347 }
348 
349 
350 status_t
351 BShape::AddShape(const BShape* otherShape)
352 {
353 	shape_data* data = (shape_data*)fPrivateData;
354 	shape_data* otherData = (shape_data*)otherShape->fPrivateData;
355 
356 	if (!AllocateOps(otherData->opCount) || !AllocatePts(otherData->ptCount))
357 		return B_NO_MEMORY;
358 
359 	memcpy(data->opList + data->opCount, otherData->opList,
360 		otherData->opCount * sizeof(uint32));
361 	data->opCount += otherData->opCount;
362 
363 	memcpy(data->ptList + data->ptCount, otherData->ptList,
364 		otherData->ptCount * sizeof(BPoint));
365 	data->ptCount += otherData->ptCount;
366 
367 	fBuildingOp = otherShape->fBuildingOp;
368 
369 	return B_OK;
370 }
371 
372 
373 status_t
374 BShape::MoveTo(BPoint point)
375 {
376 	shape_data* data = (shape_data*)fPrivateData;
377 
378 	// If the last op is MoveTo, replace the point
379 	if (fBuildingOp == OP_MOVETO) {
380 		data->ptList[data->ptCount - 1] = point;
381 		return B_OK;
382 	}
383 
384 	if (!AllocateOps(1) || !AllocatePts(1))
385 		return B_NO_MEMORY;
386 
387 	fBuildingOp = OP_MOVETO;
388 
389 	// Add op
390 	data->opList[data->opCount++] = fBuildingOp;
391 
392 	// Add point
393 	data->ptList[data->ptCount++] = point;
394 
395 	return B_OK;
396 }
397 
398 
399 status_t
400 BShape::LineTo(BPoint point)
401 {
402 	if (!AllocatePts(1))
403 		return B_NO_MEMORY;
404 
405 	shape_data* data = (shape_data*)fPrivateData;
406 
407 	// If the last op is MoveTo, replace the op and set the count
408 	// If the last op is LineTo increase the count
409 	// Otherwise add the op
410 	if (fBuildingOp & OP_LINETO || fBuildingOp == OP_MOVETO) {
411 		fBuildingOp |= OP_LINETO;
412 		fBuildingOp += 1;
413 		data->opList[data->opCount - 1] = fBuildingOp;
414 	} else {
415 		if (!AllocateOps(1))
416 			return B_NO_MEMORY;
417 
418 		fBuildingOp = OP_LINETO + 1;
419 		data->opList[data->opCount++] = fBuildingOp;
420 	}
421 
422 	// Add point
423 	data->ptList[data->ptCount++] = point;
424 
425 	return B_OK;
426 }
427 
428 
429 status_t
430 BShape::BezierTo(BPoint controlPoints[3])
431 {
432 	return BezierTo(controlPoints[0], controlPoints[1], controlPoints[2]);
433 }
434 
435 
436 status_t
437 BShape::BezierTo(const BPoint& control1, const BPoint& control2,
438 	const BPoint& endPoint)
439 {
440 	if (!AllocatePts(3))
441 		return B_NO_MEMORY;
442 
443 	shape_data* data = (shape_data*)fPrivateData;
444 
445 	// If the last op is MoveTo, replace the op and set the count
446 	// If the last op is BezierTo increase the count
447 	// Otherwise add the op
448 	if (fBuildingOp & OP_BEZIERTO || fBuildingOp == OP_MOVETO) {
449 		fBuildingOp |= OP_BEZIERTO;
450 		fBuildingOp += 3;
451 		data->opList[data->opCount - 1] = fBuildingOp;
452 	} else {
453 		if (!AllocateOps(1))
454 			return B_NO_MEMORY;
455 		fBuildingOp = OP_BEZIERTO + 3;
456 		data->opList[data->opCount++] = fBuildingOp;
457 	}
458 
459 	// Add points
460 	data->ptList[data->ptCount++] = control1;
461 	data->ptList[data->ptCount++] = control2;
462 	data->ptList[data->ptCount++] = endPoint;
463 
464 	return B_OK;
465 }
466 
467 
468 status_t
469 BShape::ArcTo(float rx, float ry, float angle, bool largeArc,
470 	bool counterClockWise, const BPoint& point)
471 {
472 	if (!AllocatePts(3))
473 		return B_NO_MEMORY;
474 
475 	shape_data* data = (shape_data*)fPrivateData;
476 
477 	uint32 op;
478 	if (largeArc) {
479 		if (counterClockWise)
480 			op = OP_LARGE_ARC_TO_CCW;
481 		else
482 			op = OP_LARGE_ARC_TO_CW;
483 	} else {
484 		if (counterClockWise)
485 			op = OP_SMALL_ARC_TO_CCW;
486 		else
487 			op = OP_SMALL_ARC_TO_CW;
488 	}
489 
490 	// If the last op is MoveTo, replace the op and set the count
491 	// If the last op is ArcTo increase the count
492 	// Otherwise add the op
493 	if (fBuildingOp == op || fBuildingOp == (op | OP_MOVETO)) {
494 		fBuildingOp |= op;
495 		fBuildingOp += 3;
496 		data->opList[data->opCount - 1] = fBuildingOp;
497 	} else {
498 		if (!AllocateOps(1))
499 			return B_NO_MEMORY;
500 
501 		fBuildingOp = op + 3;
502 		data->opList[data->opCount++] = fBuildingOp;
503 	}
504 
505 	// Add points
506 	data->ptList[data->ptCount++] = BPoint(rx, ry);
507 	data->ptList[data->ptCount++] = BPoint(angle, 0);
508 	data->ptList[data->ptCount++] = point;
509 
510 	return B_OK;
511 }
512 
513 
514 status_t
515 BShape::Close()
516 {
517 	// If the last op is Close or MoveTo, ignore this
518 	if (fBuildingOp == OP_CLOSE || fBuildingOp == OP_MOVETO)
519 		return B_OK;
520 
521 	if (!AllocateOps(1))
522 		return B_NO_MEMORY;
523 
524 	shape_data* data = (shape_data*)fPrivateData;
525 
526 	// ToDo: Decide about that, it's not BeOS compatible
527 	// If there was any op before we can attach the close to it
528 	/*if (fBuildingOp) {
529 		fBuildingOp |= OP_CLOSE;
530 		data->opList[data->opCount - 1] = fBuildingOp;
531 		return B_OK;
532 	}*/
533 
534 	fBuildingOp = OP_CLOSE;
535 	data->opList[data->opCount++] = fBuildingOp;
536 
537 	return B_OK;
538 }
539 
540 
541 //	#pragma mark - BShape private methods
542 
543 
544 status_t
545 BShape::Perform(perform_code code, void* data)
546 {
547 	return BArchivable::Perform(code, data);
548 }
549 
550 
551 //	#pragma mark - BShape FBC methods
552 
553 
554 void BShape::_ReservedShape1() {}
555 void BShape::_ReservedShape2() {}
556 void BShape::_ReservedShape3() {}
557 void BShape::_ReservedShape4() {}
558 
559 
560 //	#pragma mark - BShape private methods
561 
562 
563 void
564 BShape::GetData(int32* opCount, int32* ptCount, uint32** opList,
565 	BPoint** ptList)
566 {
567 	shape_data* data = (shape_data*)fPrivateData;
568 
569 	*opCount = data->opCount;
570 	*ptCount = data->ptCount;
571 	*opList = data->opList;
572 	*ptList = data->ptList;
573 }
574 
575 
576 void
577 BShape::SetData(int32 opCount, int32 ptCount, const uint32* opList,
578 	const BPoint* ptList)
579 {
580 	Clear();
581 
582 	if (opCount == 0)
583 		return;
584 
585 	shape_data* data = (shape_data*)fPrivateData;
586 
587 	if (!AllocateOps(opCount) || !AllocatePts(ptCount))
588 		return;
589 
590 	memcpy(data->opList, opList, opCount * sizeof(uint32));
591 	data->opCount = opCount;
592 	fBuildingOp = data->opList[data->opCount - 1];
593 
594 	if (ptCount > 0) {
595 		memcpy(data->ptList, ptList, ptCount * sizeof(BPoint));
596 		data->ptCount = ptCount;
597 	}
598 }
599 
600 
601 void
602 BShape::InitData()
603 {
604 	fPrivateData = new shape_data;
605 	shape_data* data = (shape_data*)fPrivateData;
606 
607 	fState = 0;
608 	fBuildingOp = 0;
609 
610 	data->opList = NULL;
611 	data->opCount = 0;
612 	data->opSize = 0;
613 	data->ptList = NULL;
614 	data->ptCount = 0;
615 	data->ptSize = 0;
616 }
617 
618 
619 inline bool
620 BShape::AllocateOps(int32 count)
621 {
622 	shape_data* data = (shape_data*)fPrivateData;
623 
624 	int32 newSize = (data->opCount + count + 255) / 256 * 256;
625 	if (data->opSize >= newSize)
626 		return true;
627 
628 	uint32* resizedArray = (uint32*)realloc(data->opList, newSize * sizeof(uint32));
629 	if (resizedArray) {
630 		data->opList = resizedArray;
631 		data->opSize = newSize;
632 		return true;
633 	}
634 	return false;
635 }
636 
637 
638 inline bool
639 BShape::AllocatePts(int32 count)
640 {
641 	shape_data* data = (shape_data*)fPrivateData;
642 
643 	int32 newSize = (data->ptCount + count + 255) / 256 * 256;
644 	if (data->ptSize >= newSize)
645 		return true;
646 
647 	BPoint* resizedArray = (BPoint*)realloc(data->ptList, newSize * sizeof(BPoint));
648 	if (resizedArray) {
649 		data->ptList = resizedArray;
650 		data->ptSize = newSize;
651 		return true;
652 	}
653 	return false;
654 }
655 
656 
657 //	#pragma mark - BShape binary compatibility methods
658 
659 
660 #if __GNUC__ < 3
661 
662 
663 extern "C" BShape*
664 __6BShapeR6BShape(void* self, BShape& copyFrom)
665 {
666 	return new (self) BShape(copyFrom);
667 		// we need to instantiate the object in the provided memory
668 }
669 
670 
671 extern "C" BRect
672 Bounds__6BShape(BShape* self)
673 {
674 	return self->Bounds();
675 }
676 
677 
678 extern "C" void
679 _ReservedShapeIterator1__14BShapeIterator(BShapeIterator* self)
680 {
681 }
682 
683 
684 #else // __GNUC__ < 3
685 
686 
687 extern "C" void
688 _ZN14BShapeIterator23_ReservedShapeIterator1Ev(BShapeIterator* self)
689 {
690 }
691 
692 
693 #endif // __GNUC__ >= 3
694