xref: /haiku/src/kits/interface/Shape.cpp (revision d29f332ad4210e2e2b5ef42231531dd937ebc52e)
1 /*
2  * Copyright (c) 2001-2005, 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  *
9  * Description:
10  *		BShape encapsulates a Postscript-style "path"
11  */
12 
13 #include <stdlib.h>
14 
15 
16 #include <Shape.h>
17 #include <Point.h>
18 #include <Rect.h>
19 #include <Errors.h>
20 #include <Message.h>
21 
22 
23 // NOTE: changing these defines will break Painter,
24 // currently located in src/servers/app/drawing/Painter/Painter.cpp
25 #define OP_LINETO		0x10000000
26 #define OP_BEZIERTO		0x20000000
27 #define OP_CLOSE		0x40000000
28 #define OP_MOVETO		0x80000000
29 
30 
31 struct shape_data {
32 	uint32	*opList;
33 	int32	opCount;
34 	int32	opSize;
35 	int32	opBlockSize;
36 	BPoint	*ptList;
37 	int32	ptCount;
38 	int32	ptSize;
39 	int32	ptBlockSize;
40 };
41 
42 
43 BShapeIterator::BShapeIterator()
44 {
45 }
46 
47 
48 BShapeIterator::~BShapeIterator()
49 {
50 }
51 
52 
53 status_t
54 BShapeIterator::Iterate(BShape *shape)
55 {
56 	shape_data *data = (shape_data*)shape->fPrivateData;
57 	BPoint *points = data->ptList;
58 
59 	for (int32 i = 0; i < data->opCount; i++) {
60 		switch (data->opList[i] & 0xFF000000) {
61 			case OP_LINETO: {
62 				int32 count = data->opList[i] & 0x00FFFFFF;
63 				IterateLineTo(count, points);
64 				points += count;
65 				break;
66 			}
67 			case (OP_MOVETO | OP_LINETO): {
68 				int32 count = data->opList[i] & 0x00FFFFFF;
69 				IterateMoveTo(points);
70 				points++;
71 				IterateLineTo(count, points);
72 				points += count;
73 				break;
74 			}
75 			case OP_BEZIERTO: {
76 				int32 count = data->opList[i] & 0x00FFFFFF;
77 				IterateBezierTo(count, points);
78 				points += count;
79 				break;
80 			}
81 			case (OP_MOVETO | OP_BEZIERTO): {
82 				int32 count = data->opList[i] & 0x00FFFFFF;
83 				IterateMoveTo(points);
84 				points++;
85 				IterateBezierTo(count, points);
86 				points += count;
87 				break;
88 			}
89 			case OP_CLOSE:
90 			case OP_CLOSE | OP_LINETO | OP_BEZIERTO: {
91 				IterateClose();
92 				break;
93 			}
94 		}
95 	}
96 
97 	return B_OK;
98 }
99 
100 
101 status_t
102 BShapeIterator::IterateBezierTo(int32 bezierCount,
103 										 BPoint *bezierPoints)
104 {
105 	return B_OK;
106 }
107 
108 
109 status_t
110 BShapeIterator::IterateClose()
111 {
112 	return B_OK;
113 }
114 
115 
116 status_t
117 BShapeIterator::IterateLineTo(int32 lineCount, BPoint *linePoints)
118 {
119 	return B_OK;
120 }
121 
122 
123 status_t
124 BShapeIterator::IterateMoveTo ( BPoint *point )
125 {
126 	return B_OK;
127 }
128 
129 
130 void BShapeIterator::_ReservedShapeIterator1() {}
131 void BShapeIterator::_ReservedShapeIterator2() {}
132 void BShapeIterator::_ReservedShapeIterator3() {}
133 void BShapeIterator::_ReservedShapeIterator4() {}
134 
135 
136 BShape::BShape()
137 {
138 	InitData();
139 }
140 
141 
142 BShape::BShape(const BShape &copyFrom)
143 {
144 	InitData();
145 	AddShape(&copyFrom);
146 }
147 
148 
149 BShape::BShape(BMessage *archive)
150 	:	BArchivable(archive)
151 {
152 	InitData();
153 
154 	shape_data *data = (shape_data*)fPrivateData;
155 	ssize_t size;
156 	int32 i = 0;
157 
158 	const uint32 *opPtr;
159 
160 	while (archive->FindData("ops", B_INT32_TYPE, i++, (const void**)&opPtr, &size) == B_OK) {
161 		if (data->opSize < data->opCount + 1) {
162 			int32 new_size = ((data->opCount + data->opBlockSize) /
163 				data->opBlockSize) * data->opBlockSize;
164 			data->opList = (uint32*)realloc(data->opList, new_size * sizeof(uint32));
165 			data->opSize = new_size;
166 		}
167 
168 		data->opList[data->opCount++] = *opPtr;
169 	}
170 
171 	const BPoint *ptPtr;
172 
173 	while (archive->FindData("pts", B_POINT_TYPE, i++, (const void**)&ptPtr, &size) == B_OK) {
174 		if (data->ptSize < data->ptCount + 1) {
175 			int32 new_size = ((data->ptCount + data->ptBlockSize) /
176 				data->ptBlockSize) * data->ptBlockSize;
177 			data->ptList = (BPoint*)realloc(data->ptList,
178 				new_size * sizeof(BPoint));
179 			data->ptSize = new_size;
180 		}
181 
182 		data->ptList[data->ptCount++] = *ptPtr;
183 	}
184 }
185 
186 
187 BShape::~BShape()
188 {
189 	shape_data *data = (shape_data*)fPrivateData;
190 
191 	free(data->opList);
192 	free(data->ptList);
193 
194 	delete (shape_data*)fPrivateData;
195 }
196 
197 
198 status_t
199 BShape::Archive(BMessage *archive, bool deep) const
200 {
201 	status_t err = BArchivable::Archive(archive, deep);
202 	int32 i;
203 
204 	if (err != B_OK)
205 		return err;
206 
207 	shape_data *data = (shape_data*)fPrivateData;
208 
209 	// If no valid shape data, return
210 	if (data->opCount == 0 || data->ptCount == 0)
211 		return err;
212 
213 	// Avoids allocation for each point
214 	archive->AddData("pts", B_POINT_TYPE, data->ptList, sizeof(BPoint), true,
215 		data->ptCount);
216 
217 	for (i = 1; i < data->ptCount; i++)
218 		archive->AddPoint("pts", data->ptList[i]);
219 
220 	// Avoids allocation for each op
221 	archive->AddData("ops", B_INT32_TYPE, data->opList, sizeof(int32), true,
222 			data->opCount);
223 
224 	for (i = 1; i < data->opCount; i++)
225 		archive->AddInt32("ops", data->opList[i]);
226 
227 	archive->AddInt32("ops", fBuildingOp);
228 
229 	return err;
230 }
231 
232 
233 BArchivable*
234 BShape::Instantiate(BMessage *archive)
235 {
236 	if (validate_instantiation(archive, "BShape"))
237 		return new BShape(archive);
238 	else
239 		return NULL;
240 }
241 
242 
243 void
244 BShape::Clear()
245 {
246 	shape_data *data = (shape_data*)fPrivateData;
247 
248 	if (data->opList) {
249 		free(data->opList);
250 		data->opList = NULL;
251 		data->opCount = 0;
252 		data->opSize = 0;
253 	}
254 
255 	if (data->ptList) {
256 		free(data->ptList);
257 		data->ptList = NULL;
258 		data->ptCount = 0;
259 		data->ptSize = 0;
260 	}
261 
262 	fState = 0;
263 	fBuildingOp = 0;
264 }
265 
266 
267 BRect
268 BShape::Bounds() const
269 {
270 	shape_data *data = (shape_data*)fPrivateData;
271 	BRect bounds;
272 
273 	if (data->ptCount == 0)
274 		return bounds;
275 
276 	bounds.left = data->ptList[0].x;
277 	bounds.top = data->ptList[0].y;
278 	bounds.right = data->ptList[0].x;
279 	bounds.bottom = data->ptList[0].y;
280 
281 	for (int32 i = 1; i < data->ptCount; i++)
282 	{
283 		if (bounds.left > data->ptList[0].x)
284 			bounds.left = data->ptList[0].x;
285 		if (bounds.top > data->ptList[0].y)
286 			bounds.top = data->ptList[0].y;
287 		if (bounds.right < data->ptList[0].x)
288 			bounds.right = data->ptList[0].x;
289 		if (bounds.bottom < data->ptList[0].y)
290 			bounds.bottom = data->ptList[0].y;
291 	}
292 
293 	return bounds;
294 }
295 
296 
297 status_t
298 BShape::AddShape(const BShape *otherShape)
299 {
300 	shape_data *data = (shape_data*)fPrivateData;
301 	shape_data *otherData = (shape_data*)otherShape->fPrivateData;
302 
303 	if (data->opSize < data->opCount + otherData->opCount)
304 	{
305 		int32 new_size = ((data->opCount + otherData->opBlockSize) /
306 			data->opBlockSize) * data->opBlockSize;
307 		data->opList = (uint32*)realloc(data->opList, new_size * sizeof(uint32));
308 		data->opSize = new_size;
309 	}
310 
311 	memcpy(data->opList + data->opCount * sizeof(uint32), otherData->opList,
312 		otherData->opCount * sizeof(uint32));
313 	data->opCount += otherData->opCount;
314 
315 	if (data->ptSize < data->ptCount + otherData->ptCount)
316 	{
317 		int32 new_size = ((data->ptCount + otherData->ptBlockSize) /
318 			data->ptBlockSize) * data->ptBlockSize;
319 		data->ptList = (BPoint*)realloc(data->ptList, new_size * sizeof(uint32));
320 		data->ptSize = new_size;
321 	}
322 
323 	memcpy(data->ptList + data->ptCount * sizeof(BPoint), otherData->ptList,
324 		otherData->ptCount * sizeof(BPoint));
325 	data->ptCount += otherData->ptCount;
326 
327 	fBuildingOp = otherShape->fBuildingOp;
328 
329 	return B_OK;
330 }
331 
332 
333 status_t
334 BShape::MoveTo(BPoint point)
335 {
336 	shape_data *data = (shape_data*)fPrivateData;
337 
338 	// If the last op is MoveTo, replace the point
339 	// If there was a previous op, add the op
340 	if (fBuildingOp == OP_MOVETO) {
341 		data->ptList[data->ptCount - 1] = point;
342 
343 		return B_OK;
344 	}
345 
346 	// BuildingOp is MoveTo
347 	fBuildingOp = OP_MOVETO;
348 
349 	// Add point
350 	if (data->ptSize < data->ptCount + 1) {
351 		int32 new_size = ((data->ptCount + data->ptBlockSize) /
352 			data->ptBlockSize) * data->ptBlockSize;
353 		data->ptList = (BPoint*)realloc(data->ptList, new_size * sizeof(BPoint));
354 		data->ptSize = new_size;
355 	}
356 
357 	data->ptList[data->ptCount++] = point;
358 
359 	return B_OK;
360 }
361 
362 
363 status_t
364 BShape::LineTo(BPoint point)
365 {
366 	shape_data *data = (shape_data*)fPrivateData;
367 
368 	// If the last op is MoveTo, replace the op and set the count
369 	// If the last op is LineTo increase the count
370 	// If there was a previous op, add the op
371 	bool newOp = true;
372 	if (fBuildingOp == OP_MOVETO) {
373 		fBuildingOp = OP_LINETO | OP_MOVETO;
374 	} else if (fBuildingOp & OP_LINETO) {
375 		newOp = false;
376 	} else {
377 		fBuildingOp = OP_LINETO;
378 	}
379 	fBuildingOp++;
380 
381 	if (newOp) {
382 		if (data->opSize < data->opCount + 1) {
383 			int32 new_size = ((data->opCount + data->opBlockSize) /
384 				data->opBlockSize) * data->opBlockSize;
385 			data->opList = (uint32*)realloc(data->opList, new_size * sizeof(uint32));
386 			data->opSize = new_size;
387 		}
388 		data->opCount++;
389 	}
390 	data->opList[data->opCount - 1] = fBuildingOp;
391 
392 	// Add point
393 	if (data->ptSize < data->ptCount + 1) {
394 		int32 new_size = ((data->ptCount + data->ptBlockSize) /
395 			data->ptBlockSize) * data->ptBlockSize;
396 		data->ptList = (BPoint*)realloc(data->ptList, new_size * sizeof(BPoint));
397 		data->ptSize = new_size;
398 	}
399 
400 	data->ptList[data->ptCount++] = point;
401 
402 	return B_OK;
403 }
404 
405 
406 status_t
407 BShape::BezierTo(BPoint controlPoints[3])
408 {
409 	shape_data *data = (shape_data*)fPrivateData;
410 
411 	// If the last op is MoveTo, replace the op and set the count
412 	// If the last op is BezierTo increase the count
413 	// If there was a previous op, add the op
414 	bool newOp = true;
415 	if (fBuildingOp == OP_MOVETO) {
416 		fBuildingOp = OP_BEZIERTO | OP_MOVETO;
417 	} else if (fBuildingOp & OP_BEZIERTO) {
418 		newOp = false;
419 	} else {
420 		fBuildingOp = OP_BEZIERTO;
421 	}
422 	fBuildingOp += 3;
423 
424 	if (newOp) {
425 		if (data->opSize < data->opCount + 1) {
426 			int32 new_size = ((data->opCount + data->opBlockSize) /
427 				data->opBlockSize) * data->opBlockSize;
428 			data->opList = (uint32*)realloc(data->opList, new_size * sizeof(uint32));
429 			data->opSize = new_size;
430 		}
431 		data->opCount++;
432 	}
433 	data->opList[data->opCount - 1] = fBuildingOp;
434 
435 
436 	// Add points
437 	if (data->ptSize < data->ptCount + 3)
438 	{
439 		int32 new_size = ((data->ptCount + data->ptBlockSize) /
440 			data->ptBlockSize) * data->ptBlockSize;
441 		data->ptList = (BPoint*)realloc(data->ptList, new_size * sizeof(BPoint));
442 		data->ptSize = new_size;
443 	}
444 
445 	data->ptList[data->ptCount++] = controlPoints[0];
446 	data->ptList[data->ptCount++] = controlPoints[1];
447 	data->ptList[data->ptCount++] = controlPoints[2];
448 
449 	return B_OK;
450 }
451 
452 
453 status_t
454 BShape::Close()
455 {
456 	shape_data *data = (shape_data*)fPrivateData;
457 
458 	// If there was a previous line/bezier op, add the op
459 	if (fBuildingOp & (OP_LINETO | OP_BEZIERTO)) {
460 		if (data->opSize < data->opCount + 1) {
461 			int32 new_size = ((data->opCount + data->opBlockSize) /
462 				data->opBlockSize) * data->opBlockSize;
463 			data->opList = (uint32*)realloc(data->opList, new_size * sizeof(uint32));
464 			data->opSize = new_size;
465 		}
466 
467 		data->opList[data->opCount++] = fBuildingOp;
468 		fBuildingOp = OP_CLOSE;
469 	}
470 
471 	return B_OK;
472 }
473 
474 
475 status_t
476 BShape::Perform(perform_code d, void *arg)
477 {
478 	return BArchivable::Perform(d, arg);
479 }
480 
481 
482 void BShape::_ReservedShape1() {}
483 void BShape::_ReservedShape2() {}
484 void BShape::_ReservedShape3() {}
485 void BShape::_ReservedShape4() {}
486 
487 
488 void
489 BShape::GetData(int32 *opCount, int32 *ptCount, uint32 **opList,
490 					 BPoint **ptList)
491 {
492 	shape_data *data = (shape_data*)fPrivateData;
493 
494 	*opCount = data->opCount;
495 	*ptCount = data->ptCount;
496 	*opList = data->opList;
497 	*ptList = data->ptList;
498 }
499 
500 
501 void
502 BShape::SetData(int32 opCount, int32 ptCount, uint32 *opList,
503 				BPoint *ptList)
504 {
505 	shape_data *data = (shape_data*)fPrivateData;
506 
507 	if (data->opSize < opCount) {
508 		int32 new_size = ((opCount + data->opBlockSize) /
509 			data->opBlockSize) * data->opBlockSize;
510 
511 		data->opList = (uint32*)realloc(data->opList, new_size * sizeof(uint32));
512 		data->opSize = new_size;
513 	}
514 
515 	memcpy(data->opList, opList, opCount * sizeof(uint32));
516 	data->opCount = opCount;
517 
518 	if (data->ptSize < ptCount) {
519 		int32 new_size = ((ptCount + data->ptBlockSize) /
520 			data->ptBlockSize) * data->ptBlockSize;
521 
522 		data->ptList = (BPoint*)realloc(data->ptList, new_size * sizeof(BPoint));
523 		data->ptSize = new_size;
524 	}
525 
526 	memcpy(data->ptList, ptList, ptCount * sizeof(BPoint));
527 	data->ptCount = ptCount;
528 }
529 
530 
531 void
532 BShape::InitData()
533 {
534 	fPrivateData = new shape_data;
535 	shape_data *data = (shape_data*)fPrivateData;
536 
537 	fState = 0;
538 	fBuildingOp = 0;
539 
540 	data->opList = NULL;
541 	data->opCount = 0;
542 	data->opSize = 0;
543 	data->opBlockSize = 255;
544 	data->ptList = NULL;
545 	data->ptCount = 0;
546 	data->ptSize = 0;
547 	data->ptBlockSize = 255;
548 }
549 
550 
551 //	#pragma mark -
552 //	R4.5 compatibility
553 
554 
555 #if __GNUC__ < 3
556 
557 extern "C" BRect
558 Bounds__6BShape(BShape *self)
559 {
560 	return self->Bounds();
561 }
562 
563 // ToDo: Add those:
564 //		BShape(BShape &copyFrom);
565 //		status_t AddShape(BShape *other);
566 
567 #endif	// __GNUC__ < 3
568