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