From dbf3ade04f077b118d1bbd36fe6d2cea3a5c7845 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Mon, 18 Jun 2018 11:22:05 -0700 Subject: [PATCH] Implement MS16007: Add support for PNG snapshots --- interface/src/ui/Snapshot.cpp | 76 ++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 2b306ace91..f505d7d2d7 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -50,6 +50,7 @@ const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss"; const QString SNAPSHOTS_DIRECTORY = "Snapshots"; const QString URL = "highfidelity_url"; static const int SNAPSHOT_360_TIMER_INTERVAL = 350; +static const QList SUPPORTED_IMAGE_FORMATS = { "jpg", "jpeg", "png" }; Snapshot::Snapshot() { _snapshotTimer.setSingleShot(false); @@ -67,7 +68,6 @@ Snapshot::Snapshot() { } SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { - if (!QFile(snapshotPath).exists()) { return NULL; } @@ -95,7 +95,6 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { } QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) { - QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname); if (snapshotFile) { @@ -122,11 +121,15 @@ static const glm::quat CAMERA_ORIENTATION_LEFT(glm::quat(glm::radians(glm::vec3( static const glm::quat CAMERA_ORIENTATION_BACK(glm::quat(glm::radians(glm::vec3(0.0f, 180.0f, 0.0f)))); static const glm::quat CAMERA_ORIENTATION_RIGHT(glm::quat(glm::radians(glm::vec3(0.0f, 270.0f, 0.0f)))); static const glm::quat CAMERA_ORIENTATION_UP(glm::quat(glm::radians(glm::vec3(90.0f, 0.0f, 0.0f)))); -void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat, const bool& notify, const QString& filename) { +void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, + const bool& cubemapOutputFormat, + const bool& notify, + const QString& filename) { _snapshotFilename = filename; _notify360 = notify; _cubemapOutputFormat = cubemapOutputFormat; - SecondaryCameraJobConfig* secondaryCameraRenderConfig = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + SecondaryCameraJobConfig* secondaryCameraRenderConfig = + static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); // Save initial values of secondary camera render config _oldEnabled = secondaryCameraRenderConfig->isEnabled(); @@ -141,9 +144,11 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cube } // Initialize some secondary camera render config options for 360 snapshot capture - static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(0); + static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping")) + ->setCurve(0); - secondaryCameraRenderConfig->resetSizeSpectatorCamera(static_cast(CUBEMAP_SIDE_PIXEL_DIMENSION), static_cast(CUBEMAP_SIDE_PIXEL_DIMENSION)); + secondaryCameraRenderConfig->resetSizeSpectatorCamera(static_cast(CUBEMAP_SIDE_PIXEL_DIMENSION), + static_cast(CUBEMAP_SIDE_PIXEL_DIMENSION)); secondaryCameraRenderConfig->setProperty("attachedEntityId", ""); secondaryCameraRenderConfig->setPosition(cameraPosition); secondaryCameraRenderConfig->setProperty("vFoV", SNAPSHOT_360_FOV); @@ -159,7 +164,8 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cube } void Snapshot::takeNextSnapshot() { - SecondaryCameraJobConfig* config = static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); + SecondaryCameraJobConfig* config = + static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); // Order is: // 0. Down @@ -191,7 +197,9 @@ void Snapshot::takeNextSnapshot() { _snapshotTimer.stop(); // Reset secondary camera render config - static_cast(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1); + static_cast( + qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping")) + ->setCurve(1); config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height()); config->setProperty("attachedEntityId", _oldAttachedEntityId); config->setProperty("vFoV", _oldvFoV); @@ -338,8 +346,10 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) { return static_cast(savedFileForSnapshot(image, true)); } -QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname) { - +QFile* Snapshot::savedFileForSnapshot(QImage& shot, + bool isTemporary, + const QString& userSelectedFilename, + const QString& userSelectedPathname) { // adding URL to snapshot QUrl currentURL = DependencyManager::get()->currentPublicAddress(); shot.setText(URL, currentURL.toString()); @@ -350,12 +360,22 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt QDateTime now = QDateTime::currentDateTime(); - // If user has requested specific filename then use it, else create the filename - // 'jpg" is appended, as the image is saved in jpg format. This is the case for all snapshots - // (see definition of FILENAME_PATH_FORMAT) + // If user has supplied a specific filename for the snapshot: + // If the user's requested filename has a suffix that's contained within SUPPORTED_IMAGE_FORMATS, + // DON'T append ".jpg" to the filename. QT will save the image in the format associated with the + // filename's suffix. + // Otherwise, ".jpg" is appended to the user's requested filename so that the image is saved in JPG format. + // If the user hasn't supplied a specific filename for the snapshot: + // Save the snapshot in JPG format according to FILENAME_PATH_FORMAT QString filename; if (!userSelectedFilename.isNull()) { - filename = userSelectedFilename + ".jpg"; + QFileInfo snapshotFileInfo(userSelectedFilename); + QString userSelectedFilenameSuffix = snapshotFileInfo.suffix(); + if (SUPPORTED_IMAGE_FORMATS.contains(userSelectedFilenameSuffix)) { + filename = userSelectedFilename; + } else { + filename = userSelectedFilename + ".jpg"; + } } else { filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT)); } @@ -372,11 +392,13 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt } if (snapshotFullPath.isEmpty()) { - snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); + snapshotFullPath = + OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); _snapshotsLocation.set(snapshotFullPath); } - if (!snapshotFullPath.isEmpty()) { // not cancelled + if (!snapshotFullPath.isEmpty()) { // not cancelled if (!snapshotFullPath.endsWith(QDir::separator())) { snapshotFullPath.append(QDir::separator()); @@ -393,7 +415,9 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt qApp->getApplicationCompositor().getReticleInterface()->setVisible(true); qApp->getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(true); - snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Write Error - Choose New Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); + snapshotFullPath = + OffscreenUi::getExistingDirectory(nullptr, "Write Error - Choose New Snapshots Directory", + QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); if (snapshotFullPath.isEmpty()) { return NULL; } @@ -412,7 +436,6 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt return imageFile; } - } // Either we were asked for a tempororary, or the user didn't set a directory. QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename); @@ -430,7 +453,6 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt } void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) { - const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots"; QUrl url = href; if (url.isEmpty()) { @@ -444,7 +466,7 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) { url = QUrl(DependencyManager::get()->currentShareableAddress()); } SnapshotUploader* uploader = new SnapshotUploader(url, filename); - + QFile* file = new QFile(filename); Q_ASSERT(file->exists()); file->open(QIODevice::ReadOnly); @@ -458,20 +480,16 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) { imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\"")); imagePart.setBodyDevice(file); - + QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart + file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart multiPart->append(imagePart); - + auto accountManager = DependencyManager::get(); JSONCallbackParameters callbackParams(uploader, "uploadSuccess", uploader, "uploadFailure"); - accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, - AccountManagerAuth::Required, - QNetworkAccessManager::PostOperation, - callbackParams, - nullptr, - multiPart); + accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, AccountManagerAuth::Required, QNetworkAccessManager::PostOperation, + callbackParams, nullptr, multiPart); } QString Snapshot::getSnapshotsLocation() {