mirror of
https://thingvellir.net/git/overte
synced 2025-03-27 23:52:03 +01:00
Merge pull request #9045 from zfox23/animated_snapshots
Animated GIF Snapshots
This commit is contained in:
commit
1c4b2eba25
15 changed files with 349 additions and 61 deletions
20
cmake/externals/GifCreator/CMakeLists.txt
vendored
Normal file
20
cmake/externals/GifCreator/CMakeLists.txt
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
set(EXTERNAL_NAME GifCreator)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(
|
||||
${EXTERNAL_NAME}
|
||||
URL https://hifi-public.s3.amazonaws.com/dependencies/GifCreator.zip
|
||||
URL_MD5 8ac8ef5196f47c658dce784df5ecdb70
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
LOG_DOWNLOAD 1
|
||||
)
|
||||
|
||||
# Hide this external target (for ide users)
|
||||
set_target_properties(${EXTERNAL_NAME} PROPERTIES FOLDER "hidden/externals")
|
||||
|
||||
ExternalProject_Get_Property(${EXTERNAL_NAME} INSTALL_DIR)
|
||||
|
||||
string(TOUPPER ${EXTERNAL_NAME} EXTERNAL_NAME_UPPER)
|
||||
set(${EXTERNAL_NAME_UPPER}_INCLUDE_DIRS ${INSTALL_DIR}/src/${EXTERNAL_NAME} CACHE PATH "List of GifCreator include directories")
|
26
cmake/modules/FindGifCreator.cmake
Normal file
26
cmake/modules/FindGifCreator.cmake
Normal file
|
@ -0,0 +1,26 @@
|
|||
#
|
||||
# FindGifCreator.cmake
|
||||
#
|
||||
# Try to find GifCreator include path.
|
||||
# Once done this will define
|
||||
#
|
||||
# GIFCREATOR_INCLUDE_DIRS
|
||||
#
|
||||
# Created on 11/15/2016 by Zach Fox
|
||||
# Copyright 2016 High Fidelity, Inc.
|
||||
#
|
||||
# Distributed under the Apache License, Version 2.0.
|
||||
# See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
#
|
||||
|
||||
# setup hints for GifCreator search
|
||||
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
|
||||
hifi_library_search_hints("GIFCREATOR")
|
||||
|
||||
# locate header
|
||||
find_path(GIFCREATOR_INCLUDE_DIRS "GifCreator/GifCreator.h" HINTS ${GIFCREATOR_SEARCH_DIRS})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(GIFCREATOR DEFAULT_MSG GIFCREATOR_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(GIFCREATOR_INCLUDE_DIRS GIFCREATOR_SEARCH_DIRS)
|
|
@ -351,3 +351,7 @@ if (ANDROID)
|
|||
|
||||
qt_create_apk()
|
||||
endif ()
|
||||
|
||||
add_dependency_external_projects(GifCreator)
|
||||
find_package(GifCreator REQUIRED)
|
||||
target_include_directories(${TARGET_NAME} PUBLIC ${GIFCREATOR_INCLUDE_DIRS})
|
||||
|
|
|
@ -152,6 +152,7 @@
|
|||
#include "ui/LoginDialog.h"
|
||||
#include "ui/overlays/Cube3DOverlay.h"
|
||||
#include "ui/Snapshot.h"
|
||||
#include "ui/SnapshotAnimated.h"
|
||||
#include "ui/StandAloneJSConsole.h"
|
||||
#include "ui/Stats.h"
|
||||
#include "ui/UpdateDialog.h"
|
||||
|
@ -5428,19 +5429,27 @@ void Application::toggleLogDialog() {
|
|||
}
|
||||
}
|
||||
|
||||
void Application::takeSnapshot(bool notify, float aspectRatio) {
|
||||
postLambdaEvent([notify, aspectRatio, this] {
|
||||
|
||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
|
||||
QMediaPlayer* player = new QMediaPlayer();
|
||||
QFileInfo inf = QFileInfo(PathUtils::resourcesPath() + "sounds/snap.wav");
|
||||
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
|
||||
player->play();
|
||||
|
||||
// Get a screenshot and save it
|
||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio));
|
||||
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path, notify);
|
||||
// If we're not doing an animated snapshot as well...
|
||||
if (!includeAnimated || !(SnapshotAnimated::alsoTakeAnimatedSnapshot.get())) {
|
||||
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
emit DependencyManager::get<WindowScriptingInterface>()->snapshotTaken(path, "", notify);
|
||||
} else {
|
||||
// Get an animated GIF snapshot and save it
|
||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Application::shareSnapshot(const QString& path) {
|
||||
postLambdaEvent([path] {
|
||||
// not much to do here, everything is done in snapshot code...
|
||||
|
|
|
@ -266,7 +266,7 @@ public:
|
|||
float getAvatarSimrate() const { return _avatarSimCounter.rate(); }
|
||||
float getAverageSimsPerSecond() const { return _simCounter.rate(); }
|
||||
|
||||
void takeSnapshot(bool notify, float aspectRatio = 0.0f);
|
||||
void takeSnapshot(bool notify, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& filename);
|
||||
|
||||
model::SkyboxPointer getDefaultSkybox() const { return _defaultSkybox; }
|
||||
|
|
|
@ -199,8 +199,8 @@ void WindowScriptingInterface::copyToClipboard(const QString& text) {
|
|||
QApplication::clipboard()->setText(text);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, float aspectRatio) {
|
||||
qApp->takeSnapshot(notify, aspectRatio);
|
||||
void WindowScriptingInterface::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
|
||||
qApp->takeSnapshot(notify, includeAnimated, aspectRatio);
|
||||
}
|
||||
|
||||
void WindowScriptingInterface::shareSnapshot(const QString& path) {
|
||||
|
|
|
@ -52,7 +52,7 @@ public slots:
|
|||
QScriptValue save(const QString& title = "", const QString& directory = "", const QString& nameFilter = "");
|
||||
void showAssetServer(const QString& upload = "");
|
||||
void copyToClipboard(const QString& text);
|
||||
void takeSnapshot(bool notify = true, float aspectRatio = 0.0f);
|
||||
void takeSnapshot(bool notify = true, bool includeAnimated = false, float aspectRatio = 0.0f);
|
||||
void shareSnapshot(const QString& path);
|
||||
bool isPhysicsEnabled();
|
||||
|
||||
|
@ -60,7 +60,7 @@ signals:
|
|||
void domainChanged(const QString& domainHostname);
|
||||
void svoImportRequested(const QString& url);
|
||||
void domainConnectionRefused(const QString& reasonMessage, int reasonCode, const QString& extraInfo);
|
||||
void snapshotTaken(const QString& path, bool notify);
|
||||
void snapshotTaken(const QString& pathStillSnapshot, const QString& pathAnimatedSnapshot, bool notify);
|
||||
void snapshotShared(const QString& error);
|
||||
|
||||
private:
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "LODManager.h"
|
||||
#include "Menu.h"
|
||||
#include "Snapshot.h"
|
||||
#include "SnapshotAnimated.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
#include "AmbientOcclusionEffect.h"
|
||||
|
@ -83,6 +84,20 @@ void setupPreferences() {
|
|||
auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
{
|
||||
auto getter = []()->bool { return SnapshotAnimated::alsoTakeAnimatedSnapshot.get(); };
|
||||
auto setter = [](bool value) { SnapshotAnimated::alsoTakeAnimatedSnapshot.set(value); };
|
||||
preferences->addPreference(new CheckPreference(SNAPSHOTS, "Take Animated GIF Snapshot with HUD Button", getter, setter));
|
||||
}
|
||||
{
|
||||
auto getter = []()->float { return SnapshotAnimated::snapshotAnimatedDuration.get(); };
|
||||
auto setter = [](float value) { SnapshotAnimated::snapshotAnimatedDuration.set(value); };
|
||||
auto preference = new SpinnerPreference(SNAPSHOTS, "Animated Snapshot Duration", getter, setter);
|
||||
preference->setMin(3);
|
||||
preference->setMax(10);
|
||||
preference->setStep(1);
|
||||
preferences->addPreference(preference);
|
||||
}
|
||||
|
||||
// Scripts
|
||||
{
|
||||
|
|
|
@ -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<AddressManager>()->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);
|
||||
|
|
139
interface/src/ui/SnapshotAnimated.cpp
Normal file
139
interface/src/ui/SnapshotAnimated.cpp
Normal file
|
@ -0,0 +1,139 @@
|
|||
//
|
||||
// SnapshotAnimated.cpp
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Zach Fox on 11/14/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QString>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <QtConcurrent/qtconcurrentrun.h>
|
||||
#include "SnapshotAnimated.h"
|
||||
|
||||
QTimer* SnapshotAnimated::snapshotAnimatedTimer = NULL;
|
||||
qint64 SnapshotAnimated::snapshotAnimatedTimestamp = 0;
|
||||
qint64 SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = 0;
|
||||
bool SnapshotAnimated::snapshotAnimatedTimerRunning = false;
|
||||
QString SnapshotAnimated::snapshotAnimatedPath;
|
||||
QString SnapshotAnimated::snapshotStillPath;
|
||||
QVector<QImage> SnapshotAnimated::snapshotAnimatedFrameVector;
|
||||
QVector<qint64> SnapshotAnimated::snapshotAnimatedFrameDelayVector;
|
||||
Application* SnapshotAnimated::app;
|
||||
float SnapshotAnimated::aspectRatio;
|
||||
QSharedPointer<WindowScriptingInterface> SnapshotAnimated::snapshotAnimatedDM;
|
||||
GifWriter SnapshotAnimated::snapshotAnimatedGifWriter;
|
||||
|
||||
|
||||
Setting::Handle<bool> SnapshotAnimated::alsoTakeAnimatedSnapshot("alsoTakeAnimatedSnapshot", true);
|
||||
Setting::Handle<float> SnapshotAnimated::snapshotAnimatedDuration("snapshotAnimatedDuration", SNAPSNOT_ANIMATED_DURATION_SECS);
|
||||
|
||||
void SnapshotAnimated::saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm) {
|
||||
// If we're not in the middle of capturing an animated snapshot...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
SnapshotAnimated::snapshotAnimatedTimer = new QTimer();
|
||||
SnapshotAnimated::aspectRatio = aspectRatio;
|
||||
SnapshotAnimated::app = app;
|
||||
SnapshotAnimated::snapshotAnimatedDM = dm;
|
||||
// Define the output location of the still and animated snapshots.
|
||||
SnapshotAnimated::snapshotStillPath = pathStill;
|
||||
SnapshotAnimated::snapshotAnimatedPath = pathStill;
|
||||
SnapshotAnimated::snapshotAnimatedPath.replace("jpg", "gif");
|
||||
|
||||
// Ensure the snapshot timer is Precise (attempted millisecond precision)
|
||||
SnapshotAnimated::snapshotAnimatedTimer->setTimerType(Qt::PreciseTimer);
|
||||
|
||||
// Connect the snapshotAnimatedTimer QTimer to the lambda slot function
|
||||
QObject::connect((SnapshotAnimated::snapshotAnimatedTimer), &QTimer::timeout, captureFrames);
|
||||
|
||||
// Start the snapshotAnimatedTimer QTimer - argument for this is in milliseconds
|
||||
SnapshotAnimated::snapshotAnimatedTimerRunning = true;
|
||||
SnapshotAnimated::snapshotAnimatedTimer->start(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC);
|
||||
// If we're already in the middle of capturing an animated snapshot...
|
||||
} else {
|
||||
// Just tell the dependency manager that the capture of the still snapshot has taken place.
|
||||
emit dm->snapshotTaken(pathStill, "", false);
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotAnimated::captureFrames() {
|
||||
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(SnapshotAnimated::app->getActiveDisplayPlugin()->getScreenshot(SnapshotAnimated::aspectRatio));
|
||||
frame = frame.scaledToWidth(SNAPSNOT_ANIMATED_WIDTH);
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.append(frame);
|
||||
|
||||
// If that was the first frame...
|
||||
if (SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp == 0) {
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
// Record the first frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = SnapshotAnimated::snapshotAnimatedTimestamp;
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(SNAPSNOT_ANIMATED_FRAME_DELAY_MSEC / 10);
|
||||
// If this is an intermediate or the final frame...
|
||||
} else {
|
||||
// Push the current frame delay onto the vector
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.append(round(((float)(QDateTime::currentMSecsSinceEpoch() - SnapshotAnimated::snapshotAnimatedTimestamp)) / 10));
|
||||
// Record the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
|
||||
// If that was the last frame...
|
||||
if ((SnapshotAnimated::snapshotAnimatedTimestamp - SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp) >= (SnapshotAnimated::snapshotAnimatedDuration.get() * MSECS_PER_SECOND)) {
|
||||
SnapshotAnimated::snapshotAnimatedTimerRunning = false;
|
||||
// Reset the current frame timestamp
|
||||
SnapshotAnimated::snapshotAnimatedTimestamp = 0;
|
||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = 0;
|
||||
|
||||
// Kick off the thread that'll pack the frames into the GIF
|
||||
QtConcurrent::run(processFrames);
|
||||
// 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
|
||||
SnapshotAnimated::snapshotAnimatedTimer->stop();
|
||||
delete SnapshotAnimated::snapshotAnimatedTimer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SnapshotAnimated::processFrames() {
|
||||
uint32_t width = SnapshotAnimated::snapshotAnimatedFrameVector[0].width();
|
||||
uint32_t height = SnapshotAnimated::snapshotAnimatedFrameVector[0].height();
|
||||
|
||||
// Create the GIF from the temporary files
|
||||
// Write out the header and beginning of the GIF file
|
||||
GifBegin(
|
||||
&(SnapshotAnimated::snapshotAnimatedGifWriter),
|
||||
qPrintable(SnapshotAnimated::snapshotAnimatedPath),
|
||||
width,
|
||||
height,
|
||||
1); // "1" means "yes there is a delay" with this GifCreator library.
|
||||
for (int itr = 0; itr < SnapshotAnimated::snapshotAnimatedFrameVector.size(); itr++) {
|
||||
// Write each frame to the GIF
|
||||
GifWriteFrame(&(SnapshotAnimated::snapshotAnimatedGifWriter),
|
||||
(uint8_t*)SnapshotAnimated::snapshotAnimatedFrameVector[itr].convertToFormat(QImage::Format_RGBA8888).bits(),
|
||||
width,
|
||||
height,
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector[itr]);
|
||||
}
|
||||
// Write out the end of the GIF
|
||||
GifEnd(&(SnapshotAnimated::snapshotAnimatedGifWriter));
|
||||
|
||||
// Clear out the frame and frame delay vectors.
|
||||
// Also release the memory not required to store the items.
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.clear();
|
||||
SnapshotAnimated::snapshotAnimatedFrameVector.squeeze();
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.clear();
|
||||
SnapshotAnimated::snapshotAnimatedFrameDelayVector.squeeze();
|
||||
|
||||
// Let the dependency manager know that the snapshots have been taken.
|
||||
emit SnapshotAnimated::snapshotAnimatedDM->snapshotTaken(SnapshotAnimated::snapshotStillPath, SnapshotAnimated::snapshotAnimatedPath, false);
|
||||
}
|
58
interface/src/ui/SnapshotAnimated.h
Normal file
58
interface/src/ui/SnapshotAnimated.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
//
|
||||
// SnapshotAnimated.h
|
||||
// interface/src/ui
|
||||
//
|
||||
// Created by Zach Fox on 11/14/16.
|
||||
// Copyright 2016 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_SnapshotAnimated_h
|
||||
#define hifi_SnapshotAnimated_h
|
||||
|
||||
#include <QtCore/QVector>
|
||||
#include <Application.h>
|
||||
#include <DependencyManager.h>
|
||||
#include <GifCreator.h>
|
||||
#include <qtimer.h>
|
||||
#include <SettingHandle.h>
|
||||
#include "scripting/WindowScriptingInterface.h"
|
||||
|
||||
// If the snapshot width or the framerate are too high for the
|
||||
// application to handle, the framerate of the output GIF will drop.
|
||||
#define SNAPSNOT_ANIMATED_WIDTH (480)
|
||||
// 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)
|
||||
|
||||
class SnapshotAnimated {
|
||||
private:
|
||||
static QTimer* snapshotAnimatedTimer;
|
||||
static qint64 snapshotAnimatedTimestamp;
|
||||
static qint64 snapshotAnimatedFirstFrameTimestamp;
|
||||
static bool snapshotAnimatedTimerRunning;
|
||||
static QString snapshotStillPath;
|
||||
|
||||
static QString snapshotAnimatedPath;
|
||||
static QVector<QImage> snapshotAnimatedFrameVector;
|
||||
static QVector<qint64> snapshotAnimatedFrameDelayVector;
|
||||
static QSharedPointer<WindowScriptingInterface> snapshotAnimatedDM;
|
||||
static Application* app;
|
||||
static float aspectRatio;
|
||||
|
||||
static GifWriter snapshotAnimatedGifWriter;
|
||||
|
||||
static void captureFrames();
|
||||
static void processFrames();
|
||||
public:
|
||||
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm);
|
||||
static Setting::Handle<bool> alsoTakeAnimatedSnapshot;
|
||||
static Setting::Handle<float> snapshotAnimatedDuration;
|
||||
};
|
||||
|
||||
#endif // hifi_SnapshotAnimated_h
|
|
@ -1,48 +1,48 @@
|
|||
<html>
|
||||
<head>
|
||||
<head>
|
||||
<title>Share</title>
|
||||
<link rel="stylesheet" type="text/css" href="css/edit-style.css">
|
||||
<link rel="stylesheet" type="text/css" href="css/SnapshotReview.css">
|
||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||
<script type="text/javascript" src="js/eventBridgeLoader.js"></script>
|
||||
<script type="text/javascript" src="js/SnapshotReview.js"></script>
|
||||
</head>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body>
|
||||
<div class="snapshot-container">
|
||||
<div class="snapshot-column-left">
|
||||
<div class="snapsection">
|
||||
<label class="title">Snapshot successfully saved!</label>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="snapsection">
|
||||
<div id="sharing">
|
||||
<div class="prompt">Would you like to share your pic in the Snapshots feed?</div>
|
||||
<div class="button">
|
||||
<span class="compound-button">
|
||||
<input type="button" class="blue" id="share" value="Share in Feed" onclick="shareSelected()"/>
|
||||
<span class="glyph"></span>
|
||||
</span>
|
||||
<div class="snapshot-column-left">
|
||||
<div class="snapsection">
|
||||
<label class="title">Snapshot successfully saved!</label>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="snapsection">
|
||||
<div id="sharing">
|
||||
<div class="prompt">Would you like to share your pics in the Snapshots feed?</div>
|
||||
<div class="button">
|
||||
<span class="compound-button">
|
||||
<input type="button" class="blue" id="share" value="Share in Feed" onclick="shareSelected()" />
|
||||
<span class="glyph"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button">
|
||||
<input type="button" class="black" id="close" value="Don't Share" onclick="doNotShare()" />
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="snapsection">
|
||||
<span class="setting">
|
||||
<input type="button" class="glyph naked" id="snapshotSettings" value="@" onclick="snapshotSettings()" />
|
||||
<label for="snapshotSettings">Snapshot settings</label>
|
||||
</span>
|
||||
<span class="setting checkbox">
|
||||
<input id="openFeed" type="checkbox" checked />
|
||||
<label for="openFeed">Open feed after</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="button">
|
||||
<input type="button" class="black" id="close" value="Don't Share" onclick="doNotShare()"/>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="snapsection">
|
||||
<span class="setting">
|
||||
<input type="button" class="glyph naked" id="snapshotSettings" value="@" onclick="snapshotSettings()" />
|
||||
<label for="snapshotSettings">Snapshot settings</label>
|
||||
</span>
|
||||
<span class="setting checkbox">
|
||||
<input id="openFeed" type="checkbox" checked/>
|
||||
<label for="openFeed">Open feed after</label>
|
||||
</span>
|
||||
<div id="snapshot-images" class="snapshot-column-right">
|
||||
</div>
|
||||
</div>
|
||||
<div id="snapshot-images" class="snapshot-column-right"/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
|
||||
var paths = [], idCounter = 0, useCheckboxes;
|
||||
function addImage(data) {
|
||||
if (!data.localPath) {
|
||||
return;
|
||||
}
|
||||
var div = document.createElement("DIV"),
|
||||
input = document.createElement("INPUT"),
|
||||
label = document.createElement("LABEL"),
|
||||
|
@ -20,21 +23,22 @@ 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 = true;
|
||||
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);
|
||||
|
||||
}
|
||||
function handleShareButtons(shareMsg) {
|
||||
var openFeed = document.getElementById('openFeed');
|
||||
|
@ -49,7 +53,7 @@ function handleShareButtons(shareMsg) {
|
|||
window.onload = function () {
|
||||
// Something like the following will allow testing in a browser.
|
||||
//addImage({localPath: 'c:/Users/howar/OneDrive/Pictures/hifi-snap-by--on-2016-07-27_12-58-43.jpg'});
|
||||
//addImage({localPath: 'http://lorempixel.com/1512/1680'});
|
||||
//addImage({ localPath: 'http://lorempixel.com/1512/1680' });
|
||||
openEventBridge(function () {
|
||||
// Set up a handler for receiving the data, and tell the .js we are ready to receive it.
|
||||
EventBridge.scriptEventReceived.connect(function (message) {
|
||||
|
|
|
@ -522,13 +522,13 @@ function onEditError(msg) {
|
|||
}
|
||||
|
||||
|
||||
function onSnapshotTaken(path, notify) {
|
||||
function onSnapshotTaken(pathStillSnapshot, pathAnimatedSnapshot, notify) {
|
||||
if (notify) {
|
||||
var imageProperties = {
|
||||
path: "file:///" + path,
|
||||
path: "file:///" + pathStillSnapshot,
|
||||
aspectRatio: Window.innerWidth / Window.innerHeight
|
||||
};
|
||||
createNotification(wordWrap("Snapshot saved to " + path), NotificationType.SNAPSHOT, imageProperties);
|
||||
createNotification(wordWrap("Snapshot saved to " + pathStillSnapshot), NotificationType.SNAPSHOT, imageProperties);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.)
|
||||
|
@ -120,11 +120,11 @@ function onClicked() {
|
|||
|
||||
// take snapshot (with no notification)
|
||||
Script.setTimeout(function () {
|
||||
Window.takeSnapshot(false, 1.91);
|
||||
Window.takeSnapshot(false, true, 1.91);
|
||||
}, SNAPSHOT_DELAY);
|
||||
}
|
||||
|
||||
function resetButtons(path, notify) {
|
||||
function resetButtons(pathStillSnapshot, pathAnimatedSnapshot, notify) {
|
||||
// show overlays if they were on
|
||||
if (resetOverlays) {
|
||||
Menu.setIsOptionChecked("Overlays", true);
|
||||
|
@ -141,7 +141,8 @@ function resetButtons(path, notify) {
|
|||
|
||||
// last element in data array tells dialog whether we can share or not
|
||||
confirmShare([
|
||||
{ localPath: path },
|
||||
{ localPath: pathAnimatedSnapshot },
|
||||
{ localPath: pathStillSnapshot },
|
||||
{
|
||||
canShare: !!location.placename,
|
||||
openFeedAfterShare: shouldOpenFeedAfterShare()
|
||||
|
|
Loading…
Reference in a new issue