Merge pull request #13405 from zfox23/MS16007_supportPngSnapshots

Implement MS16007: Add support for PNG snapshots
This commit is contained in:
Zach Fox 2018-06-18 13:10:45 -07:00 committed by GitHub
commit 84ed07cdb4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 57 additions and 29 deletions

View file

@ -319,6 +319,15 @@ public slots:
* {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted} * {@link Window.processingGifStarted|processingGifStarted} and {@link Window.processingGifCompleted|processingGifCompleted}
* are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings > * are emitted. The path to store the snapshots and the length of the animated GIF to capture are specified in Settings >
* General > Snapshots. * General > Snapshots.
*
* 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.
* If you want lossless Snapshots, supply a `.png` filename. Otherwise, use `.jpeg` or `.jpg`.
* 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
* @function Window.takeSnapshot * @function Window.takeSnapshot
* @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken} * @param {boolean} [notify=true] - This value is passed on through the {@link Window.stillSnapshotTaken|stillSnapshotTaken}
* signal. * signal.

View file

@ -50,6 +50,7 @@ const QString DATETIME_FORMAT = "yyyy-MM-dd_hh-mm-ss";
const QString SNAPSHOTS_DIRECTORY = "Snapshots"; const QString SNAPSHOTS_DIRECTORY = "Snapshots";
const QString URL = "highfidelity_url"; const QString URL = "highfidelity_url";
static const int SNAPSHOT_360_TIMER_INTERVAL = 350; static const int SNAPSHOT_360_TIMER_INTERVAL = 350;
static const QList<QString> SUPPORTED_IMAGE_FORMATS = { "jpg", "jpeg", "png" };
Snapshot::Snapshot() { Snapshot::Snapshot() {
_snapshotTimer.setSingleShot(false); _snapshotTimer.setSingleShot(false);
@ -67,7 +68,6 @@ Snapshot::Snapshot() {
} }
SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) { SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
if (!QFile(snapshotPath).exists()) { if (!QFile(snapshotPath).exists()) {
return NULL; return NULL;
} }
@ -95,7 +95,6 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
} }
QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) { QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) {
QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname); QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname);
if (snapshotFile) { 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_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_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)))); 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; _snapshotFilename = filename;
_notify360 = notify; _notify360 = notify;
_cubemapOutputFormat = cubemapOutputFormat; _cubemapOutputFormat = cubemapOutputFormat;
SecondaryCameraJobConfig* secondaryCameraRenderConfig = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); SecondaryCameraJobConfig* secondaryCameraRenderConfig =
static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
// Save initial values of secondary camera render config // Save initial values of secondary camera render config
_oldEnabled = secondaryCameraRenderConfig->isEnabled(); _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 // Initialize some secondary camera render config options for 360 snapshot capture
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(0); static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
->setCurve(0);
secondaryCameraRenderConfig->resetSizeSpectatorCamera(static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION), static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION)); secondaryCameraRenderConfig->resetSizeSpectatorCamera(static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION),
static_cast<int>(CUBEMAP_SIDE_PIXEL_DIMENSION));
secondaryCameraRenderConfig->setProperty("attachedEntityId", ""); secondaryCameraRenderConfig->setProperty("attachedEntityId", "");
secondaryCameraRenderConfig->setPosition(cameraPosition); secondaryCameraRenderConfig->setPosition(cameraPosition);
secondaryCameraRenderConfig->setProperty("vFoV", SNAPSHOT_360_FOV); secondaryCameraRenderConfig->setProperty("vFoV", SNAPSHOT_360_FOV);
@ -159,7 +164,8 @@ void Snapshot::save360Snapshot(const glm::vec3& cameraPosition, const bool& cube
} }
void Snapshot::takeNextSnapshot() { void Snapshot::takeNextSnapshot() {
SecondaryCameraJobConfig* config = static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera")); SecondaryCameraJobConfig* config =
static_cast<SecondaryCameraJobConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCamera"));
// Order is: // Order is:
// 0. Down // 0. Down
@ -191,7 +197,9 @@ void Snapshot::takeNextSnapshot() {
_snapshotTimer.stop(); _snapshotTimer.stop();
// Reset secondary camera render config // Reset secondary camera render config
static_cast<ToneMappingConfig*>(qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))->setCurve(1); static_cast<ToneMappingConfig*>(
qApp->getRenderEngine()->getConfiguration()->getConfig("SecondaryCameraJob.ToneMapping"))
->setCurve(1);
config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height()); config->resetSizeSpectatorCamera(qApp->getWindow()->geometry().width(), qApp->getWindow()->geometry().height());
config->setProperty("attachedEntityId", _oldAttachedEntityId); config->setProperty("attachedEntityId", _oldAttachedEntityId);
config->setProperty("vFoV", _oldvFoV); config->setProperty("vFoV", _oldvFoV);
@ -338,8 +346,10 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true)); return static_cast<QTemporaryFile*>(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 // adding URL to snapshot
QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress(); QUrl currentURL = DependencyManager::get<AddressManager>()->currentPublicAddress();
shot.setText(URL, currentURL.toString()); shot.setText(URL, currentURL.toString());
@ -350,12 +360,23 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
QDateTime now = QDateTime::currentDateTime(); QDateTime now = QDateTime::currentDateTime();
// If user has requested specific filename then use it, else create the filename // If user has supplied a specific filename for the snapshot:
// 'jpg" is appended, as the image is saved in jpg format. This is the case for all snapshots // If the user's requested filename has a suffix that's contained within SUPPORTED_IMAGE_FORMATS,
// (see definition of FILENAME_PATH_FORMAT) // DON'T append ".jpg" to the filename. QT will save the image in the format associated with the
// filename's suffix.
// If you want lossless Snapshots, supply a `.png` filename. Otherwise, use `.jpeg` or `.jpg`.
// 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; QString filename;
if (!userSelectedFilename.isNull()) { 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 { } else {
filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT)); filename = FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT));
} }
@ -372,11 +393,13 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
} }
if (snapshotFullPath.isEmpty()) { 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); _snapshotsLocation.set(snapshotFullPath);
} }
if (!snapshotFullPath.isEmpty()) { // not cancelled if (!snapshotFullPath.isEmpty()) { // not cancelled
if (!snapshotFullPath.endsWith(QDir::separator())) { if (!snapshotFullPath.endsWith(QDir::separator())) {
snapshotFullPath.append(QDir::separator()); snapshotFullPath.append(QDir::separator());
@ -393,7 +416,9 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
qApp->getApplicationCompositor().getReticleInterface()->setVisible(true); qApp->getApplicationCompositor().getReticleInterface()->setVisible(true);
qApp->getApplicationCompositor().getReticleInterface()->setAllowMouseCapture(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()) { if (snapshotFullPath.isEmpty()) {
return NULL; return NULL;
} }
@ -412,7 +437,6 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
return imageFile; return imageFile;
} }
} }
// Either we were asked for a tempororary, or the user didn't set a directory. // Either we were asked for a tempororary, or the user didn't set a directory.
QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename); QTemporaryFile* imageTempFile = new QTemporaryFile(QDir::tempPath() + "/XXXXXX-" + filename);
@ -430,7 +454,6 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
} }
void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) { void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots"; const QString SNAPSHOT_UPLOAD_URL = "/api/v1/snapshots";
QUrl url = href; QUrl url = href;
if (url.isEmpty()) { if (url.isEmpty()) {
@ -444,7 +467,7 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
url = QUrl(DependencyManager::get<AddressManager>()->currentShareableAddress()); url = QUrl(DependencyManager::get<AddressManager>()->currentShareableAddress());
} }
SnapshotUploader* uploader = new SnapshotUploader(url, filename); SnapshotUploader* uploader = new SnapshotUploader(url, filename);
QFile* file = new QFile(filename); QFile* file = new QFile(filename);
Q_ASSERT(file->exists()); Q_ASSERT(file->exists());
file->open(QIODevice::ReadOnly); file->open(QIODevice::ReadOnly);
@ -458,20 +481,16 @@ void Snapshot::uploadSnapshot(const QString& filename, const QUrl& href) {
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\"")); QVariant("form-data; name=\"image\"; filename=\"" + file->fileName() + "\""));
imagePart.setBodyDevice(file); imagePart.setBodyDevice(file);
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); 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); multiPart->append(imagePart);
auto accountManager = DependencyManager::get<AccountManager>(); auto accountManager = DependencyManager::get<AccountManager>();
JSONCallbackParameters callbackParams(uploader, "uploadSuccess", uploader, "uploadFailure"); JSONCallbackParameters callbackParams(uploader, "uploadSuccess", uploader, "uploadFailure");
accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, accountManager->sendRequest(SNAPSHOT_UPLOAD_URL, AccountManagerAuth::Required, QNetworkAccessManager::PostOperation,
AccountManagerAuth::Required, callbackParams, nullptr, multiPart);
QNetworkAccessManager::PostOperation,
callbackParams,
nullptr,
multiPart);
} }
QString Snapshot::getSnapshotsLocation() { QString Snapshot::getSnapshotsLocation() {