Commit 11a2db8f authored by Lukáš Tvrdý's avatar Lukáš Tvrdý

Implement internet updates for gmic

Updates are fetched from http://gmic.sourceforge.net/updateXXXX.gmic
where XXXX is curent gmic version. So now it is update1590.gmic

* compile gmic with zlib dependency, needed to extract update
  file from cimgz format
* implement merging of two gmic file definitions,
  if second file has same filter in same category,
  its definition will be overwritten
* applicator has to pass *.gmic definitions to gmic so that user
  defined filters work
* No need to use KisImageLayerAddCommand to add paint layer
parent 3af5fcc6
......@@ -17,10 +17,17 @@ if(APPLE)
endif()
add_definitions(-Dgmic_build )
add_definitions(-Dgmic_float_only )
add_definitions(-Dcimg_use_zlib)
add_definitions(-Dcimg_display=0 )
add_definitions(-fexceptions )
# TODO: this only for debug
find_package( ZLIB REQUIRED )
if ( ZLIB_FOUND )
include_directories( ${ZLIB_INCLUDE_DIRS} )
endif( ZLIB_FOUND )
add_definitions(-Dcimg_use_vt100 )
if(FFTW3_FOUND)
......@@ -38,6 +45,8 @@ if(FFTW3_FOUND)
target_link_libraries(gmic ${FFTW3_LIBRARIES})
endif(FFTW3_FOUND)
target_link_libraries(gmic ${ZLIB_LIBRARIES})
if (WIN32)
target_link_libraries(gmic)
else (WIN32)
......@@ -68,6 +77,7 @@ set(kritagmic_shared_PART_SRCS
kis_gmic_input_output_widget.cpp
kis_gmic_filter_proxy_model.cpp
kis_gmic_widget.cpp
kis_gmic_updater.cpp
)
set(kritagmic_PART_SRCS
......@@ -86,10 +96,10 @@ set(gmicparser_PART_SRCS main.cpp ${kritagmic_shared_PART_SRCS})
kde4_add_ui_files(kritagmic_PART_SRCS wdg_gmic.ui )
kde4_add_plugin(kritagmic ${kritagmic_PART_SRCS})
target_link_libraries(kritagmic kritaui gmic ${QT_QTXML_LIBRARY})
target_link_libraries(kritagmic kritaui gmic ${QT_QTXML_LIBRARY} ${ZLIB_LIBRARIES})
kde4_add_executable(gmicparser ${gmicparser_PART_SRCS})
target_link_libraries(gmicparser ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} kritaui gmic)
target_link_libraries(gmicparser ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} kritaui gmic ${ZLIB_LIBRARIES})
install(TARGETS kritagmic DESTINATION ${PLUGIN_INSTALL_DIR})
install(TARGETS gmicparser ${INSTALL_TARGETS_DEFAULT_ARGS})
......
......@@ -101,3 +101,8 @@ QVariant Category::data(int column)
return name();
}
void Category::replace(int position, Component* c)
{
delete m_components[position];
m_components[position] = c;
}
......@@ -46,6 +46,26 @@ public:
virtual QVariant data(int column);
template <typename T>
int indexOf(const QString &categoryName) const
{
for (int i = 0; i < m_components.size(); i++)
{
const T * component = dynamic_cast<T *>(m_components.at(i));
if (component)
{
if (component->name() == categoryName)
{
return i;
}
}
}
return -1;
}
void replace(int position, Component * c);
public:
QList<Component*> m_components; // categories and commands
private:
......
......@@ -45,6 +45,7 @@ public:
virtual void add(Component* c);
virtual Component* child(int index);
virtual Component* parent();
void setParent(Component * parent) { m_parent = parent; }
virtual int row() const;
virtual int childCount() const;
virtual int columnCount() const;
......
......@@ -37,13 +37,14 @@ KisGmicApplicator::~KisGmicApplicator()
{
}
void KisGmicApplicator::setProperties(KisImageWSP image, KisNodeSP node, const QString &actionName, KisNodeListSP kritaNodes, const QString &gmicCommand)
void KisGmicApplicator::setProperties(KisImageWSP image, KisNodeSP node, const QString &actionName, KisNodeListSP kritaNodes, const QString &gmicCommand, const QByteArray customCommands)
{
m_image = image;
m_node = node;
m_actionName = actionName;
m_kritaNodes = kritaNodes;
m_gmicCommand = gmicCommand;
m_customCommands = customCommands;
}
void KisGmicApplicator::run()
......@@ -77,13 +78,14 @@ void KisGmicApplicator::run()
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
// apply gmic filters to provided layers
applicator.applyCommand(new KisGmicCommand(m_gmicCommand, gmicLayers));
const char * customCommands = m_customCommands.isNull() ? 0 : m_customCommands.constData();
applicator.applyCommand(new KisGmicCommand(m_gmicCommand, gmicLayers, customCommands));
// synchronize layer count
applicator.applyCommand(new KisGmicSynchronizeLayersCommand(m_kritaNodes, gmicLayers, m_image), KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::EXCLUSIVE);
// would sleep(3) help here?
visitor = new KisImportGmicProcessingVisitor(m_kritaNodes, gmicLayers, layerSize, selection);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); // undo information is stored in this visitor
applicator.applyVisitor(visitor, KisStrokeJobData::SEQUENTIAL); // undo information is stored in this visitor
applicator.end();
}
......@@ -27,7 +27,7 @@ class KisGmicApplicator : public QThread
public:
KisGmicApplicator();
~KisGmicApplicator();
void setProperties(KisImageWSP image, KisNodeSP node, const QString &actionName, KisNodeListSP kritaNodes, const QString &gmicCommand);
void setProperties(KisImageWSP image, KisNodeSP node, const QString &actionName, KisNodeListSP kritaNodes, const QString &gmicCommand, const QByteArray customCommands = QByteArray());
protected:
virtual void run();
private:
......@@ -36,6 +36,7 @@ private:
QString m_actionName;
KisNodeListSP m_kritaNodes;
QString m_gmicCommand;
QByteArray m_customCommands;
};
#endif
......@@ -21,10 +21,14 @@
#include <kis_debug.h>
#include <QString>
#include <QFile>
#include <QTimer>
#include <QTime>
KisGmicCommand::KisGmicCommand(const QString &gmicCommandString, QSharedPointer< gmic_list<float> > images):
KisGmicCommand::KisGmicCommand(const QString &gmicCommandString, QSharedPointer< gmic_list<float> > images, const char * customCommands):
m_gmicCommandString(gmicCommandString),
m_images(images),
m_customCommands(customCommands),
m_firstRedo(true)
{
}
......@@ -63,7 +67,8 @@ void KisGmicCommand::redo()
QString gmicCmd = "-* 255 ";
gmicCmd.append(m_gmicCommandString);
dbgPlugins << m_gmicCommandString;
gmic(gmicCmd.toLocal8Bit().constData(), *m_images, images_names);
gmic(gmicCmd.toLocal8Bit().constData(), *m_images, images_names, m_customCommands, true);
}
// Catch exception, if an error occured in the interpreter.
catch (gmic_exception &e)
......@@ -87,33 +92,3 @@ void KisGmicCommand::redo()
}
}
/*
KisProcessingApplicator applicator(this, m_d->rootLayer,
KisProcessingApplicator::RECURSIVE,
emitSignals, actionName);
nodes.append(..);
nodes.append(..);
nodes.append(..);
QSharedPointer<gmic_list<float> > m_images(new ...);
KisProcessingVisitorSP visitor;
visitor = new KisExportGmicProcessingVisitor(nodes, images);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT);
applicator.applyCommand(new KisGmicCommand(images));
visitor = new KisImportGmicProcessingVisitor(nodes, images);
applicator.applyVisitor(visitor, KisStrokeJobData::CONCURRENT); // undo information is stored in this visitor
applicator.applyCommand(new CleanTheData(images));
CleanTheData::redo() {
m_images->clear();
}
*/
......@@ -32,7 +32,7 @@ class QString;
class KisGmicCommand : public KUndo2Command
{
public:
KisGmicCommand(const QString &gmicCommandString, QSharedPointer< gmic_list<float> > images);
KisGmicCommand(const QString &gmicCommandString, QSharedPointer< gmic_list<float> > images, const char * customCommands = 0);
void undo();
void redo();
......@@ -40,6 +40,7 @@ public:
private:
QString m_gmicCommandString;
QSharedPointer<gmic_list<float> > m_images;
const char * m_customCommands;
bool m_firstRedo;
};
......
......@@ -30,7 +30,7 @@
#include "kis_gmic_widget.h"
KisGmicParser::KisGmicParser(const QString& filePath):m_fileName(filePath)
KisGmicParser::KisGmicParser(const QStringList& filePaths):m_filePaths(filePaths)
{
}
......@@ -70,113 +70,157 @@ QString KisGmicParser::parseCategoryName(const QString& line)
Component* KisGmicParser::createFilterTree()
{
QFile file(m_fileName);
if (!file.open(QIODevice::ReadOnly))
Category * rootCategory = 0;
foreach (const QString &fileName, m_filePaths)
{
dbgPlugins << "Can't open file " << m_fileName << file.errorString();
return 0;
}
QFile file(fileName);
QTextStream in(&file);
if (!file.open(QIODevice::ReadOnly))
{
dbgPlugins << "Can't open file " << fileName << file.errorString();
continue;
}
else
{
if (!rootCategory)
{
rootCategory = new Category();
rootCategory->setName("Filters");
}
}
Category * rootCategory = new Category();
rootCategory->setName("Filters");
QTextStream in(&file);
Command * command = 0;
Category * category = rootCategory;
int lineNum = 0;
while(!in.atEnd()) {
QString line = fetchLine(in, lineNum);
Command * command = 0;
Category * category = rootCategory;
int lineNum = 0;
while(!in.atEnd()) {
QString line = fetchLine(in, lineNum);
if (line.startsWith(GIMP_COMMENT))
{
if (isCategory(line))
if (line.startsWith(GIMP_COMMENT))
{
QString categoryName = parseCategoryName(line);
if (categoryName.startsWith("_"))
if (isCategory(line))
{
// root category
if (categoryName != "_")
command = 0;
QString categoryName = parseCategoryName(line);
int toParentSteps = 0;
// count the prefix occurences of "_"
while ( (toParentSteps < categoryName.size()) && (categoryName.at(toParentSteps) == '_') )
{
category = new Category();
category->setName(categoryName.remove(0,1)); // remove _
rootCategory->add(category);
} else
toParentSteps++;
}
if (toParentSteps > 0)
{
// move to correct category
for (int i = 0; i < toParentSteps; i++)
{
Category * parent = dynamic_cast<Category *>(category->parent());
if (parent)
{
category = parent;
}
else
{
// already in root category
break;
}
}
categoryName = categoryName.remove(0,toParentSteps);
}
if (!categoryName.isEmpty())
{
// set current category to parent category
category = static_cast<Category*>(category->parent());
// create new category in current category and set it as current
int categoryChildIndex = category->indexOf<Category>(categoryName);
if (categoryChildIndex != -1)
{
category = static_cast<Category *>(category->child(categoryChildIndex));
}
else
{
Category * newCategory = new Category(category);
newCategory->setName(categoryName);
category->add(newCategory);
category = newCategory; // set current category
}
}
}
else
else if (isCommand(line))
{
// set current category as parent
Category * childCategory = new Category(category);
childCategory->setName(categoryName);
category->add(childCategory);
category = childCategory; // set current category to child
// dbgPlugins << "command" << line;
command = new Command();
command->processCommandName(line);
int commandChildIndex = category->indexOf<Command>(command->name());
if (commandChildIndex == -1)
{
category->add(command);
}
else
{
category->replace(commandChildIndex, command);
}
command->setParent(category);
}
}
else if (isCommand(line))
{
// dbgPlugins << "command" << line;
command = new Command(category);
command->processCommandName(line);
category->add(command);
}
else if (isParameter(line))
{
if (command)
else if (isParameter(line))
{
QStringList block;
block.append(line);
bool parameterIsComplete = false;
int lines = 1;
while (!parameterIsComplete)
if (command)
{
//dbgPlugins << "Line number" << lineNum;
parameterIsComplete = command->processParameter(block);
if (!parameterIsComplete)
QStringList block;
block.append(line);
bool parameterIsComplete = false;
int lines = 1;
while (!parameterIsComplete)
{
QString anotherLine = fetchLine(in, lineNum);
if (!anotherLine.isNull())
//dbgPlugins << "Line number" << lineNum;
parameterIsComplete = command->processParameter(block);
if (!parameterIsComplete)
{
block.append(anotherLine);
lines++;
QString anotherLine = fetchLine(in, lineNum);
if (!anotherLine.isNull())
{
block.append(anotherLine);
lines++;
}
else
{
warnPlugins << "We are and the end of the file unexpectedly"; // we are at the end of the file
break;
}
}
else
else if (lines > 1)
{
warnPlugins << "We are and the end of the file unexpectedly"; // we are at the end of the file
break;
dbgPlugins << "At " << lineNum << " lines: " << lines << " multiline: " << block;
}
}
else if (lines > 1)
{
dbgPlugins << "At " << lineNum << " lines: " << lines << " multiline: " << block;
}
}
else
{
dbgPlugins << "No command for given parameter, invalid gmic definition file";
}
}
else if (line.startsWith(GIMP_COMMENT+"_"))
{
// TODO: do something with those translations
}
else
{
dbgPlugins << "No command for given parameter, invalid gmic definition file";
dbgPlugins << "IGNORING:" << line;
}
}
else if (line.startsWith(GIMP_COMMENT+"_"))
{
// TODO: do something with those translations
}
else
{
dbgPlugins << "IGNORING:" << line;
}
}
}
//command->debug();
//command->debug();
//rootCategory->print();
file.close();
//rootCategory->print();
file.close();
}
return rootCategory;
}
......@@ -192,3 +236,29 @@ QString KisGmicParser::fetchLine(QTextStream& input, int& lineCounter)
}
return QString();
}
QByteArray KisGmicParser::extractGmicCommandsOnly(const QString& filePath)
{
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly))
{
//dbgPlugins() << "Can't open file: " << filePath << file.errorString();
return QByteArray();
}
QTextStream in(&file);
QByteArray result;
while(!in.atEnd())
{
QString line = in.readLine();
if (!line.startsWith("#"))
{
line.append("\n");
result.append(line.toUtf8());
}
}
return result;
}
......@@ -48,12 +48,27 @@ class KisGmicParser
public:
/**
* @param filePath path where is your gmic_def.gmic
* @param filePath path where your *.gmic files are
* */
KisGmicParser(const QString& filePath);
KisGmicParser(const QStringList& filePaths);
~KisGmicParser();
/**
* Creates tree of filters provided by filePaths
*/
Component * createFilterTree();
/**
* Extracts gmic command definitions from .gmic file to be passed to gmic interpreter
*
* Gmic definition file mixes gmic commands and UI definitions for filters
* For executing commands we have to pass to gmic gmic command definition and we don't need
* to pass UI definitions which are ignored (gmic comments).
*
* This function strips all gmic comments: Each line starting with '#' is a comment line.
*/
static QByteArray extractGmicCommandsOnly(const QString& filePath);
private:
bool isCategory(const QString &line);
/* Parses gmic command names for preview and for the filter itself */
......@@ -61,11 +76,10 @@ private:
bool isParameter(const QString &line);
bool matchesRegExp(const QRegExp &regExp, const QString& line);
QString parseCategoryName(const QString &line);
QString fetchLine(QTextStream &input, int &lineCounter);
private:
QString m_fileName;
QStringList m_filePaths;
};
#endif
......@@ -57,10 +57,11 @@
#include "kis_gmic_simple_convertor.h"
#include "kis_gmic_applicator.h"
K_PLUGIN_FACTORY(KisGmicPluginFactory, registerPlugin<KisGmicPlugin>();)
K_EXPORT_PLUGIN(KisGmicPluginFactory("krita"))
const QString STANDARD_GMIC_DEFINITION = "gmic_def.gmic";
KisGmicPlugin::KisGmicPlugin(QObject *parent, const QVariantList &)
: KisViewPlugin(parent, "kritaplugins/gmic.rc"),m_gmicWidget(0)
{
......@@ -69,9 +70,8 @@ KisGmicPlugin::KisGmicPlugin(QObject *parent, const QVariantList &)
action->setActivationConditions(KisAction::ACTIVE_NODE_EDITABLE);
addAction("gmic", action);
QString standardSettings("gmic_def.gmic");
KGlobal::dirs()->addResourceType("gmic_definitions", "data", "krita/gmic/");
m_gmicDefinitionFilePath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", standardSettings);
m_blacklistPath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", STANDARD_GMIC_DEFINITION + ".blacklist");
connect(action, SIGNAL(triggered()), this, SLOT(slotGmic()));
}
......@@ -81,6 +81,31 @@ KisGmicPlugin::~KisGmicPlugin()
delete m_gmicWidget;
}
void KisGmicPlugin::setupDefinitionPaths()
{
m_definitionFilePaths = KGlobal::dirs()->findAllResources("gmic_definitions", "*.gmic");
QMutableStringListIterator it(m_definitionFilePaths);
// remove all instances of gmic_def.gmic
while (it.hasNext())
{
if ( it.next().endsWith(STANDARD_GMIC_DEFINITION) )
{
it.remove();
}
}
// if we don't have update, prepend gmic_def.gmic
int gmicVersion = gmic_version;
QString updateFileName = "update" + QString::number(gmicVersion) + ".gmic";
QString updatedGmicDefinitionFilePath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", updateFileName);
if (updatedGmicDefinitionFilePath.isEmpty())
{
QString standardGmicDefinitionFilePath = KGlobal::mainComponent().dirs()->findResource("gmic_definitions", STANDARD_GMIC_DEFINITION);
m_definitionFilePaths.prepend(standardGmicDefinitionFilePath);
}
}
void KisGmicPlugin::slotGmic()
{
KisImageWSP image = m_view->image();
......@@ -95,21 +120,25 @@ void KisGmicPlugin::slotGmic()
slotClose();
}
KisGmicParser parser(m_gmicDefinitionFilePath);
setupDefinitionPaths();
parseGmicCommandDefinitions(m_definitionFilePaths);
KisGmicParser parser(m_definitionFilePaths);
Component * root = parser.createFilterTree();
KisGmicFilterModel * model = new KisGmicFilterModel(root); // filter mode takes owner ship
KisGmicBlacklister * blacklister = new KisGmicBlacklister(m_gmicDefinitionFilePath + ".blacklist");
KisGmicBlacklister * blacklister = new KisGmicBlacklister(m_blacklistPath);
model->setBlacklister(blacklister);
m_gmicWidget = new KisGmicWidget(model);
QString updateUrl = "http://gmic.sourceforge.net/" + QString("update") + QString::number(gmic_version) + ".gmic";
m_gmicWidget = new KisGmicWidget(model, updateUrl);
m_gmicApplicator = new KisGmicApplicator();
#ifdef Q_WS_WIN
// On windows, gmic needs a humongous stack for its parser
m_gmicApplicator->setStackSize(20 * 1024 * 1024);
#endif
// apply
connect(m_gmicWidget, SIGNAL(sigApplyCommand(KisGmicFilterSetting*)),this, SLOT(slotApplyGmicCommand(KisGmicFilterSetting*)));
// cancel
......@@ -154,7 +183,7 @@ void KisGmicPlugin::slotApplyGmicCommand(KisGmicFilterSetting* setting)