diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b21a27977e..d71cc12858 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5440,7 +5440,14 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa // Get a screenshot and save it QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio)); - SnapshotAnimated::saveSnapshotAnimated(includeAnimated, path, aspectRatio, qApp, DependencyManager::get()); + // If we're not doing an animated snapshot as well... + if (!includeAnimated) { + // Tell the dependency manager that the capture of the still snapshot has taken place. + emit DependencyManager::get()->snapshotTaken(path, "", notify); + } else { + // Get an animated GIF snapshot and save it + SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get()); + } }); } void Application::shareSnapshot(const QString& path) { diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index 1bf5f5de4e..5df0d4575b 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -51,16 +51,24 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { return NULL; } - QImage shot(snapshotPath); + QUrl url; - // no location data stored - if (shot.text(URL).isEmpty()) { + if (snapshotPath.right(3) == "jpg") { + QImage shot(snapshotPath); + + // no location data stored + if (shot.text(URL).isEmpty()) { + return NULL; + } + + // parsing URL + url = QUrl(shot.text(URL), QUrl::ParsingMode::StrictMode); + } else if (snapshotPath.right(3) == "gif") { + url = QUrl(DependencyManager::get()->currentShareableAddress()); + } else { return NULL; } - // parsing URL - QUrl url = QUrl(shot.text(URL), QUrl::ParsingMode::StrictMode); - SnapshotMetaData* data = new SnapshotMetaData(); data->setURL(url); @@ -156,7 +164,11 @@ void Snapshot::uploadSnapshot(const QString& filename) { file->open(QIODevice::ReadOnly); QHttpPart imagePart; - imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + if (filename.right(3) == "gif") { + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/gif")); + } else { + imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg")); + } imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\"")); imagePart.setBodyDevice(file); diff --git a/interface/src/ui/SnapshotAnimated.cpp b/interface/src/ui/SnapshotAnimated.cpp index fece5c6da8..0d2c6707a5 100644 --- a/interface/src/ui/SnapshotAnimated.cpp +++ b/interface/src/ui/SnapshotAnimated.cpp @@ -25,10 +25,9 @@ bool SnapshotAnimated::snapshotAnimatedTimerRunning = false; QString SnapshotAnimated::snapshotAnimatedPath; QString SnapshotAnimated::snapshotStillPath; -void SnapshotAnimated::saveSnapshotAnimated(bool includeAnimated, QString pathStill, float aspectRatio, Application* app, QSharedPointer dm) { +void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer dm) { // If we're not in the middle of capturing an animated snapshot... - if ((SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) && (includeAnimated)) - { + if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) { // Define the output location of the still and animated snapshots. SnapshotAnimated::snapshotStillPath = pathStill; SnapshotAnimated::snapshotAnimatedPath = pathStill; @@ -41,33 +40,15 @@ void SnapshotAnimated::saveSnapshotAnimated(bool includeAnimated, QString pathSt // Connect the snapshotAnimatedTimer QTimer to the lambda slot function QObject::connect(&(SnapshotAnimated::snapshotAnimatedTimer), &QTimer::timeout, [=] { - if (SnapshotAnimated::snapshotAnimatedTimerRunning) - { + if (SnapshotAnimated::snapshotAnimatedTimerRunning) { // Get a screenshot from the display, then scale the screenshot down, // then convert it to the image format the GIF library needs, // then save all that to the QImage named "frame" QImage frame(app->getActiveDisplayPlugin()->getScreenshot(aspectRatio)); - frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH); - frame = frame.convertToFormat(QImage::Format_RGBA8888); + frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH).convertToFormat(QImage::Format_RGBA8888); - // If this is the first frame... - if (SnapshotAnimated::snapshotAnimatedTimestamp == 0) - { - // Write out the header and beginning of the GIF file - GifBegin(&(SnapshotAnimated::snapshotAnimatedGifWriter), qPrintable(SnapshotAnimated::snapshotAnimatedPath), frame.width(), frame.height(), SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); - // Write the first to the gif - GifWriteFrame(&(SnapshotAnimated::snapshotAnimatedGifWriter), - (uint8_t*)frame.bits(), - frame.width(), - frame.height(), - SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); - // Record the current frame timestamp - SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp; - } - // If that was an intermediate or the final frame... - else - { + // If this is an intermediate or the final frame... + if (SnapshotAnimated::snapshotAnimatedTimestamp > 0) { // Variable used to determine how long the current frame took to pack qint64 framePackStartTime = QDateTime::currentMSecsSinceEpoch(); // Write the frame to the gif @@ -75,14 +56,13 @@ void SnapshotAnimated::saveSnapshotAnimated(bool includeAnimated, QString pathSt (uint8_t*)frame.bits(), frame.width(), frame.height(), - round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp + SnapshotAnimated::snapshotAnimatedLastWriteFrameDuration)) / 10)); - // Record how long it took for the current frame to pack - SnapshotAnimated::snapshotAnimatedLastWriteFrameDuration = QDateTime::currentMSecsSinceEpoch() - framePackStartTime; + round(((float)(framePackStartTime - SnapshotAnimated::snapshotAnimatedTimestamp + SnapshotAnimated::snapshotAnimatedLastWriteFrameDuration)) / 10)); // Record the current frame timestamp SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); + // Record how long it took for the current frame to pack + SnapshotAnimated::snapshotAnimatedLastWriteFrameDuration = SnapshotAnimated::snapshotAnimatedTimestamp - framePackStartTime; // If that was the last frame... - if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SNAPSNOT_ANIMATED_DURATION_SECS * 1000)) - { + if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SNAPSNOT_ANIMATED_DURATION_MSEC)) { // Stop the snapshot QTimer. This action by itself DOES NOT GUARANTEE // that the slot will not be called again in the future. // See: http://lists.qt-project.org/pipermail/qt-interest-old/2009-October/013926.html @@ -96,6 +76,19 @@ void SnapshotAnimated::saveSnapshotAnimated(bool includeAnimated, QString pathSt // Let the dependency manager know that the snapshots have been taken. emit dm->snapshotTaken(SnapshotAnimated::snapshotStillPath, SnapshotAnimated::snapshotAnimatedPath, false); } + // If that was the first frame... + } else { + // Write out the header and beginning of the GIF file + GifBegin(&(SnapshotAnimated::snapshotAnimatedGifWriter), qPrintable(SnapshotAnimated::snapshotAnimatedPath), frame.width(), frame.height(), SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); + // Write the first to the gif + GifWriteFrame(&(SnapshotAnimated::snapshotAnimatedGifWriter), + (uint8_t*)frame.bits(), + frame.width(), + frame.height(), + SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10); + // Record the current frame timestamp + SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch(); + SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp; } } }); @@ -103,10 +96,8 @@ void SnapshotAnimated::saveSnapshotAnimated(bool includeAnimated, QString pathSt // Start the snapshotAnimatedTimer QTimer - argument for this is in milliseconds SnapshotAnimated::snapshotAnimatedTimer.start(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC); SnapshotAnimated::snapshotAnimatedTimerRunning = true; - } // If we're already in the middle of capturing an animated snapshot... - else - { + } else { // Just tell the dependency manager that the capture of the still snapshot has taken place. emit dm->snapshotTaken(pathStill, "", false); } diff --git a/interface/src/ui/SnapshotAnimated.h b/interface/src/ui/SnapshotAnimated.h index 0c7faa1a8c..4de7c339dd 100644 --- a/interface/src/ui/SnapshotAnimated.h +++ b/interface/src/ui/SnapshotAnimated.h @@ -24,6 +24,7 @@ // This value should divide evenly into 100. Snapshot framerate is NOT guaranteed. #define SNAPSNOT_ANIMATED_TARGET_FRAMERATE (25) #define SNAPSNOT_ANIMATED_DURATION_SECS (3) +#define SNAPSNOT_ANIMATED_DURATION_MSEC (SNAPSNOT_ANIMATED_DURATION_SECS*1000) #define SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC (1000/SNAPSNOT_ANIMATED_TARGET_FRAMERATE) // This is the fudge factor that we add to the *first* GIF frame's "delay" value @@ -41,7 +42,7 @@ private: static QString snapshotAnimatedPath; static QString snapshotStillPath; public: - static void saveSnapshotAnimated(bool includeAnimated, QString pathStill, float aspectRatio, Application* app, QSharedPointer dm); + static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer dm); }; #endif // hifi_SnapshotAnimated_h diff --git a/scripts/system/html/js/SnapshotReview.js b/scripts/system/html/js/SnapshotReview.js index b2b7030b73..a1bb350789 100644 --- a/scripts/system/html/js/SnapshotReview.js +++ b/scripts/system/html/js/SnapshotReview.js @@ -23,17 +23,19 @@ function addImage(data) { function toggle() { data.share = input.checked; } img.src = data.localPath; div.appendChild(img); - data.share = true; if (useCheckboxes) { // I'd rather use css, but the included stylesheet is quite particular. // Our stylesheet(?) requires input.id to match label.for. Otherwise input doesn't display the check state. label.setAttribute('for', id); // cannot do label.for = input.id = id; input.type = "checkbox"; input.checked = (id === "p0"); + data.share = input.checked; input.addEventListener('change', toggle); div.class = "property checkbox"; div.appendChild(input); div.appendChild(label); + } else { + data.share = true; } document.getElementById("snapshot-images").appendChild(div); paths.push(data); diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index bdb54d313f..b4ebb99ef0 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -36,7 +36,7 @@ var SNAPSHOT_REVIEW_URL = Script.resolvePath("html/SnapshotReview.html"); var outstanding; function confirmShare(data) { - var dialog = new OverlayWebWindow('Snapshot Review', SNAPSHOT_REVIEW_URL, 800, 320); + var dialog = new OverlayWebWindow('Snapshot Review', SNAPSHOT_REVIEW_URL, 800, 520); function onMessage(message) { // Receives message from the html dialog via the qwebchannel EventBridge. This is complicated by the following: // 1. Although we can send POJOs, we cannot receive a toplevel object. (Arrays of POJOs are fine, though.)