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