xref: /haiku/src/kits/interface/Picture.cpp (revision 079c69cbfd7cd3c97baae91332251c8388a8bb02)
1 /*
2  * Copyright 2001-2007, Haiku Inc.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Marc Flerackers (mflerackers@androme.be)
7  */
8 
9 //! BPicture records a series of drawing instructions that can be "replayed" later.
10 
11 
12 #include <AppServerLink.h>
13 #include <PicturePlayer.h>
14 #include <ServerProtocol.h>
15 
16 //#define DEBUG 1
17 #include <Debug.h>
18 #include <ByteOrder.h>
19 #include <List.h>
20 #include <Message.h>
21 #include <Picture.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include <new>
28 
29 struct _BPictureExtent_ {
30 	_BPictureExtent_(const int32 &size = 0);
31 	~_BPictureExtent_();
32 
33 	const void *Data() const { return fNewData; }
34 	status_t ImportData(const void *data, const int32 &size);
35 	status_t ImportData(BDataIO *stream);
36 
37 	int32 Size() const { return fNewSize; }
38 	status_t SetSize(const int32 &size);
39 
40 	bool AddPicture(BPicture *picture) { return fPictures.AddItem(picture); }
41 	void DeletePicture(const int32 &index)
42 			{ delete static_cast<BPicture *>(fPictures.RemoveItem(index)); }
43 	BPicture *PictureAt(const int32 &index)
44 			{ return static_cast<BPicture *>(fPictures.ItemAt(index)); }
45 
46 	int32 CountPictures() const { return fPictures.CountItems(); }
47 	BList *Pictures() { return &fPictures; }
48 
49 private:
50 	void	*fNewData;
51 	int32	fNewSize;
52 
53 	BList	fPictures;	// In R5 this is a BArray<BPicture*> which is completely inline.
54 };
55 
56 
57 struct picture_header {
58 	int32 magic1; // ?
59 	int32 magic2; // ?
60 };
61 
62 
63 BPicture::BPicture()
64 	:
65 	token(-1),
66 	extent(NULL),
67 	usurped(NULL)
68 {
69 	_InitData();
70 }
71 
72 
73 BPicture::BPicture(const BPicture &otherPicture)
74 	:
75 	token(-1),
76 	extent(NULL),
77 	usurped(NULL)
78 {
79 	_InitData();
80 
81 	if (otherPicture.token != -1) {
82 		BPrivate::AppServerLink link;
83 		link.StartMessage(AS_CLONE_PICTURE);
84 		link.Attach<int32>(otherPicture.token);
85 
86 		status_t status = B_ERROR;
87 		if (link.FlushWithReply(status) == B_OK
88 			&& status == B_OK)
89 			link.Read<int32>(&token);
90 		if (status < B_OK)
91 			return;
92 	}
93 
94 	if (otherPicture.extent->Size() > 0) {
95 		extent->ImportData(otherPicture.extent->Data(), otherPicture.extent->Size());
96 
97 		for (int32 i = 0; i < otherPicture.extent->CountPictures(); i++) {
98 			BPicture *picture = new BPicture(*otherPicture.extent->PictureAt(i));
99 			extent->AddPicture(picture);
100 		}
101 	}
102 }
103 
104 
105 BPicture::BPicture(BMessage *archive)
106 	:
107 	token(-1),
108 	extent(NULL),
109 	usurped(NULL)
110 {
111 	_InitData();
112 
113 	int32 version;
114 	if (archive->FindInt32("_ver", &version) != B_OK)
115 		version = 0;
116 
117 	int8 endian;
118 	if (archive->FindInt8("_endian", &endian) != B_OK)
119 		endian = 0;
120 
121 	const void *data;
122 	int32 size;
123 	if (archive->FindData("_data", B_RAW_TYPE, &data, (ssize_t*)&size) != B_OK)
124 		return;
125 
126 	// Load sub pictures
127 	BMessage picMsg;
128 	int32 i = 0;
129 	while (archive->FindMessage("piclib", i++, &picMsg) == B_OK) {
130 		BPicture *pic = new BPicture(&picMsg);
131 		extent->AddPicture(pic);
132 	}
133 
134 	if (version == 0) {
135 		// TODO: For now. We'll see if it's worth to support old style data
136 		debugger("old style BPicture data is not supported");
137 	} else if (version == 1) {
138 		extent->ImportData(data, size);
139 
140 //		swap_data(extent->fNewData, extent->fNewSize);
141 
142 		if (extent->Size() > 0)
143 			_AssertServerCopy();
144 	}
145 
146 	// Do we just free the data now?
147 	if (extent->Size() > 0)
148 		extent->SetSize(0);
149 
150 	// What with the sub pictures?
151 	for (i = extent->CountPictures() - 1; i >= 0; i--)
152 		extent->DeletePicture(i);
153 }
154 
155 
156 BPicture::BPicture(const void *data, int32 size)
157 {
158 	_InitData();
159 	// TODO: For now. We'll see if it's worth to support old style data
160 	debugger("old style BPicture data is not supported");
161 }
162 
163 
164 void
165 BPicture::_InitData()
166 {
167 	token = -1;
168 	usurped = NULL;
169 
170 	extent = new (std::nothrow) _BPictureExtent_;
171 }
172 
173 
174 BPicture::~BPicture()
175 {
176 	_DisposeData();
177 }
178 
179 
180 void
181 BPicture::_DisposeData()
182 {
183 	if (token != -1) {
184 		BPrivate::AppServerLink link;
185 
186 		link.StartMessage(AS_DELETE_PICTURE);
187 		link.Attach<int32>(token);
188 		link.Flush();
189 		SetToken(-1);
190 	}
191 
192 	delete extent;
193 	extent = NULL;
194 }
195 
196 
197 BArchivable *
198 BPicture::Instantiate(BMessage *archive)
199 {
200 	if (validate_instantiation(archive, "BPicture"))
201 		return new BPicture(archive);
202 
203 	return NULL;
204 }
205 
206 
207 status_t
208 BPicture::Archive(BMessage *archive, bool deep) const
209 {
210 	if (!const_cast<BPicture*>(this)->_AssertLocalCopy())
211 		return B_ERROR;
212 
213 	status_t err = BArchivable::Archive(archive, deep);
214 	if (err != B_OK)
215 		return err;
216 
217 	err = archive->AddInt32("_ver", 1);
218 	if (err != B_OK)
219 		return err;
220 
221 	err = archive->AddInt8("_endian", B_HOST_IS_BENDIAN);
222 	if (err != B_OK)
223 		return err;
224 
225 	err = archive->AddData("_data", B_RAW_TYPE, extent->Data(), extent->Size());
226 	if (err != B_OK)
227 		return err;
228 
229 	for (int32 i = 0; i < extent->CountPictures(); i++) {
230 		BMessage picMsg;
231 
232 		extent->PictureAt(i)->Archive(&picMsg, deep);
233 		err = archive->AddMessage("piclib", &picMsg);
234 		if (err != B_OK)
235 			break;
236 	}
237 
238 	return err;
239 }
240 
241 
242 status_t
243 BPicture::Perform(perform_code d, void *arg)
244 {
245 	return BArchivable::Perform(d, arg);
246 }
247 
248 
249 status_t
250 BPicture::Play(void **callBackTable, int32 tableEntries, void *user)
251 {
252 	if (!_AssertLocalCopy())
253 		return B_ERROR;
254 
255 	BPrivate::PicturePlayer player(extent->Data(), extent->Size(), extent->Pictures());
256 
257 	return player.Play(callBackTable, tableEntries, user);
258 }
259 
260 
261 status_t
262 BPicture::Flatten(BDataIO *stream)
263 {
264 	// TODO: what about endianess?
265 
266 	if (!_AssertLocalCopy())
267 		return B_ERROR;
268 
269 	const picture_header header = { 2, 0 };
270 	ssize_t bytesWritten = stream->Write(&header, sizeof(header));
271 	if (bytesWritten < B_OK)
272 		return bytesWritten;
273 	if (bytesWritten != (ssize_t)sizeof(header))
274 		return B_IO_ERROR;
275 
276 	int32 count = extent->CountPictures();
277 	bytesWritten = stream->Write(&count, sizeof(count));
278 	if (bytesWritten < B_OK)
279 		return bytesWritten;
280 	if (bytesWritten != (ssize_t)sizeof(count))
281 		return B_IO_ERROR;
282 
283 	for (int32 i = 0; i < count; i++) {
284 		status_t status = extent->PictureAt(i)->Flatten(stream);
285 		if (status < B_OK)
286 			return status;
287 	}
288 
289 	int32 size = extent->Size();
290 	bytesWritten = stream->Write(&size, sizeof(size));
291 	if (bytesWritten < B_OK)
292 		return bytesWritten;
293 	if (bytesWritten != (ssize_t)sizeof(size))
294 		return B_IO_ERROR;
295 
296 	bytesWritten = stream->Write(extent->Data(), size);
297 	if (bytesWritten < B_OK)
298 		return bytesWritten;
299 	if (bytesWritten != size)
300 		return B_IO_ERROR;
301 
302 	return B_OK;
303 }
304 
305 
306 status_t
307 BPicture::Unflatten(BDataIO *stream)
308 {
309 	// TODO: clear current picture data?
310 
311 	picture_header header;
312 	ssize_t bytesRead = stream->Read(&header, sizeof(header));
313 	if (bytesRead < B_OK)
314 		return bytesRead;
315 	if (bytesRead != (ssize_t)sizeof(header)
316 		|| header.magic1 != 2 || header.magic2 != 0)
317 		return B_BAD_TYPE;
318 
319 	int32 count = 0;
320 	bytesRead = stream->Read(&count, sizeof(count));
321 	if (bytesRead < B_OK)
322 		return bytesRead;
323 	if (bytesRead != (ssize_t)sizeof(count))
324 		return B_BAD_DATA;
325 
326 	for (int32 i = 0; i < count; i++) {
327 		BPicture* picture = new BPicture;
328 		status_t status = picture->Unflatten(stream);
329 		if (status < B_OK)
330 			return status;
331 
332 		extent->AddPicture(picture);
333 	}
334 
335 	status_t status = extent->ImportData(stream);
336 	if (status < B_OK)
337 		return status;
338 
339 //	swap_data(extent->fNewData, extent->fNewSize);
340 
341 	if (!_AssertServerCopy())
342 		return B_ERROR;
343 
344 	// Data is now kept server side, remove the local copy
345 	if (extent->Data() != NULL)
346 		extent->SetSize(0);
347 
348 	return status;
349 }
350 
351 
352 
353 void
354 BPicture::_ImportData(const void *data, int32 size, BPicture **subs,
355 	int32 subCount)
356 {
357 	/*
358 	if (data == NULL || size == 0)
359 		return;
360 
361 	BPrivate::AppServerLink link;
362 
363 	link.StartMessage(AS_CREATE_PICTURE);
364 	link.Attach<int32>(subCount);
365 
366 	for (int32 i = 0; i < subCount; i++)
367 		link.Attach<int32>(subs[i]->token);
368 
369 	link.Attach<int32>(size);
370 	link.Attach(data, size);
371 
372 	status_t status = B_ERROR;
373 	if (link.FlushWithReply(status) == B_OK
374 		&& status == B_OK)
375 		link.Read<int32>(&token);*/
376 }
377 
378 
379 void
380 BPicture::_ImportOldData(const void *data, int32 size)
381 {
382 	// TODO: We don't support old data for now
383 }
384 
385 
386 void
387 BPicture::SetToken(int32 _token)
388 {
389 	token = _token;
390 }
391 
392 
393 bool
394 BPicture::_AssertLocalCopy()
395 {
396 	if (extent->Data() != NULL)
397 		return true;
398 
399 	if (token == -1)
400 		return false;
401 
402 	return _Download() == B_OK;
403 }
404 
405 
406 bool
407 BPicture::_AssertOldLocalCopy()
408 {
409 	// TODO: We don't support old data for now
410 
411 	return false;
412 }
413 
414 
415 bool
416 BPicture::_AssertServerCopy()
417 {
418 	if (token != -1)
419 		return true;
420 
421 	if (extent->Data() == NULL)
422 		return false;
423 
424 	for (int32 i = 0; i < extent->CountPictures(); i++)
425 		extent->PictureAt(i)->_AssertServerCopy();
426 
427 	return _Upload() == B_OK;
428 }
429 
430 
431 status_t
432 BPicture::_Upload()
433 {
434 	ASSERT((token == -1));
435 	ASSERT((extent->Data() != NULL));
436 
437 	BPrivate::AppServerLink link;
438 
439 	link.StartMessage(AS_CREATE_PICTURE);
440 	link.Attach<int32>(extent->CountPictures());
441 
442 	for (int32 i = 0; i < extent->CountPictures(); i++) {
443 		BPicture *picture = extent->PictureAt(i);
444 		if (picture)
445 			link.Attach<int32>(picture->token);
446 	}
447 	link.Attach<int32>(extent->Size());
448 	link.Attach(extent->Data(), extent->Size());
449 
450 	status_t status = B_ERROR;
451 	if (link.FlushWithReply(status) == B_OK
452 		&& status == B_OK)
453 		link.Read<int32>(&token);
454 
455 	return status;
456 }
457 
458 
459 status_t
460 BPicture::_Download()
461 {
462 	ASSERT((extent->Data() == NULL));
463 	ASSERT((token != -1));
464 
465 	BPrivate::AppServerLink link;
466 
467 	link.StartMessage(AS_DOWNLOAD_PICTURE);
468 	link.Attach<int32>(token);
469 
470 	status_t status = B_ERROR;
471 	if (link.FlushWithReply(status) == B_OK && status == B_OK) {
472 		int32 count = 0;
473 		link.Read<int32>(&count);
474 
475 		// Read sub picture tokens
476 		for (int32 i = 0; i < count; i++) {
477 			BPicture *pic = new BPicture;
478 			link.Read<int32>(&pic->token);
479 			extent->AddPicture(pic);
480 		}
481 
482 		int32 size;
483 		link.Read<int32>(&size);
484 		status = extent->SetSize(size);
485 		if (status == B_OK)
486 			link.Read(const_cast<void *>(extent->Data()), size);
487 	}
488 
489 	return status;
490 }
491 
492 
493 const void *
494 BPicture::Data() const
495 {
496 	if (extent->Data() == NULL)
497 		const_cast<BPicture*>(this)->_AssertLocalCopy();
498 
499 	return extent->Data();
500 }
501 
502 
503 int32
504 BPicture::DataSize() const
505 {
506 	if (extent->Data() == NULL)
507 		const_cast<BPicture*>(this)->_AssertLocalCopy();
508 
509 	return extent->Size();
510 }
511 
512 
513 void
514 BPicture::Usurp(BPicture *lameDuck)
515 {
516 	_DisposeData();
517 
518 	// Reinitializes the BPicture
519 	_InitData();
520 
521 	// Do the Usurping
522 	usurped = lameDuck;
523 }
524 
525 
526 BPicture *
527 BPicture::StepDown()
528 {
529 	BPicture *lameDuck = usurped;
530 	usurped = NULL;
531 
532 	return lameDuck;
533 }
534 
535 
536 void BPicture::_ReservedPicture1() {}
537 void BPicture::_ReservedPicture2() {}
538 void BPicture::_ReservedPicture3() {}
539 
540 
541 BPicture &
542 BPicture::operator=(const BPicture &)
543 {
544 	return *this;
545 }
546 
547 
548 // _BPictureExtent_
549 _BPictureExtent_::_BPictureExtent_(const int32 &size)
550 	:
551 	fNewData(NULL),
552 	fNewSize(0)
553 {
554 	SetSize(size);
555 }
556 
557 
558 _BPictureExtent_::~_BPictureExtent_()
559 {
560 	free(fNewData);
561 	for (int32 i = 0; i < fPictures.CountItems(); i++)
562 		delete static_cast<BPicture *>(fPictures.ItemAtFast(i));
563 }
564 
565 
566 status_t
567 _BPictureExtent_::ImportData(const void *data, const int32 &size)
568 {
569 	if (data == NULL)
570 		return B_BAD_VALUE;
571 
572 	status_t status = B_OK;
573 	if (Size() != size)
574 		status = SetSize(size);
575 
576 	if (status == B_OK)
577 		memcpy(fNewData, data, size);
578 
579 	return status;
580 }
581 
582 
583 status_t
584 _BPictureExtent_::ImportData(BDataIO *stream)
585 {
586 	if (stream == NULL)
587 		return B_BAD_VALUE;
588 
589 	int32 size;
590 	ssize_t bytesRead = stream->Read(&size, sizeof(size));
591 	if (bytesRead < B_OK)
592 		return bytesRead;
593 	if (bytesRead != (ssize_t)sizeof(size))
594 		return B_IO_ERROR;
595 
596 	status_t status = B_OK;
597 	if (Size() != size)
598 		status = SetSize(size);
599 
600 	if (status < B_OK)
601 		return status;
602 
603 	bytesRead = stream->Read(fNewData, size);
604 	if (bytesRead < B_OK)
605 		return bytesRead;
606 	if (bytesRead != (ssize_t)size)
607 		return B_IO_ERROR;
608 
609 	return B_OK;
610 }
611 
612 
613 status_t
614 _BPictureExtent_::SetSize(const int32 &size)
615 {
616 	if (size < 0)
617 		return B_BAD_VALUE;
618 
619 	if (size == fNewSize)
620 		return B_OK;
621 
622 	if (size == 0) {
623 		free(fNewData);
624 		fNewData = NULL;
625 	} else {
626 		void *data = realloc(fNewData, size);
627 		if (data == NULL)
628 			return B_NO_MEMORY;
629 		fNewData = data;
630 	}
631 
632 	fNewSize = size;
633 	return B_OK;
634 }
635