Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_qmic_interface.cpp
Go to the documentation of this file.
1/*
2 * This file is part of Krita
3 *
4 * SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
10
11#include <KisImageSignals.h>
12#include <KisViewManager.h>
13#include <kis_algebra_2d.h>
14#include <kis_debug.h>
15#include <kis_image.h>
16#include <KisImageBarrierLock.h>
18#include <kis_selection.h>
19#include <kundo2magicstring.h>
20
21#include "gmic.h"
25
35
37 : p(new Private)
38{
39 p->m_viewManager = parent;
40 KIS_ASSERT(p->m_viewManager);
41}
42
44
46{
47 if (!p->m_viewManager)
48 return {};
49
50 KisSelectionSP selection = p->m_viewManager->image()->globalSelection();
51
52 if (selection) {
53 QRect selectionRect = selection->selectedExactRect();
54 return selectionRect.size();
55 } else {
56 p->m_inputMode = static_cast<InputLayerMode>(mode);
57
58 QSize size(0, 0);
59
60 dbgPlugins << "getImageSize()" << mode;
61
62 KisNodeListSP nodes =
63 KisQmicImportTools::inputNodes(p->m_viewManager->image(),
64 p->m_inputMode,
65 p->m_viewManager->activeNode());
66 if (nodes->isEmpty()) {
67 return size;
68 }
69
70 switch (p->m_inputMode) {
73 break;
76 // The last layer in the list is always the layer the user
77 // has selected in Paint.NET, so it will be treated as the
78 // active layer. The clipboard layer (if present) will be
79 // placed above the active layer.
80 const auto activeLayer = nodes->last();
81
82 // G'MIC takes as the image size the size of the active layer.
83 // This is a lie since a query to All will imply a query to Active
84 // through CroppedActiveLayerProxy, and the latter will return a
85 // bogus size if we reply straight with the paint device's bounds.
86 if (activeLayer && activeLayer->paintDevice()) {
87 const QSize layerSize = activeLayer->exactBounds().size();
88 const QSize imageSize = activeLayer->image()->bounds().size();
89 size = size.expandedTo(layerSize).expandedTo(imageSize);
90 }
91 } break;
95 for (auto &node : *nodes) {
96 if (node && node->paintDevice()) {
97 // XXX: when using All, G'MIC will instead do another query
98 // through CroppedActiveLayerProxy to determine the image's
99 // "size". So we need to be both consistent with the image's
100 // bounds, but also extend them in case the layer's
101 // partially offscreen.
102 const QSize layerSize = node->exactBounds().size();
103 const QSize imageSize = node->image()->bounds().size();
104 size = size.expandedTo(layerSize).expandedTo(imageSize);
105 }
106 }
107 break;
111 warnPlugins << "Inputmode" << static_cast<int>(p->m_inputMode)
112 << "is not supported by GMic anymore";
113 break;
114 }
115 default: {
117 << "Inputmode" << static_cast<int>(p->m_inputMode)
118 << "must be specified by GMic or is not implemented in Krita";
119 break;
120 }
121 }
122
123 return size;
124 }
125}
126
128{
129 // Create the shared memory segments, and create a new "message" to send back
130
132
133 if (!p->m_viewManager)
134 return {};
135
136 if (!p->m_viewManager->image()->tryBarrierLock(true)) return {};
137
138 KisImageBarrierLock lock(p->m_viewManager->image(), std::adopt_lock);
139
140 p->m_inputMode = static_cast<InputLayerMode>(inputMode);
141
142 dbgPlugins << "prepareCroppedImages()" << message << rc << inputMode;
143
144 KisNodeListSP nodes =
145 KisQmicImportTools::inputNodes(p->m_viewManager->image(),
146 p->m_inputMode,
147 p->m_viewManager->activeNode());
148 if (nodes->isEmpty()) {
149 return {};
150 }
151
152 for (auto &node : *nodes) {
153 if (node && node->paintDevice()) {
154 KisSelectionSP selection = p->m_viewManager->image()->globalSelection();
155
156 const QRect cropRect = [&]() {
157 if (selection) {
158 return selection->selectedExactRect();
159 } else {
160 // XXX: This is a lie, see gmic_qt_get_image_size as to why
161 const QRect nodeBounds = node->exactBounds();
162 const QRect imageBounds = node->image()->bounds();
163
164 if (imageBounds.contains(nodeBounds)) {
165 return imageBounds;
166 } else {
167 return nodeBounds.united(imageBounds);
168 }
169 }
170 }();
171
172 dbgPlugins << "Converting node" << node->name() << cropRect;
173
174 const QRectF mappedRect = KisAlgebra2D::mapToRect(cropRect).mapRect(rc);
175 const QRect resultRect = mappedRect.toAlignedRect();
176
177 QString noParenthesisName(node->name());
178 noParenthesisName.replace(QChar('('), QChar(21)).replace(QChar(')'), QChar(22));
179
180 const auto translatedMode = KisQmicSimpleConvertor::blendingModeToString(node->compositeOpId());
181
182 const QString name = QString("mode(%1),opacity(%2),pos(%3,%4),name(%5)")
183 .arg(translatedMode)
184 .arg(node->percentOpacity())
185 .arg(cropRect.x())
186 .arg(cropRect.y())
187 .arg(noParenthesisName);
188
189 auto m = KisQMicImageSP::create(name, resultRect.width(), resultRect.height(), 4);
190 p->m_sharedMemorySegments << m;
191
192 {
193 QMutexLocker lock(&m->m_mutex);
194
196 node->paintDevice(),
197 *m.data(),
198 resultRect);
199 }
200
201 message << m;
202 }
203 }
204
205 dbgPlugins << message;
206
207 return message;
208}
209
211{
212 p->m_sharedMemorySegments.clear();
213}
214
216{
217 // Parse the message. read the shared memory segments, fix up the current image and send an ack
218 dbgPlugins << "gmic_qt_output_images";
219 p->m_outputMode = (OutputMode)mode;
220 if (p->m_outputMode != OutputMode::InPlace) {
221 errPlugins << "Requested mode" << static_cast<int>(p->m_outputMode) << "which is not implemented yet";
222 p->m_outputMode = OutputMode::InPlace;
223 }
224
225 dbgPlugins << "slotStartApplicator();" << layers;
226 if (!p->m_viewManager)
227 return;
228
229 dbgPlugins << "Got" << layers.size() << "gmic images";
230
231 {
232 // Start the applicator
233 KUndo2MagicString actionName = kundo2_i18n("G'MIC filter");
234 KisNodeSP rootNode = p->m_viewManager->image()->root();
235 KisNodeListSP mappedLayers =
236 KisQmicImportTools::inputNodes(p->m_viewManager->image(),
237 p->m_inputMode,
238 p->m_viewManager->activeNode());
239 // p->m_gmicApplicator->setProperties(p->m_viewManager->image(), rootNode, images, actionName, layers);
240 // p->m_gmicApplicator->apply();
241
242 KisImageSignalVector emitSignals;
243 emitSignals << ComplexSizeChangedSignal();
244
245 KisProcessingApplicator applicator(p->m_viewManager->image(),
246 rootNode,
248 emitSignals,
249 actionName);
250 dbgPlugins << "Created applicator " << &applicator;
251
252 QRect layerSize;
253 KisSelectionSP selection = p->m_viewManager->image()->globalSelection();
254 if (selection) {
255 layerSize = selection->selectedExactRect();
256 } else {
257 layerSize = QRect(0, 0, p->m_viewManager->image()->width(), p->m_viewManager->image()->height());
258 }
259
260 // This is a three-stage process.
261
262 // 1. Layer sizes must be adjusted individually
263 // 2. synchronize layer count and convert excess GMic nodes to paint
264 // layers
265 // 3. visit the existing nodes and reuse them to apply the remaining
266 // changes from GMic
267 applicator.applyCommand(
268 new KisQmicSynchronizeLayersCommand(mappedLayers,
269 layers,
270 p->m_viewManager->image(),
271 layerSize,
272 selection),
275
276 applicator.end();
277 }
278}
279
280QDebug operator<<(QDebug d, const KisQMicImage &model)
281{
282 d << QString("0x%1,%2,%3,%4").arg((quintptr)&model).arg(QString(model.m_layerName.toUtf8().toHex())).arg(model.m_width).arg(model.m_height);
283 return d;
284}
const Params2D p
void gmic_qt_output_images(int mode, QVector< KisQMicImageSP > layers)
~KisImageInterface() override
QVector< KisQMicImageSP > gmic_qt_get_cropped_images(int mode, QRectF &cropRect)
QSize gmic_qt_get_image_size(int mode)
KisImageInterface(KisViewManager *parent=nullptr)
const QScopedPointer< Private > p
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
static void convertToGmicImageFast(KisPaintDeviceSP dev, KisQMicImage &gmicImage, QRect rc=QRect())
static QString blendingModeToString(QString blendMode)
InputLayerMode
Definition gmic.h:21
@ AllInvisiblesDesc_DEPRECATED
@ AllVisiblesDesc_DEPRECATED
OutputMode
Definition gmic.h:18
#define KIS_ASSERT(cond)
Definition kis_assert.h:33
#define warnPlugins
Definition kis_debug.h:93
#define errPlugins
Definition kis_debug.h:113
#define dbgPlugins
Definition kis_debug.h:51
QDebug operator<<(QDebug d, const KisQMicImage &model)
KUndo2MagicString kundo2_i18n(const char *text)
QTransform mapToRect(const QRectF &rect)
KisNodeListSP inputNodes(KisImageSP image, InputLayerMode inputMode, KisNodeSP currentNode)
KisImageWSP image
QVector< KisQMicImageSP > m_sharedMemorySegments
KisQmicApplicator * m_gmicApplicator
QRect selectedExactRect() const
Slow, but exact way of determining the rectangle that encloses the selection.