#include "PLMovies.h" #include "BitmapImage.h" #include "MemoryManager.h" #include "PLQDraw.h" #include "PLResources.h" #include "QDManager.h" #include "QDPixMap.h" #include "ResourceManager.h" #include "rapidjson/rapidjson.h" #include "rapidjson/document.h" AnimationPlayer::AnimationPlayer() : m_animPackage(nullptr) , m_renderRect(Rect::Create(0, 0, 0, 0)) , m_constrainRect(Rect::Create(0, 0, 0, 0)) , m_surface(nullptr) , m_playing(false) , m_timer(0) , m_frameIndex(0) , m_isFrameCurrent(false) , m_next(nullptr) , m_prev(nullptr) { } void AnimationPlayer::SetPackage(AnimationPackage *animPackage) { m_animPackage = animPackage; Restart(); } void AnimationPlayer::Restart() { m_isFrameCurrent = (m_frameIndex == 0); m_playing = false; m_timer = 0; m_frameIndex = 0; } AnimationPackage *AnimationPackage::Create() { void *storage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(AnimationPackage)); if (!storage) return nullptr; return new (storage) AnimationPackage(); } void AnimationPackage::Destroy() { this->~AnimationPackage(); PortabilityLayer::MemoryManager::GetInstance()->Release(this); } bool AnimationPackage::Load(PortabilityLayer::VirtualDirectory_t virtualDir, const PLPasStr &path) { m_resArchive = PortabilityLayer::ResourceManager::GetInstance()->LoadResFile(virtualDir, path); if (!m_resArchive) return false; THandle movieMetadataRes = m_resArchive->LoadResource('muvi', 0); if (!movieMetadataRes) return false; const void *movieMetadata = *movieMetadataRes; rapidjson::Document document; document.Parse(static_cast(movieMetadata), movieMetadataRes.MMBlock()->m_size); movieMetadataRes.Dispose(); if (document.HasParseError() || !document.IsObject()) return false; if (!document.HasMember("frameRateNumerator") || !document.HasMember("frameRateDenominator")) return false; const rapidjson::Value &frameRateNumeratorJSON = document["frameRateNumerator"]; const rapidjson::Value &frameRateDenominatorJSON = document["frameRateDenominator"]; if (!frameRateNumeratorJSON.IsInt() && !frameRateDenominatorJSON.IsInt()) return false; const int frameRateNumerator = frameRateNumeratorJSON.GetInt(); const int frameRateDenominator = frameRateDenominatorJSON.GetInt(); if (frameRateNumerator < 1 || frameRateDenominator < 1) return false; if (frameRateDenominator > INT_MAX / 60 || frameRateDenominator * 60 < frameRateNumerator) return false; // We only support up to 60fps m_frameRateNumerator = frameRateNumerator; m_frameRateDenominator = frameRateDenominator; uint32_t numFrames = 0; for (;;) { if (numFrames + 1 > 0x7fff) return false; THandle frameRes = m_resArchive->LoadResource('PICT', numFrames + 1); if (!frameRes) break; else { if (frameRes.MMBlock()->m_size < sizeof(BitmapImage)) return false; numFrames++; } } if (numFrames == 0) return false; void *imageListStorage = PortabilityLayer::MemoryManager::GetInstance()->Alloc(sizeof(THandle) * numFrames); if (!imageListStorage) return false; m_images = static_cast*>(imageListStorage); for (uint32_t i = 0; i < numFrames; i++) new (m_images + i) THandle(); for (uint32_t i = 0; i < numFrames; i++) m_images[i] = m_resArchive->LoadResource('PICT', i + 1).StaticCast(); m_numImages = numFrames; return true; } const THandle &AnimationPackage::GetFrame(size_t index) const { return m_images[index]; } size_t AnimationPackage::NumFrames() const { return m_numImages; } uint32_t AnimationPackage::GetFrameRateNumerator() const { return m_frameRateNumerator; } uint32_t AnimationPackage::GetFrameRateDenominator() const { return m_frameRateDenominator; } AnimationPackage::AnimationPackage() : m_images(nullptr) , m_resArchive(nullptr) , m_numImages(0) { } AnimationPackage::~AnimationPackage() { if (m_resArchive) m_resArchive->Destroy(); PortabilityLayer::MemoryManager::GetInstance()->Release(m_images); } void AnimationManager::RegisterPlayer(AnimationPlayer *player) { assert(player->m_prev == nullptr || player->m_next == nullptr); if (!m_firstPlayer) m_firstPlayer = player; if (m_lastPlayer) m_lastPlayer->m_next = player; player->m_prev = m_lastPlayer; player->m_next = nullptr; m_lastPlayer = player; } void AnimationManager::RemovePlayer(AnimationPlayer *player) { if (m_firstPlayer == player) m_firstPlayer = m_firstPlayer->m_next; if (m_lastPlayer == player) m_lastPlayer = m_lastPlayer->m_prev; if (player->m_prev) player->m_prev->m_next = player->m_next; if (player->m_next) player->m_next->m_prev = player->m_prev; player->m_prev = player->m_next = nullptr; } void AnimationManager::RefreshPlayer(AnimationPlayer *player) { if (!player->m_playing || !player->m_animPackage || !player->m_surface) return; AnimationPackage *anim = player->m_animPackage; if (!player->m_isFrameCurrent) { player->m_isFrameCurrent = true; THandle img = anim->GetFrame(player->m_frameIndex); DrawSurface *surface = player->m_surface; if (player->m_renderRect == player->m_constrainRect) surface->DrawPicture(img, player->m_renderRect); else { DrawSurface *renderSurface = nullptr; if (PortabilityLayer::QDManager::GetInstance()->NewGWorld(&renderSurface, surface->m_port.GetPixelFormat(), player->m_renderRect, nullptr) != PLErrors::kNone) return; renderSurface->DrawPicture(img, player->m_renderRect); CopyBits(*renderSurface->m_port.GetPixMap(), *surface->m_port.GetPixMap(), &player->m_constrainRect, &player->m_constrainRect, srcCopy); PortabilityLayer::QDManager::GetInstance()->DisposeGWorld(renderSurface); } } } void AnimationManager::TickPlayers(uint32_t numTicks) { for (AnimationPlayer *player = m_firstPlayer; player; player = player->m_next) { AnimationPackage *anim = player->m_animPackage; uint32_t fptNumerator = anim->GetFrameRateNumerator(); uint32_t fptDenominator = anim->GetFrameRateDenominator() * 60; for (uint32_t t = 0; t < numTicks; t++) { player->m_timer += fptNumerator; if (player->m_timer >= fptDenominator) { player->m_timer -= fptDenominator; player->m_frameIndex++; if (player->m_frameIndex == anim->NumFrames()) player->m_frameIndex = 0; player->m_isFrameCurrent = false; } } } } AnimationManager *AnimationManager::GetInstance() { return &ms_instance; } AnimationManager::AnimationManager() : m_firstPlayer(nullptr) , m_lastPlayer(nullptr) { } AnimationManager AnimationManager::ms_instance;