xref: /haiku/src/kits/interface/Picture.cpp (revision 16d5c24e533eb14b7b8a99ee9f3ec9ba66335b1e)
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 	fToken(-1),
66 	fExtent(NULL),
67 	fUsurped(NULL)
68 {
69 	_InitData();
70 }
71 
72 
73 BPicture::BPicture(const BPicture &otherPicture)
74 	:
75 	fToken(-1),
76 	fExtent(NULL),
77 	fUsurped(NULL)
78 {
79 	_InitData();
80 
81 	if (otherPicture.fToken != -1) {
82 		BPrivate::AppServerLink link;
83 		link.StartMessage(AS_CLONE_PICTURE);
84 		link.Attach<int32>(otherPicture.fToken);
85 
86 		status_t status = B_ERROR;
87 		if (link.FlushWithReply(status) == B_OK
88 			&& status == B_OK)
89 			link.Read<int32>(&fToken);
90 		if (status < B_OK)
91 			return;
92 	}
93 
94 	if (otherPicture.fExtent->Size() > 0) {
95 		fExtent->ImportData(otherPicture.fExtent->Data(), otherPicture.fExtent->Size());
96 
97 		for (int32 i = 0; i < otherPicture.fExtent->CountPictures(); i++) {
98 			BPicture *picture = new BPicture(*otherPicture.fExtent->PictureAt(i));
99 			fExtent->AddPicture(picture);
100 		}
101 	}
102 }
103 
104 
105 BPicture::BPicture(BMessage *archive)
106 	:
107 	fToken(-1),
108 	fExtent(NULL),
109 	fUsurped(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 		fExtent->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 		fExtent->ImportData(data, size);
139 
140 //		swap_data(fExtent->fNewData, fExtent->fNewSize);
141 
142 		if (fExtent->Size() > 0)
143 			_AssertServerCopy();
144 	}
145 
146 	// Do we just free the data now?
147 	if (fExtent->Size() > 0)
148 		fExtent->SetSize(0);
149 
150 	// What with the sub pictures?
151 	for (i = fExtent->CountPictures() - 1; i >= 0; i--)
152 		fExtent->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 	fToken = -1;
168 	fUsurped = NULL;
169 
170 	fExtent = new (std::nothrow) _BPictureExtent_;
171 }
172 
173 
174 BPicture::~BPicture()
175 {
176 	_DisposeData();
177 }
178 
179 
180 void
181 BPicture::_DisposeData()
182 {
183 	if (fToken != -1) {
184 		BPrivate::AppServerLink link;
185 
186 		link.StartMessage(AS_DELETE_PICTURE);
187 		link.Attach<int32>(fToken);
188 		link.Flush();
189 		SetToken(-1);
190 	}
191 
192 	delete fExtent;
193 	fExtent = 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, fExtent->Data(), fExtent->Size());
226 	if (err != B_OK)
227 		return err;
228 
229 	for (int32 i = 0; i < fExtent->CountPictures(); i++) {
230 		BMessage picMsg;
231 
232 		fExtent->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(fExtent->Data(), fExtent->Size(), fExtent->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 = fExtent->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 = fExtent->PictureAt(i)->Flatten(stream);
285 		if (status < B_OK)
286 			return status;
287 	}
288 
289 	int32 size = fExtent->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(fExtent->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 		fExtent->AddPicture(picture);
333 	}
334 
335 	status_t status = fExtent->ImportData(stream);
336 	if (status < B_OK)
337 		return status;
338 
339 //	swap_data(fExtent->fNewData, fExtent->fNewSize);
340 
341 	if (!_AssertServerCopy())
342 		return B_ERROR;
343 
344 	// Data is now kept server side, remove the local copy
345 	if (fExtent->Data() != NULL)
346 		fExtent->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]->fToken);
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>(&fToken);*/
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 	fToken = token;
390 }
391 
392 
393 int32
394 BPicture::Token() const
395 {
396 	return fToken;
397 }
398 
399 
400 bool
401 BPicture::_AssertLocalCopy()
402 {
403 	if (fExtent->Data() != NULL)
404 		return true;
405 
406 	if (fToken == -1)
407 		return false;
408 
409 	return _Download() == B_OK;
410 }
411 
412 
413 bool
414 BPicture::_AssertOldLocalCopy()
415 {
416 	// TODO: We don't support old data for now
417 
418 	return false;
419 }
420 
421 
422 bool
423 BPicture::_AssertServerCopy()
424 {
425 	if (fToken != -1)
426 		return true;
427 
428 	if (fExtent->Data() == NULL)
429 		return false;
430 
431 	for (int32 i = 0; i < fExtent->CountPictures(); i++)
432 		fExtent->PictureAt(i)->_AssertServerCopy();
433 
434 	return _Upload() == B_OK;
435 }
436 
437 
438 status_t
439 BPicture::_Upload()
440 {
441 	ASSERT((fToken == -1));
442 	ASSERT((fExtent->Data() != NULL));
443 
444 	BPrivate::AppServerLink link;
445 
446 	link.StartMessage(AS_CREATE_PICTURE);
447 	link.Attach<int32>(fExtent->CountPictures());
448 
449 	for (int32 i = 0; i < fExtent->CountPictures(); i++) {
450 		BPicture *picture = fExtent->PictureAt(i);
451 		if (picture)
452 			link.Attach<int32>(picture->fToken);
453 	}
454 	link.Attach<int32>(fExtent->Size());
455 	link.Attach(fExtent->Data(), fExtent->Size());
456 
457 	status_t status = B_ERROR;
458 	if (link.FlushWithReply(status) == B_OK
459 		&& status == B_OK)
460 		link.Read<int32>(&fToken);
461 
462 	return status;
463 }
464 
465 
466 status_t
467 BPicture::_Download()
468 {
469 	ASSERT((fExtent->Data() == NULL));
470 	ASSERT((fToken != -1));
471 
472 	BPrivate::AppServerLink link;
473 
474 	link.StartMessage(AS_DOWNLOAD_PICTURE);
475 	link.Attach<int32>(fToken);
476 
477 	status_t status = B_ERROR;
478 	if (link.FlushWithReply(status) == B_OK && status == B_OK) {
479 		int32 count = 0;
480 		link.Read<int32>(&count);
481 
482 		// Read sub picture tokens
483 		for (int32 i = 0; i < count; i++) {
484 			BPicture *pic = new BPicture;
485 			link.Read<int32>(&pic->fToken);
486 			fExtent->AddPicture(pic);
487 		}
488 
489 		int32 size;
490 		link.Read<int32>(&size);
491 		status = fExtent->SetSize(size);
492 		if (status == B_OK)
493 			link.Read(const_cast<void *>(fExtent->Data()), size);
494 	}
495 
496 	return status;
497 }
498 
499 
500 const void *
501 BPicture::Data() const
502 {
503 	if (fExtent->Data() == NULL)
504 		const_cast<BPicture*>(this)->_AssertLocalCopy();
505 
506 	return fExtent->Data();
507 }
508 
509 
510 int32
511 BPicture::DataSize() const
512 {
513 	if (fExtent->Data() == NULL)
514 		const_cast<BPicture*>(this)->_AssertLocalCopy();
515 
516 	return fExtent->Size();
517 }
518 
519 
520 void
521 BPicture::Usurp(BPicture *lameDuck)
522 {
523 	_DisposeData();
524 
525 	// Reinitializes the BPicture
526 	_InitData();
527 
528 	// Do the Usurping
529 	fUsurped = lameDuck;
530 }
531 
532 
533 BPicture *
534 BPicture::StepDown()
535 {
536 	BPicture *lameDuck = fUsurped;
537 	fUsurped = NULL;
538 
539 	return lameDuck;
540 }
541 
542 
543 void BPicture::_ReservedPicture1() {}
544 void BPicture::_ReservedPicture2() {}
545 void BPicture::_ReservedPicture3() {}
546 
547 
548 BPicture &
549 BPicture::operator=(const BPicture &)
550 {
551 	return *this;
552 }
553 
554 
555 // _BPictureExtent_
556 _BPictureExtent_::_BPictureExtent_(const int32 &size)
557 	:
558 	fNewData(NULL),
559 	fNewSize(0)
560 {
561 	SetSize(size);
562 }
563 
564 
565 _BPictureExtent_::~_BPictureExtent_()
566 {
567 	free(fNewData);
568 	for (int32 i = 0; i < fPictures.CountItems(); i++)
569 		delete static_cast<BPicture *>(fPictures.ItemAtFast(i));
570 }
571 
572 
573 status_t
574 _BPictureExtent_::ImportData(const void *data, const int32 &size)
575 {
576 	if (data == NULL)
577 		return B_BAD_VALUE;
578 
579 	status_t status = B_OK;
580 	if (Size() != size)
581 		status = SetSize(size);
582 
583 	if (status == B_OK)
584 		memcpy(fNewData, data, size);
585 
586 	return status;
587 }
588 
589 
590 status_t
591 _BPictureExtent_::ImportData(BDataIO *stream)
592 {
593 	if (stream == NULL)
594 		return B_BAD_VALUE;
595 
596 	int32 size;
597 	ssize_t bytesRead = stream->Read(&size, sizeof(size));
598 	if (bytesRead < B_OK)
599 		return bytesRead;
600 	if (bytesRead != (ssize_t)sizeof(size))
601 		return B_IO_ERROR;
602 
603 	status_t status = B_OK;
604 	if (Size() != size)
605 		status = SetSize(size);
606 
607 	if (status < B_OK)
608 		return status;
609 
610 	bytesRead = stream->Read(fNewData, size);
611 	if (bytesRead < B_OK)
612 		return bytesRead;
613 	if (bytesRead != (ssize_t)size)
614 		return B_IO_ERROR;
615 
616 	return B_OK;
617 }
618 
619 
620 status_t
621 _BPictureExtent_::SetSize(const int32 &size)
622 {
623 	if (size < 0)
624 		return B_BAD_VALUE;
625 
626 	if (size == fNewSize)
627 		return B_OK;
628 
629 	if (size == 0) {
630 		free(fNewData);
631 		fNewData = NULL;
632 	} else {
633 		void *data = realloc(fNewData, size);
634 		if (data == NULL)
635 			return B_NO_MEMORY;
636 		fNewData = data;
637 	}
638 
639 	fNewSize = size;
640 	return B_OK;
641 }
642