xref: /haiku/src/kits/interface/Shape.cpp (revision 344ded80d400028c8f561b4b876257b94c12db4a)
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 #if defined(__cplusplus) && __cplusplus >= 201103L
147 BShape::BShape(BShape&& other)
148 {
149 	InitData();
150 	MoveFrom(other);
151 }
152 #endif
153 
154 
155 BShape::BShape(BMessage* archive)
156 	:
157 	BArchivable(archive)
158 {
159 	InitData();
160 
161 	shape_data* data = (shape_data*)fPrivateData;
162 
163 	ssize_t size = 0;
164 	int32 count = 0;
165 	type_code type = 0;
166 	archive->GetInfo("ops", &type, &count);
167 	if (!AllocateOps(count))
168 		return;
169 
170 	int32 i = 0;
171 	const uint32* opPtr;
172 	while (archive->FindData("ops", B_INT32_TYPE, i++,
173 			(const void**)&opPtr, &size) == B_OK) {
174 		data->opList[data->opCount++] = *opPtr;
175 	}
176 
177 	archive->GetInfo("pts", &type, &count);
178 	if (!AllocatePts(count)) {
179 		Clear();
180 		return;
181 	}
182 
183 	i = 0;
184 	const BPoint* ptPtr;
185 	while (archive->FindData("pts", B_POINT_TYPE, i++,
186 			(const void**)&ptPtr, &size) == B_OK) {
187 		data->ptList[data->ptCount++] = *ptPtr;
188 	}
189 }
190 
191 
192 BShape::~BShape()
193 {
194 	shape_data* data = (shape_data*)fPrivateData;
195 	if (!data->fOwnsMemory) {
196 		free(data->opList);
197 		free(data->ptList);
198 	}
199 
200 	data->ReleaseReference();
201 }
202 
203 
204 status_t
205 BShape::Archive(BMessage* archive, bool deep) const
206 {
207 	status_t result = BArchivable::Archive(archive, deep);
208 
209 	if (result != B_OK)
210 		return result;
211 
212 	shape_data* data = (shape_data*)fPrivateData;
213 
214 	// If no valid shape data, return
215 	if (data->opCount == 0 || data->ptCount == 0)
216 		return result;
217 
218 	// Avoids allocation for each point
219 	result = archive->AddData("pts", B_POINT_TYPE, data->ptList,
220 		sizeof(BPoint), true, data->ptCount);
221 	if (result != B_OK)
222 		return result;
223 
224 	for (int32 i = 1; i < data->ptCount && result == B_OK; i++)
225 		result = archive->AddPoint("pts", data->ptList[i]);
226 
227 	// Avoids allocation for each op
228 	if (result == B_OK) {
229 		result = archive->AddData("ops", B_INT32_TYPE, data->opList,
230 			sizeof(int32), true, data->opCount);
231 	}
232 
233 	for (int32 i = 1; i < data->opCount && result == B_OK; i++)
234 		result = archive->AddInt32("ops", data->opList[i]);
235 
236 	return result;
237 }
238 
239 
240 BArchivable*
241 BShape::Instantiate(BMessage* archive)
242 {
243 	if (validate_instantiation(archive, "BShape"))
244 		return new BShape(archive);
245 	else
246 		return NULL;
247 }
248 
249 
250 BShape&
251 BShape::operator=(const BShape& other)
252 {
253 	if (this != &other) {
254 		Clear();
255 		AddShape(&other);
256 	}
257 
258 	return *this;
259 }
260 
261 
262 #if defined(__cplusplus) && __cplusplus >= 201103L
263 BShape&
264 BShape::operator=(BShape&& other)
265 {
266 	MoveFrom(other);
267 
268 	return *this;
269 }
270 #endif
271 
272 
273 bool
274 BShape::operator==(const BShape& other) const
275 {
276 	if (this == &other)
277 		return true;
278 
279 	shape_data* data = (shape_data*)fPrivateData;
280 	shape_data* otherData = (shape_data*)other.fPrivateData;
281 
282 	if (data->opCount != otherData->opCount)
283 		return false;
284 
285 	if (data->ptCount != otherData->ptCount)
286 		return false;
287 
288 	return memcmp(data->opList, otherData->opList,
289 			data->opCount * sizeof(uint32)) == 0
290 		&& memcmp(data->ptList, otherData->ptList,
291 			data->ptCount * sizeof(BPoint)) == 0;
292 }
293 
294 
295 bool
296 BShape::operator!=(const BShape& other) const
297 {
298 	return !(*this == other);
299 }
300 
301 
302 void
303 BShape::Clear()
304 {
305 	shape_data* data = (shape_data*)fPrivateData;
306 
307 	data->opCount = 0;
308 	data->opSize = 0;
309 	if (data->opList) {
310 		free(data->opList);
311 		data->opList = NULL;
312 	}
313 
314 	data->ptCount = 0;
315 	data->ptSize = 0;
316 	if (data->ptList) {
317 		free(data->ptList);
318 		data->ptList = NULL;
319 	}
320 
321 	fState = 0;
322 	fBuildingOp = 0;
323 }
324 
325 
326 void
327 BShape::MoveFrom(BShape& other)
328 {
329 	fState = other.fState;
330 	fBuildingOp = other.fBuildingOp;
331 
332 	shape_data* data = (shape_data*)fPrivateData;
333 	fPrivateData = other.fPrivateData;
334 	other.fPrivateData = data;
335 
336 	other.Clear();
337 }
338 
339 
340 BRect
341 BShape::Bounds() const
342 {
343 	shape_data* data = (shape_data*)fPrivateData;
344 	return data->DetermineBoundingBox();
345 }
346 
347 
348 BPoint
349 BShape::CurrentPosition() const
350 {
351 	shape_data* data = (shape_data*)fPrivateData;
352 
353 	if (data->ptCount == 0)
354 		return B_ORIGIN;
355 
356 	return data->ptList[data->ptCount - 1];
357 }
358 
359 
360 status_t
361 BShape::AddShape(const BShape* otherShape)
362 {
363 	shape_data* data = (shape_data*)fPrivateData;
364 	shape_data* otherData = (shape_data*)otherShape->fPrivateData;
365 
366 	if (!AllocateOps(otherData->opCount) || !AllocatePts(otherData->ptCount))
367 		return B_NO_MEMORY;
368 
369 	memcpy(data->opList + data->opCount, otherData->opList,
370 		otherData->opCount * sizeof(uint32));
371 	data->opCount += otherData->opCount;
372 
373 	memcpy((void*)(data->ptList + data->ptCount), otherData->ptList,
374 		otherData->ptCount * sizeof(BPoint));
375 	data->ptCount += otherData->ptCount;
376 
377 	fBuildingOp = otherShape->fBuildingOp;
378 
379 	return B_OK;
380 }
381 
382 
383 status_t
384 BShape::MoveTo(BPoint point)
385 {
386 	shape_data* data = (shape_data*)fPrivateData;
387 
388 	// If the last op is MoveTo, replace the point
389 	if (fBuildingOp == OP_MOVETO) {
390 		data->ptList[data->ptCount - 1] = point;
391 		return B_OK;
392 	}
393 
394 	if (!AllocateOps(1) || !AllocatePts(1))
395 		return B_NO_MEMORY;
396 
397 	fBuildingOp = OP_MOVETO;
398 
399 	// Add op
400 	data->opList[data->opCount++] = fBuildingOp;
401 
402 	// Add point
403 	data->ptList[data->ptCount++] = point;
404 
405 	return B_OK;
406 }
407 
408 
409 status_t
410 BShape::LineTo(BPoint point)
411 {
412 	if (!AllocatePts(1))
413 		return B_NO_MEMORY;
414 
415 	shape_data* data = (shape_data*)fPrivateData;
416 
417 	// If the last op is MoveTo, replace the op and set the count
418 	// If the last op is LineTo increase the count
419 	// Otherwise add the op
420 	if (fBuildingOp & OP_LINETO || fBuildingOp == OP_MOVETO) {
421 		fBuildingOp |= OP_LINETO;
422 		fBuildingOp += 1;
423 		data->opList[data->opCount - 1] = fBuildingOp;
424 	} else {
425 		if (!AllocateOps(1))
426 			return B_NO_MEMORY;
427 
428 		fBuildingOp = OP_LINETO + 1;
429 		data->opList[data->opCount++] = fBuildingOp;
430 	}
431 
432 	// Add point
433 	data->ptList[data->ptCount++] = point;
434 
435 	return B_OK;
436 }
437 
438 
439 status_t
440 BShape::BezierTo(BPoint controlPoints[3])
441 {
442 	return BezierTo(controlPoints[0], controlPoints[1], controlPoints[2]);
443 }
444 
445 
446 status_t
447 BShape::BezierTo(const BPoint& control1, const BPoint& control2,
448 	const BPoint& endPoint)
449 {
450 	if (!AllocatePts(3))
451 		return B_NO_MEMORY;
452 
453 	shape_data* data = (shape_data*)fPrivateData;
454 
455 	// If the last op is MoveTo, replace the op and set the count
456 	// If the last op is BezierTo increase the count
457 	// Otherwise add the op
458 	if (fBuildingOp & OP_BEZIERTO || fBuildingOp == OP_MOVETO) {
459 		fBuildingOp |= OP_BEZIERTO;
460 		fBuildingOp += 3;
461 		data->opList[data->opCount - 1] = fBuildingOp;
462 	} else {
463 		if (!AllocateOps(1))
464 			return B_NO_MEMORY;
465 		fBuildingOp = OP_BEZIERTO + 3;
466 		data->opList[data->opCount++] = fBuildingOp;
467 	}
468 
469 	// Add points
470 	data->ptList[data->ptCount++] = control1;
471 	data->ptList[data->ptCount++] = control2;
472 	data->ptList[data->ptCount++] = endPoint;
473 
474 	return B_OK;
475 }
476 
477 
478 status_t
479 BShape::ArcTo(float rx, float ry, float angle, bool largeArc,
480 	bool counterClockWise, const BPoint& point)
481 {
482 	if (!AllocatePts(3))
483 		return B_NO_MEMORY;
484 
485 	shape_data* data = (shape_data*)fPrivateData;
486 
487 	uint32 op;
488 	if (largeArc) {
489 		if (counterClockWise)
490 			op = OP_LARGE_ARC_TO_CCW;
491 		else
492 			op = OP_LARGE_ARC_TO_CW;
493 	} else {
494 		if (counterClockWise)
495 			op = OP_SMALL_ARC_TO_CCW;
496 		else
497 			op = OP_SMALL_ARC_TO_CW;
498 	}
499 
500 	// If the last op is MoveTo, replace the op and set the count
501 	// If the last op is ArcTo increase the count
502 	// Otherwise add the op
503 	if (fBuildingOp == op || fBuildingOp == (op | OP_MOVETO)) {
504 		fBuildingOp |= op;
505 		fBuildingOp += 3;
506 		data->opList[data->opCount - 1] = fBuildingOp;
507 	} else {
508 		if (!AllocateOps(1))
509 			return B_NO_MEMORY;
510 
511 		fBuildingOp = op + 3;
512 		data->opList[data->opCount++] = fBuildingOp;
513 	}
514 
515 	// Add points
516 	data->ptList[data->ptCount++] = BPoint(rx, ry);
517 	data->ptList[data->ptCount++] = BPoint(angle, 0);
518 	data->ptList[data->ptCount++] = point;
519 
520 	return B_OK;
521 }
522 
523 
524 status_t
525 BShape::Close()
526 {
527 	// If the last op is Close or MoveTo, ignore this
528 	if (fBuildingOp == OP_CLOSE || fBuildingOp == OP_MOVETO)
529 		return B_OK;
530 
531 	if (!AllocateOps(1))
532 		return B_NO_MEMORY;
533 
534 	shape_data* data = (shape_data*)fPrivateData;
535 
536 	// ToDo: Decide about that, it's not BeOS compatible
537 	// If there was any op before we can attach the close to it
538 	/*if (fBuildingOp) {
539 		fBuildingOp |= OP_CLOSE;
540 		data->opList[data->opCount - 1] = fBuildingOp;
541 		return B_OK;
542 	}*/
543 
544 	fBuildingOp = OP_CLOSE;
545 	data->opList[data->opCount++] = fBuildingOp;
546 
547 	return B_OK;
548 }
549 
550 
551 //	#pragma mark - BShape private methods
552 
553 
554 status_t
555 BShape::Perform(perform_code code, void* data)
556 {
557 	return BArchivable::Perform(code, data);
558 }
559 
560 
561 //	#pragma mark - BShape FBC methods
562 
563 
564 void BShape::_ReservedShape1() {}
565 void BShape::_ReservedShape2() {}
566 void BShape::_ReservedShape3() {}
567 void BShape::_ReservedShape4() {}
568 
569 
570 //	#pragma mark - BShape private methods
571 
572 
573 void
574 BShape::Private::GetData(int32* opCount, int32* ptCount, uint32** opList,
575 	BPoint** ptList)
576 {
577 	shape_data* data = PrivateData();
578 
579 	*opCount = data->opCount;
580 	*ptCount = data->ptCount;
581 	*opList = data->opList;
582 	*ptList = data->ptList;
583 }
584 
585 
586 void
587 BShape::Private::SetData(int32 opCount, int32 ptCount, const uint32* opList,
588 	const BPoint* ptList)
589 {
590 	fShape.Clear();
591 
592 	if (opCount == 0)
593 		return;
594 
595 	shape_data* data = PrivateData();
596 
597 	if (!fShape.AllocateOps(opCount) || !fShape.AllocatePts(ptCount))
598 		return;
599 
600 	memcpy(data->opList, opList, opCount * sizeof(uint32));
601 	data->opCount = opCount;
602 	fShape.fBuildingOp = data->opList[data->opCount - 1];
603 
604 	if (ptCount > 0) {
605 		memcpy((void*)data->ptList, ptList, ptCount * sizeof(BPoint));
606 		data->ptCount = ptCount;
607 	}
608 }
609 
610 
611 void
612 BShape::InitData()
613 {
614 	fPrivateData = new shape_data;
615 	shape_data* data = (shape_data*)fPrivateData;
616 
617 	fState = 0;
618 	fBuildingOp = 0;
619 
620 	data->opList = NULL;
621 	data->opCount = 0;
622 	data->opSize = 0;
623 	data->ptList = NULL;
624 	data->ptCount = 0;
625 	data->ptSize = 0;
626 }
627 
628 
629 inline bool
630 BShape::AllocateOps(int32 count)
631 {
632 	shape_data* data = (shape_data*)fPrivateData;
633 
634 	int32 newSize = (data->opCount + count + 255) / 256 * 256;
635 	if (data->opSize >= newSize)
636 		return true;
637 
638 	uint32* resizedArray = (uint32*)realloc(data->opList, newSize * sizeof(uint32));
639 	if (resizedArray) {
640 		data->opList = resizedArray;
641 		data->opSize = newSize;
642 		return true;
643 	}
644 	return false;
645 }
646 
647 
648 inline bool
649 BShape::AllocatePts(int32 count)
650 {
651 	shape_data* data = (shape_data*)fPrivateData;
652 
653 	int32 newSize = (data->ptCount + count + 255) / 256 * 256;
654 	if (data->ptSize >= newSize)
655 		return true;
656 
657 	BPoint* resizedArray = (BPoint*)realloc((void*)data->ptList,
658 		newSize * sizeof(BPoint));
659 	if (resizedArray) {
660 		data->ptList = resizedArray;
661 		data->ptSize = newSize;
662 		return true;
663 	}
664 	return false;
665 }
666 
667 
668 //	#pragma mark - BShape binary compatibility methods
669 
670 
671 #if __GNUC__ < 3
672 
673 
674 extern "C" BShape*
675 __6BShapeR6BShape(void* self, BShape& copyFrom)
676 {
677 	return new (self) BShape(copyFrom);
678 		// we need to instantiate the object in the provided memory
679 }
680 
681 
682 extern "C" BRect
683 Bounds__6BShape(BShape* self)
684 {
685 	return self->Bounds();
686 }
687 
688 
689 extern "C" void
690 _ReservedShapeIterator1__14BShapeIterator(BShapeIterator* self)
691 {
692 }
693 
694 
695 #else // __GNUC__ < 3
696 
697 
698 extern "C" void
699 _ZN14BShapeIterator23_ReservedShapeIterator1Ev(BShapeIterator* self)
700 {
701 }
702 
703 
704 #endif // __GNUC__ >= 3
705