Commit d5c39fbd authored by Dmitry Kazakov's avatar Dmitry Kazakov

Barrier-lock the image before start saving (needs testing!)

This is a bit dangerous commit. We should test saving of the document
extensively. What should be tested:

1) Saving to KRA
2) Saving to any other formats
3) Saving while some action in still executing
4) Saving when LoD mode is active
5) Saving in batch mode

The program should not crash and/or hangup. The latter is the
most probable bug.

CC:kimageshop@kde.org
Fixes T2332
BUG:361883
parent ec87c47a
......@@ -436,6 +436,10 @@ bool KisApplication::start(const KisApplicationArguments &args)
KisDocument *doc = KisPart::instance()->createDocument();
doc->openUrl(QUrl::fromLocalFile(fileName));
qApp->processEvents(); // For vector layers to be updated
KisImageBarrierLocker locker(doc->image());
KisImportExportFilter::ConversionStatus status = KisImportExportFilter::OK;
KisImportExportManager manager(doc);
manager.setBatchMode(true);
......
......@@ -230,6 +230,54 @@ private:
KisDocument *m_doc;
};
class SafeSavingLocker {
public:
SafeSavingLocker(KisDocument *doc)
: m_doc(doc),
m_locked(false)
{
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
const int emergencyAutoSaveInterval = 10; // sec
if (!m_doc->image()->tryBarrierLock(true)) {
if (m_doc->isAutosaving()) {
m_doc->setDisregardAutosaveFailure(true);
if (realAutoSaveInterval) {
m_doc->setAutoSave(emergencyAutoSaveInterval);
}
m_locked = false;
} else {
m_doc->image()->requestStrokeEnd();
QApplication::processEvents();
m_locked = m_doc->image()->tryBarrierLock(true);
}
} else {
m_locked = true;
}
if (m_locked) {
m_doc->setDisregardAutosaveFailure(false);
}
}
~SafeSavingLocker() {
if (m_locked) {
m_doc->image()->unlock();
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
m_doc->setAutoSave(realAutoSaveInterval);
}
}
bool successfullyLocked() const {
return m_locked;
}
private:
KisDocument *m_doc;
bool m_locked;
};
class Q_DECL_HIDDEN KisDocument::Private
{
public:
......@@ -612,7 +660,13 @@ bool KisDocument::saveFile()
if (!isNativeFormat(outputMimeType)) {
dbgUI << "Saving to format" << outputMimeType << "in" << localFilePath();
status = d->filterManager->exportDocument(localFilePath(), outputMimeType);
SafeSavingLocker locker(this);
if (locker.successfullyLocked()) {
status = d->filterManager->exportDocument(localFilePath(), outputMimeType);
} else {
status = KisImportExportFilter::UsageError;
}
ret = status == KisImportExportFilter::OK;
suppressErrorDialog = (status == KisImportExportFilter::UserCancelled || status == KisImportExportFilter::BadConversionGraph);
dbgFile << "Export status was" << status;
......@@ -812,26 +866,8 @@ bool KisDocument::isModified() const
bool KisDocument::saveNativeFormat(const QString & file)
{
const int realAutoSaveInterval = KisConfig().autoSaveInterval();
const int emergencyAutoSaveInterval = 10; // sec
if (!d->image->tryBarrierLock(true)) {
if (isAutosaving()) {
setDisregardAutosaveFailure(true);
if (realAutoSaveInterval) {
setAutoSave(emergencyAutoSaveInterval);
}
return false;
} else {
d->image->requestStrokeEnd();
QApplication::processEvents();
if (!d->image->tryBarrierLock(true)) {
return false;
}
}
}
setDisregardAutosaveFailure(false);
SafeSavingLocker locker(this);
if (!locker.successfullyLocked()) return false;
d->lastErrorMessage.clear();
//dbgUI <<"Saving to store";
......@@ -863,22 +899,18 @@ bool KisDocument::saveNativeFormat(const QString & file)
if (store->bad()) {
d->lastErrorMessage = i18n("Could not create the file for saving"); // more details needed?
delete store;
d->image->unlock();
setAutoSave(realAutoSaveInterval);
return false;
}
d->image->unlock();
setAutoSave(realAutoSaveInterval);
bool result = false;
if (!isAutosaving()) {
KisAsyncActionFeedback f(i18n("Saving document..."), 0);
return f.runAction(std::bind(&KisDocument::saveNativeFormatCalligra, this, store));
result = f.runAction(std::bind(&KisDocument::saveNativeFormatCalligra, this, store));
} else {
result = saveNativeFormatCalligra(store);
}
return saveNativeFormatCalligra(store);
return result;
}
bool KisDocument::saveNativeFormatCalligra(KoStore *store)
......
......@@ -617,6 +617,7 @@ Q_SIGNALS:
private:
friend class KisPart;
friend class SafeSavingLocker;
/**
* Generate a name for the document.
......
......@@ -58,14 +58,10 @@ KisImportExportFilter::ConversionStatus KisBMPExport::convert(const QByteArray&
if (from != "application/x-krita")
return KisImportExportFilter::NotImplemented;
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
QRect rc = input->image()->bounds();
input->image()->refreshGraph();
input->image()->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
QImage image = input->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
input->image()->unlock();
image.save(filename);
return KisImportExportFilter::OK;
}
......
......@@ -141,11 +141,10 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(const QByteArray
qApp->processEvents(); // For vector layers to be updated
}
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
input->image()->waitForDone();
QRect rc = input->image()->bounds();
input->image()->refreshGraph();
input->image()->lock();
brush->setName(exportOptions.name);
brush->setSpacing(exportOptions.spacing);
......@@ -200,8 +199,6 @@ KisImportExportFilter::ConversionStatus KisBrushExport::convert(const QByteArray
brush->setWidth(w);
brush->setHeight(h);
input->image()->unlock();
QFile f(filename);
f.open(QIODevice::WriteOnly);
brush->saveToDevice(&f);
......
......@@ -106,7 +106,6 @@ KisImageBuilder_Result CSVSaver::encode(const QString &filename)
//according to the QT docs, the slash is a universal directory separator
path.append("/");
m_image->lock();
node = m_image->rootLayer()->firstChild();
//TODO: correct handling of the layer tree.
......@@ -336,8 +335,6 @@ KisImageBuilder_Result CSVSaver::encode(const QString &filename)
step++;
} while((retval == KisImageBuilder_RESULT_OK) && (step < 8));
m_image->unlock();
qDeleteAll(layers);
f.close();
......
......@@ -86,8 +86,6 @@ KisImportExportFilter::ConversionStatus KisCSVExport::convert(const QByteArray&
}
return KisImportExportFilter::InvalidFormat;
}
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
......
......@@ -89,10 +89,6 @@ KisImportExportFilter::ConversionStatus exrExport::convert(const QByteArray& fro
return KisImportExportFilter::UserCancelled;
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
image->waitForDone();
cfg.setProperty("flatten", widget.flatten->isChecked());
KisConfig().setExportConfiguration("EXR", cfg);
......@@ -105,20 +101,19 @@ KisImportExportFilter::ConversionStatus exrExport::convert(const QByteArray& fro
KisImageBuilder_Result res;
if (widget.flatten->isChecked()) {
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
image->unlock();
res = kpc.buildFile(filename, l);
}
else {
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
res = kpc.buildFile(filename, image->rootLayer());
image->unlock();
}
dbgFile << " Result =" << res;
......
......@@ -116,10 +116,6 @@ KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(const QByteA
return KisImportExportFilter::UserCancelled;
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
inputDoc->image()->waitForDone();
if (optionsHeightMap.radioMac->isChecked()) {
cfg.setProperty("endianness", 0);
......@@ -141,10 +137,10 @@ KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(const QByteA
== QMessageBox::Yes);
}
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(image->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
image->unlock();
QFile f(filename);
f.open(QIODevice::WriteOnly);
......@@ -169,4 +165,4 @@ KisImportExportFilter::ConversionStatus KisHeightMapExport::convert(const QByteA
return KisImportExportFilter::OK;
}
#include "kis_heightmap_export.moc"
\ No newline at end of file
#include "kis_heightmap_export.moc"
......@@ -95,10 +95,6 @@ KisImportExportFilter::ConversionStatus jp2Export::convert(const QByteArray& fro
return KisImportExportFilter::UserCancelled;
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
image->waitForDone();
JP2ConvertOptions options;
options.numberresolution = optionsJP2.numberResolutions->value();
......@@ -109,14 +105,12 @@ KisImportExportFilter::ConversionStatus jp2Export::convert(const QByteArray& fro
KisConfig().setExportConfiguration("JP2", cfg);
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
jp2Converter kpc(input);
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
image->unlock();
KisImageBuilder_Result res;
......
......@@ -134,10 +134,6 @@ KisImportExportFilter::ConversionStatus KisJPEGExport::convert(const QByteArray&
return KisImportExportFilter::UserCancelled;
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
image->waitForDone();
KisJPEGOptions options;
options.progressive = wdgUi.progressive->isChecked();
......@@ -195,13 +191,11 @@ KisImportExportFilter::ConversionStatus KisJPEGExport::convert(const QByteArray&
if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
KisJPEGConverter kpc(input, getBatchMode());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
image->unlock();
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
vKisAnnotationSP_it beginIt = image->beginAnnotations();
......
......@@ -82,11 +82,9 @@ KisImportExportFilter::ConversionStatus OraExport::convert(const QByteArray& fro
KisDocument *input = inputDocument();
QString filename = outputFile();
if (!input)
if (!input) {
return KisImportExportFilter::NoDocumentCreated;
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
}
if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
......
......@@ -92,15 +92,12 @@ KisImportExportFilter::ConversionStatus KisPNGExport::convert(const QByteArray&
kdb->setButtons(KoDialog::Ok | KoDialog::Cancel);
KisImageWSP image = input->image();
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(image->locked());
KisPaintDeviceSP pd;
pd = new KisPaintDevice(*image->projection());
KisPaintLayerSP l = new KisPaintLayer(image, "projection", OPACITY_OPAQUE_U8, pd);
image->unlock();
if (!KisPNGConverter::isColorSpaceSupported(pd->colorSpace())) {
......
......@@ -174,11 +174,6 @@ KisImportExportFilter::ConversionStatus KisPPMExport::convert(const QByteArray&
return KisImportExportFilter::UserCancelled;
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
input->image()->waitForDone();
bool rgb = (to == "image/x-portable-pixmap");
bool binary = optionsPPM.type->currentIndex() == 0;
......@@ -189,10 +184,9 @@ KisImportExportFilter::ConversionStatus KisPPMExport::convert(const QByteArray&
KisImageWSP image = input->image();
Q_CHECK_PTR(image);
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
KisPaintDeviceSP pd = new KisPaintDevice(*image->projection());
image->unlock();
// Test color space
if (((rgb && (pd->colorSpace()->id() != "RGBA" && pd->colorSpace()->id() != "RGBA16"))
......@@ -289,4 +283,4 @@ KisImportExportFilter::ConversionStatus KisPPMExport::convert(const QByteArray&
return KisImportExportFilter::OK;
}
#include "kis_ppm_export.moc"
\ No newline at end of file
#include "kis_ppm_export.moc"
......@@ -107,9 +107,6 @@ KisImportExportFilter::ConversionStatus psdExport::convert(const QByteArray& fro
return KisImportExportFilter::InvalidFormat;
}
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
if (filename.isEmpty()) return KisImportExportFilter::FileNotFound;
PSDSaver kpc(input);
......
......@@ -63,10 +63,6 @@ KisImportExportFilter::ConversionStatus QMLExport::convert(const QByteArray& fro
}
KisImageWSP image = input->image();
qApp->processEvents(); // For vector layers to be updated
image->waitForDone();
Q_CHECK_PTR(image);
QMLConverter converter;
......
......@@ -353,9 +353,6 @@ KisImportExportFilter::ConversionStatus KisSpriterExport::convert(const QByteArr
return KisImportExportFilter::NoDocumentCreated;
}
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
QFileInfo fi(filename);
QDomDocument scmlDoc;
......
......@@ -61,14 +61,11 @@ KisImportExportFilter::ConversionStatus KisTGAExport::convert(const QByteArray&
if (from != "application/x-krita")
return KisImportExportFilter::NotImplemented;
qApp->processEvents(); // For vector layers to be updated
input->image()->waitForDone();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
QRect rc = input->image()->bounds();
input->image()->refreshGraph();
input->image()->lock();
QImage image = input->image()->projection()->convertToQImage(0, 0, 0, rc.width(), rc.height(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());
input->image()->unlock();
QFile f(filename);
f.open(QIODevice::WriteOnly);
......
......@@ -84,11 +84,6 @@ KisImportExportFilter::ConversionStatus KisTIFFExport::convert(const QByteArray&
return KisImportExportFilter::UserCancelled;
}
}
else {
qApp->processEvents(); // For vector layers to be updated
}
input->image()->waitForDone();
KisTIFFOptions options = dlg.options();
......@@ -114,17 +109,16 @@ KisImportExportFilter::ConversionStatus KisTIFFExport::convert(const QByteArray&
image = input->image();
}
image->refreshGraph();
image->lock();
// the image must be locked at the higher levels
KIS_ASSERT_RECOVER_NOOP(input->image()->locked());
KisTIFFConverter ktc(input);
KisImageBuilder_Result res;
if ((res = ktc.buildFile(filename, image, options)) == KisImageBuilder_RESULT_OK) {
dbgFile << "success !";
image->unlock();
return KisImportExportFilter::OK;
}
image->unlock();
dbgFile << " Result =" << res;
return KisImportExportFilter::InternalError;
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment