From b60f86ae044ff40ca5692377228bc30886466331 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Tue, 4 Jul 2023 17:47:00 +0200 Subject: [PATCH] Added zoom support to the PDF viewer Also increased page rasterization resolution when running at lower screen resolutions --- es-app/src/PDFViewer.cpp | 182 ++++++++++++++++++++++++++++++++------- es-app/src/PDFViewer.h | 21 +++-- es-core/src/Window.cpp | 18 ++-- es-core/src/Window.h | 13 ++- 4 files changed, 188 insertions(+), 46 deletions(-) diff --git a/es-app/src/PDFViewer.cpp b/es-app/src/PDFViewer.cpp index 58c301625..7baaf54e9 100644 --- a/es-app/src/PDFViewer.cpp +++ b/es-app/src/PDFViewer.cpp @@ -29,7 +29,6 @@ PDFViewer::PDFViewer() , mHelpInfoPosition {HelpInfoPosition::TOP} { Window::getInstance()->setPDFViewer(this); - mTexture = TextureResource::get(""); } bool PDFViewer::startPDFViewer(FileData* game) @@ -74,6 +73,19 @@ bool PDFViewer::startPDFViewer(FileData* game) mPageCount = 0; mCurrentPage = 0; mScaleFactor = 1.0f; + mZoom = 1.0f; + mPanAmount = 0.0f; + mPanOffset = {0.0f, 0.0f, 0.0f}; + + // Increase the rasterization resolution when running at lower screen resolutions to make + // the texture look ok when zoomed in. + const float resolutionModifier {mRenderer->getScreenResolutionModifier()}; + if (resolutionModifier < 1.0f) + mScaleFactor = 1.8f; + else if (resolutionModifier < 1.2f) + mScaleFactor = 1.3f; + else if (resolutionModifier < 1.4f) + mScaleFactor = 1.15f; if (!getDocumentInfo()) { LOG(LogError) << "PDFViewer: Couldn't load file \"" << mManualPath << "\""; @@ -303,30 +315,31 @@ void PDFViewer::convertPage(int pageNum) { assert(pageNum <= static_cast(mPages.size())); + if (mPages[pageNum].imageData.empty()) { #if defined(_WIN64) - std::wstring command { - Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mESConvertPath))}; - command.append(L" -convert ") - .append(Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mManualPath))) - .append(L" ") - .append(std::to_wstring(pageNum)) - .append(L" ") - .append(std::to_wstring(static_cast(mPages[pageNum].width))) - .append(L" ") - .append(std::to_wstring(static_cast(mPages[pageNum].height))); + std::wstring command { + Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mESConvertPath))}; + command.append(L" -convert ") + .append( + Utils::String::stringToWideString(Utils::FileSystem::getEscapedPath(mManualPath))) + .append(L" ") + .append(std::to_wstring(pageNum)) + .append(L" ") + .append(std::to_wstring(static_cast(mPages[pageNum].width))) + .append(L" ") + .append(std::to_wstring(static_cast(mPages[pageNum].height))); #else - std::string command {Utils::FileSystem::getEscapedPath(mESConvertPath)}; - command.append(" -convert ") - .append(Utils::FileSystem::getEscapedPath(mManualPath)) - .append(" ") - .append(std::to_string(pageNum)) - .append(" ") - .append(std::to_string(static_cast(mPages[pageNum].width))) - .append(" ") - .append(std::to_string(static_cast(mPages[pageNum].height))); + std::string command {Utils::FileSystem::getEscapedPath(mESConvertPath)}; + command.append(" -convert ") + .append(Utils::FileSystem::getEscapedPath(mManualPath)) + .append(" ") + .append(std::to_string(pageNum)) + .append(" ") + .append(std::to_string(static_cast(mPages[pageNum].width))) + .append(" ") + .append(std::to_string(static_cast(mPages[pageNum].height))); #endif - if (mPages[pageNum].imageData.empty()) { #if (DEBUG_PDF_CONVERSION) LOG(LogDebug) << "Converting page: " << mCurrentPage; #if defined(_WIN64) @@ -436,6 +449,7 @@ void PDFViewer::convertPage(int pageNum) mPageImage.reset(); mPageImage = std::make_unique(false, false); mPageImage->setFlipY(true); + mPageImage->setLinearInterpolation(true); mPageImage->setOrigin(0.5f, 0.5f); if (mHelpInfoPosition == HelpInfoPosition::TOP) { mPageImage->setPosition(mRenderer->getScreenWidth() / 2.0f, @@ -451,16 +465,20 @@ void PDFViewer::convertPage(int pageNum) } float sizeReduction {0.0f}; - if (mPages[pageNum].height > mRenderer->getScreenHeight() - mFrameHeight) - sizeReduction = mPages[pageNum].height - (mRenderer->getScreenHeight() - mFrameHeight); + if (mPages[pageNum].height / mScaleFactor > mRenderer->getScreenHeight() - mFrameHeight) + sizeReduction = + (mPages[pageNum].height / mScaleFactor) - (mRenderer->getScreenHeight() - mFrameHeight); - mPageImage->setMaxSize(glm::vec2 {mPages[pageNum].width / mScaleFactor, - (mPages[pageNum].height / mScaleFactor) - sizeReduction}); + mPageImage->setMaxSize( + glm::vec2 {(mPages[pageNum].width / mScaleFactor) * mZoom, + ((mPages[pageNum].height / mScaleFactor) * mZoom) - sizeReduction}); mPageImage->setRawImage(reinterpret_cast(&mPages[pageNum].imageData[0]), static_cast(mPages[pageNum].width), static_cast(mPages[pageNum].height)); + mPanAmount = std::min(mRenderer->getScreenWidth(), mRenderer->getScreenHeight()) * 0.1f; + #if (DEBUG_PDF_CONVERSION) LOG(LogDebug) << "ABGR32 data stream size: " << mPages[pageNum].imageData.size(); #endif @@ -475,9 +493,15 @@ void PDFViewer::render(const glm::mat4& /*parentTrans*/) mRenderer->drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), 0x000000FF, 0x000000FF); + if (mZoom != 1.0f) + mPageImage->setPosition(mPageImage->getPosition() + (mPanOffset * mZoom)); + if (mPageImage != nullptr) mPageImage->render(trans); + if (mZoom != 1.0f) + mPageImage->setPosition(mPageImage->getPosition() - (mPanOffset * mZoom)); + if (mHelpInfoPosition != HelpInfoPosition::DISABLED) { // Render a dark gray frame behind the help info. mRenderer->setMatrix(mRenderer->getIdentity()); @@ -494,10 +518,18 @@ void PDFViewer::render(const glm::mat4& /*parentTrans*/) std::vector PDFViewer::getHelpPrompts() { std::vector prompts; - prompts.push_back(HelpPrompt("left/right", "browse")); - prompts.push_back(HelpPrompt("down", "game media")); - prompts.push_back(HelpPrompt("lt", "first")); - prompts.push_back(HelpPrompt("rt", "last")); + if (mZoom > 1.0f) { + prompts.push_back(HelpPrompt("up/down/left/right", "pan")); + prompts.push_back(HelpPrompt("ltrt", "reset")); + } + else { + prompts.push_back(HelpPrompt("left/right", "browse")); + prompts.push_back(HelpPrompt("down", "game media")); + prompts.push_back(HelpPrompt("lt", "first")); + prompts.push_back(HelpPrompt("rt", "last")); + } + + prompts.push_back(HelpPrompt("lr", "zoom")); return prompts; } @@ -524,22 +556,110 @@ void PDFViewer::showPreviousPage() convertPage(mCurrentPage); } -void PDFViewer::showFirstPage() +void PDFViewer::navigateUp() { + if (mZoom != 1.0f) { + if (mPanOffset.y * mZoom <= mPageImage->getSize().y / 2.0f) + mPanOffset.y += mPanAmount; + } +} + +void PDFViewer::navigateDown() +{ + if (mZoom != 1.0f) { + if (mPanOffset.y * mZoom >= -(mPageImage->getSize().y / 2.0f)) + mPanOffset.y -= mPanAmount; + } + else { + launchMediaViewer(); + } +} + +void PDFViewer::navigateLeft() +{ + if (mZoom != 1.0f) { + if (mPanOffset.x * mZoom <= mPageImage->getSize().x / 2.0f) + mPanOffset.x += mPanAmount; + } + else { + mPanOffset = {0.0f, 0.0f, 0.0f}; + showPreviousPage(); + } +} + +void PDFViewer::navigateRight() +{ + if (mZoom != 1.0f) { + if (mPanOffset.x * mZoom > -(mPageImage->getSize().x / 2.0f)) + mPanOffset.x -= mPanAmount; + } + else { + mPanOffset = {0.0f, 0.0f, 0.0f}; + showNextPage(); + } +} + +void PDFViewer::navigateRightShoulder() +{ + if (mZoom <= 2.5f) + mZoom += 0.5f; + + if (mZoom == 1.5f) + mHelp->setPrompts(getHelpPrompts()); + + convertPage(mCurrentPage); +} + +void PDFViewer::navigateLeftShoulder() +{ + if (mZoom == 1.0f) + mPanOffset = {0.0f, 0.0f, 0.0f}; + + if (mZoom >= 1.5f) + mZoom -= 0.5f; + + if (mZoom == 1.0f) + mHelp->setPrompts(getHelpPrompts()); + + convertPage(mCurrentPage); +} + +void PDFViewer::navigateLeftTrigger() +{ + if (mZoom != 1.0f) { + mZoom = 1.0f; + mPanOffset = {0.0f, 0.0f, 0.0f}; + mHelp->setPrompts(getHelpPrompts()); + convertPage(mCurrentPage); + return; + } + if (mCurrentPage == 1) return; + mPanOffset = {0.0f, 0.0f, 0.0f}; + NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); mCurrentPage = 1; mEntryNumText->setText(std::to_string(mCurrentPage) + "/" + mEntryCount); convertPage(mCurrentPage); } -void PDFViewer::showLastPage() +void PDFViewer::navigateRightTrigger() { + if (mZoom != 1.0f) { + mZoom = 1.0f; + mPanOffset = {0.0f, 0.0f, 0.0f}; + mHelp->setPrompts(getHelpPrompts()); + convertPage(mCurrentPage); + return; + } + if (mCurrentPage == mPageCount) return; + mPanOffset = {0.0f, 0.0f, 0.0f}; + NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); mCurrentPage = mPageCount; mEntryNumText->setText(std::to_string(mCurrentPage) + "/" + mEntryCount); diff --git a/es-app/src/PDFViewer.h b/es-app/src/PDFViewer.h index 13234b71f..26d2d00f3 100644 --- a/es-app/src/PDFViewer.h +++ b/es-app/src/PDFViewer.h @@ -37,10 +37,19 @@ public: }; private: - void showNextPage() override; - void showPreviousPage() override; - void showFirstPage() override; - void showLastPage() override; + void showNextPage(); + void showPreviousPage(); + + void navigateUp() override; + void navigateDown() override; + void navigateLeft() override; + void navigateRight() override; + + void navigateLeftShoulder() override; + void navigateRightShoulder() override; + + void navigateLeftTrigger() override; + void navigateRightTrigger() override; struct PageEntry { float width; @@ -56,11 +65,13 @@ private: float mScaleFactor; int mCurrentPage; int mPageCount; + float mZoom; + float mPanAmount; + glm::vec3 mPanOffset; std::string mESConvertPath; std::string mManualPath; - std::shared_ptr mTexture; std::unique_ptr mPageImage; std::map mPages; diff --git a/es-core/src/Window.cpp b/es-core/src/Window.cpp index f84c19714..879bbcf15 100644 --- a/es-core/src/Window.cpp +++ b/es-core/src/Window.cpp @@ -249,23 +249,29 @@ void Window::input(InputConfig* config, Input input) if (mPDFViewer && mRenderPDFViewer) { if (config->isMappedLike("up", input) && input.value != 0) { - return; + mPDFViewer->navigateUp(); } else if (config->isMappedLike("down", input) && input.value != 0) { - mPDFViewer->launchMediaViewer(); + mPDFViewer->navigateDown(); return; } else if (config->isMappedLike("left", input) && input.value != 0) { - mPDFViewer->showPreviousPage(); + mPDFViewer->navigateLeft(); } else if (config->isMappedLike("right", input) && input.value != 0) { - mPDFViewer->showNextPage(); + mPDFViewer->navigateRight(); + } + else if (config->isMappedLike("leftshoulder", input) && input.value != 0) { + mPDFViewer->navigateLeftShoulder(); + } + else if (config->isMappedLike("rightshoulder", input) && input.value != 0) { + mPDFViewer->navigateRightShoulder(); } else if (config->isMappedLike("lefttrigger", input) && input.value != 0) { - mPDFViewer->showFirstPage(); + mPDFViewer->navigateLeftTrigger(); } else if (config->isMappedLike("righttrigger", input) && input.value != 0) { - mPDFViewer->showLastPage(); + mPDFViewer->navigateRightTrigger(); } else if (input.value != 0) { // Any other input stops the PDF viewer. diff --git a/es-core/src/Window.h b/es-core/src/Window.h index 3de0c6077..0a495f242 100644 --- a/es-core/src/Window.h +++ b/es-core/src/Window.h @@ -75,10 +75,15 @@ public: virtual void stopPDFViewer() = 0; virtual void launchMediaViewer() = 0; - virtual void showNextPage() = 0; - virtual void showPreviousPage() = 0; - virtual void showFirstPage() = 0; - virtual void showLastPage() = 0; + virtual void navigateUp() = 0; + virtual void navigateDown() = 0; + virtual void navigateLeft() = 0; + virtual void navigateRight() = 0; + + virtual void navigateLeftShoulder() = 0; + virtual void navigateRightShoulder() = 0; + virtual void navigateLeftTrigger() = 0; + virtual void navigateRightTrigger() = 0; virtual void render(const glm::mat4& parentTrans) = 0; virtual std::vector getHelpPrompts() = 0;