Krita Source Code Documentation
Loading...
Searching...
No Matches
PSDLayerMaskSection Class Reference

#include <psd_layer_section.h>

Classes

struct  GlobalLayerMaskInfo
 

Public Member Functions

 PSDLayerMaskSection (const PSDHeader &header)
 
bool read (QIODevice &io)
 
bool write (QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
 
 ~PSDLayerMaskSection ()
 

Public Attributes

QString error
 
PsdAdditionalLayerInfoBlock globalInfoSection
 
GlobalLayerMaskInfo globalLayerMaskInfo
 
bool hasTransparency {false}
 
QVector< PSDLayerRecord * > layers
 
qint16 nLayers {0}
 

Private Member Functions

template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
bool readGlobalMask (QIODevice &io)
 
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
bool readLayerInfoImpl (QIODevice &io)
 
bool readPsdImpl (QIODevice &io)
 
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
bool readTiffImpl (QIODevice &io)
 
void writePsdImpl (QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
 
template<psd_byte_order byteOrder = psd_byte_order::psdBigEndian>
void writeTiffImpl (QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
 

Private Attributes

const PSDHeader m_header
 

Detailed Description

Definition at line 22 of file psd_layer_section.h.

Constructor & Destructor Documentation

◆ PSDLayerMaskSection()

PSDLayerMaskSection::PSDLayerMaskSection ( const PSDHeader & header)

Definition at line 54 of file psd_layer_section.cpp.

55 : globalInfoSection(header)
56 , m_header(header)
57{
58}
const PSDHeader m_header
PsdAdditionalLayerInfoBlock globalInfoSection

◆ ~PSDLayerMaskSection()

PSDLayerMaskSection::~PSDLayerMaskSection ( )

Definition at line 60 of file psd_layer_section.cpp.

61{
62 qDeleteAll(layers);
63}
QVector< PSDLayerRecord * > layers

References layers.

Member Function Documentation

◆ read()

bool PSDLayerMaskSection::read ( QIODevice & io)

Definition at line 65 of file psd_layer_section.cpp.

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}
bool tiffStyleLayerBlock
Definition psd_header.h:50
psd_byte_order byteOrder
Definition psd_header.h:49
bool readTiffImpl(QIODevice &io)
bool readPsdImpl(QIODevice &io)
#define warnKrita
Definition kis_debug.h:94

References PSDHeader::byteOrder, m_header, psdLittleEndian, readPsdImpl(), readTiffImpl(), PSDHeader::tiffStyleLayerBlock, and warnKrita.

◆ readGlobalMask()

template<psd_byte_order byteOrder>
bool PSDLayerMaskSection::readGlobalMask ( QIODevice & io)
private

Definition at line 327 of file psd_layer_section.cpp.

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}
GlobalLayerMaskInfo globalLayerMaskInfo
#define dbgFile
Definition kis_debug.h:57

References PSDLayerMaskSection::GlobalLayerMaskInfo::colorComponents, dbgFile, error, globalLayerMaskInfo, PSDLayerMaskSection::GlobalLayerMaskInfo::kind, PSDLayerMaskSection::GlobalLayerMaskInfo::opacity, and PSDLayerMaskSection::GlobalLayerMaskInfo::overlayColorSpace.

◆ readLayerInfoImpl()

template<psd_byte_order byteOrder>
bool PSDLayerMaskSection::readLayerInfoImpl ( QIODevice & io)
private

Definition at line 91 of file psd_layer_section.cpp.

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}
quint16 version
Definition psd_header.h:43
QVector< ChannelInfo * > channelInfoRecords
QRect channelRect(ChannelInfo *channel) const
#define SAFE_READ_EX(byteOrder, device, varname)
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding)
psd_compression_type
Definition psd.h:39
@ RLE
Definition psd.h:41
QVector< quint32 > rleRowLengths
psd_compression_type compressionType
quint64 channelDataLength
quint64 channelDataStart

References ChannelInfo::channelDataLength, ChannelInfo::channelDataStart, ChannelInfo::channelId, PSDLayerRecord::channelInfoRecords, PSDLayerRecord::channelRect(), ChannelInfo::compressionType, dbgFile, error, hasTransparency, KIS_ASSERT_RECOVER, layers, m_header, PSDLayerRecord::nChannels, nLayers, RLE, ChannelInfo::rleRowLengths, SAFE_READ_EX, SETUP_OFFSET_VERIFIER, PSDHeader::tiffStyleLayerBlock, PSDHeader::version, and warnKrita.

