diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h index 3b54112f6c..0167ec0035 100644 --- a/interface/src/scripting/WindowScriptingInterface.h +++ b/interface/src/scripting/WindowScriptingInterface.h @@ -334,9 +334,9 @@ public slots: * dimensions is adjusted in order to match the aspect ratio. * @param {string} [filename=""] - If a filename is not provided, the image is saved as "overte-snap-by-<user * name>-on-YYYY-MM-DD_HH-MM-SS". - *

Still images are saved in JPEG or PNG format according to the extension provided — ".jpg", - * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of - * ".jpg". Animated images are saved in GIF format.

+ *

Still images are saved in JPEG, PNG or WEBP format according to the extension provided — ".jpg", + * ".jpeg", ".png", or ".webp" — or if not provided then in the format chosen in general settings, + * Default is PNG. Animated images are saved in GIF format.

* * @example Using the snapshot function and signals. * function onStillSnapshotTaken(path, notify) { @@ -371,11 +371,11 @@ public slots: * @function Window.takeSecondaryCameraSnapshot * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * signal. - * @param {string} [filename=""] - If a filename is not provided, the image is saved as "overte-snap-by-<user + * @param {string} [filename=""] - If a filename is not provided, the image is saved as "overte-snap-by-<user * name>-on-YYYY-MM-DD_HH-MM-SS". - *

Images are saved in JPEG or PNG format according to the extension provided — ".jpg", - * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of - * ".jpg".

+ *

Still images are saved in JPEG, PNG or WEBP format according to the extension provided — ".jpg", + * ".jpeg", ".png", or ".webp" — or if not provided then in the format chosen in general settings, + * Default is PNG. Animated images are saved in GIF format.

*/ void takeSecondaryCameraSnapshot(const bool& notify = true, const QString& filename = QString()); @@ -390,11 +390,11 @@ public slots: * otherwise it is saved as an equirectangular image. * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * signal. - * @param {string} [filename=""] - If a filename is not provided, the image is saved as "overte-snap-by-<user + * @param {string} [filename=""] - If a filename is not provided, the image is saved as "overte-snap-by-<user * name>-on-YYYY-MM-DD_HH-MM-SS". - *

Images are saved in JPEG or PNG format according to the extension provided — ".jpg", - * ".jpeg", or ".png" — or if not provided then in JPEG format with an extension of - * ".jpg".

+ *

Still images are saved in JPEG, PNG or WEBP format according to the extension provided — ".jpg", + * ".jpeg", ".png", or ".webp" — or if not provided then in the format chosen in general settings, + * Default is PNG. Animated images are saved in GIF format.

*/ void takeSecondaryCamera360Snapshot(const glm::vec3& cameraPosition, const bool& cubemapOutputFormat = false, const bool& notify = true, const QString& filename = QString()); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index adeb0d1dcd..6d13f586d4 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -238,15 +238,6 @@ void setupPreferences() { auto preference = new BrowsePreference(SNAPSHOTS, "Put my snapshots here", getter, setter); preferences->addPreference(preference); } - { - 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(1); - preference->setMax(5); - preference->setStep(1); - preferences->addPreference(preference); - } { auto getter = []()->bool { @@ -258,6 +249,46 @@ void setupPreferences() { preferences->addPreference(new CheckPreference(SNAPSHOTS, "Display snapshot notifications", getter, setter)); } + { + auto getter = []()->int { + if (!DependencyManager::get()->_snapshotFormat.isSet()) { + DependencyManager::get()->setSnapshotFormat(DependencyManager::get()->getAvailableSnapshotFormats()[0]); + } + return DependencyManager::get()->getAvailableSnapshotFormats().indexOf(DependencyManager::get()->getSnapshotFormat()); }; + auto setter = [](int value) { DependencyManager::get()->setSnapshotFormat(DependencyManager::get()->getAvailableSnapshotFormats()[value]); }; + auto preference = new RadioButtonsPreference(SNAPSHOTS, "Snapshot format", getter, setter); + QStringList items; + items << DependencyManager::get()->getAvailableSnapshotFormatsWithDescriptions(); + preference->setHeading("Snapshot format"); + preference->setItems(items); + preferences->addPreference(preference); + } + + { + auto getter = []()->int { + if (!DependencyManager::get()->_animatedSnapshotFormat.isSet()) { + DependencyManager::get()->setAnimatedSnapshotFormat(DependencyManager::get()->getAvailableAnimatedSnapshotFormats()[0]); + } + return DependencyManager::get()->getAvailableAnimatedSnapshotFormats().indexOf(DependencyManager::get()->getAnimatedSnapshotFormat()); }; + auto setter = [](int value) { DependencyManager::get()->setAnimatedSnapshotFormat(DependencyManager::get()->getAvailableAnimatedSnapshotFormats()[value]); }; + auto preference = new RadioButtonsPreference(SNAPSHOTS, "Animated snapshot format", getter, setter); + QStringList items; + items << DependencyManager::get()->getAvailableAnimatedSnapshotFormatsWithDescriptions(); + preference->setHeading("Animated snapshot format"); + preference->setItems(items); + preferences->addPreference(preference); + } + + { + 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(1); + preference->setMax(5); + preference->setStep(1); + preferences->addPreference(preference); + } + { auto getter = []()->bool { return !Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger); }; auto setter = [](bool value) { Menu::getInstance()->setIsOptionChecked(MenuOption::DisableActivityLogger, !value); }; diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp index e736ed9430..2884574767 100644 --- a/interface/src/ui/Snapshot.cpp +++ b/interface/src/ui/Snapshot.cpp @@ -4,6 +4,7 @@ // // Created by Stojce Slavkovski on 1/26/14. // Copyright 2014 High Fidelity, Inc. +// Copyright 2022 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html @@ -44,12 +45,13 @@ // filename format: overte-snap-by-%username%-on-%date%_%time%_@-%location%.png // %1 <= username, %2 <= date and time, %3 <= current location -const QString FILENAME_PATH_FORMAT = "overte-snap-by-%1-on-%2.png"; +const QString FILENAME_PATH_FORMAT = "overte-snap-by-%1-on-%2"; +const QString DEFAULT_FORMAT = "png"; const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss"; const QString SNAPSHOTS_DIRECTORY = "Snapshots"; const QString URL = "overte_url"; static const int SNAPSHOT_360_TIMER_INTERVAL = 350; -static const QList SUPPORTED_IMAGE_FORMATS = { "jpg", "jpeg", "png" }; +static const QList SUPPORTED_IMAGE_FORMATS = { "jpg", "jpeg", "png", "webp" }; Snapshot::Snapshot() { _snapshotTimer.setSingleShot(false); @@ -110,6 +112,7 @@ QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QStr return ""; } + static const float CUBEMAP_SIDE_PIXEL_DIMENSION = 2048.0f; static const float SNAPSHOT_360_FOV = 90.0f; static const float SNAPSHOT_360_NEARCLIP = 0.3f; @@ -389,7 +392,11 @@ QFile* Snapshot::savedFileForSnapshot(QImage& shot, imageQuality = 50; } } else { - filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT)); + QString selectedFormat(DEFAULT_FORMAT); + if (_snapshotFormat.get() != "") { + selectedFormat = _snapshotFormat.get(); + } + filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT)) + "." + selectedFormat; QFileInfo snapshotFileInfo(filename); QString filenameSuffix = snapshotFileInfo.suffix(); filenameSuffix = filenameSuffix.toLower(); @@ -516,3 +523,43 @@ QString Snapshot::getSnapshotsLocation() { void Snapshot::setSnapshotsLocation(const QString& location) { _snapshotsLocation.set(location); } + +QString Snapshot::getSnapshotFormat(){ + return _snapshotFormat.get(); +} + +void Snapshot::setSnapshotFormat(const QString& format){ + if (getAvailableSnapshotFormats().contains(format)) { + _snapshotFormat.set(format); + } else { + qDebug() << "Snapshot format not supported: " << format; + } +} + +QString Snapshot::getAnimatedSnapshotFormat(){ + return _animatedSnapshotFormat.get(); +} + +void Snapshot::setAnimatedSnapshotFormat(const QString& format){ + if (getAvailableAnimatedSnapshotFormats().contains(format)) { + _animatedSnapshotFormat.set(format); + } else { + qDebug() << "Snapshot format not supported: " << format; + } +} + +QStringList Snapshot::getAvailableSnapshotFormats() { + return QStringList({"png", "jpg", "webp"}); +} + +QStringList Snapshot::getAvailableSnapshotFormatsWithDescriptions() { + return QStringList({"PNG - lossless, large file size", "JPG - lossy, fast compression", "WEBP - lossy, higher quality and file size than JPG"}); +} + +QStringList Snapshot::getAvailableAnimatedSnapshotFormats() { + return QStringList({"gif"}); +} + +QStringList Snapshot::getAvailableAnimatedSnapshotFormatsWithDescriptions() { + return QStringList({"GIF"}); +} diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h index f6c57b0e42..61af735af7 100644 --- a/interface/src/ui/Snapshot.h +++ b/interface/src/ui/Snapshot.h @@ -65,6 +65,8 @@ public: Setting::Handle _snapshotsLocation{ "snapshotsLocation" }; Setting::Handle _snapshotNotifications{ "snapshotNotifications", true }; + Setting::Handle _snapshotFormat{ "snapshotFormat" }; + Setting::Handle _animatedSnapshotFormat{ "animatedSnapshotFormat" }; void uploadSnapshot(const QString& filename, const QUrl& href = QUrl("")); signals: @@ -97,6 +99,64 @@ public slots: * @param {String} location - The path to save snapshots to. */ Q_INVOKABLE void setSnapshotsLocation(const QString& location); + + /*@jsdoc + * Gets the currently selected snapshot format. + * @function Snapshot.getSnapshotFormat + * @returns {string} Currently selected snapshot format. + */ + Q_INVOKABLE QString getSnapshotFormat(); + + /*@jsdoc + * Sets the snapshot format. + * @function Snapshot.setSnapshotFormat + * @param {String} format - one of the format names returned by Snapshot.getAvailableSnapshotFormats(). + */ + Q_INVOKABLE void setSnapshotFormat(const QString& format); + + /*@jsdoc + * Gets the currently selected animated snapshot format. + * @function Snapshot.getAnimatedSnapshotFormat + * @returns {Array.} Currently selected snapshot format. + */ + Q_INVOKABLE QString getAnimatedSnapshotFormat(); + + /*@jsdoc + * Sets the snapshot format. + * @function Snapshot.setAnimatedSnapshotFormat + * @param {String} format - one of the format names returned by Snapshot.getAvailableSnapshotFormats(). + */ + Q_INVOKABLE void setAnimatedSnapshotFormat(const QString& format); + + /*@jsdoc + * Returns a list of supported snapshot formats. + * @function Snapshot.getAvailableSnapshotFormats + * @returns {Array.} List of supported snapshot formats. + */ + Q_INVOKABLE QStringList getAvailableSnapshotFormats(); + + /*@jsdoc + * Returns a list of supported snapshot formats with short descriptions. + * @function Snapshot.getAvailableSnapshotFormatsWithDescriptions + * @returns {Array.} List of supported snapshot formats with short descriptions. + */ + Q_INVOKABLE QStringList getAvailableSnapshotFormatsWithDescriptions(); + + /*@jsdoc + * Returns a list of supported animated snapshot formats. + * @function Snapshot.getAvailableAnimatedSnapshotFormats + * @returns {Array.} List of supported animated snapshot formats. + */ + Q_INVOKABLE QStringList getAvailableAnimatedSnapshotFormats(); + + /*@jsdoc + * Returns a list of supported animated snapshot formats with short descriptions. + * @function Snapshot.getAvailableAnimatedSnapshotFormatsWithDescriptions + * @returns {Array.} List of supported animated snapshot formats with short descriptions. + */ + Q_INVOKABLE QStringList getAvailableAnimatedSnapshotFormatsWithDescriptions(); + + private slots: void takeNextSnapshot();