diff --git a/.gitignore b/.gitignore index e16515c..f3de21e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ Makefile .ui/* ./Makefile gmic_gimp_qt -gmic_krita_qt gmic_paintdotnet_qt gmic_qt CMakeLists.txt.user diff --git a/.travis.yml b/.travis.yml index 457f020..df60501 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/CMakeLists.txt b/CMakeLists.txt index eb4005b..17b0fdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index 41ed0be..619ce97 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/gmic_qt.pro b/gmic_qt.pro index 90461bb..475378d 100644 --- a/gmic_qt.pro +++ b/gmic_qt.pro @@ -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 diff --git a/scripts/travis_build_qmake.sh b/scripts/travis_build_qmake.sh index fc10549..ed69b40 100755 --- a/scripts/travis_build_qmake.sh +++ b/scripts/travis_build_qmake.sh @@ -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 diff --git a/src/Host/Krita/host_krita.cpp b/src/Host/Krita/host_krita.cpp deleted file mode 100644 index 9a588cc..0000000 --- a/src/Host/Krita/host_krita.cpp +++ /dev/null @@ -1,384 +0,0 @@ -/* - * Copyright (C) 2017 Boudewijn Rempt - * - * @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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#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 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(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 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 & images, gmic_list & 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 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::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 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 & images, const gmic_list & 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 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 &) {} - -} // 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(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 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 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; -}