Notes on packaging Krita with G’MIC
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.
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
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
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
[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.hexports a G’MIC-alike
launchentry point that the plugin will implement
kis_qmic_interface.himplements the G’MIC request-response APIs
kritaqmicinterface_export.his 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
- the requirement for the
krita-gmic-devpackage to be available in
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.
Bug #44 on c-koi/gmic-qt: “CentOS7: Krita 4.0.4 + gmic_krita_qt 2.3.0/2.2.3 - QSharedMemory::attach”. https://github.com/c-koi/gmic-qt/issues/44 ↩
Bug 424514 on krita: “Guaranteed crash when opening 2 G’MIC-qt”. https://bugs.kde.org/show_bug.cgi?id=424514 ↩
e.g. https://stackoverflow.com/questions/2017004/setting-shmmax-etc-values-on-mac-os-x-10-6-for-postgresql ↩
See https://developer.apple.com/forums/thread/669625 for an approach applicable after
com.apple.rootlesswas fully enforced. ↩
Note that this was already possible, simply by crashing G’MIC the host app would deadlock, waiting forever for a response. ↩
An older version of what’s https://github.com/c-koi/gmic-qt/blob/master/src/Host/GmicQtHost.h nowadays. ↩