/* * Copyright 2001-2014 Haiku Inc. All rights reserved. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers, mflerackers@androme.be */ // Records a series of drawing instructions that can be "replayed" later. #include #include #include #include #include //#define DEBUG 1 #include #include #include #include #include #include #include #include #include #include "PicturePrivate.h" static BObjectList sPictureList; static BLocker sPictureListLock; void reconnect_pictures_to_app_server() { BAutolock _(sPictureListLock); for (int32 i = 0; i < sPictureList.CountItems(); i++) { BPicture::Private picture(sPictureList.ItemAt(i)); picture.ReconnectToAppServer(); } } BPicture::Private::Private(BPicture* picture) : fPicture(picture) { } void BPicture::Private::ReconnectToAppServer() { fPicture->_Upload(); } struct _BPictureExtent_ { _BPictureExtent_(int32 size = 0); ~_BPictureExtent_(); const void* Data() const { return fNewData; } status_t ImportData(const void* data, int32 size); status_t Flatten(BDataIO* stream); status_t Unflatten(BDataIO* stream); int32 Size() const { return fNewSize; } status_t SetSize(int32 size); bool AddPicture(BPicture* picture) { return fPictures.AddItem(picture); } void DeletePicture(int32 index) { delete static_cast (fPictures.RemoveItem(index)); } BList* Pictures() { return &fPictures; } BPicture* PictureAt(int32 index) { return static_cast (fPictures.ItemAt(index)); } int32 CountPictures() const { return fPictures.CountItems(); } private: void* fNewData; int32 fNewSize; BList fPictures; // In R5 this is a BArray // which is completely inline. }; struct picture_header { int32 magic1; // version ? int32 magic2; // endianess ? }; BPicture::BPicture() : fToken(-1), fExtent(NULL), fUsurped(NULL) { _InitData(); } BPicture::BPicture(const BPicture& otherPicture) : fToken(-1), fExtent(NULL), fUsurped(NULL) { _InitData(); if (otherPicture.fToken != -1) { BPrivate::AppServerLink link; link.StartMessage(AS_CLONE_PICTURE); link.Attach(otherPicture.fToken); status_t status = B_ERROR; if (link.FlushWithReply(status) == B_OK && status == B_OK) link.Read(&fToken); if (status < B_OK) return; } if (otherPicture.fExtent->Size() > 0) { fExtent->ImportData(otherPicture.fExtent->Data(), otherPicture.fExtent->Size()); for (int32 i = 0; i < otherPicture.fExtent->CountPictures(); i++) { BPicture* picture = new BPicture(*otherPicture.fExtent->PictureAt(i)); fExtent->AddPicture(picture); } } } BPicture::BPicture(BMessage* data) : fToken(-1), fExtent(NULL), fUsurped(NULL) { _InitData(); int32 version; if (data->FindInt32("_ver", &version) != B_OK) version = 0; int8 endian; if (data->FindInt8("_endian", &endian) != B_OK) endian = 0; const void* pictureData; ssize_t size; if (data->FindData("_data", B_RAW_TYPE, &pictureData, &size) != B_OK) return; // Load sub pictures BMessage pictureMessage; int32 i = 0; while (data->FindMessage("piclib", i++, &pictureMessage) == B_OK) { BPicture* picture = new BPicture(&pictureMessage); fExtent->AddPicture(picture); } if (version == 0) { // TODO: For now. We'll see if it's worth to support old style data debugger("old style BPicture data is not supported"); } else if (version == 1) { fExtent->ImportData(pictureData, size); // swap_data(fExtent->fNewData, fExtent->fNewSize); if (fExtent->Size() > 0) _AssertServerCopy(); } // Do we just free the data now? if (fExtent->Size() > 0) fExtent->SetSize(0); // What with the sub pictures? for (i = fExtent->CountPictures() - 1; i >= 0; i--) fExtent->DeletePicture(i); } BPicture::BPicture(const void* data, int32 size) { _InitData(); // TODO: For now. We'll see if it's worth to support old style data debugger("old style BPicture data is not supported"); } void BPicture::_InitData() { fToken = -1; fUsurped = NULL; fExtent = new (std::nothrow) _BPictureExtent_; BAutolock _(sPictureListLock); sPictureList.AddItem(this); } BPicture::~BPicture() { BAutolock _(sPictureListLock); sPictureList.RemoveItem(this, false); _DisposeData(); } void BPicture::_DisposeData() { if (fToken != -1) { BPrivate::AppServerLink link; link.StartMessage(AS_DELETE_PICTURE); link.Attach(fToken); link.Flush(); SetToken(-1); } delete fExtent; fExtent = NULL; } BArchivable* BPicture::Instantiate(BMessage* data) { if (validate_instantiation(data, "BPicture")) return new BPicture(data); return NULL; } status_t BPicture::Archive(BMessage* data, bool deep) const { if (!const_cast(this)->_AssertLocalCopy()) return B_ERROR; status_t err = BArchivable::Archive(data, deep); if (err != B_OK) return err; err = data->AddInt32("_ver", 1); if (err != B_OK) return err; err = data->AddInt8("_endian", B_HOST_IS_BENDIAN); if (err != B_OK) return err; err = data->AddData("_data", B_RAW_TYPE, fExtent->Data(), fExtent->Size()); if (err != B_OK) return err; for (int32 i = 0; i < fExtent->CountPictures(); i++) { BMessage pictureMessage; err = fExtent->PictureAt(i)->Archive(&pictureMessage, deep); if (err != B_OK) break; err = data->AddMessage("piclib", &pictureMessage); if (err != B_OK) break; } return err; } status_t BPicture::Perform(perform_code code, void* arg) { return BArchivable::Perform(code, arg); } status_t BPicture::Play(void** callBackTable, int32 tableEntries, void* user) { if (!_AssertLocalCopy()) return B_ERROR; BPrivate::PicturePlayer player(fExtent->Data(), fExtent->Size(), fExtent->Pictures()); return player.Play(callBackTable, tableEntries, user); } status_t BPicture::Flatten(BDataIO* stream) { // TODO: what about endianess? if (!_AssertLocalCopy()) return B_ERROR; const picture_header header = { 2, 0 }; ssize_t bytesWritten = stream->Write(&header, sizeof(header)); if (bytesWritten < B_OK) return bytesWritten; if (bytesWritten != (ssize_t)sizeof(header)) return B_IO_ERROR; return fExtent->Flatten(stream); } status_t BPicture::Unflatten(BDataIO* stream) { // TODO: clear current picture data? picture_header header; ssize_t bytesRead = stream->Read(&header, sizeof(header)); if (bytesRead < B_OK) return bytesRead; if (bytesRead != (ssize_t)sizeof(header) || header.magic1 != 2 || header.magic2 != 0) return B_BAD_TYPE; status_t status = fExtent->Unflatten(stream); if (status < B_OK) return status; // swap_data(fExtent->fNewData, fExtent->fNewSize); if (!_AssertServerCopy()) return B_ERROR; // Data is now kept server side, remove the local copy if (fExtent->Data() != NULL) fExtent->SetSize(0); return status; } void BPicture::_ImportOldData(const void* data, int32 size) { // TODO: We don't support old data for now } void BPicture::SetToken(int32 token) { fToken = token; } int32 BPicture::Token() const { return fToken; } bool BPicture::_AssertLocalCopy() { if (fExtent->Data() != NULL) return true; if (fToken == -1) return false; return _Download() == B_OK; } bool BPicture::_AssertOldLocalCopy() { // TODO: We don't support old data for now return false; } bool BPicture::_AssertServerCopy() { if (fToken != -1) return true; if (fExtent->Data() == NULL) return false; for (int32 i = 0; i < fExtent->CountPictures(); i++) { if (!fExtent->PictureAt(i)->_AssertServerCopy()) return false; } return _Upload() == B_OK; } status_t BPicture::_Upload() { if (fExtent == NULL || fExtent->Data() == NULL) return B_BAD_VALUE; BPrivate::AppServerLink link; link.StartMessage(AS_CREATE_PICTURE); link.Attach(fExtent->CountPictures()); for (int32 i = 0; i < fExtent->CountPictures(); i++) { BPicture* picture = fExtent->PictureAt(i); if (picture != NULL) link.Attach(picture->fToken); else link.Attach(-1); } link.Attach(fExtent->Size()); link.Attach(fExtent->Data(), fExtent->Size()); status_t status = B_ERROR; if (link.FlushWithReply(status) == B_OK && status == B_OK) { link.Read(&fToken); } return status; } status_t BPicture::_Download() { ASSERT(fExtent->Data() == NULL); ASSERT(fToken != -1); BPrivate::AppServerLink link; link.StartMessage(AS_DOWNLOAD_PICTURE); link.Attach(fToken); status_t status = B_ERROR; if (link.FlushWithReply(status) == B_OK && status == B_OK) { int32 count = 0; link.Read(&count); // Read sub picture tokens for (int32 i = 0; i < count; i++) { BPicture* picture = new BPicture; link.Read(&picture->fToken); fExtent->AddPicture(picture); } int32 size; link.Read(&size); status = fExtent->SetSize(size); if (status == B_OK) link.Read(const_cast(fExtent->Data()), size); } return status; } const void* BPicture::Data() const { if (fExtent->Data() == NULL) const_cast(this)->_AssertLocalCopy(); return fExtent->Data(); } int32 BPicture::DataSize() const { if (fExtent->Data() == NULL) const_cast(this)->_AssertLocalCopy(); return fExtent->Size(); } void BPicture::Usurp(BPicture* lameDuck) { _DisposeData(); // Reinitializes the BPicture _InitData(); // Do the Usurping fUsurped = lameDuck; } BPicture* BPicture::StepDown() { BPicture* lameDuck = fUsurped; fUsurped = NULL; return lameDuck; } void BPicture::_ReservedPicture1() {} void BPicture::_ReservedPicture2() {} void BPicture::_ReservedPicture3() {} BPicture& BPicture::operator=(const BPicture&) { return* this; } // _BPictureExtent_ _BPictureExtent_::_BPictureExtent_(int32 size) : fNewData(NULL), fNewSize(0) { SetSize(size); } _BPictureExtent_::~_BPictureExtent_() { free(fNewData); for (int32 i = 0; i < fPictures.CountItems(); i++) delete static_cast(fPictures.ItemAtFast(i)); } status_t _BPictureExtent_::ImportData(const void* data, int32 size) { if (data == NULL) return B_BAD_VALUE; status_t status = B_OK; if (Size() != size) status = SetSize(size); if (status == B_OK) memcpy(fNewData, data, size); return status; } status_t _BPictureExtent_::Unflatten(BDataIO* stream) { if (stream == NULL) return B_BAD_VALUE; int32 count = 0; ssize_t bytesRead = stream->Read(&count, sizeof(count)); if (bytesRead < B_OK) return bytesRead; if (bytesRead != (ssize_t)sizeof(count)) return B_BAD_DATA; for (int32 i = 0; i < count; i++) { BPicture* picture = new BPicture; status_t status = picture->Unflatten(stream); if (status < B_OK) { delete picture; return status; } AddPicture(picture); } int32 size; bytesRead = stream->Read(&size, sizeof(size)); if (bytesRead < B_OK) return bytesRead; if (bytesRead != (ssize_t)sizeof(size)) return B_IO_ERROR; status_t status = B_OK; if (Size() != size) status = SetSize(size); if (status < B_OK) return status; bytesRead = stream->Read(fNewData, size); if (bytesRead < B_OK) return bytesRead; if (bytesRead != (ssize_t)size) return B_IO_ERROR; return B_OK; } status_t _BPictureExtent_::Flatten(BDataIO* stream) { int32 count = fPictures.CountItems(); ssize_t bytesWritten = stream->Write(&count, sizeof(count)); if (bytesWritten < B_OK) return bytesWritten; if (bytesWritten != (ssize_t)sizeof(count)) return B_IO_ERROR; for (int32 i = 0; i < count; i++) { status_t status = PictureAt(i)->Flatten(stream); if (status < B_OK) return status; } bytesWritten = stream->Write(&fNewSize, sizeof(fNewSize)); if (bytesWritten < B_OK) return bytesWritten; if (bytesWritten != (ssize_t)sizeof(fNewSize)) return B_IO_ERROR; bytesWritten = stream->Write(fNewData, fNewSize); if (bytesWritten < B_OK) return bytesWritten; if (bytesWritten != fNewSize) return B_IO_ERROR; return B_OK; } status_t _BPictureExtent_::SetSize(int32 size) { if (size < 0) return B_BAD_VALUE; if (size == fNewSize) return B_OK; if (size == 0) { free(fNewData); fNewData = NULL; } else { void* data = realloc(fNewData, size); if (data == NULL) return B_NO_MEMORY; fNewData = data; } fNewSize = size; return B_OK; }