◆ readPsdImpl()

bool PSDLayerMaskSection::readPsdImpl ( QIODevice & io)
private

PSD files created in some weird web applications may have invalid layer-mask-block-size set. Just do a simple sanity check to catch this case

Newer versions of PSD have layers info block wrapped into 'Lr16' or 'Lr32' additional section, while the main block is absent.

Here we pass the callback which should be used when such additional section is recognized.

Definition at line 219 of file psd_layer_section.cpp.

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}
bool readLayerInfoImpl(QIODevice &io)
void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler)
std::enable_if_t< std::is_arithmetic< T >::value, bool > psdread(QIODevice &io, T &v)
Definition psd_utils.h:397

References PSDLayerMaskSection::GlobalLayerMaskInfo::colorComponents, dbgFile, error, globalInfoSection, globalLayerMaskInfo, PSDLayerMaskSection::GlobalLayerMaskInfo::kind, m_header, PSDLayerMaskSection::GlobalLayerMaskInfo::opacity, PSDLayerMaskSection::GlobalLayerMaskInfo::overlayColorSpace, psdBigEndian, psdread(), PsdAdditionalLayerInfoBlock::read(), readLayerInfoImpl(), SAFE_READ_EX, PsdAdditionalLayerInfoBlock::setExtraLayerInfoBlockHandler(), PSDHeader::version, and warnKrita.

◆ readTiffImpl()

template<psd_byte_order byteOrder>
bool PSDLayerMaskSection::readTiffImpl ( QIODevice & io)
private

Just like PSD, new versions of PSD have layers info block wrapped into 'Lr16' or 'Lr32' additional section, while the main block is absent. Additionally, the global mask info is stored in a separate "LMsk" block.

So, instead of having special handling, we just ship everything to the additional layer info block handlers

Definition at line 378 of file psd_layer_section.cpp.

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}
void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler)

References dbgFile, globalInfoSection, PsdAdditionalLayerInfoBlock::read(), PsdAdditionalLayerInfoBlock::setExtraLayerInfoBlockHandler(), and PsdAdditionalLayerInfoBlock::setUserMaskInfoBlockHandler().

◆ write()

bool PSDLayerMaskSection::write ( QIODevice & io,
KisNodeSP rootLayer,
psd_compression_type compressionType )

Definition at line 608 of file psd_layer_section.cpp.

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}
void writeTiffImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
void writePsdImpl(QIODevice &io, KisNodeSP rootLayer, psd_compression_type compressionType)
#define PREPEND_METHOD(msg)
Definition kis_debug.h:180

References PSDHeader::byteOrder, error, m_header, PREPEND_METHOD, psdLittleEndian, PSDHeader::tiffStyleLayerBlock, writePsdImpl(), and writeTiffImpl().

◆ writePsdImpl()

void PSDLayerMaskSection::writePsdImpl ( QIODevice & io,
KisNodeSP rootLayer,
psd_compression_type compressionType )
private

For fill layers we save their internal selection as a separate transparency mask

Definition at line 633 of file psd_layer_section.cpp.

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}
const Params2D p
const quint8 OPACITY_OPAQUE_U8
const KoColorSpace * colorSpace() const
qint32 width() const
double xRes() const
double yRes() const
qint32 height() const
virtual KisFilterConfigurationSP filter() const
QRect exactBounds() const
KoColor defaultPixel() const
static bool checkDeviceHasTransparency(KisPaintDeviceSP dev)
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
virtual KoShapeStrokeModelSP stroke() const
Definition KoShape.cpp:885
QTransform absoluteTransformation() const
Definition KoShape.cpp:330
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:754
@ InlineSizeId
KoSvgText::AutoValue.
QVariant property(PropertyId id, const QVariant &defaultValue=QVariant()) const
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
psd_fill_type fillType
void writePixelData(QIODevice &io, psd_compression_type compressionType)
psd_vector_mask vectorMask
psd_layer_type_shape textShape
QDomDocument vectorOriginationData
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)
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_WRITE_EX(byteOrder, device, varname)
const char * name(StandardAction id)
@ HorizontalTB
Definition KoSvgText.h:38
const QString Patterns
QString composite_op_to_psd_blendmode(const QString &compositeop)
Definition psd.cpp:150
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)
void flattenNodes(KisNodeSP node, QList< FlattenedNode > &nodes)
void addBackgroundIfNeeded(KisNodeSP root, QList< FlattenedNode > &nodes)
void mergePatternsXMLSection(const QDomDocument &src, QDomDocument &dst)
virtual KisPaintDeviceSP projection() const =0
const QString & compositeOpId() const
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
KisSelectionSP selection
Definition kis_mask.cc:44
bool alphaLocked() const
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)

