Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_mimedata.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2011 Boudewijn Rempt <boud@valdyas.org>
3 *
4 * SPDX-License-Identifier: LGPL-2.1-or-later
5 */
6
7#include "kis_mimedata.h"
8#include "kis_config.h"
9#include "kis_node.h"
10#include "kis_paint_device.h"
11#include "kis_shared_ptr.h"
12#include "kis_image.h"
13#include "kis_layer.h"
14#include "kis_shape_layer.h"
15#include "kis_paint_layer.h"
16#include "kis_clone_layer.h"
17#include "KisDocument.h"
19#include "KisPart.h"
20#include "kis_layer_utils.h"
24#include "kis_generator_layer.h"
25#include "kis_selection.h"
30#include "KisImageBarrierLock.h"
34
35
36#include <KoProperties.h>
37#include <KoStore.h>
38#include <KoColorProfile.h>
40#include <KisDisplayConfig.h>
43
44#include <QApplication>
45#include <QImage>
46#include <QByteArray>
47#include <QBuffer>
48#include <QDomDocument>
49#include <QDomElement>
50#include <QScreen>
51#include <QDir>
52
53namespace {
54KisNodeSP safeCopyNode(KisNodeSP node, bool detachClones = true) {
55 KisCloneLayerSP cloneLayer = dynamic_cast<KisCloneLayer*>(node.data());
56 return cloneLayer && detachClones ?
57 KisNodeSP(cloneLayer->reincarnateAsPaintLayer()) : node->clone();
58}
59}
60
61
63 : QMimeData()
64 , m_nodes(nodes)
65 , m_forceCopy(forceCopy)
66 , m_image(image)
67{
68 Q_ASSERT(m_nodes.size() > 0);
69
71
72 Q_FOREACH (KisNodeSP node, nodes) {
74 }
75}
76
102
104{
105 KisNodeList newNodes;
106
107 {
108 KisImageReadOnlyBarrierLock lock(m_image, std::defer_lock);
109 if (m_image) {
110 lock.lock();
111 }
112
113 Q_FOREACH (KisNodeSP node, m_nodes) {
114 KisNodeSP newNode = safeCopyNode(node);
115 newNode->setImage(nullptr);
116
117 newNodes << newNode;
118 }
119 }
120
121 m_nodes = newNodes;
122 m_image = 0;
123}
124
126{
127 return m_nodes;
128}
129
131{
132 QStringList f = QMimeData::formats();
133 if (m_nodes.size() > 0) {
134 f << "application/x-qt-image"
135 << "application/zip"
136 << "application/x-krita-node-internal-pointer";
137 }
138 return f;
139}
140
141KisDocument *createDocument(QList<KisNodeSP> nodes, KisImageSP srcImage, const QRect &copiedBounds)
142{
144 QRect rc = copiedBounds;
145
146 QRect offset(0, 0, rc.width(), rc.height());
147 rc |= offset;
148
149 if (rc.isEmpty() && srcImage) {
150 rc = srcImage->bounds();
151 }
152
153 KisImageSP image = new KisImage(0, rc.width(), rc.height(), nodes.first()->colorSpace(), nodes.first()->name());
154 image->setAllowMasksOnRootNode(true);
155
156 if (srcImage) {
157 image->setResolution(srcImage->xRes(), srcImage->yRes());
158 }
159
160 {
161 KisImageReadOnlyBarrierLock lock(srcImage, std::defer_lock);
162 if (srcImage) {
163 lock.lock();
164 }
165
166 Q_FOREACH (KisNodeSP node, nodes) {
167 KisNodeSP clonedNode = safeCopyNode(node);
172 image->addNode(clonedNode);
173 KIS_SAFE_ASSERT_RECOVER(clonedNode->image() == KisImageWSP(image)) {
174 clonedNode->setImage(image);
175 }
176 }
177 }
178
179 doc->setCurrentImage(image);
180
181 return doc;
182}
183
184QByteArray serializeToByteArray(QList<KisNodeSP> nodes, KisImageSP srcImage, const QRect &copiedBounds)
185{
186 QScopedPointer<KisDocument> doc(createDocument(nodes, srcImage, copiedBounds));
187 QByteArray result = doc->serializeToNativeByteArray();
188
189 // avoid a sanity check failure caused by the fact that the image outlives
190 // the document (and it does)
191 doc->setCurrentImage(0);
192
193 return result;
194}
195#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
196QVariant KisMimeData::retrieveData(const QString &mimetype, QVariant::Type preferredType) const
197#else
198QVariant KisMimeData::retrieveData(const QString &mimetype, QMetaType preferredType) const
199#endif
200{
208 if (!QApplication::instance()) return QVariant();
209
210
211 Q_ASSERT(m_nodes.size() > 0);
212
213 if (mimetype == "application/x-qt-image") {
214 KisConfig cfg(true);
215
216 QScopedPointer<KisDocument> doc(createDocument(m_nodes, m_image, m_copiedBounds));
217
218 const KisDisplayConfig displayConfig = this->displayConfigForMimePastes();
219 return doc->image()->projection()->convertToQImage(displayConfig.profile,
220 displayConfig.intent,
221 displayConfig.conversionFlags);
222 }
223 else if (mimetype == "application/zip") {
224
225 QByteArray ba = serializeToByteArray(m_nodes, m_image, m_copiedBounds);
226 return ba;
227
228 }
229 else if (mimetype == "application/x-krita-node-internal-pointer") {
230
231 QDomDocument doc("krita_internal_node_pointer");
232 QDomElement root = doc.createElement("pointer");
233 root.setAttribute("application_pid", (qint64)QApplication::applicationPid());
234 root.setAttribute("force_copy", m_forceCopy);
235 root.setAttribute("image_pointer_value", (qint64)m_image.data());
236 doc.appendChild(root);
237
238 Q_FOREACH (KisNodeSP node, m_nodes) {
239 QDomElement element = doc.createElement("node");
240 element.setAttribute("pointer_value", (qint64)node.data());
241 root.appendChild(element);
242 }
243
244 return doc.toByteArray();
245
246 }
247 else {
248 return QMimeData::retrieveData(mimetype, preferredType);
249 }
250}
251
253 KisImageSP srcImage,
254 KisImageSP dstImage,
255 KisShapeController *shapeController)
256{
257 KisShapeLayer *shapeLayer = dynamic_cast<KisShapeLayer*>(node->data());
258 if (shapeLayer) {
259 // attach the layer to a new shape controller
260 KisShapeLayer *shapeLayer2 = new KisShapeLayer(*shapeLayer, shapeController);
261
262 if (srcImage
263 && (!qFuzzyCompare(dstImage->xRes(), srcImage->xRes())
264 || !qFuzzyCompare(dstImage->yRes(), srcImage->yRes()))) {
265
266 const QTransform t = QTransform::fromScale(srcImage->xRes() / dstImage->xRes(),
267 srcImage->yRes() / dstImage->yRes());
268
269 shapeLayer2->setTransformation(shapeLayer2->transformation() * t);
270 }
271
272 *node = shapeLayer2;
273 }
274}
275
277 KisImageSP image,
278 KisShapeController *shapeController,
279 bool /* IN-OUT */ &copyNode)
280{
282 bool forceCopy = false;
283 KisImageSP sourceImage;
284
285 // Qt 4.7 and Qt 5.5 way
286 const KisMimeData *mimedata = qobject_cast<const KisMimeData*>(data);
287 if (mimedata) {
288 nodes = mimedata->nodes();
289 forceCopy = mimedata->m_forceCopy;
290 sourceImage = mimedata->m_image;
291 }
292
293 // Qt 4.8 way
294 if (nodes.isEmpty() && data->hasFormat("application/x-krita-node-internal-pointer")) {
295 QByteArray nodeXml = data->data("application/x-krita-node-internal-pointer");
296
297 QDomDocument doc;
298 doc.setContent(nodeXml);
299
300 QDomElement root = doc.documentElement();
301 qint64 pid = root.attribute("application_pid").toLongLong();
302 forceCopy = root.attribute("force_copy").toInt();
303 qint64 imagePointerValue = root.attribute("image_pointer_value").toLongLong();
304 sourceImage = reinterpret_cast<KisImage*>(imagePointerValue);
305
306 if (pid == QApplication::applicationPid()) {
307 QDomElement e;
308 for (e = root.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) {
309 qint64 pointerValue = e.attribute("pointer_value").toLongLong();
310 if (pointerValue) {
311 nodes << reinterpret_cast<KisNode*>(pointerValue);
312 }
313 }
314 }
315 }
316
317 if (!nodes.isEmpty() && (forceCopy || copyNode || sourceImage != image)) {
318 KisImageReadOnlyBarrierLock lock(sourceImage, std::defer_lock);
319 if (sourceImage) {
320 lock.lock();
321 }
322
323 QList<KisNodeSP> clones;
324 Q_FOREACH (KisNodeSP node, nodes) {
325 node = safeCopyNode(node, sourceImage != image);
326 if ((forceCopy || copyNode) && sourceImage == image) {
328 }
329 initializeExternalNode(&node, sourceImage, image, shapeController);
330 clones << node;
331 }
332 nodes = clones;
333 copyNode = true;
334 }
335 return nodes;
336}
337
339 KisImageWSP image)
340{
341 bool doRecenter = false;
343
344 if (nodes.isEmpty() && (data->hasFormat("application/x-color") || data->hasFormat("krita/x-colorsetentry"))) {
345 QColor color = data->hasColor() ? qvariant_cast<QColor>(data->colorData()) : QColor(255, 0, 255);
346 if (!data->hasColor() && data->hasFormat("krita/x-colorsetentry")) {
347 QByteArray byteData = data->data("krita/x-colorsetentry");
349 color = s.color().toQColor();
350 }
353 defaultConfig->setProperty("color", color);
354 defaultConfig->createLocalResourcesSnapshot(KisGlobalResourcesInterface::instance());
355
356 if (image) {
357 KisGeneratorLayerSP fillLayer = new KisGeneratorLayer(image, image->nextLayerName(i18n("Fill Layer")),
358 defaultConfig, image->globalSelection());
359 nodes << fillLayer;
360 }
361 }
362
363 if (nodes.isEmpty() && data->hasImage()) {
364 QImage qimage = qvariant_cast<QImage>(data->imageData());
365
367 device->convertFromQImage(qimage, 0);
368
369 if (image) {
370 nodes << new KisPaintLayer(image.data(), image->nextLayerName(), OPACITY_OPAQUE_U8, device);
371 }
372
373 doRecenter = true;
374 }
375
376 if (!nodes.isEmpty() && doRecenter) {
377 const QRect imageBounds = image->bounds();
378
379 Q_FOREACH (KisNodeSP node, nodes) {
380 const QRect layerBounds = node->projection()->exactBounds();
381 if (doRecenter) {
382 QPoint pt = imageBounds.center() - layerBounds.center();
383 node->setX(pt.x());
384 node->setY(pt.y());
385 }
386 }
387 }
388
389 return nodes;
390}
391
392QMimeData* KisMimeData::mimeForLayers(const KisNodeList &nodes, KisImageSP image, bool forceCopy)
393{
394 KisNodeList inputNodes = nodes;
395 KisNodeList sortedNodes;
396 KisLayerUtils::sortMergeableNodes(image->root(), inputNodes, sortedNodes);
397 if (sortedNodes.isEmpty()) return 0;
398
399 KisMimeData* data = new KisMimeData(sortedNodes, image, forceCopy);
400 return data;
401}
402
403QMimeData* KisMimeData::mimeForLayersDeepCopy(const KisNodeList &nodes, KisImageSP image, bool forceCopy)
404{
405 KisNodeList inputNodes = nodes;
406 KisNodeList sortedNodes;
407 KisLayerUtils::sortMergeableNodes(image->root(), inputNodes, sortedNodes);
408 if (sortedNodes.isEmpty()) return 0;
409
410 KisMimeData* data = new KisMimeData(sortedNodes, image, forceCopy);
411 data->deepCopyNodes();
412 return data;
413}
414
416{
417 bool result = true;
418 Q_FOREACH (KisNodeSP node, nodes) {
419 if (!parent->allowAsChild(node) || !parent->isEditable(false)) {
420 result = false;
421 break;
422 }
423 }
424 return result;
425}
426
428 KisNodeDummy* &parentDummy,
429 KisNodeDummy* &aboveThisDummy)
430{
431 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(parentDummy, false);
432
433 KisNodeSP parentNode = parentDummy->node();
434 bool result = true;
435
436 if(!nodeAllowsAsChild(parentDummy->node(), nodes) ||
437 (parentDummy->node()->inherits("KisGroupLayer") && parentDummy->node()->collapsed())) {
438 aboveThisDummy = parentDummy;
439 parentDummy = parentDummy->parent();
440
441 result = (!parentDummy) ? false :
442 correctNewNodeLocation(nodes, parentDummy, aboveThisDummy);
443 }
444
445 return result;
446}
447
449 const QMimeData *data,
450 KisImageSP image,
451 KisShapeController *shapeController,
452 bool &copyNode)
453{
456 image,
457 shapeController,
458 copyNode /* IN-OUT */);
459
460 if (nodes.isEmpty()) {
466 copyNode = true;
467 }
468
469 return nodes;
470}
471
472KisNodeList KisMimeData::loadNodesFastAndRecenter(const QPoint &preferredCenter, const QMimeData *data, KisImageSP image, KisShapeController *shapeController, bool &copyNode)
473{
474 KisNodeList nodes = loadNodesFast(data, image, shapeController, copyNode);
475
476 Q_FOREACH (KisNodeSP node, nodes) {
477 const QRect layerBounds = node->exactBounds();
478 const QPoint offset = preferredCenter - layerBounds.center();
479
480 node->setX(node->x() + offset.x());
481 node->setY(node->y() + offset.y());
482 }
483
484 return nodes;
485}
486
487bool KisMimeData::insertMimeLayers(const QMimeData *data,
488 KisImageSP image,
489 KisShapeController *shapeController,
490 KisNodeDummy *parentDummy,
491 KisNodeDummy *aboveThisDummy,
492 bool copyNode,
493 KisNodeInsertionAdapter *nodeInsertionAdapter,
494 bool changeOffset,
495 QPointF offset,
496 KisProcessingApplicator *applicator)
497{
498 QList<KisNodeSP> nodes = loadNodesFast(data, image, shapeController, copyNode /* IN-OUT */);
499
500 if (changeOffset) {
501 Q_FOREACH (KisNodeSP node, nodes) {
502 KisLayerUtils::recursiveApplyNodes(node, [offset] (KisNodeSP node){
503 const QPoint newOffset(node->x() + offset.x(), node->y() + offset.y());
504 node->setX(newOffset.x());
505 node->setY(newOffset.y());
506 });
507 }
508 }
509
510 if (nodes.isEmpty()) return false;
511
512 bool result = true;
513
514 if (!correctNewNodeLocation(nodes, parentDummy, aboveThisDummy)) {
515 return false;
516 }
517
518 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(nodeInsertionAdapter, false);
519 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(parentDummy, false);
520
521 KisNodeSP aboveThisNode = aboveThisDummy ? aboveThisDummy->node() : 0;
522
523 if (!applicator) {
524 if (copyNode) {
525 nodeInsertionAdapter->addNodes(nodes, parentDummy->node(), aboveThisNode);
526 }
527 else {
528 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(nodes.first()->graphListener() == image.data(), false);
529 nodeInsertionAdapter->moveNodes(nodes, parentDummy->node(), aboveThisNode);
530 }
531 } else {
532 {
533 if (copyNode) {
534 KisNodeSP prevNode = aboveThisNode;
535 Q_FOREACH (KisNodeSP node, nodes) {
536 applicator->applyCommand(
537 new KisImageLayerAddCommand(image, node, parentDummy->node(), prevNode));
538 prevNode = node;
539 }
540 } else {
541 KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(nodes.first()->graphListener() == image.data(), false);
542
543 KisNodeSP prevNode = aboveThisNode;
544 Q_FOREACH (KisNodeSP node, nodes) {
545 applicator->applyCommand(
546 new KisImageLayerMoveCommand(image, node, parentDummy->node(), prevNode));
547 prevNode = node;
548 }
549 }
550 }
551 }
552
553 const bool hasDelayedNodes =
554 std::find_if(nodes.begin(), nodes.end(),
555 [] (KisNodeSP node) {
556 return bool(dynamic_cast<KisDelayedUpdateNodeInterface*>(node.data()));
557 }) != nodes.end();
558
559 if (hasDelayedNodes) {
565 image->requestStrokeEnd();
566 }
567
568 return result;
569}
const quint8 OPACITY_OPAQUE_U8
const KoColorProfile * displayProfile(int screen) const
KisDisplayConfig This class keeps track of the color management configuration for image to display....
KoColorConversionTransformation::ConversionFlags conversionFlags
const KoColorProfile * profile
KoColorConversionTransformation::Intent intent
void setCurrentImage(KisImageSP image, bool forceInitialUpdate=true, KisNodeSP preActivatedNode=nullptr)
static KisGeneratorRegistry * instance()
static KisResourcesInterfaceSP instance()
The command for adding a layer.
The command for layer moves inside the layer stack.
void setAllowMasksOnRootNode(bool value)
QString nextLayerName(const QString &baseName="") const
Definition kis_image.cc:715
void requestStrokeEnd()
double xRes() const
double yRes() const
KisSelectionSP globalSelection() const
Definition kis_image.cc:695
QRect bounds() const override
void setResolution(double xres, double yres)
static void initializeExternalNode(KisNodeSP *nodes, KisImageSP srcImage, KisImageSP dstImage, KisShapeController *shapeController)
QVariant retrieveData(const QString &mimetype, QMetaType preferredType) const override
static KisDisplayConfig displayConfigForMimePastes()
QList< KisNodeSP > nodes() const
return the node set on this mimedata object – for internal use
QStringList formats() const override
static KisNodeList tryLoadInternalNodes(const QMimeData *data, KisImageSP image, KisShapeController *shapeController, bool &copyNode)
static QMimeData * mimeForLayersDeepCopy(const KisNodeList &nodes, KisImageSP image, bool forceCopy)
static QMimeData * mimeForLayers(const KisNodeList &nodes, KisImageSP image, bool forceCopy=false)
QRect m_copiedBounds
KisImageSP m_image
static KisNodeList loadNodesFastAndRecenter(const QPoint &preferredCenter, const QMimeData *data, KisImageSP image, KisShapeController *shapeController, bool &copyNode)
static KisNodeList loadNodesFast(const QMimeData *data, KisImageSP image, KisShapeController *shapeController, bool &copyNode)
KisMimeData(QList< KisNodeSP > nodes, KisImageSP image, bool forceCopy=false)
void deepCopyNodes()
QList< KisNodeSP > m_nodes
static bool insertMimeLayers(const QMimeData *data, KisImageSP image, KisShapeController *shapeController, KisNodeDummy *parentDummy, KisNodeDummy *aboveThisDummy, bool copyNode, KisNodeInsertionAdapter *nodeInsertionAdapter, bool changeOffset=false, QPointF offset=QPointF(), KisProcessingApplicator *applicator=nullptr)
static KisNodeList loadNonNativeNodes(const QMimeData *data, KisImageWSP image)
KisNodeSP node() const
KisNodeDummy * parent() const
void addNodes(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
void moveNodes(KisNodeList nodes, KisNodeSP parent, KisNodeSP aboveThis)
static KisOpenGLModeProber * instance()
QRect exactBounds() const
void convertFromQImage(const QImage &image, const KoColorProfile *profile, qint32 offsetX=0, qint32 offsetY=0)
static KisPart * instance()
Definition KisPart.cpp:131
KisDocument * createTemporaryDocument() const
Definition KisPart.cpp:236
static KisPlatformPluginInterfaceFactory * instance()
void applyCommand(KUndo2Command *command, KisStrokeJobData::Sequentiality sequentiality=KisStrokeJobData::SEQUENTIAL, KisStrokeJobData::Exclusivity exclusivity=KisStrokeJobData::NORMAL)
KoColor color() const
Definition KisSwatch.h:30
static KisSwatch fromByteArray(QByteArray &data, QString &groupName, int &originalRow, int &originalColumn)
Definition KisSwatch.cpp:57
void toQColor(QColor *c) const
a convenience method for the above.
Definition KoColor.cpp:198
const T value(const QString &id) const
void setTransformation(const QTransform &matrix)
Definition KoShape.cpp:417
QTransform transformation() const
Returns the shapes local transformation matrix.
Definition KoShape.cpp:424
static bool qFuzzyCompare(half p1, half p2)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(cond, val)
Definition kis_assert.h:129
#define KIS_SAFE_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:128
KisDocument * createDocument(QList< KisNodeSP > nodes, KisImageSP srcImage, const QRect &copiedBounds)
QByteArray serializeToByteArray(QList< KisNodeSP > nodes, KisImageSP srcImage, const QRect &copiedBounds)
bool nodeAllowsAsChild(KisNodeSP parent, KisNodeList nodes)
bool correctNewNodeLocation(KisNodeList nodes, KisNodeDummy *&parentDummy, KisNodeDummy *&aboveThisDummy)
KisSharedPtr< KisNode > KisNodeSP
Definition kis_types.h:86
void sortMergeableNodes(KisNodeSP root, KisNodeList &inputNodes, KisNodeList &outputNodes)
QRect recursiveTightNodeVisibleBounds(KisNodeSP rootNode)
void addCopyOfNameTag(KisNodeSP node)
void recursiveApplyNodes(NodePointer node, Functor func)
int getScreenNumberForWidget(const QWidget *w)
virtual KisPaintDeviceSP projection() const =0
virtual qint32 y() const
virtual QRect exactBounds() const
virtual void setX(qint32)
virtual void setY(qint32)
KisImageWSP image
virtual qint32 x() const
virtual KisFilterConfigurationSP factoryConfiguration(KisResourcesInterfaceSP resourcesInterface) const
bool addNode(KisNodeSP node, KisNodeSP parent=KisNodeSP(), KisNodeAdditionFlags flags=KisNodeAdditionFlag::None)
void setImage(KisImageWSP newImage) override
Definition kis_node.cpp:254
static KoColorSpaceRegistry * instance()
const KoColorProfile * p709SRGBProfile() const