diff --git a/libs/image/kis_stroke.cpp b/libs/image/kis_stroke.cpp index 4d7d67b572..3563333d6a 100644 --- a/libs/image/kis_stroke.cpp +++ b/libs/image/kis_stroke.cpp @@ -1,336 +1,336 @@ /* * Copyright (c) 2011 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_stroke.h" #include "kis_stroke_strategy.h" KisStroke::KisStroke(KisStrokeStrategy *strokeStrategy, Type type, int levelOfDetail) : m_strokeStrategy(strokeStrategy), m_strokeInitialized(false), m_strokeEnded(false), m_strokeSuspended(false), m_isCancelled(false), m_worksOnLevelOfDetail(levelOfDetail), m_type(type) { m_initStrategy.reset(m_strokeStrategy->createInitStrategy()); m_dabStrategy.reset(m_strokeStrategy->createDabStrategy()); m_cancelStrategy.reset(m_strokeStrategy->createCancelStrategy()); m_finishStrategy.reset(m_strokeStrategy->createFinishStrategy()); m_suspendStrategy.reset(m_strokeStrategy->createSuspendStrategy()); m_resumeStrategy.reset(m_strokeStrategy->createResumeStrategy()); m_strokeStrategy->notifyUserStartedStroke(); if(!m_initStrategy) { m_strokeInitialized = true; } else { enqueue(m_initStrategy.data(), m_strokeStrategy->createInitData()); } } KisStroke::~KisStroke() { Q_ASSERT(m_strokeEnded); Q_ASSERT(m_jobsQueue.isEmpty()); } bool KisStroke::supportsSuspension() { return !m_strokeInitialized || (m_suspendStrategy && m_resumeStrategy); } void KisStroke::suspendStroke(KisStrokeSP recipient) { if (!m_strokeInitialized || m_strokeSuspended || (m_strokeEnded && !hasJobs())) { return; } KIS_ASSERT_RECOVER_NOOP(m_suspendStrategy && m_resumeStrategy); prepend(m_resumeStrategy.data(), m_strokeStrategy->createResumeData(), worksOnLevelOfDetail(), false); recipient->prepend(m_suspendStrategy.data(), m_strokeStrategy->createSuspendData(), worksOnLevelOfDetail(), false); m_strokeSuspended = true; } void KisStroke::addJob(KisStrokeJobData *data) { Q_ASSERT(!m_strokeEnded || m_isCancelled); enqueue(m_dabStrategy.data(), data); } void KisStroke::addMutatedJobs(const QVector list) { // factory methods can return null, if no action is needed if (!m_dabStrategy) { qDeleteAll(list); return; } // Find first non-alien (non-suspend/non-resume) job // // Please note that this algorithm will stop working at the day we start // adding alien jobs not to the beginning of the stroke, but to other places. // Right now both suspend and resume jobs are added to the beginning of // the stroke. auto it = std::find_if(m_jobsQueue.begin(), m_jobsQueue.end(), [] (KisStrokeJob *job) { return job->isOwnJob(); }); Q_FOREACH (KisStrokeJobData *data, list) { it = m_jobsQueue.insert(it, new KisStrokeJob(m_dabStrategy.data(), data, worksOnLevelOfDetail(), true)); ++it; } } KisStrokeJob* KisStroke::popOneJob() { KisStrokeJob *job = dequeue(); if(job) { m_strokeInitialized = true; m_strokeSuspended = false; } return job; } KUndo2MagicString KisStroke::name() const { return m_strokeStrategy->name(); } bool KisStroke::hasJobs() const { return !m_jobsQueue.isEmpty(); } qint32 KisStroke::numJobs() const { return m_jobsQueue.size(); } void KisStroke::endStroke() { - Q_ASSERT(!m_strokeEnded); + KIS_SAFE_ASSERT_RECOVER_RETURN(!m_strokeEnded); m_strokeEnded = true; enqueue(m_finishStrategy.data(), m_strokeStrategy->createFinishData()); m_strokeStrategy->notifyUserEndedStroke(); } /** * About cancelling the stroke * There may be four different states of the stroke, when cancel * is requested: * 1) Not initialized, has jobs -- just clear the queue * 2) Initialized, has jobs, not finished -- clear the queue, * enqueue the cancel job * 5) Initialized, no jobs, not finished -- enqueue the cancel job * 3) Initialized, has jobs, finished -- clear the queue, enqueue * the cancel job * 4) Initialized, no jobs, finished -- it's too late to cancel * anything * 6) Initialized, has jobs, cancelled -- cancelling twice is a permitted * operation, though it does nothing */ void KisStroke::cancelStroke() { // case 6 if (m_isCancelled) return; const bool effectivelyInitialized = m_strokeInitialized || m_strokeStrategy->needsExplicitCancel(); if(!effectivelyInitialized) { /** * Lod0 stroke cannot be suspended and !initialized at the * same time, because the suspend job is created iff the * stroke has already done some meaningful work. * * At the same time, LodN stroke can be prepended with a * 'suspend' job even when it has not been started yet. That * is obvious: we should suspend the other stroke before doing * anything else. */ KIS_ASSERT_RECOVER_NOOP(type() == LODN || sanityCheckAllJobsAreCancellable()); clearQueueOnCancel(); } else if(effectivelyInitialized && (!m_jobsQueue.isEmpty() || !m_strokeEnded)) { clearQueueOnCancel(); enqueue(m_cancelStrategy.data(), m_strokeStrategy->createCancelData()); } // else { // too late ... // } m_isCancelled = true; m_strokeEnded = true; } bool KisStroke::canCancel() const { return m_isCancelled || !m_strokeInitialized || !m_jobsQueue.isEmpty() || !m_strokeEnded; } bool KisStroke::sanityCheckAllJobsAreCancellable() const { Q_FOREACH (KisStrokeJob *item, m_jobsQueue) { if (!item->isCancellable()) { return false; } } return true; } void KisStroke::clearQueueOnCancel() { QQueue::iterator it = m_jobsQueue.begin(); while (it != m_jobsQueue.end()) { if ((*it)->isCancellable()) { delete (*it); it = m_jobsQueue.erase(it); } else { ++it; } } } bool KisStroke::isInitialized() const { return m_strokeInitialized; } bool KisStroke::isEnded() const { return m_strokeEnded; } bool KisStroke::isCancelled() const { return m_isCancelled; } bool KisStroke::isExclusive() const { return m_strokeStrategy->isExclusive(); } bool KisStroke::supportsWrapAroundMode() const { return m_strokeStrategy->supportsWrapAroundMode(); } int KisStroke::worksOnLevelOfDetail() const { return m_worksOnLevelOfDetail; } bool KisStroke::canForgetAboutMe() const { return m_strokeStrategy->canForgetAboutMe(); } qreal KisStroke::balancingRatioOverride() const { return m_strokeStrategy->balancingRatioOverride(); } KisStrokeJobData::Sequentiality KisStroke::nextJobSequentiality() const { return !m_jobsQueue.isEmpty() ? m_jobsQueue.head()->sequentiality() : KisStrokeJobData::SEQUENTIAL; } void KisStroke::enqueue(KisStrokeJobStrategy *strategy, KisStrokeJobData *data) { // factory methods can return null, if no action is needed if(!strategy) { delete data; return; } m_jobsQueue.enqueue(new KisStrokeJob(strategy, data, worksOnLevelOfDetail(), true)); } void KisStroke::prepend(KisStrokeJobStrategy *strategy, KisStrokeJobData *data, int levelOfDetail, bool isOwnJob) { // factory methods can return null, if no action is needed if(!strategy) { delete data; return; } // LOG_MERGE_FIXME: Q_UNUSED(levelOfDetail); m_jobsQueue.prepend(new KisStrokeJob(strategy, data, worksOnLevelOfDetail(), isOwnJob)); } KisStrokeJob* KisStroke::dequeue() { return !m_jobsQueue.isEmpty() ? m_jobsQueue.dequeue() : 0; } void KisStroke::setLodBuddy(KisStrokeSP buddy) { m_lodBuddy = buddy; } KisStrokeSP KisStroke::lodBuddy() const { return m_lodBuddy; } KisStroke::Type KisStroke::type() const { if (m_type == LOD0) { KIS_ASSERT_RECOVER_NOOP(m_lodBuddy && "LOD0 strokes must always have a buddy"); } else if (m_type == LODN) { KIS_ASSERT_RECOVER_NOOP(m_worksOnLevelOfDetail > 0 && "LODN strokes must work on LOD > 0!"); } else if (m_type == LEGACY) { KIS_ASSERT_RECOVER_NOOP(m_worksOnLevelOfDetail == 0 && "LEGACY strokes must work on LOD == 0!"); } return m_type; } diff --git a/plugins/extensions/qmic/QMic.cpp b/plugins/extensions/qmic/QMic.cpp index 6f8499856b..83f27d3490 100644 --- a/plugins/extensions/qmic/QMic.cpp +++ b/plugins/extensions/qmic/QMic.cpp @@ -1,464 +1,463 @@ /* * Copyright (c) 2017 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "QMic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kis_input_output_mapper.h" #include "kis_qmic_simple_convertor.h" #include "kis_import_qmic_processing_visitor.h" #include #include "kis_qmic_applicator.h" static const char ack[] = "ack"; K_PLUGIN_FACTORY_WITH_JSON(QMicFactory, "kritaqmic.json", registerPlugin();) QMic::QMic(QObject *parent, const QVariantList &) : KisActionPlugin(parent) , m_gmicApplicator(0) { #ifndef Q_OS_MAC KisPreferenceSetRegistry *preferenceSetRegistry = KisPreferenceSetRegistry::instance(); PluginSettingsFactory* settingsFactory = new PluginSettingsFactory(); preferenceSetRegistry->add("QMicPluginSettingsFactory", settingsFactory); m_qmicAction = createAction("QMic"); m_qmicAction->setActivationFlags(KisAction::ACTIVE_DEVICE); connect(m_qmicAction , SIGNAL(triggered()), this, SLOT(slotQMic())); m_againAction = createAction("QMicAgain"); m_againAction->setActivationFlags(KisAction::ACTIVE_DEVICE); m_againAction->setEnabled(false); connect(m_againAction, SIGNAL(triggered()), this, SLOT(slotQMicAgain())); m_gmicApplicator = new KisQmicApplicator(); connect(m_gmicApplicator, SIGNAL(gmicFinished(bool,int,QString)), this, SLOT(slotGmicFinished(bool,int,QString))); #endif } QMic::~QMic() { Q_FOREACH(QSharedMemory *memorySegment, m_sharedMemorySegments) { // dbgPlugins << "detaching" << memorySegment->key(); memorySegment->detach(); } qDeleteAll(m_sharedMemorySegments); m_sharedMemorySegments.clear(); if (m_pluginProcess) { m_pluginProcess->close(); } delete m_gmicApplicator; delete m_localServer; } void QMic::slotQMicAgain() { slotQMic(true); } void QMic::slotQMic(bool again) { m_qmicAction->setEnabled(false); m_againAction->setEnabled(false); // find the krita-gmic-qt plugin QString pluginPath = PluginSettings::gmicQtPath(); if (pluginPath.isEmpty() || !QFileInfo(pluginPath).exists() || !QFileInfo(pluginPath).isFile()) { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Krita cannot find the gmic-qt plugin. You can set the location of the gmic-qt plugin in Settings/Configure Krita.")); return; } m_key = QUuid::createUuid().toString(); m_localServer = new QLocalServer(); m_localServer->listen(m_key); connect(m_localServer, SIGNAL(newConnection()), SLOT(connected())); m_pluginProcess = new QProcess(this); connect(viewManager(), SIGNAL(destroyed(QObject*)), m_pluginProcess, SLOT(terminate())); m_pluginProcess->setProcessChannelMode(QProcess::ForwardedChannels); connect(m_pluginProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(pluginFinished(int,QProcess::ExitStatus))); connect(m_pluginProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(pluginStateChanged(QProcess::ProcessState))); m_pluginProcess->start(pluginPath, QStringList() << m_key << (again ? QString(" reapply") : QString())); bool r = m_pluginProcess->waitForStarted(); while (m_pluginProcess->waitForFinished(10)) { qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } dbgPlugins << "Plugin started" << r << m_pluginProcess->state(); } void QMic::connected() { dbgPlugins << "connected"; if (!viewManager()) return; QLocalSocket *socket = m_localServer->nextPendingConnection(); if (!socket) { return; } while (socket->bytesAvailable() < static_cast(sizeof(quint32))) { if (!socket->isValid()) { // stale request return; } socket->waitForReadyRead(1000); } QDataStream ds(socket); QByteArray msg; quint32 remaining; ds >> remaining; msg.resize(remaining); int got = 0; char* uMsgBuf = msg.data(); // FIXME: Should use read transaction for Qt >= 5.7: // https://doc.qt.io/qt-5/qdatastream.html#using-read-transactions do { got = ds.readRawData(uMsgBuf, remaining); remaining -= got; uMsgBuf += got; } while (remaining && got >= 0 && socket->waitForReadyRead(2000)); if (got < 0) { qWarning() << "Message reception failed" << socket->errorString(); delete socket; m_localServer->close(); delete m_localServer; m_localServer = 0; return; } QString message = QString::fromUtf8(msg); dbgPlugins << "Received" << message; // Check the message: we can get three different ones QMultiMap messageMap; Q_FOREACH(QString line, message.split('\n', QString::SkipEmptyParts)) { QList kv = line.split('=', QString::SkipEmptyParts); if (kv.size() == 2) { messageMap.insert(kv[0], kv[1]); } else { qWarning() << "line" << line << "is invalid."; } } if (!messageMap.contains("command")) { qWarning() << "Message did not contain a command"; return; } int mode = 0; if (messageMap.contains("mode")) { mode = messageMap.values("mode").first().toInt(); } QByteArray ba; QString messageBoxWarningText; if (messageMap.values("command").first() == "gmic_qt_get_image_size") { KisSelectionSP selection = viewManager()->image()->globalSelection(); if (selection) { QRect selectionRect = selection->selectedExactRect(); ba = QByteArray::number(selectionRect.width()) + "," + QByteArray::number(selectionRect.height()); } else { ba = QByteArray::number(viewManager()->image()->width()) + "," + QByteArray::number(viewManager()->image()->height()); } } else if (messageMap.values("command").first() == "gmic_qt_get_cropped_images") { // Parse the message, create the shared memory segments, and create a new message to send back and waid for ack QRectF cropRect(0.0, 0.0, 1.0, 1.0); if (!messageMap.contains("croprect") || messageMap.values("croprect").first().split(',', QString::SkipEmptyParts).size() != 4) { qWarning() << "gmic-qt didn't send a croprect or not a valid croprect"; } else { QStringList cr = messageMap.values("croprect").first().split(',', QString::SkipEmptyParts); cropRect.setX(cr[0].toFloat()); cropRect.setY(cr[1].toFloat()); cropRect.setWidth(cr[2].toFloat()); cropRect.setHeight(cr[3].toFloat()); } if (!prepareCroppedImages(&ba, cropRect, mode)) { qWarning() << "Failed to prepare images for gmic-qt"; } } else if (messageMap.values("command").first() == "gmic_qt_output_images") { // Parse the message. read the shared memory segments, fix up the current image and send an ack dbgPlugins << "gmic_qt_output_images"; QStringList layers = messageMap.values("layer"); m_outputMode = (OutputMode)mode; if (m_outputMode != IN_PLACE) { messageBoxWarningText = i18n("Sorry, this output mode is not implemented yet."); m_outputMode = IN_PLACE; } slotStartApplicator(layers); } else if (messageMap.values("command").first() == "gmic_qt_detach") { Q_FOREACH(QSharedMemory *memorySegment, m_sharedMemorySegments) { dbgPlugins << "detaching" << memorySegment->key() << memorySegment->isAttached(); if (memorySegment->isAttached()) { if (!memorySegment->detach()) { dbgPlugins << "\t" << memorySegment->error() << memorySegment->errorString(); } } } qDeleteAll(m_sharedMemorySegments); m_sharedMemorySegments.clear(); } else { qWarning() << "Received unknown command" << messageMap.values("command"); } dbgPlugins << "Sending" << QString::fromUtf8(ba); // HACK: Make sure QDataStream does not refuse to write! // Proper fix: Change the above read to use read transaction ds.resetStatus(); ds.writeBytes(ba.constData(), ba.length()); // Flush the socket because we might not return to the event loop! if (!socket->waitForBytesWritten(2000)) { qWarning() << "Failed to write response:" << socket->error(); } // Wait for the ack bool r = true; r &= socket->waitForReadyRead(2000); // wait for ack r &= (socket->read(qstrlen(ack)) == ack); if (!socket->waitForDisconnected(2000)) { qWarning() << "Remote not disconnected:" << socket->error(); // Wait again socket->disconnectFromServer(); if (socket->waitForDisconnected(2000)) { qWarning() << "Disconnect timed out:" << socket->error(); } } if (!messageBoxWarningText.isEmpty()) { // Defer the message box to the event loop QTimer::singleShot(0, [messageBoxWarningText]() { QMessageBox::warning(KisPart::instance()->currentMainwindow(), i18nc("@title:window", "Krita"), messageBoxWarningText); }); } } void QMic::pluginStateChanged(QProcess::ProcessState state) { dbgPlugins << "stateChanged" << state; } void QMic::pluginFinished(int exitCode, QProcess::ExitStatus exitStatus) { dbgPlugins << "pluginFinished" << exitCode << exitStatus; delete m_pluginProcess; m_pluginProcess = 0; delete m_localServer; m_localServer = 0; m_qmicAction->setEnabled(true); m_againAction->setEnabled(true); } void QMic::slotGmicFinished(bool successfully, int milliseconds, const QString &msg) { dbgPlugins << "slotGmicFinished();" << successfully << milliseconds << msg; if (successfully) { m_gmicApplicator->finish(); } else { m_gmicApplicator->cancel(); QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("G'Mic failed, reason:") + msg); } } void QMic::slotStartApplicator(QStringList gmicImages) { dbgPlugins << "slotStartApplicator();" << gmicImages; if (!viewManager()) return; // Create a vector of gmic images QVector *> images; Q_FOREACH(const QString &image, gmicImages) { QStringList parts = image.split(',', QString::SkipEmptyParts); KIS_SAFE_ASSERT_RECOVER_BREAK(parts.size() == 5); QString key = parts[0]; QString layerName = QByteArray::fromHex(parts[1].toLatin1()); int spectrum = parts[2].toInt(); int width = parts[3].toInt(); int height = parts[4].toInt(); dbgPlugins << key << layerName << width << height; QSharedMemory m(key); if (!m.attach(QSharedMemory::ReadOnly)) { qWarning() << "Could not attach to shared memory area." << m.error() << m.errorString(); } if (m.isAttached()) { if (!m.lock()) { dbgPlugins << "Could not lock memory segment" << m.error() << m.errorString(); } dbgPlugins << "Memory segment" << key << m.size() << m.constData() << m.data(); gmic_image *gimg = new gmic_image(); gimg->assign(width, height, 1, spectrum); gimg->name = layerName; gimg->_data = new float[width * height * spectrum * sizeof(float)]; dbgPlugins << "width" << width << "height" << height << "size" << width * height * spectrum * sizeof(float) << "shared memory size" << m.size(); memcpy(gimg->_data, m.constData(), width * height * spectrum * sizeof(float)); dbgPlugins << "created gmic image" << gimg->name << gimg->_width << gimg->_height; if (!m.unlock()) { dbgPlugins << "Could not unlock memory segment" << m.error() << m.errorString(); } if (!m.detach()) { dbgPlugins << "Could not detach from memory segment" << m.error() << m.errorString(); } images.append(gimg); } } dbgPlugins << "Got" << images.size() << "gmic images"; // Start the applicator KUndo2MagicString actionName = kundo2_i18n("Gmic filter"); KisNodeSP rootNode = viewManager()->image()->root(); KisInputOutputMapper mapper(viewManager()->image(), viewManager()->activeNode()); KisNodeListSP layers = mapper.inputNodes(m_inputMode); m_gmicApplicator->setProperties(viewManager()->image(), rootNode, images, actionName, layers); m_gmicApplicator->apply(); - m_gmicApplicator->finish(); } bool QMic::prepareCroppedImages(QByteArray *message, QRectF &rc, int inputMode) { if (!viewManager()) return false; viewManager()->image()->lock(); m_inputMode = (InputLayerMode)inputMode; dbgPlugins << "prepareCroppedImages()" << QString::fromUtf8(*message) << rc << inputMode; KisInputOutputMapper mapper(viewManager()->image(), viewManager()->activeNode()); KisNodeListSP nodes = mapper.inputNodes(m_inputMode); if (nodes->isEmpty()) { viewManager()->image()->unlock(); return false; } for (int i = 0; i < nodes->size(); ++i) { KisNodeSP node = nodes->at(i); if (node && node->paintDevice()) { QRect cropRect; KisSelectionSP selection = viewManager()->image()->globalSelection(); if (selection) { cropRect = selection->selectedExactRect(); } else { cropRect = viewManager()->image()->bounds(); } dbgPlugins << "Converting node" << node->name() << cropRect; const QRectF mappedRect = KisAlgebra2D::mapToRect(cropRect).mapRect(rc); const QRect resultRect = mappedRect.toAlignedRect(); QSharedMemory *m = new QSharedMemory(QString("key_%1").arg(QUuid::createUuid().toString())); m_sharedMemorySegments.append(m); if (!m->create(resultRect.width() * resultRect.height() * 4 * sizeof(float))) { //buf.size())) { qWarning() << "Could not create shared memory segment" << m->error() << m->errorString(); return false; } m->lock(); gmic_image img; img.assign(resultRect.width(), resultRect.height(), 1, 4); img._data = reinterpret_cast(m->data()); KisQmicSimpleConvertor::convertToGmicImageFast(node->paintDevice(), &img, resultRect); message->append(m->key().toUtf8()); m->unlock(); message->append(","); message->append(node->name().toUtf8().toHex()); message->append(","); message->append(QByteArray::number(resultRect.width())); message->append(","); message->append(QByteArray::number(resultRect.height())); message->append("\n"); } } dbgPlugins << QString::fromUtf8(*message); viewManager()->image()->unlock(); return true; } #include "QMic.moc" diff --git a/plugins/extensions/qmic/kis_qmic_applicator.cpp b/plugins/extensions/qmic/kis_qmic_applicator.cpp index 6109453acb..c84e97c6da 100644 --- a/plugins/extensions/qmic/kis_qmic_applicator.cpp +++ b/plugins/extensions/qmic/kis_qmic_applicator.cpp @@ -1,132 +1,123 @@ /* * Copyright (c) 2013 Lukáš Tvrdý #include #include #include #include "kis_import_qmic_processing_visitor.h" #include "kis_qmic_synchronize_layers_command.h" #include "kis_qmic_synchronize_image_size_command.h" -KisQmicApplicator::KisQmicApplicator():m_applicator(0),m_applicatorStrokeEnded(false) +KisQmicApplicator::KisQmicApplicator() { } KisQmicApplicator::~KisQmicApplicator() { - delete m_applicator; } void KisQmicApplicator::setProperties(KisImageWSP image, KisNodeSP node, QVector *> images, const KUndo2MagicString &actionName, KisNodeListSP kritaNodes) { dbgPlugins << "KisQmicApplicator::setProperties();" << ppVar(image) << ppVar(node) << images.size() << actionName << kritaNodes->count(); m_image = image; m_node = node; m_actionName = actionName; m_kritaNodes = kritaNodes; m_images = images; } void KisQmicApplicator::apply() { dbgPlugins << "Request for applying the result"; cancel(); KisImageSignalVector emitSignals; emitSignals << ComplexSizeChangedSignal() << ModifiedSignal; - m_applicator = new KisProcessingApplicator(m_image, m_node, - KisProcessingApplicator::RECURSIVE | KisProcessingApplicator::NO_UI_UPDATES, - emitSignals, m_actionName); + m_applicator.reset( + new KisProcessingApplicator(m_image, m_node, + KisProcessingApplicator::RECURSIVE | + KisProcessingApplicator::NO_UI_UPDATES, + emitSignals, m_actionName)); dbgPlugins << "Created applicator " << m_applicator; m_gmicData = KisQmicDataSP(new KisQmicData()); QRect layerSize; KisSelectionSP selection = m_image->globalSelection(); if (selection) { layerSize = selection->selectedExactRect(); } else { layerSize = QRect(0, 0, m_image->width(), m_image->height()); } if (!selection) { // synchronize Krita image size with biggest gmic layer size m_applicator->applyCommand(new KisQmicSynchronizeImageSizeCommand(m_images, m_image)); } // synchronize layer count m_applicator->applyCommand(new KisQmicSynchronizeLayersCommand(m_kritaNodes, m_images, m_image, layerSize, selection), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE); KisProcessingVisitorSP importVisitor = new KisImportQmicProcessingVisitor(m_kritaNodes, m_images, layerSize, selection); m_applicator->applyVisitor(importVisitor, KisStrokeJobData::SEQUENTIAL); // undo information is stored in this visitor m_applicator->explicitlyEmitFinalSignals(); emit gmicFinished(true, 0, "done!"); } void KisQmicApplicator::cancel() { dbgPlugins << "KisQmicApplicator::cancel"; if (m_applicator) { - if (!m_applicatorStrokeEnded) { - dbgPlugins << "Cancelling applicator: Yes!"; - m_applicator->cancel(); - } - else { - dbgPlugins << "Cancelling applicator: No! Reason: Already finished!"; - } + dbgPlugins << "Cancelling applicator!"; + m_applicator->cancel(); dbgPlugins << "deleting applicator: " << m_applicator; - delete m_applicator; - m_applicator = 0; - - m_applicatorStrokeEnded = false; - dbgPlugins << ppVar(m_applicatorStrokeEnded); + m_applicator.reset(); } else { dbgPlugins << "Cancelling applicator: No! Reason: Null applicator!"; } } void KisQmicApplicator::finish() { dbgPlugins << "Applicator " << m_applicator << " finished"; if (m_applicator) { m_applicator->end(); - m_applicatorStrokeEnded = true; + m_applicator.reset(); } - dbgPlugins << ppVar(m_applicatorStrokeEnded); } float KisQmicApplicator::getProgress() const { dbgPlugins << "KisQmicApplicator::getProgress"; if (m_gmicData) { m_gmicData->progress(); } return KisQmicData::INVALID_PROGRESS_VALUE; } diff --git a/plugins/extensions/qmic/kis_qmic_applicator.h b/plugins/extensions/qmic/kis_qmic_applicator.h index 46c164cf32..e3b12d0a83 100644 --- a/plugins/extensions/qmic/kis_qmic_applicator.h +++ b/plugins/extensions/qmic/kis_qmic_applicator.h @@ -1,64 +1,64 @@ /* * Copyright (c) 2013-2014 Lukáš Tvrdý #include #include #include "gmic.h" #include "kis_qmic_data.h" +#include class KisProcessingApplicator; class KisQmicApplicator : public QObject { Q_OBJECT public: KisQmicApplicator(); ~KisQmicApplicator(); void setProperties(KisImageWSP image, KisNodeSP node, QVector *> images, const KUndo2MagicString &actionName, KisNodeListSP kritaNodes); void apply(); void cancel(); void finish(); float getProgress() const; Q_SIGNALS: void gmicFinished(bool successfully, int milliseconds = -1, const QString &msg = QString()); private: - KisProcessingApplicator *m_applicator; + QScopedPointer m_applicator; KisImageWSP m_image; KisNodeSP m_node; KUndo2MagicString m_actionName; KisNodeListSP m_kritaNodes; - bool m_applicatorStrokeEnded; QVector *> m_images; KisQmicDataSP m_gmicData; }; #endif