References KoShape::absoluteTransformation(), addBackgroundIfNeeded(), PSDLayerRecord::addPathShapeToPSDPath(), KisPaintLayer::alphaLocked(), KoShape::background(), PSDLayerRecord::blendModeKey, PSDLayerRecord::bottom, psd_layer_type_shape::boundingBox, KoSvgTextShape::boundingRect(), psd_layer_type_shape::bounds, ChannelInfo::channelId, PSDLayerRecord::channelInfoRecords, KisPainter::checkDeviceHasTransparency(), PSDLayerRecord::clipping, KoColorSpace::colorChannelCount(), KisBaseNode::colorLabelIndex(), KisImage::colorSpace(), KisBaseNode::colorSpace(), composite_op_to_psd_blendmode(), KisBaseNode::compositeOpId(), PsdTextDataConverter::convertToPSDTextEngineData(), KoSvgTextShapeMarkupConverter::convertToSvg(), psd_layer_solid_color::cs, KoSvgText::AutoValue::customValue, KisSharedPtr< T >::data(), dbgFile, KisPaintDevice::defaultPixel(), KisTxt2Utils::defaultTxt2(), psd_layer_type_shape::engineData, PsdTextDataConverter::errors(), KisPaintDevice::exactBounds(), fetchLayerStyleXmlData(), PSDLayerRecord::fillConfig, psd_vector_stroke_data::fillEnabled, PSDLayerRecord::fillType, KisNodeFilterInterface::filter(), findOnlyTransparencyMask(), flattenNodes(), FlattenedNode::FOLDER_OPEN, psd_vector_origination_data::getASL(), psd_layer_solid_color::getASLXML(), psd_layer_gradient_fill::getASLXML(), psd_layer_pattern_fill::getASLXML(), psd_vector_stroke_data::getASLXML(), globalInfoSection, KoGradientBackground::gradient(), KisSelection::hasNonEmptyShapeSelection(), KisImage::height(), KoSvgText::HorizontalTB, KisBaseNode::image, psd_layer_gradient_fill::imageHeight, psd_layer_gradient_fill::imageWidth, KoSvgTextProperties::InlineSizeId, KisSelectionBasedLayer::internalSelection(), PSDLayerRecord::irrelevant, KoSvgText::AutoValue::isAuto, KisBaseNode::isFakeNode(), psd_layer_type_shape::isHorizontal, PSDLayerRecord::isPassThrough, PSDLayerRecord::labelColor, PSDLayerRecord::layerName, layers, PSDLayerRecord::left, psd_layer_solid_color::loadFromConfig(), psd_layer_gradient_fill::loadFromConfig(), psd_layer_pattern_fill::loadFromConfig(), psd_vector_stroke_data::loadFromShapeStroke(), m_header, mergePatternsXMLSection(), KisBaseNode::name(), PSDLayerRecord::nChannels, FlattenedNode::node, KisBaseNode::opacity(), PSDLayerRecord::opacity, OPACITY_OPAQUE_U8, KoColor::opacityU8(), KisBaseNode::original(), psd_vector_origination_data::originBoxCorners, psd_vector_origination_data::originShapeBBox, psd_vector_origination_data::originType, KoPathShape::outlineRect(), KoSvgTextShape::outlineRect(), p, KisBaseNode::paintDevice(), KisGroupLayer::passThroughMode, psd_vector_mask::path, KoPathShape::pathShapeId(), psd_layer_pattern_fill::pattern, psd_layer_pattern_fill::patternID, ResourceType::Patterns, KoPathShape::pointCount(), KisBaseNode::projection(), KoSvgTextProperties::property(), psd_bounding_divider, psd_closed_folder, psd_fill_gradient, psd_fill_pattern, psd_fill_solid_color, psd_open_folder, psd_other, psdBigEndian, FlattenedNode::RASTER_LAYER, psd_vector_stroke_data::resolution, PSDLayerRecord::right, SAFE_WRITE_EX, FlattenedNode::SECTION_DIVIDER, KisMask::selection, psd_layer_solid_color::setColor(), psd_layer_gradient_fill::setFromQGradient(), KoShapeContainer::shapes(), KisSelection::shapeSelection, KoSvgTextShape::shapesInside, KoShape::stroke(), psd_vector_stroke_data::strokeEnabled, psd_layer_type_shape::text, psd_layer_type_shape::textIndex, KoSvgTextShape::textProperties(), PSDLayerRecord::textShape, PSDLayerRecord::top, psd_layer_type_shape::transform, psd_vector_origination_data::transform, PSDLayerRecord::transparencyProtected, PsdAdditionalLayerInfoBlock::txt2Data, FlattenedNode::type, psd_vector_origination_data::typeToName, KisTxt2Utils::tyShFromTxt2(), KisBaseNode::userLocked(), PSDLayerRecord::userLocked, PSDLayerRecord::vectorMask, PSDLayerRecord::vectorOriginationData, PSDLayerRecord::vectorStroke, KisBaseNode::visible(), PSDLayerRecord::visible, PsdTextDataConverter::warnings(), KisImage::width(), PSDLayerRecord::write(), PsdAdditionalLayerInfoBlock::writePattBlockEx(), PsdAdditionalLayerInfoBlock::writeTxt2BlockEx(), KoSvgTextShape::writingMode(), KisImage::xRes(), and KisImage::yRes().

