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