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 QScopedPointer<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.take();
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;
511 nodes << item;
512 }
513 } else if (shapeLayer) {
514 if (shapeLayer->shapes().size() > 1) {
515 {
516 FlattenedNode item;
517 item.node = child;
519 nodes << item;
520 }
521 flattenShapes(shapeLayer, shapeLayer->shapes(), nodes);
522 {
523 FlattenedNode item;
524 item.node = child;
526 nodes << item;
527 }
528 } else {
529 FlattenedNode item;
530 item.node = child;
532 nodes << item;
533 }
534 } else if (isLayer) {
535 FlattenedNode item;
536 item.node = child;
538 nodes << item;
539 }
540
541 child = child->nextSibling();
542 }
543}
544
546{
548 return 0;
549 }
550
551 KisLayer *layer = qobject_cast<KisLayer *>(node.data());
552 QList<KisEffectMaskSP> masks = layer->effectMasks();
553
554 if (masks.size() != 1)
555 return 0;
556
557 KisEffectMaskSP onlyMask = masks.first();
558 return onlyMask->inherits("KisTransparencyMask") ? onlyMask : 0;
559}
560
562{
563 const KisLayer *layer = qobject_cast<KisLayer *>(node.data());
564 KisPSDLayerStyleSP layerStyle = layer->layerStyle();
565
566 if (!layerStyle)
567 return QDomDocument();
568
570 serializer.setStyles(QVector<KisPSDLayerStyleSP>() << layerStyle);
571 return serializer.formPsdXmlDocument();
572}
573
574inline QDomNode findNodeByKey(const QString &key, QDomNode parent)
575{
576 return KisDomUtils::findElementByAttribute(parent, "node", "key", key);
577}
578
579void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
580{
581 QDomNode srcPatternsNode = findNodeByKey(ResourceType::Patterns, src.documentElement());
582 QDomNode dstPatternsNode = findNodeByKey(ResourceType::Patterns, dst.documentElement());
583
584 if (srcPatternsNode.isNull())
585 return;
586 if (dstPatternsNode.isNull()) {
587 dst = src;
588 return;
589 }
590
591 KIS_ASSERT_RECOVER_RETURN(!srcPatternsNode.isNull());
592 KIS_ASSERT_RECOVER_RETURN(!dstPatternsNode.isNull());
593
594 QDomNode node = srcPatternsNode.firstChild();
595 while (!node.isNull()) {
596 QDomNode importedNode = dst.importNode(node, true);
597 KIS_ASSERT_RECOVER_RETURN(!importedNode.isNull());
598
599 dstPatternsNode.appendChild(importedNode);
600 node = node.nextSibling();
601 }
602}
603
604bool PSDLayerMaskSection::write(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
605{
606 bool retval = true;
607
608 try {
610 switch (m_header.byteOrder) {
612 writeTiffImpl<psd_byte_order::psdLittleEndian>(io, rootLayer, compressionType);
613 break;
614 default:
615 writeTiffImpl(io, rootLayer, compressionType);
616 break;
617 }
618 } else {
619 writePsdImpl(io, rootLayer, compressionType);
620 }
622 error = PREPEND_METHOD(e.what());
623 retval = false;
624 }
625
626 return retval;
627}
628
629void PSDLayerMaskSection::writePsdImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
630{
631 dbgFile << "Writing layer section";
632
634 int textCount = 0;
635
636 // Build the whole layer structure
638 addBackgroundIfNeeded(rootLayer, nodes);
639 flattenNodes(rootLayer, nodes);
640
641 if (nodes.isEmpty()) {
642 throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
643 }
644
645 {
647 QDomDocument mergedPatternsXmlDoc;
648
649 {
651
652 {
653 // number of layers (negative, because krita always has alpha)
654 const qint16 layersSize = static_cast<qint16>(-nodes.size());
656
657 dbgFile << "Number of layers" << layersSize << "at" << io.pos();
658 }
659
660 // Layer records section
661 Q_FOREACH (const FlattenedNode &item, nodes) {
662 KisNodeSP node = item.node;
663
664 PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
665 layers.append(layerRecord);
666
667 KisNodeSP onlyTransparencyMask = findOnlyTransparencyMask(node, item.type);
668 QRect maskRect = onlyTransparencyMask ? onlyTransparencyMask->paintDevice()->exactBounds() : QRect();
669
670 const bool nodeVisible = node->visible();
671 const KoColorSpace *colorSpace = node->colorSpace();
672 const quint8 nodeOpacity = node->opacity();
673 const quint8 nodeClipping = 0;
674 const int nodeLabelColor = node->colorLabelIndex();
675 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer *>(node.data());
676 const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
677 const QString nodeCompositeOp = node->compositeOpId();
678
679 const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer *>(node.data());
680 const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
681
682 const KisGeneratorLayer *fillLayer = qobject_cast<KisGeneratorLayer *>(node.data());
683 QDomDocument fillConfig;
685 if (fillLayer) {
686 QString generatorName = fillLayer->filter()->name();
687 if (generatorName == "color") {
689 if (fill.loadFromConfig(fillLayer->filter())) {
690 if (node->image()) {
691 fill.cs = node->image()->colorSpace();
692 } else {
693 fill.cs = node->colorSpace();
694 }
695 fillConfig = fill.getASLXML();
696 fillType = psd_fill_solid_color;
697 }
698 } else if (generatorName == "gradient") {
700 fill.imageWidth = node->image()->width();
701 fill.imageHeight = node->image()->height();
702 if (fill.loadFromConfig(fillLayer->filter())) {
703 fillConfig = fill.getASLXML();
704 fillType = psd_fill_gradient;
705 }
706 } else if (generatorName == "pattern") {
707
709 if (fill.loadFromConfig(fillLayer->filter())) {
710 if (fill.pattern) {
712 w.enterList(ResourceType::Patterns);
713 QString uuid = w.writePattern("", fill.pattern);
714 w.leaveList();
715 mergedPatternsXmlDoc = w.document();
716 fill.patternID = uuid;
717 fillConfig = fill.getASLXML();
718 fillType = psd_fill_pattern;
719 }
720 }
721
722 }
723 // And if anything else, it cannot be stored as a PSD fill layer.
724 }
725
726 double vectorWidth = rootLayer->image()? rootLayer->image()->width() / rootLayer->image()->xRes(): 1;
727 double vectorHeight = rootLayer->image()? rootLayer->image()->height() / rootLayer->image()->yRes(): 1;
728 QTransform FlaketoPixels = QTransform::fromScale(rootLayer->image()->xRes(), rootLayer->image()->yRes());
729
730 const KisShapeLayer *shapeLayer = qobject_cast<KisShapeLayer*>(node.data());
731 psd_layer_type_shape textData;
732 psd_vector_mask vectorMask;
733 QDomDocument strokeData;
734 QDomDocument vogkData;
735
736 if (shapeLayer && !shapeLayer->isFakeNode()) {
737 // only store the first shape.
738 if (shapeLayer->shapes().size() == 1) {
739 KoSvgTextShape * text = dynamic_cast<KoSvgTextShape*>(shapeLayer->shapes().first());
740 if (text) {
741 PsdTextDataConverter convert;
742 KoSvgTextShapeMarkupConverter svgConverter(text);
743 QString svgtext;
744 QString styles;
745 svgConverter.convertToSvg(&svgtext, &styles);
746 // unsure about the boundingBox, needs more research.
747 textData.boundingBox = text->boundingRect().normalized();
748 if (text->shapesInside().isEmpty()) {
749 // Scale bbox to inline
750 const KoSvgText::AutoValue inlineSizeProp =
752 if (!inlineSizeProp.isAuto) {
753 if (text->writingMode() == KoSvgText::HorizontalTB) {
754 textData.boundingBox.setWidth(inlineSizeProp.customValue);
755 } else {
756 textData.boundingBox.setHeight(inlineSizeProp.customValue);
757 }
758 }
759 }
760 textData.bounds = text->outlineRect().normalized();
761
762 bool res = convert.convertToPSDTextEngineData(svgtext, textData.bounds, text->shapesInside(), globalInfoSection.txt2Data, textData.textIndex, textData.text, textData.isHorizontal, FlaketoPixels);
763 if (!res && !convert.errors().isEmpty()) {
764 qWarning() << convert.errors();
765
766 }
767 dbgFile << convert.warnings();
768 textData.engineData = KisTxt2Utils::tyShFromTxt2(globalInfoSection.txt2Data, FlaketoPixels.mapRect(textData.boundingBox), textData.textIndex);
769 textCount += 1;
770 if (!text->shapesInside().isEmpty()) {
771 textData.bounds = text->outlineRect().normalized();
772 }
773 if (!textData.bounds.isEmpty()) {
774 textData.boundingBox = FlaketoPixels.mapRect(textData.boundingBox);
775 textData.bounds = FlaketoPixels.mapRect(textData.bounds);
776 } else {
777 textData.boundingBox = QRectF();
778 }
779 textData.transform = FlaketoPixels.inverted() * text->absoluteTransformation() * FlaketoPixels;
780 } else {
781 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shapeLayer->shapes().first());
782 if (pathShape){
783 layerRecord->addPathShapeToPSDPath(vectorMask.path, pathShape, vectorWidth, vectorHeight);
784
785 // Right now only saving rect and ellipse when they are 'simple', as the actual parametric
786 // shapes themselves are plugins, so we cannot include them and access the object data.
787 if ((pathShape->pathShapeId() == "RectangleShape" || pathShape->pathShapeId() == "EllipseShape")
788 && pathShape->pointCount() == 4) {
790 data.originType = data.typeToName.key(pathShape->pathShapeId(), 1);
791 QPolygonF poly = pathShape->absoluteTransformation().map(pathShape->outlineRect());
792 data.originShapeBBox = poly.boundingRect();
793 data.originBoxCorners = poly;
794 data.transform = pathShape->absoluteTransformation();
795 vogkData = data.getASL();
796 }
797 KoColorBackground *b = dynamic_cast<KoColorBackground *>(pathShape->background().data());
798 KoGradientBackground *g = dynamic_cast<KoGradientBackground *>(pathShape->background().data());
799 KoPatternBackground *p = dynamic_cast<KoPatternBackground *>(pathShape->background().data());
800 if (b) {
802
803 if (node->image()) {
804 fill.cs = node->image()->colorSpace();
805 } else {
806 fill.cs = node->colorSpace();
807 }
808 fill.setColor(KoColor(b->color(), fill.cs));
809 fillConfig = fill.getASLXML();
810 fillType = psd_fill_solid_color;
811 } else if (g) {
813 fill.setFromQGradient(g->gradient());
814 fillConfig = fill.getASLXML();
815 fillType = psd_fill_gradient;
816 } else if (p) {
818 fillConfig = fill.getASLXML();
819 fillType = psd_fill_pattern;
820 } else if (!pathShape->background()) {
822
823 if (node->image()) {
824 fill.cs = node->image()->colorSpace();
825 } else {
826 fill.cs = node->colorSpace();
827 }
828 fill.setColor(KoColor(Qt::transparent, fill.cs));
829 fillConfig = fill.getASLXML();
830 fillType = psd_fill_solid_color;
831 }
832 KoShapeStrokeSP shapeStroke = qSharedPointerDynamicCast<KoShapeStroke>(pathShape->stroke());
833 if (shapeStroke) {
834 psd_vector_stroke_data strokeDataStruct;
835 strokeDataStruct.loadFromShapeStroke(shapeStroke);
836 strokeDataStruct.strokeEnabled = shapeStroke->isVisible();
837 strokeDataStruct.fillEnabled = pathShape->background()? true: false;
838 strokeDataStruct.resolution = node->image()->xRes()*72.0;
839 strokeData = strokeDataStruct.getASLXML();
840 }
841 }
842 }
843 }
844 }
845
846 QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
847
848 if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
849 mergedPatternsXmlDoc = stylesXmlDoc;
850 } else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
851 mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
852 }
853
854 bool nodeIrrelevant = false;
855 QString nodeName;
856 KisPaintDeviceSP layerContentDevice;
857 psd_section_type sectionType;
858
859 if (item.type == FlattenedNode::RASTER_LAYER) {
860 nodeIrrelevant = false;
861 nodeName = node->name();
862 layerContentDevice = onlyTransparencyMask ? node->original() : node->projection();
863
867 if (fillLayer) {
868 bool transparency = KisPainter::checkDeviceHasTransparency(node->paintDevice());
869 bool semiOpacity = node->paintDevice()->defaultPixel().opacityU8() < OPACITY_OPAQUE_U8;
870 if (transparency || semiOpacity) {
871 KisSelectionSP selection = fillLayer->internalSelection();
872 if(selection) {
873 if(selection->hasNonEmptyShapeSelection()) {
874 KisShapeSelection* shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
875 if (shapeSelection) {
876 Q_FOREACH(KoShape *shape, shapeSelection->shapes()) {
877 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
878 if (pathShape){
879 layerRecord->addPathShapeToPSDPath(vectorMask.path, pathShape, vectorWidth, vectorHeight);
880
881 }
882 }
883 }
884 }
885 }
886 layerContentDevice = node->original();
887 onlyTransparencyMask = node;
888 maskRect = onlyTransparencyMask->paintDevice()->exactBounds();
889 }
890 } else {
891 KisTransparencyMask *mask = qobject_cast<KisTransparencyMask*>(onlyTransparencyMask.data());
892 if (mask) {
893 KisSelectionSP selection = mask->selection();
894 if(selection) {
895 if(selection->hasNonEmptyShapeSelection()) {
896 KisShapeSelection* shapeSelection = dynamic_cast<KisShapeSelection*>(selection->shapeSelection());
897 if (shapeSelection) {
898 Q_FOREACH(KoShape *shape, shapeSelection->shapes()) {
899 KoPathShape *pathShape = dynamic_cast<KoPathShape*>(shape);
900 if (pathShape){
901 layerRecord->addPathShapeToPSDPath(vectorMask.path, pathShape, vectorWidth, vectorHeight);
902 }
903 }
904 }
905 }
906 }
907 }
908 }
909 sectionType = psd_other;
910 } else {
911 nodeIrrelevant = true;
912 nodeName = item.type == FlattenedNode::SECTION_DIVIDER ? QString("</Layer group>") : node->name();
913 layerContentDevice = 0;
917 }
918
919 // === no access to node anymore
920
921 QRect layerRect;
922
923 if (layerContentDevice) {
924 QRect rc = layerContentDevice->exactBounds();
925 rc = rc.normalized();
926
927 // keep to the max of photoshop's capabilities
928 if (rc.width() > 30000)
929 rc.setWidth(30000);
930 if (rc.height() > 30000)
931 rc.setHeight(30000);
932
933 layerRect = rc;
934 }
935
936 layerRecord->top = layerRect.y();
937 layerRecord->left = layerRect.x();
938 layerRecord->bottom = layerRect.y() + layerRect.height();
939 layerRecord->right = layerRect.x() + layerRect.width();
940
941 // colors + alpha channel
942 // note: transparency mask not included
943 layerRecord->nChannels = static_cast<quint16>(colorSpace->colorChannelCount() + 1);
944
945 ChannelInfo *info = new ChannelInfo;
946 info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
947 layerRecord->channelInfoRecords << info;
948
949 // the rest is in display order: rgb, cmyk, lab...
950 for (qint16 i = 0; i < (int)colorSpace->colorChannelCount(); ++i) {
951 info = new ChannelInfo;
952 info->channelId = i; // 0 for red, 1 = green, etc
953 layerRecord->channelInfoRecords << info;
954 }
955
956 layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
957 layerRecord->isPassThrough = nodeIsPassThrough;
958 layerRecord->opacity = nodeOpacity;
959 layerRecord->clipping = nodeClipping;
960
961 layerRecord->labelColor = nodeLabelColor;
962
963 layerRecord->transparencyProtected = alphaLocked;
964 layerRecord->visible = nodeVisible;
965 layerRecord->irrelevant = nodeIrrelevant;
966
967 layerRecord->layerName = nodeName.isEmpty() ? i18n("Unnamed Layer") : nodeName;
968
969 layerRecord->fillType = fillType;
970 layerRecord->fillConfig = fillConfig;
971
972 layerRecord->vectorMask = vectorMask;
973 layerRecord->vectorStroke = strokeData;
974 layerRecord->vectorOriginationData = vogkData;
975
976 layerRecord->textShape = textData;
977
978 layerRecord->write(io, layerContentDevice, onlyTransparencyMask, maskRect, sectionType, stylesXmlDoc, node->inherits("KisGroupLayer"));
979 }
980
981 dbgFile << "start writing layer pixel data" << io.pos();
982
983 // Now save the pixel data
984 for (PSDLayerRecord *layerRecord : layers) {
985 layerRecord->writePixelData(io, compressionType);
986 }
987 }
988
989 {
990 // write the global layer mask info -- which is empty
991 const quint32 globalMaskSize = 0;
992 SAFE_WRITE_EX(psd_byte_order::psdBigEndian, io, globalMaskSize);
993 }
994
995 globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
996
997#if 0
1016 if (textCount > 0) {
1018 }
1019#endif
1020 }
1021}
1022
1023template<psd_byte_order byteOrder>
1024void PSDLayerMaskSection::writeTiffImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
1025{
1026 dbgFile << "(TIFF) Writing layer section";
1027
1028 // Build the whole layer structure
1030 addBackgroundIfNeeded(rootLayer, nodes);
1031 flattenNodes(rootLayer, nodes);
1032
1033 if (nodes.isEmpty()) {
1034 throw KisAslWriterUtils::ASLWriteException("Could not find paint layers to save");
1035 }
1036
1037 {
1038 QDomDocument mergedPatternsXmlDoc;
1039
1040 {
1041 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
1042 KisAslWriterUtils::writeFixedString<byteOrder>("Layr", io);
1043
1044 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
1045 // number of layers (negative, because krita always has alpha)
1046 const qint16 layersSize = nodes.size();
1047 SAFE_WRITE_EX(byteOrder, io, layersSize);
1048
1049 dbgFile << "Number of layers" << layersSize << "at" << io.pos();
1050
1051 // Layer records section
1052 for (const FlattenedNode &item : nodes) {
1053 KisNodeSP node = item.node;
1054
1055 PSDLayerRecord *layerRecord = new PSDLayerRecord(m_header);
1056 layers.append(layerRecord);
1057
1058 const bool nodeVisible = node->visible();
1059 const KoColorSpace *colorSpace = node->colorSpace();
1060 const quint8 nodeOpacity = node->opacity();
1061 const quint8 nodeClipping = 0;
1062 const int nodeLabelColor = node->colorLabelIndex();
1063 const KisPaintLayer *paintLayer = qobject_cast<KisPaintLayer *>(node.data());
1064 const bool alphaLocked = (paintLayer && paintLayer->alphaLocked());
1065 const QString nodeCompositeOp = node->compositeOpId();
1066
1067 const KisGroupLayer *groupLayer = qobject_cast<KisGroupLayer *>(node.data());
1068 const bool nodeIsPassThrough = groupLayer && groupLayer->passThroughMode();
1069
1070 QDomDocument stylesXmlDoc = fetchLayerStyleXmlData(node);
1071
1072 if (mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
1073 mergedPatternsXmlDoc = stylesXmlDoc;
1074 } else if (!mergedPatternsXmlDoc.isNull() && !stylesXmlDoc.isNull()) {
1075 mergePatternsXMLSection(stylesXmlDoc, mergedPatternsXmlDoc);
1076 }
1077
1078 bool nodeIrrelevant = false;
1079 QString nodeName;
1080 KisPaintDeviceSP layerContentDevice;
1081 psd_section_type sectionType;
1082
1083 if (item.type == FlattenedNode::RASTER_LAYER) {
1084 nodeIrrelevant = false;
1085 nodeName = node->name();
1086 layerContentDevice = node->projection();
1087 sectionType = psd_other;
1088 } else {
1089 nodeIrrelevant = true;
1090 nodeName = item.type == FlattenedNode::SECTION_DIVIDER ? QString("</Layer group>") : node->name();
1091 layerContentDevice = 0;
1092 sectionType = item.type == FlattenedNode::SECTION_DIVIDER ? psd_bounding_divider
1095 }
1096
1097 // === no access to node anymore
1098
1099 QRect layerRect;
1100
1101 if (layerContentDevice) {
1102 QRect rc = layerContentDevice->exactBounds();
1103 rc = rc.normalized();
1104
1105 // keep to the max of photoshop's capabilities
1106 // XXX: update this to PSB
1107 if (rc.width() > 30000)
1108 rc.setWidth(30000);
1109 if (rc.height() > 30000)
1110 rc.setHeight(30000);
1111
1112 layerRect = rc;
1113 }
1114
1115 layerRecord->top = layerRect.y();
1116 layerRecord->left = layerRect.x();
1117 layerRecord->bottom = layerRect.y() + layerRect.height();
1118 layerRecord->right = layerRect.x() + layerRect.width();
1119
1120 // colors + alpha channel
1121 // note: transparency mask not included
1122 layerRecord->nChannels = static_cast<quint16>(colorSpace->colorChannelCount() + 1);
1123
1124 ChannelInfo *info = new ChannelInfo;
1125 info->channelId = -1; // For the alpha channel, which we always have in Krita, and should be saved first in
1126 layerRecord->channelInfoRecords << info;
1127
1128 // the rest is in display order: rgb, cmyk, lab...
1129 for (quint32 i = 0; i < colorSpace->colorChannelCount(); ++i) {
1130 info = new ChannelInfo;
1131 info->channelId = static_cast<qint16>(i); // 0 for red, 1 = green, etc
1132 layerRecord->channelInfoRecords << info;
1133 }
1134
1135 layerRecord->blendModeKey = composite_op_to_psd_blendmode(nodeCompositeOp);
1136 layerRecord->isPassThrough = nodeIsPassThrough;
1137 layerRecord->opacity = nodeOpacity;
1138 layerRecord->clipping = nodeClipping;
1139
1140 layerRecord->transparencyProtected = alphaLocked;
1141 layerRecord->visible = nodeVisible;
1142 layerRecord->irrelevant = nodeIrrelevant;
1143 layerRecord->labelColor = nodeLabelColor;
1144
1145 layerRecord->layerName = nodeName.isEmpty() ? i18n("Unnamed Layer") : nodeName;
1146
1147 layerRecord->write(io, layerContentDevice, nullptr, QRect(), sectionType, stylesXmlDoc, node->inherits("KisGroupLayer"));
1148 }
1149
1150 dbgFile << "start writing layer pixel data" << io.pos();
1151
1152 // Now save the pixel data
1153 for (PSDLayerRecord *layerRecord : layers) {
1154 layerRecord->writePixelData(io, compressionType);
1155 }
1156 }
1157
1158 // {
1159 // // write the global layer mask info -- which is NOT empty but fixed
1160 // KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
1161 // KisAslWriterUtils::writeFixedString<byteOrder>("LMsk", io);
1162
1163 // KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> layerAndMaskSectionSizeTag(io, 4);
1164 // // https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_22664
1165 // psdwrite<byteOrder>(io, quint16(0)); // CS: RGB
1166 // psdwrite<byteOrder>(io, quint16(65535)); // Pure red verification
1167 // psdwrite<byteOrder>(io, quint16(0));
1168 // psdwrite<byteOrder>(io, quint16(0));
1169 // psdwrite<byteOrder>(io, quint16(0));
1170 // psdwrite<byteOrder>(io, quint16(50)); // opacity
1171 // psdwrite<byteOrder>(io, quint16(128)); // kind
1172 // }
1173
1174 globalInfoSection.writePattBlockEx(io, mergedPatternsXmlDoc);
1175 }
1176}
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:722
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:1067
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
void setTransformation(const QTransform &matrix)
Definition KoShape.cpp:417
virtual KoShape * cloneShape() const
creates a deep copy of the shape or shape's subtree
Definition KoShape.cpp:200
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
void setVisible(bool on)
Definition KoShape.cpp:972
QString name() const
Definition KoShape.cpp:1150
bool isVisible(bool recursive=true) const
Definition KoShape.cpp:979
qreal transparency(bool recursive=false) const
Definition KoShape.cpp:730
@ 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:172
#define warnKrita
Definition kis_debug.h:87
#define dbgFile
Definition kis_debug.h:53
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
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:521
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)