1 /** -*- mode: c++ ; c-basic-offset: 2 -*-
2 *
3 * @file FilterThread.cpp
4 *
5 * Copyright 2017 Sebastien Fourey
6 *
7 * This file is part of G'MIC-Qt, a generic plug-in for raster graphics
8 * editors, offering hundreds of filters thanks to the underlying G'MIC
9 * image processing framework.
10 *
11 * gmic_qt is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * gmic_qt is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with gmic_qt. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25 #include "FilterThread.h"
26 #include <QDebug>
27 #include <iostream>
28 #include "FilterParameters/AbstractParameter.h"
29 #include "GmicStdlib.h"
30 #include "ImageConverter.h"
31 #include "Logger.h"
32 #include "Utils.h"
33 #include "gmic.h"
34 using namespace cimg_library;
35
FilterThread(QObject * parent,const QString & name,const QString & command,const QString & arguments,const QString & environment,GmicQt::OutputMessageMode mode)36 FilterThread::FilterThread(QObject * parent, const QString & name, const QString & command, const QString & arguments, const QString & environment, GmicQt::OutputMessageMode mode)
37 : QThread(parent), _command(command), _arguments(arguments), _environment(environment), _images(new cimg_library::CImgList<float>), _imageNames(new cimg_library::CImgList<char>), _name(name),
38 _messageMode(mode)
39 {
40 _gmicAbort = false;
41 _failed = false;
42 _gmicProgress = 0.0f;
43 // ENTERING;
44 #ifdef _IS_MACOS_
45 setStackSize(8 * 1024 * 1024);
46 #endif
47 }
48
~FilterThread()49 FilterThread::~FilterThread()
50 {
51 delete _images;
52 delete _imageNames;
53 }
54
setArguments(const QString & str)55 void FilterThread::setArguments(const QString & str)
56 {
57 _arguments = str;
58 }
59
setImageNames(const cimg_library::CImgList<char> & imageNames)60 void FilterThread::setImageNames(const cimg_library::CImgList<char> & imageNames)
61 {
62 *_imageNames = imageNames;
63 }
64
swapImages(cimg_library::CImgList<float> & images)65 void FilterThread::swapImages(cimg_library::CImgList<float> & images)
66 {
67 _images->swap(images);
68 }
69
setInputImages(const cimg_library::CImgList<float> & list)70 void FilterThread::setInputImages(const cimg_library::CImgList<float> & list)
71 {
72 *_images = list;
73 }
74
images() const75 const cimg_library::CImgList<float> & FilterThread::images() const
76 {
77 return *_images;
78 }
79
imageNames() const80 const cimg_library::CImgList<char> & FilterThread::imageNames() const
81 {
82 return *_imageNames;
83 }
84
status2StringList(const QString & status)85 QStringList FilterThread::status2StringList(const QString & status)
86 {
87 // Check if status matches something like "{...}{...}_1{...}_0"
88 QRegExp statusRegExp(QString("^") + QChar(gmic_lbrace) + "(.*)" + QChar(gmic_rbrace) + QString("(_[012][+*-]?)?$"));
89 QRegExp statusSeparatorRegExp(QChar(gmic_rbrace) + QString("(_[012][+*-]?)?") + QChar(gmic_lbrace));
90 if (status.isEmpty()) {
91 return QStringList();
92 }
93 if (statusRegExp.indexIn(status) == -1) {
94 // TRACE << "Warning: Incorrect status syntax " << status;
95 return QStringList();
96 }
97 QList<QString> list = statusRegExp.cap(1).split(statusSeparatorRegExp);
98 if (!list.isEmpty()) {
99 QList<QString>::iterator it = list.begin();
100 while (it != list.end()) {
101 QByteArray array = it->toLocal8Bit();
102 gmic::strreplace_fw(array.data());
103 *it++ = array;
104 }
105 }
106 return list;
107 }
108
status2Visibilities(const QString & status)109 QList<int> FilterThread::status2Visibilities(const QString & status)
110 {
111 if (status.isEmpty()) {
112 return QList<int>();
113 }
114 // Check if status matches something like "{...}{...}_1{...}_0"
115 QRegExp statusRegExp(QString("^") + QChar(gmic_lbrace) + "(.*)" + QChar(gmic_rbrace) + QString("(_[012])?$"));
116 if (!status.isEmpty() && statusRegExp.indexIn(status) == -1) {
117 // TRACE << "Incorrect status syntax " << status;
118 return QList<int>();
119 }
120 QByteArray ba = status.toLocal8Bit();
121 const char * pc = ba.constData();
122 const char * limit = pc + ba.size();
123
124 QList<int> result;
125 while (pc < limit) {
126 if (*pc == gmic_rbrace) {
127 if ((pc < limit - 2) && pc[1] == '_' && pc[2] >= '0' && pc[2] <= '2' && (!pc[3] || pc[3] == gmic_lbrace)) {
128 auto visibilityState = static_cast<AbstractParameter::VisibilityState>(pc[2] - '0');
129 result.push_back(visibilityState);
130 pc += 3;
131 } else if (!pc[1] || (pc[1] == gmic_lbrace)) {
132 result.push_back(AbstractParameter::UnspecifiedVisibilityState);
133 ++pc;
134 } else {
135 // TRACE << "Ignoring status" << qPrintable(status);
136 return QList<int>();
137 }
138 } else {
139 ++pc;
140 }
141 }
142 return result;
143 }
144
gmicStatus() const145 QStringList FilterThread::gmicStatus() const
146 {
147 return status2StringList(_gmicStatus);
148 }
149
parametersVisibilityStates() const150 QList<int> FilterThread::parametersVisibilityStates() const
151 {
152 return status2Visibilities(_gmicStatus);
153 }
154
errorMessage() const155 QString FilterThread::errorMessage() const
156 {
157 return _errorMessage;
158 }
159
failed() const160 bool FilterThread::failed() const
161 {
162 return _failed;
163 }
164
aborted() const165 bool FilterThread::aborted() const
166 {
167 return _gmicAbort;
168 }
169
duration() const170 int FilterThread::duration() const
171 {
172 return _startTime.elapsed();
173 }
174
progress() const175 float FilterThread::progress() const
176 {
177 return _gmicProgress;
178 }
179
name() const180 QString FilterThread::name() const
181 {
182 return _name;
183 }
184
fullCommand() const185 QString FilterThread::fullCommand() const
186 {
187 QString result = _command;
188 GmicQt::appendWithSpace(result, _arguments);
189 return result;
190 }
191
setLogSuffix(const QString & text)192 void FilterThread::setLogSuffix(const QString & text)
193 {
194 _logSuffix = text;
195 }
196
abortGmic()197 void FilterThread::abortGmic()
198 {
199 _gmicAbort = true;
200 }
201
run()202 void FilterThread::run()
203 {
204 _startTime.start();
205 _errorMessage.clear();
206 _failed = false;
207 QString fullCommandLine;
208 try {
209 fullCommandLine = QString::fromLocal8Bit(GmicQt::commandFromOutputMessageMode(_messageMode));
210 GmicQt::appendWithSpace(fullCommandLine, _command);
211 GmicQt::appendWithSpace(fullCommandLine, _arguments);
212 _gmicAbort = false;
213 _gmicProgress = -1;
214 if (_messageMode > GmicQt::Quiet) {
215 Logger::log(QString("\n[%1]%2 %3\n").arg(GmicQt::pluginCodeName()).arg(_logSuffix).arg(fullCommandLine));
216 }
217 gmic gmicInstance(_environment.isEmpty() ? nullptr : QString("%1").arg(_environment).toLocal8Bit().constData(), GmicStdLib::Array.constData(), true);
218 gmicInstance.set_variable("_host", GmicQt::HostApplicationShortname, '=');
219 gmicInstance.set_variable("_tk", "qt", '=');
220 gmicInstance.run(fullCommandLine.toLocal8Bit().constData(), *_images, *_imageNames, &_gmicProgress, &_gmicAbort);
221 _gmicStatus = gmicInstance.status;
222 } catch (gmic_exception & e) {
223 _images->assign();
224 _imageNames->assign();
225 const char * message = e.what();
226 _errorMessage = message;
227 if (_messageMode > GmicQt::Quiet) {
228 Logger::log(QString("\n[%1]./error/ When running command '%2', this error occurred:\n%3\n").arg(GmicQt::pluginCodeName()).arg(fullCommandLine).arg(message));
229 }
230 _failed = true;
231 }
232 }
233