Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_layer_section.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7#include "psd_layer_section.h"
8
9#include <QBuffer>
10#include <QIODevice>
11
12#include <KoColor.h>
13#include <KoColorSpace.h>
14
15#include <kis_debug.h>
16#include <kis_effect_mask.h>
17#include <kis_group_layer.h>
18#include <kis_generator_layer.h>
19#include <kis_image.h>
20#include <kis_node.h>
21#include <kis_paint_layer.h>
22#include <kis_painter.h>
23#include <kis_selection.h>
24#include <kis_shape_selection.h>
26#include <kis_shape_layer.h>
27#include <KoSvgTextShape.h>
28#include <KoShapeBackground.h>
29#include <KoColorBackground.h>
30#include <KoPatternBackground.h>
32#include <KoShapeStroke.h>
33
34#include <KoPathShape.h>
35#include <KoShapeGroup.h>
36#include <KoShapeManager.h>
38
39#include "kis_dom_utils.h"
40
41#include "psd.h"
42#include "psd_header.h"
43#include "psd_utils.h"
44
45#include "compression.h"
46
51#include <cos/kis_txt2_utls.h>
53
55 : globalInfoSection(header)
56 , m_header(header)
57{
58}
59
64
65bool PSDLayerMaskSection::read(QIODevice &io)
66{
67 bool retval = true; // be optimistic! <:-)
68
69 try {
71 switch (m_header.byteOrder) {
73 retval = readTiffImpl<psd_byte_order::psdLittleEndian>(io);
74 break;
75 default:
76 retval = readTiffImpl(io);
77 break;
78 }
79 } else {
80 retval = readPsdImpl(io);
81 }
83 warnKrita << "WARNING: PSD (emb. pattern):" << e.what();
84 retval = false;
85 }
86
87 return retval;
88}
89
90template<psd_byte_order byteOrder>
92{
93 quint64 layerInfoSectionSize = 0;
94 if (m_header.version == 1) {
95 quint32 _layerInfoSectionSize = 0;
96 SAFE_READ_EX(byteOrder, io, _layerInfoSectionSize);
97 layerInfoSectionSize = _layerInfoSectionSize;
98 } else if (m_header.version == 2) {
99 SAFE_READ_EX(byteOrder, io, layerInfoSectionSize);
100 }
101
102 if (layerInfoSectionSize & 0x1) {
103 warnKrita << "WARNING: layerInfoSectionSize is NOT even! Fixing...";
104 layerInfoSectionSize++;
105 }
106
107 {
108 SETUP_OFFSET_VERIFIER(layerInfoSectionTag, io, layerInfoSectionSize, 0);
109 dbgFile << "Layer info block size" << layerInfoSectionSize;
110
111 if (layerInfoSectionSize > 0) {
112 if (!psdread<byteOrder>(io, nLayers) || nLayers == 0) {
113 error = QString("Could not read number of layers or no layers in image. %1").arg(nLayers);
114 return false;
115 }
116
117 hasTransparency = nLayers < 0; // first alpha channel is the alpha channel of the projection.
118 nLayers = qAbs(nLayers);
119
120 dbgFile << "Number of layers:" << nLayers;
121 dbgFile << "Has separate projection transparency:" << hasTransparency;
122
123 for (int i = 0; i < nLayers; ++i) {
124 dbgFile << "Going to read layer" << i << "pos" << io.pos();
125 dbgFile << "== Enter PSDLayerRecord";
126 PSDHeader sanitizedHeader(m_header);
127 sanitizedHeader.tiffStyleLayerBlock = false; // disable padding
128 std::unique_ptr<PSDLayerRecord> layerRecord(new PSDLayerRecord(sanitizedHeader));
129 if (!layerRecord->read(io)) {
130 error = QString("Could not load layer %1: %2").arg(i).arg(layerRecord->error);
131 return false;
132 }
133 dbgFile << "== Leave PSDLayerRecord";
134 dbgFile << "Finished reading layer" << i << layerRecord->layerName << "blending mode" << layerRecord->blendModeKey << io.pos()
135 << "Number of channels:" << layerRecord->channelInfoRecords.size();
136 layers << layerRecord.release();
137 }
138 }
139
140 // get the positions for the channels belonging to each layer
141 for (int i = 0; i < nLayers; ++i) {
142 dbgFile << "Going to seek channel positions for layer" << i << "pos" << io.pos();
143 if (i > layers.size()) {
144 error = QString("Expected layer %1, but only have %2 layers").arg(i).arg(layers.size());
145 return false;
146 }
147
148 PSDLayerRecord *layerRecord = layers.at(i);
149
150 for (int j = 0; j < layerRecord->nChannels; ++j) {
151 // save the current location so we can jump beyond this block later on.
152 quint64 channelStartPos = io.pos();
153 dbgFile << "\tReading channel image data for channel" << j << "from pos" << io.pos();
154
155 KIS_ASSERT_RECOVER(j < layerRecord->channelInfoRecords.size())
156 {
157 return false;
158 }
159
160 ChannelInfo *channelInfo = layerRecord->channelInfoRecords.at(j);
161
162 quint16 compressionType;
163 if (!psdread<byteOrder>(io, compressionType)) {
164 error = "Could not read compression type for channel";
165 return false;
166 }
167 channelInfo->compressionType = static_cast<psd_compression_type>(compressionType);
168 dbgFile << "\t\tChannel" << j << "has compression type" << compressionType;
169
170 QRect channelRect = layerRecord->channelRect(channelInfo);
171
172 // read the rle row lengths;
173 if (channelInfo->compressionType == psd_compression_type::RLE) {
174 for (qint64 row = 0; row < channelRect.height(); ++row) {
175 // dbgFile << "Reading the RLE byte count position of row" << row << "at pos" << io.pos();
176
177 quint32 byteCount;
178 if (m_header.version == 1) {
179 quint16 _byteCount;
180 if (!psdread<byteOrder>(io, _byteCount)) {
181 error = QString("Could not read byteCount for rle-encoded channel");
182 return 0;
183 }
184 byteCount = _byteCount;
185 } else {
186 if (!psdread<byteOrder>(io, byteCount)) {
187 error = QString("Could not read byteCount for rle-encoded channel");
188 return 0;
189 }
190 }
192 channelInfo->rleRowLengths << byteCount;
193 }
194 }
195
196 // we're beyond all the length bytes, rle bytes and whatever, this is the
197 // location of the real pixel data
198 channelInfo->channelDataStart = io.pos();
199
200 dbgFile << "\t\tstart" << channelStartPos << "data start" << channelInfo->channelDataStart << "data length" << channelInfo->channelDataLength
201 << "pos" << io.pos();
202
203 // make sure we are at the start of the next channel data block
204 io.seek(channelStartPos + channelInfo->channelDataLength);
205
206 // this is the length of the actual channel data bytes
207 channelInfo->channelDataLength = channelInfo->channelDataLength - (channelInfo->channelDataStart - channelStartPos);
208
209 dbgFile << "\t\tchannel record" << j << "for layer" << i << "with id" << channelInfo->channelId << "starting position"
210 << channelInfo->channelDataStart << "with length" << channelInfo->channelDataLength << "and has compression type"
211 << channelInfo->compressionType;
212 }
213 }
214 }
215
216 return true;
217}
218
220{
221 dbgFile << "(PSD) reading layer section. Pos:" << io.pos() << "bytes left:" << io.bytesAvailable();
222
223 // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_21849
224 boost::optional<quint64> layerMaskBlockSize = 0;
225
226 if (m_header.version == 1) {
227 quint32 _layerMaskBlockSize = 0;
228 SAFE_READ_EX(psd_byte_order::psdBigEndian, io, _layerMaskBlockSize);
229 layerMaskBlockSize = _layerMaskBlockSize;
230 } else if (m_header.version == 2) {
231 SAFE_READ_EX(psd_byte_order::psdBigEndian, io, *layerMaskBlockSize);
232 }
233
234 qint64 start = io.pos();
235
236 dbgFile << "layer block size" << *layerMaskBlockSize;
237
238 if (*layerMaskBlockSize == 0) {
239 dbgFile << "No layer info, so no PSD layers available";
240 return true;
241 }
242
248 if (static_cast<qint64>(*layerMaskBlockSize) > io.bytesAvailable()) {
249 warnKrita << "WARNING: invalid layer block size. Got" << *layerMaskBlockSize << "Bytes left" << io.bytesAvailable() << "Triggering a workaround...";
250
251 // just don't use this value for offset recovery at the end
252 layerMaskBlockSize = boost::none;
253 }
254
255 if (!readLayerInfoImpl(io)) {
256 return false;
257 }
258
259 dbgFile << "Leftover before additional blocks:" << io.pos() << io.bytesAvailable();
260
261 quint32 globalMaskBlockLength;
262 if (!psdread(io, globalMaskBlockLength)) {
263 error = "Could not read global mask info block";
264 return false;
265 }
266
267 dbgFile << "Global mask size:" << globalMaskBlockLength << "(" << io.pos() << io.bytesAvailable() << ")";
268
269 if (globalMaskBlockLength > 0) {
271 error = "Could not read global mask info overlay colorspace";
272 return false;
273 }
274
275 for (int i = 0; i < 4; ++i) {
277 error = QString("Could not read mask info visualization color component %1").arg(i);
278 return false;
279 }
280 }
281
283 error = "Could not read global mask info visualization opacity";
284 return false;
285 }
286
287 if (!psdread(io, globalLayerMaskInfo.kind)) {
288 error = "Could not read global mask info visualization type";
289 return false;
290 }
291
292 // Global mask must measure at least 13 bytes
293 // (excluding the 1 byte compiler enforced padding)
294 if (globalMaskBlockLength >= 13) {
295 dbgFile << "Padding for global mask block:"
296 << globalMaskBlockLength - 13 << "(" << io.pos() << ")";
297 io.skip(static_cast<size_t>(globalMaskBlockLength) - 13);
298 }
299 }
300
301 // global additional sections
302
312 std::bind(&PSDLayerMaskSection::readLayerInfoImpl<psd_byte_order::psdBigEndian>, this, std::placeholders::_1));
313
314 dbgFile << "Position before starting global info section:" << io.pos();
315
317
318 if (layerMaskBlockSize) {
319 /* put us after this section so reading the next section will work even if we mess up */
320 io.seek(start + static_cast<qint64>(*layerMaskBlockSize));
321 }
322
323 return true;
324}
325
326template<psd_byte_order byteOrder>
328{
329 quint32 globalMaskBlockLength;
330 if (!psdread<byteOrder>(io, globalMaskBlockLength)) {
331 error = "Could not read global mask info block";
332 return false;
333 }
334
335 dbgFile << "Global mask size:" << globalMaskBlockLength << "(" << io.pos() << io.bytesAvailable() << ")";
336
337 if (globalMaskBlockLength > 0) {
338 if (!psdread<byteOrder>(io, globalLayerMaskInfo.overlayColorSpace)) {
339 error = "Could not read global mask info overlay colorspace";
340 return false;
341 }
342
343 for (int i = 0; i < 4; ++i) {
344 if (!psdread<byteOrder>(io, globalLayerMaskInfo.colorComponents[i])) {
345 error = QString("Could not read mask info visualization color component %1").arg(i);
346 return false;
347 }
348 }
349
350 if (!psdread<byteOrder>(io, globalLayerMaskInfo.opacity)) {
351 error = "Could not read global mask info visualization opacity";
352 return false;
353 }
354
355 if (!psdread<byteOrder>(io, globalLayerMaskInfo.kind)) {
356 error = "Could not read global mask info visualization type";
357 return false;
358 }
359
360 dbgFile << "Global mask info: ";
361 dbgFile << "\tOverlay:" << globalLayerMaskInfo.overlayColorSpace; // 0
362 dbgFile << "\tColor components:" << globalLayerMaskInfo.colorComponents[0] // 65535
366 dbgFile << "\tOpacity:" << globalLayerMaskInfo.opacity; // 50
367 dbgFile << "\tKind:" << globalLayerMaskInfo.kind; // 128
368
369 if (globalMaskBlockLength >= 15) {
370 io.skip(qMax(globalMaskBlockLength - 15, 0x0U));
371 }
372 }
373
374 return true;
375}
376
377template<psd_byte_order byteOrder>
379{
380 dbgFile << "(TIFF) reading layer section. Pos:" << io.pos() << "bytes left:" << io.bytesAvailable();
381
382 // TIFF additional sections
383
394 globalInfoSection.setExtraLayerInfoBlockHandler(std::bind(&PSDLayerMaskSection::readLayerInfoImpl<byteOrder>, this, std::placeholders::_1));
395 globalInfoSection.setUserMaskInfoBlockHandler(std::bind(&PSDLayerMaskSection::readGlobalMask<byteOrder>, this, std::placeholders::_1));
396
397 if (!globalInfoSection.read(io)) {
398 dbgFile << "Failed to read TIFF Photoshop blocks!";
399 return false;
400 }
401
402 dbgFile << "Leftover data after parsing layer/extra blocks:" << io.pos() << io.bytesAvailable() << io.peek(io.bytesAvailable());
403
404 return true;
405}
406
419
421{
422 KisGroupLayer *group = dynamic_cast<KisGroupLayer *>(root.data());
423 if (!group)
424 return;
425
426 KoColor projectionColor = group->defaultProjectionColor();
427 if (projectionColor.opacityU8() == OPACITY_TRANSPARENT_U8)
428 return;
429
430 KisPaintLayerSP layer = new KisPaintLayer(group->image(), i18nc("Automatically created layer name when saving into PSD", "Background"), OPACITY_OPAQUE_U8);
431
432 layer->paintDevice()->setDefaultPixel(projectionColor);
433
434 {
435 FlattenedNode item;
436 item.node = layer;
438 nodes << item;
439 }
440}
441
442void flattenShapes(const KisShapeLayer* parentShapeLayer, QList<KoShape*> shapes, QList<FlattenedNode> &nodes) {
443 Q_FOREACH (KoShape *shape, shapes) {
444 const QString name = shape->name().isEmpty()? "shape "+QString::number(nodes.size()): shape->name();
445 KoShapeGroup *group = dynamic_cast<KoShapeGroup*>(shape);
446 if (group) {
447 KisGroupLayerSP newGroup(new KisGroupLayer(parentShapeLayer->image(),
448 name,
449 (1.0-shape->transparency(false))*255,
450 parentShapeLayer->colorSpace()));
451 newGroup->setVisible(shape->isVisible(false));
452 newGroup->setOpacity(shape->transparency(false)*255);
453 {
454 FlattenedNode item;
455 item.node = newGroup;
457 nodes << item;
458 }
459 flattenShapes(parentShapeLayer, group->shapes(), nodes);
460 {
461 FlattenedNode item;
462 item.node = newGroup;
464 nodes << item;
465 }
466 } else {
467 KisShapeLayerSP newLayer(new KisShapeLayer(nullptr,
468 parentShapeLayer->image(),
469 name,
470 (1.0-shape->transparency(false))*255));
471 KoShape *newShape = shape->cloneShape();
472 newShape->setTransparency(0.0);
473 newShape->setTransformation(shape->absoluteTransformation());
474 newLayer->setVisible(newShape->isVisible(false));
475 newShape->setVisible(true);
477 newLayer->addShape(newShape);
478
479 {
480 FlattenedNode item;
481 item.node = newLayer;
483 nodes << item;
484 }
485 }
486 }
487}
488
490{
491 KisNodeSP child = node->firstChild();
492 while (child) {
493 const bool isLayer = child->inherits("KisLayer");
494 const bool isGroupLayer = child->inherits("KisGroupLayer");
495 const KisShapeLayer *shapeLayer = qobject_cast<KisShapeLayer*>(child.data());
496
497 if (isGroupLayer) {
498 {
499 FlattenedNode item;
500 item.node = child;
502 nodes << item;
503 }
504
505 flattenNodes(child, nodes);
506
507 {
508 FlattenedNode item;
509 item.node = child;
510 if (child->collapsed()) {
512 } else {
514 }
515 nodes << item;
516 }
517 } else if (shapeLayer) {
518 if (shapeLayer->shapes().size() > 1) {
519 {
520 FlattenedNode item;
521 item.node = child;
523 nodes << item;
524 }
525 flattenShapes(shapeLayer, shapeLayer->shapes(), nodes);
526 {
527 FlattenedNode item;
528 item.node = child;
530 nodes << item;
531 }
532 } else {
533 FlattenedNode item;
534 item.node = child;
536 nodes << item;
537 }
538 } else if (isLayer) {
539 FlattenedNode item;
540 item.node = child;
542 nodes << item;
543 }
544
545 child = child->nextSibling();
546 }
547}
548
550{
552 return 0;
553 }
554
555 KisLayer *layer = qobject_cast<KisLayer *>(node.data());
556 QList<KisEffectMaskSP> masks = layer->effectMasks();
557
558 if (masks.size() != 1)
559 return 0;
560
561 KisEffectMaskSP onlyMask = masks.first();
562 return onlyMask->inherits("KisTransparencyMask") ? onlyMask : 0;
563}
564
566{
567 const KisLayer *layer = qobject_cast<KisLayer *>(node.data());
568 KisPSDLayerStyleSP layerStyle = layer->layerStyle();
569
570 if (!layerStyle)
571 return QDomDocument();
572
574 serializer.setStyles(QVector<KisPSDLayerStyleSP>() << layerStyle);
575 return serializer.formPsdXmlDocument();
576}
577
578inline QDomNode findNodeByKey(const QString &key, QDomNode parent)
579{
580 return KisDomUtils::findElementByAttribute(parent, "node", "key", key);
581}
582
583void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
584{
585 QDomNode srcPatternsNode = findNodeByKey(ResourceType::Patterns, src.documentElement());
586 QDomNode dstPatternsNode = findNodeByKey(ResourceType::Patterns, dst.documentElement());
587
588 if (srcPatternsNode.isNull())
589 return;
590 if (dstPatternsNode.isNull()) {
591 dst = src;
592 return;
593 }
594
595 KIS_ASSERT_RECOVER_RETURN(!srcPatternsNode.isNull());
596 KIS_ASSERT_RECOVER_RETURN(!dstPatternsNode.isNull());
597
598 QDomNode node = srcPatternsNode.firstChild();
599 while (!node.isNull()) {
600 QDomNode importedNode = dst.importNode(node, true);
601 KIS_ASSERT_RECOVER_RETURN(!importedNode.isNull());
602
603 dstPatternsNode.appendChild(importedNode);
604 node = node.nextSibling();
605 }
606}
607
608bool PSDLayerMaskSection::write(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
609{
610 bool retval = true;
611
612 try {
614 switch (m_header.byteOrder) {
616 writeTiffImpl<psd_byte_order::psdLittleEndian>(io, rootLayer, compressionType);
617 break;
618 default:
619 writeTiffImpl(io, rootLayer, compressionType);
620 break;
621 }
622 } else {
623 writePsdImpl(io, rootLayer, compressionType);
624 }
626 error = PREPEND_METHOD(e.what());
627 retval = false;
628 }
629
630 return retval;
631}
632
633void PSDLayerMaskSection::writePsdImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
634{
635 dbgFile << "Writing layer section";
636
638 //int textCount = 0;
639
640 // Build the whole layer structure
642 addBackgroundIfNeeded(rootLayer, nodes);
643 flattenNodes(rootLayer, nodes);
644
645 if (nodes.isEmpty()) {
646 throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
647 }
648
649 {
651 QDomDocument mergedPatternsXmlDoc;
652
653 {
655
656 {
657 // number of layers (negative, because krita always has alpha)
658 const qint16 layersSize = static_cast<qint16>(-nodes.size());
660
661 dbgFile << "Number of layers" << layersSize << "at" << io.pos();
662 }
663
664 // Layer records section
665 Q_FOREACH (const FlattenedNode &item, nodes) {
666 KisNodeSP node = item.node;
667
668 PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
669 layers.append(layerRecord);
670
671 KisNodeSP onlyTransparencyMask = findOnlyTransparencyMask(node, item.type);
672 QRect maskRect = onlyTransparencyMask ? onlyTransparencyMask->paintDevice()->exactBounds() : QRect();
673
674 const bool nodeVisible = node->visible();
675 const bool nodeLocked = node->userLocked();
676 const KoColorSpace *colorSpace = node->colorSpace();
677 const quint8 nodeOpacity = node->opacity();
678 const quint8 nodeClipping = 0;
679 const int nodeLabelColor = node->colorLabelIndex();
680 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer *>(node.data());
681 const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
682 const QString nodeCompositeOp = node->compositeOpId();
683
684 const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer *>(node.data());
685 const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
686
687 const KisGeneratorLayer *fillLayer = qobject_cast<KisGeneratorLayer *>(node.data());
688 QDomDocument fillConfig;
690 if (fillLayer) {
691 QString generatorName = fillLayer->filter()->name();
692 if (generatorName == "color") {
694 if (fill.loadFromConfig(fillLayer->filter())) {
695 if (node->image()) {
696 fill.cs = node->image()->colorSpace();
697 } else {
698 fill.cs = node->colorSpace();
699 }
700 fillConfig = fill.getASLXML();
701 fillType = psd_fill_solid_color;
702 }
703 } else if (generatorName == "gradient") {
705 fill.imageWidth = node->image()->width();
706 fill.imageHeight = node->image()->height();
707 if (fill.loadFromConfig(fillLayer->filter())) {
708 fillConfig = fill.getASLXML();
709 fillType = psd_fill_gradient;
710 }
711 } else if (generatorName == "pattern") {
712
714 if (fill.loadFromConfig(fillLayer->filter())) {
715 if (fill.pattern) {
717 w.enterList(ResourceType::Patterns);
718 QString uuid = w.writePattern("", fill.pattern);
719 w.leaveList();
720 mergedPatternsXmlDoc = w.document();
721 fill.patternID = uuid;
722 fillConfig = fill.getASLXML();
723 fillType = psd_fill_pattern;
724 }
725 }
726
727 }
728 // And if anything else, it cannot be stored as a PSD fill layer.
729 }
730
731 double vectorWidth = rootLayer->image()? rootLayer->image()->width() / rootLayer->image()->xRes(): 1;
732 double vectorHeight = rootLayer->image()? rootLayer->image()->height() / rootLayer->image()->yRes(): 1;
733 QTransform FlaketoPixels = QTransform::fromScale(rootLayer->image()->xRes(), rootLayer->image()->yRes());
734
735 const KisShapeLayer *shapeLayer = qobject_cast<KisShapeLayer*>(node.data());
736 psd_layer_type_shape textData;
737 psd_vector_mask vectorMask;
738 QDomDocument strokeData;
739 QDomDocument vogkData;
740
741 if (shapeLayer && !shapeLayer->isFakeNode()) {
742 // only store the first shape.
743 if (shapeLayer->shapes().size() == 1) {
744 KoSvgTextShape * text = dynamic_cast<KoSvgTextShape*>(shapeLayer->shapes().first());
745 if (text) {
746 PsdTextDataConverter convert;
747 KoSvgTextShapeMarkupConverter svgConverter(text);
748 QString svgtext;
749 QString styles;
750 svgConverter.convertToSvg(&svgtext, &styles);
751 // unsure about the boundingBox, needs more research.
752 textData.boundingBox = text->boundingRect().normalized();
753 if (text->shapesInside().isEmpty()) {
754 // Scale bbox to inline
755 const KoSvgText::AutoValue inlineSizeProp =
757 if (!inlineSizeProp.isAuto) {
758 if (text->writingMode() == KoSvgText::HorizontalTB) {
759 textData.boundingBox.setWidth(inlineSizeProp.customValue);
760 } else {
761 textData.boundingBox.setHeight(inlineSizeProp.customValue);
762 }
763 }
764 }
765 textData.bounds = text->outlineRect().normalized();
766
767 bool res = convert.convertToPSDTextEngineData(svgtext, textData.bounds, text->shapesInside(), globalInfoSection.txt2Data, textData.textIndex, textData.text, textData.isHorizontal, FlaketoPixels);
768 if (!res && !convert.errors().isEmpty()) {
769 qWarning() << convert.errors();
770
771 }
772 dbgFile << convert.warnings();
773 textData.engineData = KisTxt2Utils::tyShFromTxt2(globalInfoSection.txt2Data, FlaketoPixels.mapRect(textData.boundingBox), textData.textIndex);
774 //textCount += 1;
775 if (!text->shapesInside().isEmpty()) {
776 textData.bounds = text->outlineRect().normalized();
777 }
778 if (!textData.bounds.isEmpty()) {
779 textData.boundingBox = FlaketoPixels.mapRect(textData.boundingBox);
780 textData.bounds = FlaketoPixels.mapRect(textData.bounds);
781 } else {
782 textData.boundingBox = QRectF();
783 }
784 textData.transform = FlaketoPixels.inverted() * text->absoluteTransformation() * FlaketoPixels;
785 } else {
786 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shapeLayer->shapes().first());
787 if (pathShape){
788 layerRecord->addPathShapeToPSDPath(vectorMask.path, pathShape, vectorWidth, vectorHeight);
789
790 // Right now only saving rect and ellipse when they are 'simple', as the actual parametric
791 // shapes themselves are plugins, so we cannot include them and access the object data.
792 if ((pathShape->pathShapeId() == "RectangleShape" || pathShape->pathShapeId() == "EllipseShape")
793 && pathShape->pointCount() == 4) {
795 data.originType = data.typeToName.key(pathShape->pathShapeId(), 1);
796 QPolygonF poly = pathShape->absoluteTransformation().map(pathShape->outlineRect());
797 data.originShapeBBox = poly.boundingRect();
798 data.originBoxCorners = poly;
799 data.transform = pathShape->absoluteTransformation();
800 vogkData = data.getASL();
801 }
802 KoColorBackground *b = dynamic_cast<KoColorBackground *>(pathShape->background().data());
803 KoGradientBackground *g = dynamic_cast<KoGradientBackground *>(pathShape->background().data());
804 KoPatternBackground *p = dynamic_cast<KoPatternBackground *>(pathShape->background().data());
805 if (b) {
807
808 if (node->image()) {
809 fill.cs = node->image()->colorSpace();
810 } else {
811 fill.cs = node->colorSpace();
812 }
813 fill.setColor(KoColor(b->color(), fill.cs));
814 fillConfig = fill.getASLXML();
815 fillType = psd_fill_solid_color;
816 } else if (g) {
818 fill.setFromQGradient(g->gradient());
819 fillConfig = fill.getASLXML();
820 fillType = psd_fill_gradient;
821 } else if (p) {
823 fillConfig = fill.getASLXML();
824 fillType = psd_fill_pattern;
825 } else if (!pathShape->background()) {
827
828 if (node->image()) {
829 fill.cs = node->image()->colorSpace();
830 } else {
831 fill.cs = node->colorSpace();
832 }
833 fill.setColor(KoColor(Qt::transparent, fill.cs));
834 fillConfig = fill.getASLXML();
835 fillType = psd_fill_solid_color;
836 }
837 KoShapeStrokeSP shapeStroke = qSharedPointerDynamicCast<KoShapeStroke>(pathShape->stroke());
838 if (shapeStroke) {
839 psd_vector_stroke_data strokeDataStruct;
840 strokeDataStruct.loadFromShapeStroke(shapeStroke);
841 strokeDataStruct.strokeEnabled = shapeStroke->isVisible();
842 strokeDataStruct.fillEnabled = pathShape->background()? true: false;
843 strokeDataStruct.resolution = node->image()->xRes()*72.0;
844 strokeData = strokeDataStruct.getASLXML();
845 }
846 }
847 }
848 }
849 }
850
851 QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
852
853 if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
854 mergedPatternsXmlDoc = stylesXmlDoc;
855 } else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
856 mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
857 }
858
859 bool nodeIrrelevant = false;
860 QString nodeName;
861 KisPaintDeviceSP layerContentDevice;
862 psd_section_type sectionType;
863
864 if (item.type == FlattenedNode::RASTER_LAYER) {
865 nodeIrrelevant = false;
866 nodeName = node->name();
867 layerContentDevice = onlyTransparencyMask ? node->original() : node->projection();
868
872 if (fillLayer) {
873 bool transparency = KisPainter::checkDeviceHasTransparency(node->paintDevice());
874 bool semiOpacity = node->paintDevice()->defaultPixel().opacityU8() < OPACITY_OPAQUE_U8;
875 if (transparency || semiOpacity) {
876 KisSelectionSP selection = fillLayer->internalSelection();
877 if(selection) {
878 if(selection->hasNonEmptyShapeSelection()) {
879 KisShapeSelection* shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
880 if (shapeSelection) {
881 Q_FOREACH(KoShape *shape, shapeSelection->shapes()) {
882 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
883 if (pathShape){
884 layerRecord->addPathShapeToPSDPath(vectorMask.path, pathShape, vectorWidth, vectorHeight);
885
886 }
887 }
888 }
889 }
890 }
891 layerContentDevice = node->original();
892 onlyTransparencyMask = node;
893 maskRect = onlyTransparencyMask->paintDevice()->exactBounds();
894 }
895 } else {
896 KisTransparencyMask *mask = qobject_cast<KisTransparencyMask*>(onlyTransparencyMask.data());
897 if (mask) {
898 KisSelectionSP selection = mask->selection();
899 if(selection) {
900 if(selection->hasNonEmptyShapeSelection()) {
901 KisShapeSelection* shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
902 if (shapeSelection) {
903 Q_FOREACH(KoShape *shape, shapeSelection->shapes()) {
904 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
905 if (pathShape){
906 layerRecord->addPathShapeToPSDPath(vectorMask.path, pathShape, vectorWidth, vectorHeight);
907 }
908 }
909 }
910 }
911 }
912 }
913 }
914 sectionType = psd_other;
915 } else {
916 nodeIrrelevant = true;
917 nodeName = item.type == FlattenedNode::SECTION_DIVIDER ? QString("</Layer group>") : node->name();
918 layerContentDevice = 0;
922 }
923
924 // === no access to node anymore
925
926 QRect layerRect;
927
928 if (layerContentDevice) {
929 QRect rc = layerContentDevice->exactBounds();
930 rc = rc.normalized();
931
932 // keep to the max of photoshop's capabilities
933 if (rc.width() > 30000)
934 rc.setWidth(30000);
935 if (rc.height() > 30000)
936 rc.setHeight(30000);
937
938 layerRect = rc;
939 }
940
941 layerRecord->top = layerRect.y();
942 layerRecord->left = layerRect.x();
943 layerRecord->bottom = layerRect.y() + layerRect.height();
944 layerRecord->right = layerRect.x() + layerRect.width();
945
946 // colors + alpha channel
947 // note: transparency mask not included
948 layerRecord->nChannels = static_cast<quint16>(colorSpace->colorChannelCount() + 1);
949
950 ChannelInfo *info = new ChannelInfo;
951 info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
952 layerRecord->channelInfoRecords << info;
953
954 // the rest is in display order: rgb, cmyk, lab...
955 for (qint16 i = 0; i < (int)colorSpace->colorChannelCount(); ++i) {
956 info = new ChannelInfo;
957 info->channelId = i; // 0 for red, 1 = green, etc
958 layerRecord->channelInfoRecords << info;
959 }
960
961 layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
962 layerRecord->isPassThrough = nodeIsPassThrough;
963 layerRecord->opacity = nodeOpacity;
964 layerRecord->clipping = nodeClipping;
965
966 layerRecord->labelColor = nodeLabelColor;
967
968 layerRecord->transparencyProtected = alphaLocked;
969 layerRecord->visible = nodeVisible;
970 layerRecord->irrelevant = nodeIrrelevant;
971 layerRecord->userLocked = nodeLocked;
972
973 layerRecord->layerName = nodeName.isEmpty() ? i18n("Unnamed Layer") : nodeName;
974
975 layerRecord->fillType = fillType;
976 layerRecord->fillConfig = fillConfig;
977
978 layerRecord->vectorMask = vectorMask;
979 layerRecord->vectorStroke = strokeData;
980 layerRecord->vectorOriginationData = vogkData;
981
982 layerRecord->textShape = textData;
983
984 layerRecord->write(io, layerContentDevice, onlyTransparencyMask, maskRect, sectionType, stylesXmlDoc, node->inherits("KisGroupLayer"));
985 }
986
987 dbgFile << "start writing layer pixel data" << io.pos();
988
989 // Now save the pixel data
990 for (PSDLayerRecord *layerRecord : layers) {
991 layerRecord->writePixelData(io, compressionType);
992 }
993 }
994
995 {
996 // write the global layer mask info -- which is empty
997 const quint32 globalMaskSize = 0;
998 SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, globalMaskSize);
999 }
1000
1001 globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
1002
1003#if 0
1022 if (textCount > 0) {
1024 }
1025#endif
1026 }
1027}
1028
1029template<psd_byte_order byteOrder>
1030void PSDLayerMaskSection::writeTiffImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
1031{
1032 dbgFile << "(TIFF) Writing layer section";
1033
1034 // Build the whole layer structure
1036 addBackgroundIfNeeded(rootLayer, nodes);
1037 flattenNodes(rootLayer, nodes);
1038
1039 if (nodes.isEmpty()) {
1040 throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
1041 }
1042
1043 {
1044 QDomDocument mergedPatternsXmlDoc;
1045
1046 {
1047 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
1048 KisAslWriterUtils::writeFixedString<byteOrder>("Layr", io);
1049
1050 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
1051 // number of layers (negative, because krita always has alpha)
1052 const qint16 layersSize = nodes.size();
1053 SAFE_WRITE_EX(byteOrder, io, layersSize);
1054
1055 dbgFile << "Number of layers" << layersSize << "at" << io.pos();
1056
1057 // Layer records section
1058 for (const FlattenedNode &item : nodes) {
1059 KisNodeSP node = item.node;
1060
1061 PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
1062 layers.append(layerRecord);
1063
1064 const bool nodeVisible = node->visible();
1065 const KoColorSpace *colorSpace = node->colorSpace();
1066 const quint8 nodeOpacity = node->opacity();
1067 const quint8 nodeClipping = 0;
1068 const int nodeLabelColor = node->colorLabelIndex();
1069 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer *>(node.data());
1070 const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
1071 const QString nodeCompositeOp = node->compositeOpId();
1072
1073 const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer *>(node.data());
1074 const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
1075
1076 QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
1077
1078 if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
1079 mergedPatternsXmlDoc = stylesXmlDoc;
1080 } else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
1081 mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
1082 }
1083
1084 bool nodeIrrelevant = false;
1085 QString nodeName;
1086 KisPaintDeviceSP layerContentDevice;
1087 psd_section_type sectionType;
1088
1089 if (item.type == FlattenedNode::RASTER_LAYER) {
1090 nodeIrrelevant = false;
1091 nodeName = node->name();
1092 layerContentDevice = node->projection();
1093 sectionType = psd_other;
1094 } else {
1095 nodeIrrelevant = true;
1096 nodeName = item.type == FlattenedNode::SECTION_DIVIDER ? QString("</Layer group>") : node->name();
1097 layerContentDevice = 0;
1098 sectionType = item.type == FlattenedNode::SECTION_DIVIDER ? psd_bounding_divider
1101 }
1102
1103 // === no access to node anymore
1104
1105 QRect layerRect;
1106
1107 if (layerContentDevice) {
1108 QRect rc = layerContentDevice->exactBounds();
1109 rc = rc.normalized();
1110
1111 // keep to the max of photoshop's capabilities
1112 // XXX: update this to PSB
1113 if (rc.width() > 30000)
1114 rc.setWidth(30000);
1115 if (rc.height() > 30000)
1116 rc.setHeight(30000);
1117
1118 layerRect = rc;
1119 }
1120
1121 layerRecord->top = layerRect.y();
1122 layerRecord->left = layerRect.x();
1123 layerRecord->bottom = layerRect.y() + layerRect.height();
1124 layerRecord->right = layerRect.x() + layerRect.width();
1125
1126 // colors + alpha channel
1127 // note: transparency mask not included
1128 layerRecord->nChannels = static_cast<quint16>(colorSpace->colorChannelCount() + 1);
1129
1130 ChannelInfo *info = new ChannelInfo;
1131 info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
1132 layerRecord->channelInfoRecords << info;
1133
1134 // the rest is in display order: rgb, cmyk, lab...
1135 for (quint32 i = 0; i < colorSpace->colorChannelCount(); ++i) {
1136 info = new ChannelInfo;
1137 info->channelId = static_cast<qint16>(i); // 0 for red, 1 = green, etc
1138 layerRecord->channelInfoRecords << info;
1139 }
1140
1141 layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
1142 layerRecord->isPassThrough = nodeIsPassThrough;
1143 layerRecord->opacity = nodeOpacity;
1144 layerRecord->clipping = nodeClipping;
1145
1146 layerRecord->transparencyProtected = alphaLocked;
1147 layerRecord->visible = nodeVisible;
1148 layerRecord->irrelevant = nodeIrrelevant;
1149 layerRecord->labelColor = nodeLabelColor;
1150
1151 layerRecord->layerName = nodeName.isEmpty() ? i18n("Unnamed Layer") : nodeName;
1152
1153 layerRecord->write(io, layerContentDevice, nullptr, QRect(), sectionType, stylesXmlDoc, node->inherits("KisGroupLayer"));
1154 }
1155
1156 dbgFile << "start writing layer pixel data" << io.pos();
1157
1158 // Now save the pixel data
1159 for (PSDLayerRecord *layerRecord : layers) {
1160 layerRecord->writePixelData(io, compressionType);
1161 }
1162 }
1163
1164 // {
1165 // // write the global layer mask info -- which is NOT empty but fixed
1166 // KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
1167 // KisAslWriterUtils::writeFixedString<byteOrder>("LMsk", io);
1168
1169 // KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
1170 // // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_22664
1171 // psdwrite<byteOrder>(io, quint16(0)); // CS: RGB
1172 // psdwrite<byteOrder>(io, quint16(65535)); // Pure red verification
1173 // psdwrite<byteOrder>(io, quint16(0));
1174 // psdwrite<byteOrder>(io, quint16(0));
1175 // psdwrite<byteOrder>(io, quint16(0));
1176 // psdwrite<byteOrder>(io, quint16(50)); // opacity
1177 // psdwrite<byteOrder>(io, quint16(128)); // kind
1178 // }
1179
1180 globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
1181 }
1182}
const Params2D p
const quint8 OPACITY_TRANSPARENT_U8
const quint8 OPACITY_OPAQUE_U8
void setStyles(const QVector< KisPSDLayerStyleSP > &styles)
const KoColorSpace * colorSpace() const
qint32 width() const
double xRes() const
double yRes() const
qint32 height() const
virtual KisFilterConfigurationSP filter() const
void setDefaultPixel(const KoColor &defPixel)
QRect exactBounds() const
KoColor defaultPixel() const
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev)
void setVisible(bool visible, bool isLoading=false) override
KoShapeManager * shapeManager() const
static QVariantHash tyShFromTxt2(const QVariantHash Txt2, const QRectF boundsInPx, int textIndex)
tyShFromTxt2 Txt2 is more or less a superset of tySh, so this function allows generating the tySh dat...
static QVariantHash defaultTxt2()
defaultTxt2 Generate a default txt2 varianthash. This includes stuff like Kinsoku sets default fonts ...
A simple solid color shape background.
virtual quint32 colorChannelCount() const =0
quint8 opacityU8() const
Definition KoColor.cpp:341
A gradient shape background.
const QGradient * gradient() const
Returns the gradient.
The position of a path point within a path shape.
Definition KoPathShape.h:63
virtual QString pathShapeId() const
QRectF outlineRect() const override
reimplemented
int pointCount() const
Returns the number of points in the path.
A pattern shape background.
QList< KoShape * > shapes() const
void addShape(KoShape *shape)
@ AddWithoutRepaint
Avoids each shapes 'update()' to be called for faster addition when its possible.
void addShape(KoShape *shape, KoShapeManager::Repaint repaint=PaintShapeOnAdd)
void setTransparency(qreal transparency)
Definition KoShape.cpp:637
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:885
QTransform absoluteTransformation() const
Definition KoShape.cpp:330
void setTransformation(const QTransform &matrix)
Definition KoShape.cpp:369
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
Definition KoShape.cpp:173
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:754
void setVisible(bool on)
Definition KoShape.cpp:790
QString name() const
Definition KoShape.cpp:950
bool isVisible(bool recursive=true) const
Definition KoShape.cpp:797
qreal transparency(bool recursive=false) const
Definition KoShape.cpp:645
@ InlineSizeId
KoSvgText::AutoValue.
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
bool convertToSvg(QString *svgText, QString *stylesText)
QRectF boundingRect() const override
Get the bounding box of the shape.
QList< KoShape * > shapesInside
KoSvgText::WritingMode writingMode() const
writingMode There's a number of places we need to check the writing mode to provide proper controls.
QRectF outlineRect() const override
KoSvgTextProperties textProperties() const
quint16 version
Definition psd_header.h:43
bool tiffStyleLayerBlock
Definition psd_header.h:50
psd_byte_order byteOrder
Definition psd_header.h:49
bool readGlobalMask(QIODevice &io)
const PSDHeader m_header
bool readTiffImpl(QIODevice &io)
void writeTiffImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
bool readLayerInfoImpl(QIODevice &io)
void writePsdImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
bool read(QIODevice &io)
bool readPsdImpl(QIODevice &io)
PsdAdditionalLayerInfoBlock globalInfoSection
bool write(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
QVector< PSDLayerRecord * > layers
PSDLayerMaskSection(const PSDHeader &header)
GlobalLayerMaskInfo globalLayerMaskInfo
psd_fill_type fillType
psd_vector_mask vectorMask
psd_layer_type_shape textShape
QDomDocument vectorOriginationData
QVector< ChannelInfo * > channelInfoRecords
QRect channelRect(ChannelInfo *channel) const
QDomDocument vectorStroke
void write(QIODevice &io, KisPaintDeviceSP layerContentDevice, KisNodeSP onlyTransparencyMask, const QRect &maskRect, psd_section_type sectionType, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
void addPathShapeToPSDPath(psd_path &path, KoPathShape *shape, double shapeWidth, double shapeHeight)
addPathShapeToPSDPath add all KoPathShape subpaths to the given psd_path struct.
QDomDocument fillConfig
void writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash)
void writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc)
void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler)
void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler)
The PsdTextDataConverter class.
bool convertToPSDTextEngineData(const QString &svgText, QRectF &boundingBox, const QList< KoShape * > &shapesInside, QVariantHash &txt2, int &textIndex, QString &textTotal, bool &isHorizontal, QTransform scaleToPx=QTransform())
#define SAFE_READ_EX(byteOrder, device, varname)
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define PREPEND_METHOD(msg)
Definition kis_debug.h:180
#define warnKrita
Definition kis_debug.h:94
#define dbgFile
Definition kis_debug.h:57
static bool isLayer(KisNodeSP node)
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding)
QDomElement findElementByAttribute(QDomNode parent, const QString &tag, const QString &attribute, const QString &value)
@ HorizontalTB
Definition KoSvgText.h:38
const QString Patterns
QString composite_op_to_psd_blendmode(const QString &compositeop)
Definition psd.cpp:150
psd_compression_type
Definition psd.h:39
@ RLE
Definition psd.h:41
psd_section_type
Definition psd.h:144
@ psd_other
Definition psd.h:144
@ psd_open_folder
Definition psd.h:144
@ psd_closed_folder
Definition psd.h:144
@ psd_bounding_divider
Definition psd.h:144
psd_fill_type
Definition psd.h:123
@ psd_fill_solid_color
Definition psd.h:124
@ psd_fill_gradient
Definition psd.h:125
@ psd_fill_pattern
Definition psd.h:126
QDomDocument fetchLayerStyleXmlData(KisNodeSP node)
KisNodeSP findOnlyTransparencyMask(KisNodeSP node, FlattenedNode::Type type)
QDomNode findNodeByKey(const QString &key, QDomNode parent)
void flattenShapes(const KisShapeLayer *parentShapeLayer, QList< KoShape * > shapes, QList< FlattenedNode > &nodes)
void flattenNodes(KisNodeSP node, QList< FlattenedNode > &nodes)
void addBackgroundIfNeeded(KisNodeSP root, QList< FlattenedNode > &nodes)
void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
std::enable_if_t< std::is_arithmetic< T >::value, bool > psdread(QIODevice &io, T &v)
Definition psd_utils.h:397
QVector< quint32 > rleRowLengths
psd_compression_type compressionType
quint64 channelDataLength
quint64 channelDataStart
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
void setOpacity(quint8 val)
virtual void setVisible(bool visible, bool loading=false)
virtual KisPaintDeviceSP original() const =0
virtual const KoColorSpace * colorSpace() const =0
KisImageWSP image
int colorLabelIndex() const
bool userLocked() const
virtual KisPaintDeviceSP paintDevice() const =0
QString name() const
virtual bool isFakeNode() const
quint8 opacity() const
virtual bool visible(bool recursive=false) const
KoColor defaultProjectionColor() const
QList< KisEffectMaskSP > effectMasks() const
Definition kis_layer.cc:527
KisPSDLayerStyleSP layerStyle
Definition kis_layer.cc:171
const KoColorSpace * colorSpace() const override
returns the image's colorSpace or null, if there is no image
Definition kis_layer.cc:225
KisSelectionSP selection
Definition kis_mask.cc:44
KisNodeSP firstChild() const
Definition kis_node.cpp:361
KisNodeSP nextSibling() const
Definition kis_node.cpp:408
bool alphaLocked() const
KisPaintDeviceSP paintDevice
KisSelectionSP internalSelection() const
bool hasNonEmptyShapeSelection() const
KisSelectionComponent * shapeSelection
bool loadFromConfig(KisFilterConfigurationSP cfg)
void setFromQGradient(const QGradient *gradient)
bool loadFromConfig(KisFilterConfigurationSP cfg)
void setColor(const KoColor &color)
bool loadFromConfig(KisFilterConfigurationSP cfg)
int originType
1 = rect, 2 = rounded rect?, 3 = ? 4 = ? 5 = ellipse, 6 = ?, 7 = polygon, 8 = star,...
QPolygonF originBoxCorners
post-transform bbox as a polygon, seen on 1, 7, 8 and 9.
void loadFromShapeStroke(KoShapeStrokeSP stroke)