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
29BShapeIterator::BShapeIterator()
30{
31}
32
33
34BShapeIterator::~BShapeIterator()
35{
36}
37
38
39status_t
40BShapeIterator::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
86status_t
87BShapeIterator::IterateMoveTo(BPoint* point)
88{
89	return B_OK;
90}
91
92
93status_t
94BShapeIterator::IterateLineTo(int32 lineCount, BPoint* linePoints)
95{
96	return B_OK;
97}
98
99
100status_t
101BShapeIterator::IterateBezierTo(int32 bezierCount, BPoint* bezierPoints)
102{
103	return B_OK;
104}
105
106
107status_t
108BShapeIterator::IterateClose()
109{
110	return B_OK;
111}
112
113
114status_t
115BShapeIterator::IterateArcTo(float& rx, float& ry, float& angle, bool largeArc,
116	bool counterClockWise, BPoint& point)
117{
118	return B_OK;
119}
120
121
122void BShapeIterator::_ReservedShapeIterator2() {}
123void BShapeIterator::_ReservedShapeIterator3() {}
124void BShapeIterator::_ReservedShapeIterator4() {}
125
126
127// #pragma mark -
128
129
130BShape::BShape()
131{
132	InitData();
133}
134
135
136BShape::BShape(const BShape &copyFrom)
137{
138	InitData();
139	AddShape(&copyFrom);
140}
141
142
143BShape::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
175BShape::~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
186status_t
187BShape::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
221BArchivable*
222BShape::Instantiate(BMessage* archive)
223{
224	if (validate_instantiation(archive, "BShape"))
225		return new BShape(archive);
226	else
227		return NULL;
228}
229
230
231BShape&
232BShape::operator=(const BShape& other)
233{
234	if (this != &other) {
235		Clear();
236		AddShape(&other);
237	}
238
239	return *this;
240}
241
242
243bool
244BShape::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
264bool
265BShape::operator!=(const BShape& other) const
266{
267	return !(*this == other);
268}
269
270
271void
272BShape::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
295BRect
296BShape::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
325BPoint
326BShape::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
337status_t
338BShape::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, otherData->opList,
347		otherData->opCount * sizeof(uint32));
348	data->opCount += otherData->opCount;
349
350	memcpy(data->ptList + data->ptCount, 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
360status_t
361BShape::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
386status_t
387BShape::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
415status_t
416BShape::BezierTo(BPoint controlPoints[3])
417{
418	return BezierTo(controlPoints[0], controlPoints[1], controlPoints[2]);
419}
420
421
422status_t
423BShape::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
454status_t
455BShape::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
499status_t
500BShape::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
526status_t
527BShape::Perform(perform_code d, void* arg)
528{
529	return BArchivable::Perform(d, arg);
530}
531
532
533void BShape::_ReservedShape1() {}
534void BShape::_ReservedShape2() {}
535void BShape::_ReservedShape3() {}
536void BShape::_ReservedShape4() {}
537
538
539void
540BShape::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
552void
553BShape::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
579void
580BShape::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
597inline bool
598BShape::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
616inline bool
617BShape::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
641extern "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
649extern "C" BRect
650Bounds__6BShape(BShape* self)
651{
652	return self->Bounds();
653}
654
655
656extern "C" void
657_ReservedShapeIterator1__14BShapeIterator(BShapeIterator* self)
658{
659}
660
661
662#else // __GNUC__ < 3
663
664
665extern "C" void
666_ZN14BShapeIterator23_ReservedShapeIterator1Ev(BShapeIterator* self)
667{
668}
669
670
671#endif // __GNUC__ >= 3
672