mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-06-27 21:58:47 +02:00
Fix MS15103: Interface crashes on snap if snap dir is deleted
This commit is contained in:
parent
1313d28b9d
commit
012ae741a5
7 changed files with 74 additions and 30 deletions
|
@ -7590,8 +7590,9 @@ void Application::loadAvatarBrowser() const {
|
||||||
|
|
||||||
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
|
||||||
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
|
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
|
||||||
|
bool initialWriteFailed = false;
|
||||||
// Get a screenshot and save it
|
// Get a screenshot and save it
|
||||||
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename,
|
QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, initialWriteFailed,
|
||||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||||
|
|
||||||
// If we're not doing an animated snapshot as well...
|
// If we're not doing an animated snapshot as well...
|
||||||
|
@ -7599,15 +7600,20 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
|
||||||
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
// Tell the dependency manager that the capture of the still snapshot has taken place.
|
||||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
|
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(path, notify);
|
||||||
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
|
} else if (!SnapshotAnimated::isAlreadyTakingSnapshotAnimated()) {
|
||||||
|
if (initialWriteFailed) {
|
||||||
|
emit DependencyManager::get<WindowScriptingInterface>()->processingGifStarted(path, true);
|
||||||
|
} else {
|
||||||
// Get an animated GIF snapshot and save it
|
// Get an animated GIF snapshot and save it
|
||||||
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
|
SnapshotAnimated::saveSnapshotAnimated(path, aspectRatio, qApp, DependencyManager::get<WindowScriptingInterface>());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
|
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
|
||||||
postLambdaEvent([filename, this] {
|
postLambdaEvent([filename, this] {
|
||||||
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename,
|
bool initialWriteFailed = false;
|
||||||
|
QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, initialWriteFailed,
|
||||||
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
TestScriptingInterface::getInstance()->getTestResultsLocation());
|
||||||
|
|
||||||
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
|
emit DependencyManager::get<WindowScriptingInterface>()->stillSnapshotTaken(snapshotPath, true);
|
||||||
|
|
|
@ -594,9 +594,11 @@ signals:
|
||||||
* starting to be processed.
|
* starting to be processed.
|
||||||
* @function Window.processingGifStarted
|
* @function Window.processingGifStarted
|
||||||
* @param {string} pathStillSnapshot - The path and name of the still snapshot image file.
|
* @param {string} pathStillSnapshot - The path and name of the still snapshot image file.
|
||||||
|
* @param {bool} stillWriteFailed - True if the still snapshot (taken before the GIF) failed to write to disk
|
||||||
|
* (this means the GIF won't capture).
|
||||||
* @returns {Signal}
|
* @returns {Signal}
|
||||||
*/
|
*/
|
||||||
void processingGifStarted(const QString& pathStillSnapshot);
|
void processingGifStarted(const QString& pathStillSnapshot, const bool& stillWriteFailed);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Triggered when a GIF has been prepared of the snapshot images captured by {@link Window.takeSnapshot|takeSnapshot}.
|
* Triggered when a GIF has been prepared of the snapshot images captured by {@link Window.takeSnapshot|takeSnapshot}.
|
||||||
|
|
|
@ -74,10 +74,11 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) {
|
QString Snapshot::saveSnapshot(QImage image, const QString& filename, bool& initialWriteFailed, const QString& pathname) {
|
||||||
|
|
||||||
QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname);
|
QFile* snapshotFile = savedFileForSnapshot(image, false, initialWriteFailed, filename, pathname);
|
||||||
|
|
||||||
|
if (snapshotFile) {
|
||||||
// we don't need the snapshot file, so close it, grab its filename and delete it
|
// we don't need the snapshot file, so close it, grab its filename and delete it
|
||||||
snapshotFile->close();
|
snapshotFile->close();
|
||||||
|
|
||||||
|
@ -88,12 +89,16 @@ QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QStr
|
||||||
return snapshotPath;
|
return snapshotPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
|
return "";
|
||||||
// return whatever we get back from saved file for snapshot
|
|
||||||
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname) {
|
QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
|
||||||
|
// return whatever we get back from saved file for snapshot
|
||||||
|
bool initialWriteFailed = false;
|
||||||
|
return static_cast<QTemporaryFile*>(savedFileForSnapshot(image, true, initialWriteFailed));
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, bool& initialWriteFailed, 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();
|
||||||
|
@ -140,7 +145,21 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
|
||||||
snapshotFullPath.append(filename);
|
snapshotFullPath.append(filename);
|
||||||
|
|
||||||
QFile* imageFile = new QFile(snapshotFullPath);
|
QFile* imageFile = new QFile(snapshotFullPath);
|
||||||
imageFile->open(QIODevice::WriteOnly);
|
while (!imageFile->open(QIODevice::WriteOnly)) {
|
||||||
|
initialWriteFailed = true;
|
||||||
|
snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Write Error - Choose New Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
|
||||||
|
if (snapshotFullPath.isEmpty()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
snapshotsLocation.set(snapshotFullPath);
|
||||||
|
|
||||||
|
if (!snapshotFullPath.endsWith(QDir::separator())) {
|
||||||
|
snapshotFullPath.append(QDir::separator());
|
||||||
|
}
|
||||||
|
snapshotFullPath.append(filename);
|
||||||
|
|
||||||
|
imageFile = new QFile(snapshotFullPath);
|
||||||
|
}
|
||||||
|
|
||||||
shot.save(imageFile, 0, IMAGE_QUALITY);
|
shot.save(imageFile, 0, IMAGE_QUALITY);
|
||||||
imageFile->close();
|
imageFile->close();
|
||||||
|
|
|
@ -38,7 +38,7 @@ class Snapshot : public QObject, public Dependency {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
SINGLETON_DEPENDENCY
|
SINGLETON_DEPENDENCY
|
||||||
public:
|
public:
|
||||||
static QString saveSnapshot(QImage image, const QString& filename, const QString& pathname = QString());
|
static QString saveSnapshot(QImage image, const QString& filename, bool& initialWriteFailed, const QString& pathname = QString());
|
||||||
static QTemporaryFile* saveTempSnapshot(QImage image);
|
static QTemporaryFile* saveTempSnapshot(QImage image);
|
||||||
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ public slots:
|
||||||
private:
|
private:
|
||||||
static QFile* savedFileForSnapshot(QImage& image,
|
static QFile* savedFileForSnapshot(QImage& image,
|
||||||
bool isTemporary,
|
bool isTemporary,
|
||||||
|
bool& initialWriteFailed,
|
||||||
const QString& userSelectedFilename = QString(),
|
const QString& userSelectedFilename = QString(),
|
||||||
const QString& userSelectedPathname = QString());
|
const QString& userSelectedPathname = QString());
|
||||||
};
|
};
|
||||||
|
|
|
@ -89,7 +89,7 @@ void SnapshotAnimated::captureFrames() {
|
||||||
|
|
||||||
// Notify the user that we're processing the snapshot
|
// Notify the user that we're processing the snapshot
|
||||||
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
|
// This also pops up the "Share" dialog. The unprocessed GIF will be visualized as a loading icon until processingGifCompleted() is called.
|
||||||
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath);
|
emit SnapshotAnimated::snapshotAnimatedDM->processingGifStarted(SnapshotAnimated::snapshotStillPath, false);
|
||||||
|
|
||||||
// Kick off the thread that'll pack the frames into the GIF
|
// Kick off the thread that'll pack the frames into the GIF
|
||||||
QtConcurrent::run(processFrames);
|
QtConcurrent::run(processFrames);
|
||||||
|
@ -103,18 +103,40 @@ void SnapshotAnimated::captureFrames() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SnapshotAnimated::clearTempVariables() {
|
||||||
|
// 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();
|
||||||
|
// Reset the current frame timestamp
|
||||||
|
SnapshotAnimated::snapshotAnimatedTimestamp = 0;
|
||||||
|
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = 0;
|
||||||
|
}
|
||||||
|
|
||||||
void SnapshotAnimated::processFrames() {
|
void SnapshotAnimated::processFrames() {
|
||||||
uint32_t width = SnapshotAnimated::snapshotAnimatedFrameVector[0].width();
|
uint32_t width = SnapshotAnimated::snapshotAnimatedFrameVector[0].width();
|
||||||
uint32_t height = SnapshotAnimated::snapshotAnimatedFrameVector[0].height();
|
uint32_t height = SnapshotAnimated::snapshotAnimatedFrameVector[0].height();
|
||||||
|
|
||||||
// Create the GIF from the temporary files
|
// Create the GIF from the temporary files
|
||||||
// Write out the header and beginning of the GIF file
|
// Write out the header and beginning of the GIF file
|
||||||
GifBegin(
|
if (!GifBegin(
|
||||||
&(SnapshotAnimated::snapshotAnimatedGifWriter),
|
&(SnapshotAnimated::snapshotAnimatedGifWriter),
|
||||||
qPrintable(SnapshotAnimated::snapshotAnimatedPath),
|
qPrintable(SnapshotAnimated::snapshotAnimatedPath),
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
1); // "1" means "yes there is a delay" with this GifCreator library.
|
1)) { // "1" means "yes there is a delay" with this GifCreator library.
|
||||||
|
|
||||||
|
// We should never, ever get here. If we do, that means that writing a still JPG to the filesystem
|
||||||
|
// has succeeded, but that writing the tiny header to a GIF file in the same directory failed.
|
||||||
|
// If that happens, we _could_ throw up the "Folder Chooser" dialog like we do for still JPG images,
|
||||||
|
// but I have no way of testing whether or not that'll work or get properly exercised,
|
||||||
|
// so I'm not going to bother for now.
|
||||||
|
SnapshotAnimated::clearTempVariables();
|
||||||
|
qDebug() << "Animated snapshot header failed to write - aborting GIF processing.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (int itr = 0; itr < SnapshotAnimated::snapshotAnimatedFrameVector.size(); itr++) {
|
for (int itr = 0; itr < SnapshotAnimated::snapshotAnimatedFrameVector.size(); itr++) {
|
||||||
// Write each frame to the GIF
|
// Write each frame to the GIF
|
||||||
GifWriteFrame(&(SnapshotAnimated::snapshotAnimatedGifWriter),
|
GifWriteFrame(&(SnapshotAnimated::snapshotAnimatedGifWriter),
|
||||||
|
@ -126,16 +148,6 @@ void SnapshotAnimated::processFrames() {
|
||||||
// Write out the end of the GIF
|
// Write out the end of the GIF
|
||||||
GifEnd(&(SnapshotAnimated::snapshotAnimatedGifWriter));
|
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();
|
|
||||||
// Reset the current frame timestamp
|
|
||||||
SnapshotAnimated::snapshotAnimatedTimestamp = 0;
|
|
||||||
SnapshotAnimated::snapshotAnimatedFirstFrameTimestamp = 0;
|
|
||||||
|
|
||||||
// Update the "Share" dialog with the processed GIF.
|
// Update the "Share" dialog with the processed GIF.
|
||||||
emit SnapshotAnimated::snapshotAnimatedDM->processingGifCompleted(SnapshotAnimated::snapshotAnimatedPath);
|
emit SnapshotAnimated::snapshotAnimatedDM->processingGifCompleted(SnapshotAnimated::snapshotAnimatedPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,7 @@ private:
|
||||||
|
|
||||||
static void captureFrames();
|
static void captureFrames();
|
||||||
static void processFrames();
|
static void processFrames();
|
||||||
|
static void clearTempVariables();
|
||||||
public:
|
public:
|
||||||
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm);
|
static void saveSnapshotAnimated(QString pathStill, float aspectRatio, Application* app, QSharedPointer<WindowScriptingInterface> dm);
|
||||||
static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; };
|
static bool isAlreadyTakingSnapshotAnimated() { return snapshotAnimatedFirstFrameTimestamp != 0; };
|
||||||
|
|
|
@ -574,7 +574,7 @@ function snapshotDirChanged(snapshotPath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processingGifStarted(pathStillSnapshot) {
|
function processingGifStarted(pathStillSnapshot, stillWriteFailed) {
|
||||||
Window.processingGifStarted.disconnect(processingGifStarted);
|
Window.processingGifStarted.disconnect(processingGifStarted);
|
||||||
Window.processingGifCompleted.connect(processingGifCompleted);
|
Window.processingGifCompleted.connect(processingGifCompleted);
|
||||||
isLoggedIn = Account.isLoggedIn();
|
isLoggedIn = Account.isLoggedIn();
|
||||||
|
@ -608,6 +608,9 @@ function processingGifStarted(pathStillSnapshot) {
|
||||||
image_data: imageData
|
image_data: imageData
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
if (stillWriteFailed) {
|
||||||
|
processingGifCompleted("");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function processingGifCompleted(pathAnimatedSnapshot) {
|
function processingGifCompleted(pathAnimatedSnapshot) {
|
||||||
|
|
Loading…
Reference in a new issue