Skip to main content Skip to navigation

Notes on packaging Krita with G’MIC

Categories:

This blog post explains the rationale behind the new G’MIC-Qt plugin we ship as part of Krita 5, and how to build and package it.

It has also been published as the new README.packagers.md in the Krita repo.

Krita 3 and later are compatible with G’MIC, an open-source digital image processing framework. This support is provided by G’MIC-Qt, a Qt-based frontend for G’MIC. Since its inception, G’MIC-Qt was shipped as a standalone, externally built executable that is an optional, runtime dependency of Krita.

Krita 5 changes the way G’MIC-Qt is consumed. In order to support CentOS and macOS, G’MIC-Qt has been converted into a dynamically loadable library that is a dependent of Krita.

This file reviews these changes, and how to package Krita accordingly.

Rationale

We have chosen to ship G’MIC-Qt as a library because of two longstanding bugs.

The Krita host for G’MIC-Qt relies on QSharedMemory, i.e. a shared memory segment, on wich a pipe is instantiated to pass messages to and from the host app. Firstly, this approach made opening two simultaneous G’MIC-Qt instances (each paired to its own Krita instance) impossible 1. Secondly, it also forbade using G’MIC-Qt with Krita on CentOS, as well as macOS, because the former doesn’t support QSharedMemory 2, and the latter has a meager 4KB as the maximum shared segment size. While there’s no workaround (to our knowledge) in CentOS, the only workaround for macOS is to manipulate the maximum segment size via sysctl 3, which was already difficult pre-Mojave 4 and now, due to the significant security measures of recent macOS versions, is nothing short of a sysadmin task 5.

There were two approaches. One was to move to a mmap-d file, which is unpredictable to sync due to each canvas’s differing space requirements. The easiest, and the one we chose, was to move to a tighter coupled memory model– a dynamically loadable plugin, as shown in my proposal PR 6. This was rejected by the G’MIC developers because of the possibility of crashing the host app due to a G’MIC internal bug 78. This decision was later enacted as part of G’MIC contributing policies 9.

How did you fix it?

Due to the above, the only path forward was to fork G’MIC, which we did in Krita MR !581 10.

From a source code point of view, our fork is based on top of the latest version’s tarball. Each tarball’s contents are committed to the main branch of the amyspark/gmic GitHub repository 11. For every covered release, there is a branch that in turn overlays our own plugin implementation, along with additional fixes that ensure that G’MIC-Qt doesn’t attempt to overwrite the internal state of the host application; namely, QCoreApplication settings, widget styles, and the installed translators.

From a technical point of view, this library interfaces with Krita through a new, purpose specific library, kisqmicinterface. This library contains nothing more than the previous iteration of the communications system, but now exported through namesake APIs 12.

In short, we have reversed the dependency flow; while in Krita v4 and earlier G’MIC-Qt was a runtime dependency, in v5, it’s G’MIC-Qt that depends on Krita as a build and runtime dependency.

Getting the source code

The patched version’s tarballs are GPG signed and available at the Releases section of the GitHub repository 13. Alternatively, the tarballs (though not the signatures) are also mirrored at our dependencies stash at files.kde.org 14. The tarballs are signed with the GPG key which is available at my GitHub profile. Its fingerprint is 4894424D2412FEE5176732A3FC00108CFD9DBF1E.

Building Krita’s G’MIC-Qt library

After building Krita with your standard process, the CMake install process should have put kisqmicinterface.so in your lib folder:

[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/libkritaqmicinterface.so.18.0.0
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/libkritaqmicinterface.so.18
[2022-01-09T16:21:32.589Z] -- Set runtime path of "/home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/libkritaqmicinterface.so.18.0.0" to "/home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu:/home/appimage/appimage-workspace/deps/usr/lib:/home/appimage/appimage-workspace/deps/usr/lib/x86_64-linux-gnu"
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/lib/x86_64-linux-gnu/libkritaqmicinterface.so

It should also install these headers, as illustrated below:

  • kis_qmic_plugin_interface.h exports a G’MIC-alike launch entry point that the plugin will implement
  • kis_qmic_interface.h implements the G’MIC request-response APIs
  • kritaqmicinterface_export.h is the CMake auto-generated export decoration header
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/include/kis_qmic_interface.h
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/include/kis_qmic_plugin_interface.h
[2022-01-09T16:21:32.589Z] -- Installing: /home/appimage/appimage-workspace/krita.appdir/usr/include/kritaqmicinterface_export.h

The three headers, along with the libkritaqmicinterface.a archive library (if building for Windows under MinGW), comprise a krita-gmic-dev package that’ll be a build dependency of the new G’MIC-Qt plugin. Please note that libkritaqmicinterface.so is consumed by Krita and MUST NOT be placed inside this dev package.

Now, download the G’MIC-Qt tarball from one of the sources listed previously, and unpack it to an isolated directory. Then, you can build it with these lines (adjust them as described):

mkdir build
cmake -S ./gmic-$<the tarball's G'MIC version>-patched/gmic-qt \
      -B ./build \
      -DCMAKE_PREFIX_PATH=$<installation prefix of krita-gmic-dev> \
      -DCMAKE_INSTALL_PREFIX=$<installation prefix of krita itself> \ 
      -DENABLE_SYSTEM_GMIC=$<false if you don't want to use your system's G'MIC> \
      -DGMIC_QT_HOST=krita-plugin
cmake --build . --config $<your desired build type> --target install

The changes from a standard G’MIC build are:

  • the new GMIC_QT_HOST value, krita-plugin
  • the requirement for the krita-gmic-dev package to be available in CMAKE_PREFIX_PATH

This process is illustrated in any of our official build scripts for Windows 15 and for macOS/Linux 16. You can also check the 3rdparty_plugins section of our source tree 17 to see what other hardening we apply to the build.