#include "AnimationStrategy.h" #include "Renderer.h" // Renderer의 그리기 헬퍼 함수들을 사용하기 위해 포함 #include // for std::max, std::clamp #include #include #include // ==================================================================== // --- PAN (왕복 이동) 애니메이션 --- // ==================================================================== class PanAnimation : public AnimationStrategy { public: PanAnimation(float speed) : AnimationStrategy(speed) { reset(); } void reset() override { offsetX_ = 0.0f; offsetY_ = 0.0f; cycleComplete_ = false; xDirection_ = 1; yDirection_ = 1; } bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override { // 1. 애니메이션이 이미 끝났으면 더 이상 계산하지 않음 if (cycleComplete_) { // 마지막 위치에 고정하여 그림 renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, getBaseScale(media, surfaceWidth, surfaceHeight)); return cycleComplete_; } // 2. Pan 모드에 필요한 overflow(화면 밖으로 넘친 영역) 계산 float overflowX = 0.0f, overflowY = 0.0f; float scale = getBaseScale(media, surfaceWidth, surfaceHeight); float mediaW = static_cast(media.getWidth()); float mediaH = static_cast(media.getHeight()); if ((mediaW / mediaH) > ((float)surfaceWidth / surfaceHeight)) { overflowX = std::max(0.0f, mediaW * scale - surfaceWidth); } else { overflowY = std::max(0.0f, mediaH * scale - surfaceHeight); } // 3. 좌표 업데이트 bool xDone = (overflowX <= 0); bool yDone = (overflowY <= 0); if (overflowX > 0) { offsetX_ += animationSpeed_ * xDirection_; if (xDirection_ == 1 && offsetX_ >= overflowX) { offsetX_ = overflowX; xDirection_ = -1; } else if (xDirection_ == -1 && offsetX_ <= 0) { offsetX_ = 0; xDirection_ = 1; xDone = true; } } if (overflowY > 0) { offsetY_ += animationSpeed_ * yDirection_; if (yDirection_ == 1 && offsetY_ >= overflowY) { offsetY_ = overflowY; yDirection_ = -1; } else if (yDirection_ == -1 && offsetY_ <= 0) { offsetY_ = 0; yDirection_ = 1; yDone = true; } } // 4. X, Y축 왕복이 모두 끝났는지 확인 if (xDone && yDone) { cycleComplete_ = true; } // 5. 계산된 최종 좌표로 그림 renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, scale); return cycleComplete_; } private: float offsetX_, offsetY_; int xDirection_, yDirection_; bool cycleComplete_; float getBaseScale(MediaAsset& media, int surfaceWidth, int surfaceHeight) { float scale; if ((static_cast(media.getWidth()) / media.getHeight()) > ((float)surfaceWidth / surfaceHeight)) { scale = (float)surfaceHeight / media.getHeight(); } else { scale = (float)surfaceWidth / media.getWidth(); } return scale; } }; // ==================================================================== // --- PAN_ONE_WAY (편도 이동) 애니메이션 --- // ==================================================================== class PanOneWayAnimation : public AnimationStrategy { public: PanOneWayAnimation(float speed) : AnimationStrategy(speed) { reset(); } void reset() override { offsetX_ = 0.0f; offsetY_ = 0.0f; cycleComplete_ = false; } bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override { if (cycleComplete_) { renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, getBaseScale(media, surfaceWidth, surfaceHeight)); return cycleComplete_; } float overflowX = 0.0f, overflowY = 0.0f; float scale = getBaseScale(media, surfaceWidth, surfaceHeight); float mediaW = static_cast(media.getWidth()); float mediaH = static_cast(media.getHeight()); if ((mediaW / mediaH) > ((float)surfaceWidth / surfaceHeight)) { overflowX = std::max(0.0f, mediaW * scale - surfaceWidth); } else { overflowY = std::max(0.0f, mediaH * scale - surfaceHeight); } bool xReachedEnd = (overflowX <= 0); bool yReachedEnd = (overflowY <= 0); if (overflowX > 0) { offsetX_ += animationSpeed_; if (offsetX_ >= overflowX) { offsetX_ = overflowX; xReachedEnd = true; } } if (overflowY > 0) { offsetY_ += animationSpeed_; if (offsetY_ >= overflowY) { offsetY_ = overflowY; yReachedEnd = true; } } if (xReachedEnd && yReachedEnd) { cycleComplete_ = true; } renderer->drawMedia(buffer, media, 1.0f, offsetX_, offsetY_, scale); return cycleComplete_; } private: float offsetX_, offsetY_; bool cycleComplete_; float getBaseScale(MediaAsset& media, int surfaceWidth, int surfaceHeight) { // (PanAnimation과 중복되지만, 각 클래스의 독립성을 위해 포함) float scale; if ((static_cast(media.getWidth()) / media.getHeight()) > ((float)surfaceWidth / surfaceHeight)) { scale = (float)surfaceHeight / media.getHeight(); } else { scale = (float)surfaceWidth / media.getWidth(); } return scale; } }; // ==================================================================== // --- ZOOM 애니메이션 --- // ==================================================================== class ZoomAnimation : public AnimationStrategy { public: ZoomAnimation(float speed) : AnimationStrategy(speed) { reset(); } void reset() override { scaleMultiplier_ = 1.0f; cycleComplete_ = false; zoomDirection_ = 1; } bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override { if (!cycleComplete_) { scaleMultiplier_ += 0.0005f * animationSpeed_ * zoomDirection_; if (zoomDirection_ == 1 && scaleMultiplier_ >= 1.2f) { scaleMultiplier_ = 1.2f; zoomDirection_ = -1; } else if (zoomDirection_ == -1 && scaleMultiplier_ <= 1.0f) { scaleMultiplier_ = 1.0f; zoomDirection_ = 1; cycleComplete_ = true; } } // ZOOM은 중앙 정렬을 기본으로 함 float baseScale, baseOffsetX, baseOffsetY; renderer->calculateFitScaleAndOffset(media, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY); renderer->drawMedia(buffer, media, 1.0f, baseOffsetX, baseOffsetY, baseScale * scaleMultiplier_); return cycleComplete_; } private: float scaleMultiplier_; int zoomDirection_; bool cycleComplete_; }; // ==================================================================== // --- PAGE_TURN (대기) 애니메이션 --- // ==================================================================== class PageTurnAnimation : public AnimationStrategy { public: PageTurnAnimation(float speed, long long delay) : AnimationStrategy(speed), delayMs_(delay) { reset(); } void reset() override { cycleComplete_ = false; startTime_ = std::chrono::steady_clock::now(); } bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override { if (!cycleComplete_) { long long elapsed = std::chrono::duration_cast(std::chrono::steady_clock::now() - startTime_).count(); if (elapsed >= delayMs_) { cycleComplete_ = true; } } // 대기하는 동안 중앙에 고정된 이미지를 그림 float baseScale, baseOffsetX, baseOffsetY; renderer->calculateFitScaleAndOffset(media, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY); renderer->drawMedia(buffer, media, 1.0f, baseOffsetX, baseOffsetY, baseScale); return cycleComplete_; } private: long long delayMs_; std::chrono::steady_clock::time_point startTime_; bool cycleComplete_; }; // ==================================================================== // --- NONE (애니메이션 없음) --- // ==================================================================== class NoneAnimation : public AnimationStrategy { public: NoneAnimation(float speed) : AnimationStrategy(speed) {} void reset() override {} // 아무것도 안 함 bool execute(Renderer* renderer, ANativeWindow_Buffer& buffer, MediaAsset& media, int surfaceWidth, int surfaceHeight) override { // 중앙에 고정된 이미지만 그림 float baseScale, baseOffsetX, baseOffsetY; renderer->calculateFitScaleAndOffset(media, surfaceWidth, surfaceHeight, baseScale, baseOffsetX, baseOffsetY); renderer->drawMedia(buffer, media, 1.0f, baseOffsetX, baseOffsetY, baseScale); return true; // 즉시 완료 } };