◆ writeTiffImpl()

template<psd_byte_order byteOrder>
void PSDLayerMaskSection::writeTiffImpl ( QIODevice & io,
KisNodeSP rootLayer,
psd_compression_type compressionType )
private

Definition at line 1030 of file psd_layer_section.cpp.

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}

References addBackgroundIfNeeded(), KisPaintLayer::alphaLocked(), PSDLayerRecord::blendModeKey, PSDLayerRecord::bottom, ChannelInfo::channelId, PSDLayerRecord::channelInfoRecords, PSDLayerRecord::clipping, KoColorSpace::colorChannelCount(), KisBaseNode::colorLabelIndex(), KisBaseNode::colorSpace(), composite_op_to_psd_blendmode(), KisBaseNode::compositeOpId(), KisSharedPtr< T >::data(), dbgFile, KisPaintDevice::exactBounds(), fetchLayerStyleXmlData(), flattenNodes(), FlattenedNode::FOLDER_OPEN, globalInfoSection, PSDLayerRecord::irrelevant, PSDLayerRecord::isPassThrough, PSDLayerRecord::labelColor, PSDLayerRecord::layerName, layers, PSDLayerRecord::left, m_header, mergePatternsXMLSection(), KisBaseNode::name(), PSDLayerRecord::nChannels, KisBaseNode::opacity(), PSDLayerRecord::opacity, KisGroupLayer::passThroughMode, KisBaseNode::projection(), psd_bounding_divider, psd_closed_folder, psd_open_folder, psd_other, FlattenedNode::RASTER_LAYER, PSDLayerRecord::right, SAFE_WRITE_EX, FlattenedNode::SECTION_DIVIDER, PSDLayerRecord::top, PSDLayerRecord::transparencyProtected, KisBaseNode::visible(), PSDLayerRecord::visible, PSDLayerRecord::write(), and PsdAdditionalLayerInfoBlock::writePattBlockEx().

Member Data Documentation

◆ error

QString PSDLayerMaskSection::error

Definition at line 31 of file psd_layer_section.h.

◆ globalInfoSection

PsdAdditionalLayerInfoBlock PSDLayerMaskSection::globalInfoSection

Definition at line 49 of file psd_layer_section.h.

◆ globalLayerMaskInfo

GlobalLayerMaskInfo PSDLayerMaskSection::globalLayerMaskInfo

Definition at line 48 of file psd_layer_section.h.

◆ hasTransparency

bool PSDLayerMaskSection::hasTransparency {false}

Definition at line 34 of file psd_layer_section.h.

34{false};

◆ layers

QVector<PSDLayerRecord *> PSDLayerMaskSection::layers

Definition at line 38 of file psd_layer_section.h.

◆ m_header

const PSDHeader PSDLayerMaskSection::m_header
private

Definition at line 63 of file psd_layer_section.h.

◆ nLayers

qint16 PSDLayerMaskSection::nLayers {0}

Definition at line 36 of file psd_layer_section.h.

36{0}; // If layer count is a negative number, its absolute value is the number of layers and the first alpha channel contains the transparency

The documentation for this class was generated from the following files: