/* * Copyright 2001-2007, Haiku Inc. * Distributed under the terms of the MIT License. * * Authors: * Marc Flerackers (mflerackers@androme.be) */ //! BPicture records a series of drawing instructions that can be "replayed" later. #include #include #include #include #include #include #include #include #include #include #include struct _BPictureExtent_ { _BPictureExtent_(const int32 &size = 0); ~_BPictureExtent_(); const void *Data() const { return fNewData; } status_t ImportData(const void *data, const int32 &size); status_t ImportData(BDataIO *stream); int32 Size() const { return fNewSize; } status_t SetSize(const int32 &size); bool AddPicture(BPicture *picture) { return fPictures.AddItem(picture); } void DeletePicture(const int32 &index) { delete static_cast(fPictures.RemoveItem(index)); } BPicture *PictureAt(const int32 &index) { return static_cast(fPictures.ItemAt(index)); } int32 CountPictures() const { return fPictures.CountItems(); } BList *Pictures() { return &fPictures; } private: void *fNewData; int32 fNewSize; BList fPictures; // In R5 this is a BArray which is completely inline. }; struct picture_header { int32 magic1; // ? int32 magic2; // ? }; BPicture::BPicture() : token(-1), extent(NULL), usurped(NULL) { init_data(); } BPicture::BPicture(const BPicture &otherPicture) : token(-1), extent(NULL), usurped(NULL) { init_data(); if (otherPicture.token != -1) { BPrivate::AppServerLink link; link.StartMessage(AS_CLONE_PICTURE); link.Attach(otherPicture.token); status_t status = B_ERROR; if (link.FlushWithReply(status) == B_OK && status == B_OK) link.Read(&token); if (status < B_OK) return; } if (otherPicture.extent->Size() > 0) { extent->ImportData(otherPicture.extent->Data(), otherPicture.extent->Size()); for (int32 i = 0; i < otherPicture.extent->CountPictures(); i++) { BPicture *picture = new BPicture(*otherPicture.extent->PictureAt(i)); extent->AddPicture(picture); } } } BPicture::BPicture(BMessage *archive) : token(-1), extent(NULL), usurped(NULL) { init_data(); int32 version; if (archive->FindInt32("_ver", &version) != B_OK) version = 0; int8 endian; if (archive->FindInt8("_endian", &endian) != B_OK) endian = 0; const void *data; int32 size; if (archive->FindData("_data", B_RAW_TYPE, &data, (ssize_t*)&size) != B_OK) return; // Load sub pictures BMessage picMsg; int32 i = 0; while (archive->FindMessage("piclib", i++, &picMsg) == B_OK) { BPicture *pic = new BPicture(&picMsg); extent->AddPicture(pic); } 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) { extent->ImportData(data, size); // swap_data(extent->fNewData, extent->fNewSize); if (extent->Size() > 0) assert_server_copy(); } // Do we just free the data now? if (extent->Size() > 0) extent->SetSize(0); // What with the sub pictures? for (i = extent->CountPictures() - 1; i >= 0; i--) extent->DeletePicture(i); } BPicture::BPicture(const void *data, int32 size) { init_data(); // 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::init_data() { token = -1; usurped = NULL; extent = new (std::nothrow) _BPictureExtent_; } BPicture::~BPicture() { if (token != -1) { BPrivate::AppServerLink link; link.StartMessage(AS_DELETE_PICTURE); link.Attach(token); link.Flush(); } delete extent; } BArchivable * BPicture::Instantiate(BMessage *archive) { if (validate_instantiation(archive, "BPicture")) return new BPicture(archive); return NULL; } status_t BPicture::Archive(BMessage *archive, bool deep) const { if (!const_cast(this)->assert_local_copy()) return B_ERROR; status_t err = BArchivable::Archive(archive, deep); if (err != B_OK) return err; err = archive->AddInt32("_ver", 1); if (err != B_OK) return err; err = archive->AddInt8("_endian", B_HOST_IS_BENDIAN); if (err != B_OK) return err; err = archive->AddData("_data", B_RAW_TYPE, extent->Data(), extent->Size()); if (err != B_OK) return err; for (int32 i = 0; i < extent->CountPictures(); i++) { BMessage picMsg; extent->PictureAt(i)->Archive(&picMsg, deep); err = archive->AddMessage("piclib", &picMsg); if (err != B_OK) break; } return err; } status_t BPicture::Perform(perform_code d, void *arg) { return BArchivable::Perform(d, arg); } status_t BPicture::Play(void **callBackTable, int32 tableEntries, void *user) { if (!assert_local_copy()) return B_ERROR; PicturePlayer player(extent->Data(), extent->Size(), extent->Pictures()); return player.Play(callBackTable, tableEntries, user); } status_t BPicture::Flatten(BDataIO *stream) { // TODO: what about endianess? if (!assert_local_copy()) 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; int32 count = extent->CountPictures(); 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 = extent->PictureAt(i)->Flatten(stream); if (status < B_OK) return status; } int32 size = extent->Size(); bytesWritten = stream->Write(&size, sizeof(size)); if (bytesWritten < B_OK) return bytesWritten; if (bytesWritten != (ssize_t)sizeof(size)) return B_IO_ERROR; bytesWritten = stream->Write(extent->Data(), size); if (bytesWritten < B_OK) return bytesWritten; if (bytesWritten != size) return B_IO_ERROR; return B_OK; } 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; int32 count = 0; 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) return status; extent->AddPicture(picture); } status_t status = extent->ImportData(stream); if (status < B_OK) return status; // swap_data(extent->fNewData, extent->fNewSize); if (!assert_server_copy()) return B_ERROR; // Data is now kept server side, remove the local copy if (extent->Data() != NULL) extent->SetSize(0); return status; } void BPicture::import_data(const void *data, int32 size, BPicture **subs, int32 subCount) { /* if (data == NULL || size == 0) return; BPrivate::AppServerLink link; link.StartMessage(AS_CREATE_PICTURE); link.Attach(subCount); for (int32 i = 0; i < subCount; i++) link.Attach(subs[i]->token); link.Attach(size); link.Attach(data, size); status_t status = B_ERROR; if (link.FlushWithReply(status) == B_OK && status == B_OK) link.Read(&token);*/ } void BPicture::import_old_data(const void *data, int32 size) { // TODO: We don't support old data for now } void BPicture::set_token(int32 _token) { token = _token; } bool BPicture::assert_local_copy() { if (extent->Data() != NULL) return true; if (token == -1) return false; BPrivate::AppServerLink link; link.StartMessage(AS_DOWNLOAD_PICTURE); link.Attach(token); 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 *pic = new BPicture; link.Read(&pic->token); extent->AddPicture(pic); } int32 size; link.Read(&size); status = extent->SetSize(size); if (status == B_OK) link.Read(const_cast(extent->Data()), size); } return status == B_OK; } bool BPicture::assert_old_local_copy() { // TODO: We don't support old data for now return false; } bool BPicture::assert_server_copy() { if (token != -1) return true; if (extent->Data() == NULL) return false; for (int32 i = 0; i < extent->CountPictures(); i++) extent->PictureAt(i)->assert_server_copy(); BPrivate::AppServerLink link; link.StartMessage(AS_CREATE_PICTURE); link.Attach(extent->CountPictures()); for (int32 i = 0; i < extent->CountPictures(); i++) { BPicture *picture = extent->PictureAt(i); if (picture) link.Attach(picture->token); } link.Attach(extent->Size()); link.Attach(extent->Data(), extent->Size()); status_t status = B_ERROR; if (link.FlushWithReply(status) == B_OK && status == B_OK) link.Read(&token); return token != -1; } const void * BPicture::Data() const { if (extent->Data() == NULL) const_cast(this)->assert_local_copy(); return extent->Data(); } int32 BPicture::DataSize() const { if (extent->Data() == NULL) const_cast(this)->assert_local_copy(); return extent->Size(); } void BPicture::usurp(BPicture *lameDuck) { if (token != -1) { BPrivate::AppServerLink link; link.StartMessage(AS_DELETE_PICTURE); link.Attach(token); link.Flush(); } delete extent; // Reinitializes the BPicture init_data(); // Do the usurping usurped = lameDuck; } BPicture * BPicture::step_down() { BPicture *lameDuck = usurped; usurped = NULL; return lameDuck; } void BPicture::_ReservedPicture1() {} void BPicture::_ReservedPicture2() {} void BPicture::_ReservedPicture3() {} BPicture & BPicture::operator=(const BPicture &) { return *this; } // _BPictureExtent_ _BPictureExtent_::_BPictureExtent_(const 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, const 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_::ImportData(BDataIO *stream) { if (stream == NULL) return B_BAD_VALUE; int32 size; ssize_t 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_::SetSize(const 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; }