Remove Krita host specific code

This commit is contained in:
Sebastien Fourey 2022-11-21 16:03:33 +01:00
parent 44b0ed389d
commit 65a0e9493d
7 changed files with 6 additions and 421 deletions

1
.gitignore vendored
View File

@ -12,7 +12,6 @@ Makefile
.ui/*
./Makefile
gmic_gimp_qt
gmic_krita_qt
gmic_paintdotnet_qt
gmic_qt
CMakeLists.txt.user

View File

@ -33,10 +33,6 @@ matrix:
dist: bionic
env:
- BUILD="cmake" GMIC_HOST="gimp"
- os: linux
dist: bionic
env:
- BUILD="cmake" GMIC_HOST="krita"
fast_finish: true
before_install:

View File

@ -22,7 +22,7 @@ endif()
message("Build type is " ${CMAKE_BUILD_TYPE})
set (GMIC_QT_HOST "gimp" CACHE STRING "Define for which host gmic-qt will be built: gimp, gimp3 (experimental), krita, none, paintdotnet or 8bf.")
set (GMIC_QT_HOST "gimp" CACHE STRING "Define for which host gmic-qt will be built: gimp, gimp3 (experimental), none, paintdotnet or 8bf.")
if (${GMIC_QT_HOST} STREQUAL "none")
message("Building standalone version.")
else()
@ -611,19 +611,6 @@ if (${GMIC_QT_HOST} STREQUAL "gimp" OR ${GMIC_QT_HOST} STREQUAL "gimp3")
)
install(TARGETS gmic_gimp_qt RUNTIME DESTINATION "${GIMP_PKGLIBDIR}/plug-ins/gmic_gimp_qt")
elseif (${GMIC_QT_HOST} STREQUAL "krita")
set (gmic_qt_SRCS ${gmic_qt_SRCS} src/Host/Krita/host_krita.cpp)
qt5_wrap_ui(gmic_qt_SRCS ${gmic_qt_FORMS})
add_definitions(-DGMIC_HOST=krita)
add_executable(gmic_krita_qt ${gmic_qt_SRCS} ${gmic_qt_QRC})
target_link_libraries(
gmic_krita_qt
PRIVATE
${gmic_qt_LIBRARIES}
)
install(TARGETS gmic_krita_qt RUNTIME DESTINATION bin)
elseif (${GMIC_QT_HOST} STREQUAL "none")
set (gmic_qt_SRCS ${gmic_qt_SRCS}
@ -663,7 +650,7 @@ elseif (${GMIC_QT_HOST} STREQUAL "8bf")
find_package(PkgConfig REQUIRED)
pkg_check_modules(LCMS2 REQUIRED lcms2)
endif()
set (gmic_qt_SRCS ${gmic_qt_SRCS} src/Host/8bf/host_8bf.cpp)
qt5_wrap_ui(gmic_qt_SRCS ${gmic_qt_FORMS})
add_definitions(-DGMIC_HOST=plugin8bf)
@ -676,7 +663,7 @@ elseif (${GMIC_QT_HOST} STREQUAL "8bf")
)
else()
message(FATAL_ERROR "GMIC_QT_HOST is not defined as gimp, gimp3, krita, none, paintdotnet or 8bf")
message(FATAL_ERROR "GMIC_QT_HOST is not defined as gimp, gimp3, none, paintdotnet or 8bf")
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)

View File

@ -59,7 +59,7 @@ git clone https://github.com/dtschump/gmic.git
git clone https://github.com/c-koi/gmic-qt.git
make -C gmic/src CImg.h gmic_stdlib_community.h
cd gmic-qt
qmake [HOST=none|gimp|krita|paintdotnet|8bf]
qmake [HOST=none|gimp|paintdotnet|8bf]
make
```
@ -82,7 +82,7 @@ cd build
```
```sh
cmake .. [-DGMIC_QT_HOST=none|gimp|krita|paintdotnet|8bf] [-DGMIC_PATH=/path/to/gmic] [-DCMAKE_BUILD_TYPE=[Debug|Release|RelwithDebInfo]
cmake .. [-DGMIC_QT_HOST=none|gimp|paintdotnet|8bf] [-DGMIC_PATH=/path/to/gmic] [-DCMAKE_BUILD_TYPE=[Debug|Release|RelwithDebInfo]
make
```

View File

@ -1,6 +1,6 @@
#
# Set HOST variable to define target host software.
# Possible values are "none", "gimp", "gimp3" (experimental) "krita" and "paintdotnet"
# Possible values are "none", "gimp", "gimp3" (experimental), and "paintdotnet"
#
#
@ -199,14 +199,6 @@ equals( HOST, "none") {
message(Building standalone version)
}
equals( HOST, "krita") {
TARGET = gmic_krita_qt
SOURCES += src/Host/Krita/host_krita.cpp
DEFINES += GMIC_HOST=krita
DEPENDPATH += $$PWD/src/Host/Krita
message(Target host software is Krita)
}
equals( HOST, "paintdotnet") {
TARGET = gmic_paintdotnet_qt
SOURCES += src/Host/PaintDotNet/host_paintdotnet.cpp

View File

@ -17,8 +17,3 @@ echo "Building Gimp plugin"
qmake CONFIG+=${config} HOST=gimp GMIC_PATH=gmic-clone/src
make
make
echo "Building Krita plugin"
qmake CONFIG+=${config} HOST=krita GMIC_PATH=gmic-clone/src
make
make

View File

@ -1,384 +0,0 @@
/*
* Copyright (C) 2017 Boudewijn Rempt <boud@valdyas.org>
*
* @contributor Sebastien Fourey (2021) : Adapt to new plugin API
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <QApplication>
#include <QBuffer>
#include <QByteArray>
#include <QCommandLineOption>
#include <QCommandLineParser>
#include <QDataStream>
#include <QDebug>
#include <QDesktopWidget>
#include <QFileDialog>
#include <QFileInfo>
#include <QLocalServer>
#include <QLocalSocket>
#include <QProcess>
#include <QSharedMemory>
#include <QStandardPaths>
#include <QUuid>
#include <algorithm>
#include <list>
#include "Common.h"
#include "Host/GmicQtHost.h"
#include "GmicQt.h"
#ifndef gmic_core
#include "CImg.h"
#endif
#include "gmic.h"
/*
* Messages to Krita are built like this:
*
* command
* mode=int
* layer=key,imagename
* croprect=x,y,w,h
*
* Messages from Krita are built like this:
*
* key,imagename
*
* After a message has been received, "ack" is sent
*
*/
namespace GmicQtHost
{
const QString ApplicationName = QString("Krita");
const char * const ApplicationShortname = GMIC_QT_XSTRINGIFY(GMIC_HOST);
const bool DarkThemeIsDefault = true;
} // namespace GmicQtHost
static QString socketKey = "gmic-krita";
static const char ack[] = "ack";
static QVector<QSharedMemory *> sharedMemorySegments;
QByteArray sendMessageSynchronously(const QByteArray ba)
{
QByteArray answer;
// Send a message to Krita to ask for the images and image with the given crop and mode
QLocalSocket socket;
socket.connectToServer(socketKey);
bool connected = socket.waitForConnected(1000);
if (!connected) {
qWarning() << "Could not connect to the Krita instance.";
return answer;
}
// Send the message to Krita
QDataStream ds(&socket);
ds.writeBytes(ba.constData(), ba.length());
socket.waitForBytesWritten();
while (socket.bytesAvailable() < static_cast<int>(sizeof(quint32))) {
if (!socket.isValid()) {
qWarning() << "Stale request";
return answer;
}
socket.waitForReadyRead(1000);
}
// Get the answer
quint32 remaining;
ds >> remaining;
answer.resize(remaining);
int got = 0;
char * answerBuf = answer.data();
do {
got = ds.readRawData(answerBuf, remaining);
remaining -= got;
answerBuf += got;
} while (remaining && got >= 0 && socket.waitForReadyRead(2000));
if (got < 0) {
qWarning() << "Could not receive the answer." << socket.errorString();
return answer;
}
// Acknowledge receipt
socket.write(ack, qstrlen(ack));
socket.waitForBytesWritten(1000);
socket.disconnectFromServer();
return answer;
}
namespace GmicQtHost
{
void getLayersExtent(int * width, int * height, GmicQt::InputMode mode)
{
*width = 0;
*height = 0;
QByteArray command = QString("command=gmic_qt_get_image_size\nmode=%1").arg((int)mode).toUtf8();
QString answer = QString::fromUtf8(sendMessageSynchronously(command));
if (answer.length() > 0) {
QList<QString> wh = answer.split(',', QT_SKIP_EMPTY_PARTS);
if (wh.length() == 2) {
*width = wh[0].toInt();
*height = wh[1].toInt();
}
}
// qDebug() << "gmic-qt: layers extent:" << *width << *height;
}
void getCroppedImages(gmic_list<float> & images, gmic_list<char> & imageNames, double x, double y, double width, double height, GmicQt::InputMode mode)
{
// qDebug() << "gmic-qt: get_cropped_images:" << x << y << width << height;
const bool entireImage = x < 0 && y < 0 && width < 0 && height < 0;
if (entireImage) {
x = 0.0;
y = 0.0;
width = 1.0;
height = 1.0;
}
// Create a message for Krita
QString message = QString("command=gmic_qt_get_cropped_images\nmode=%5\ncroprect=%1,%2,%3,%4").arg(x).arg(y).arg(width).arg(height).arg((int)mode);
QByteArray command = message.toUtf8();
QString answer = QString::fromUtf8(sendMessageSynchronously(command));
if (answer.isEmpty()) {
qWarning() << "\tgmic-qt: empty answer!";
return;
}
// qDebug() << "\tgmic-qt: " << answer;
QStringList imagesList = answer.split("\n", QT_SKIP_EMPTY_PARTS);
images.assign(imagesList.size());
imageNames.assign(imagesList.size());
// qDebug() << "\tgmic-qt: imagelist size" << imagesList.size();
// Parse the answer -- there should be no new lines in layernames
QStringList memoryKeys;
QList<QSize> sizes;
// Get the keys for the shared memory areas and the imageNames as prepared by Krita in G'Mic format
for (int i = 0; i < imagesList.length(); ++i) {
const QString & layer = imagesList[i];
QStringList parts = layer.split(',', QT_SKIP_EMPTY_PARTS);
if (parts.size() != 4) {
qWarning() << "\tgmic-qt: Got the wrong answer!";
}
memoryKeys << parts[0];
QByteArray ba = parts[1].toLatin1();
ba = QByteArray::fromHex(ba);
gmic_image<char>::string(ba.constData()).move_to(imageNames[i]);
sizes << QSize(parts[2].toInt(), parts[3].toInt());
}
// qDebug() << "\tgmic-qt: keys" << memoryKeys;
// Fill images from the shared memory areas
for (int i = 0; i < memoryKeys.length(); ++i) {
const QString & key = memoryKeys[i];
QSharedMemory m(key);
if (!m.attach(QSharedMemory::ReadOnly)) {
qWarning() << "\tgmic-qt: Could not attach to shared memory area." << m.error() << m.errorString();
}
if (m.isAttached()) {
if (!m.lock()) {
qWarning() << "\tgmic-qt: Could not lock memory segment" << m.error() << m.errorString();
}
// qDebug() << "Memory segment" << key << m.size() << m.constData() << m.data();
// convert the data to the list of float
gmic_image<float> gimg;
gimg.assign(sizes[i].width(), sizes[i].height(), 1, 4);
memcpy(gimg._data, m.constData(), sizes[i].width() * sizes[i].height() * 4 * sizeof(float));
gimg.move_to(images[i]);
if (!m.unlock()) {
qWarning() << "\tgmic-qt: Could not unlock memory segment" << m.error() << m.errorString();
}
if (!m.detach()) {
qWarning() << "\tgmic-qt: Could not detach from memory segment" << m.error() << m.errorString();
}
} else {
qWarning() << "gmic-qt: Could not attach to shared memory area." << m.error() << m.errorString();
}
}
sendMessageSynchronously("command=gmic_qt_detach");
// qDebug() << "\tgmic-qt: Images size" << images.size() << ", names size" << imageNames.size();
}
void outputImages(gmic_list<float> & images, const gmic_list<char> & imageNames, GmicQt::OutputMode mode)
{
// qDebug() << "qmic-qt-output-images";
Q_FOREACH (QSharedMemory * sharedMemory, sharedMemorySegments) {
if (sharedMemory->isAttached()) {
sharedMemory->detach();
}
}
qDeleteAll(sharedMemorySegments);
sharedMemorySegments.clear();
// qDebug() << "\tqmic-qt: shared memory" << sharedMemorySegments.count();
// Create qsharedmemory segments for each image
// Create a message for Krita based on mode, the keys of the qsharedmemory segments and the imageNames
QString message = QString("command=gmic_qt_output_images\nmode=%1\n").arg((int)mode);
for (uint i = 0; i < images.size(); ++i) {
// qDebug() << "\tgmic-qt: image number" << i;
gmic_image<float> gimg = images.at(i);
QSharedMemory * m = new QSharedMemory(QString("key_%1").arg(QUuid::createUuid().toString()));
sharedMemorySegments.append(m);
if (!m->create(gimg._width * gimg._height * gimg._spectrum * sizeof(float))) {
qWarning() << "Could not create shared memory" << m->error() << m->errorString();
return;
}
m->lock();
memcpy(m->data(), gimg._data, gimg._width * gimg._height * gimg._spectrum * sizeof(float));
m->unlock();
QString layerName((const char *)imageNames[i]);
message += "layer=" + m->key() + "," + layerName.toUtf8().toHex() + "," + QString("%1,%2,%3").arg(gimg._spectrum).arg(gimg._width).arg(gimg._height) + +"\n";
}
sendMessageSynchronously(message.toUtf8());
}
void showMessage(const char *)
{
// May be left empty for Krita.
// Only used by launchPluginHeadless(), called in the non-interactive
// script mode of GIMP.
}
void applyColorProfile(cimg_library::CImg<gmic_pixel_type> &) {}
} // namespace GmicQtHost
#if defined Q_OS_WIN
#if defined DRMINGW
namespace
{
void tryInitDrMingw()
{
wchar_t path[MAX_PATH];
QString pathStr = QCoreApplication::applicationDirPath().replace(L'/', L'\\') + QStringLiteral("\\exchndl.dll");
if (pathStr.size() > MAX_PATH - 1) {
return;
}
int pathLen = pathStr.toWCharArray(path);
path[pathLen] = L'\0'; // toWCharArray doesn't add NULL terminator
HMODULE hMod = LoadLibraryW(path);
if (!hMod) {
return;
}
// No need to call ExcHndlInit since the crash handler is installed on DllMain
auto myExcHndlSetLogFileNameA = reinterpret_cast<BOOL(APIENTRY *)(const char *)>(GetProcAddress(hMod, "ExcHndlSetLogFileNameA"));
if (!myExcHndlSetLogFileNameA) {
return;
}
// Set the log file path to %LocalAppData%\kritacrash.log
QString logFile = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation).replace(L'/', L'\\') + QStringLiteral("\\gmic_krita_qt_crash.log");
myExcHndlSetLogFileNameA(logFile.toLocal8Bit());
}
} // namespace
#endif // DRMINGW
#endif // Q_OS_WIN
int main(int argc, char * argv[])
{
bool headless = false;
{
QCommandLineParser parser;
parser.setApplicationDescription("Krita G'Mic Plugin");
parser.addHelpOption();
parser.addPositionalArgument("socket key", "Key to find Krita's local server socket");
QCoreApplication app(argc, argv);
parser.process(app);
const QStringList args = parser.positionalArguments();
if (args.size() > 0) {
socketKey = args[0];
}
if (args.size() > 1) {
if (args[1] == "reapply") {
headless = true;
}
}
#if defined Q_OS_WIN
#if defined DRMINGW
tryInitDrMingw();
#endif
#endif
}
std::list<GmicQt::InputMode> disabledInputModes;
disabledInputModes.push_back(GmicQt::InputMode::NoInput);
// disabledInputModes.push_back(GmicQt::InputMode::Active);
// disabledInputModes.push_back(GmicQt::InputMode::All);
// disabledInputModes.push_back(GmicQt::InputMode::ActiveAndBelow);
// disabledInputModes.push_back(GmicQt::InputMode::ActiveAndAbove);
disabledInputModes.push_back(GmicQt::InputMode::AllVisible);
disabledInputModes.push_back(GmicQt::InputMode::AllInvisible);
std::list<GmicQt::OutputMode> disabledOutputModes;
// disabledOutputModes.push_back(GmicQt::OutputMode::InPlace);
disabledOutputModes.push_back(GmicQt::OutputMode::NewImage);
disabledOutputModes.push_back(GmicQt::OutputMode::NewLayers);
disabledOutputModes.push_back(GmicQt::OutputMode::NewActiveLayers);
qWarning() << "gmic-qt: socket Key:" << socketKey;
int r = 0;
if (headless) {
GmicQt::RunParameters parameters = GmicQt::lastAppliedFilterRunParameters(GmicQt::ReturnedRunParametersFlag::AfterFilterExecution);
r = GmicQt::run(GmicQt::UserInterfaceMode::ProgressDialog, parameters);
} else {
r = GmicQt::run(GmicQt::UserInterfaceMode::Full, //
GmicQt::RunParameters(), //
disabledInputModes, //
disabledOutputModes);
}
Q_FOREACH (QSharedMemory * sharedMemory, sharedMemorySegments) {
if (sharedMemory->isAttached()) {
sharedMemory->detach();
}
}
qDeleteAll(sharedMemorySegments);
sharedMemorySegments.clear();
return r;
}