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:87

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:53

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 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}
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 604 of file psd_layer_section.cpp.

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}
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:172

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 629 of file psd_layer_section.cpp.

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}
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:1067
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
virtual QSharedPointer< KoShapeBackground > background() const
Definition KoShape.cpp:926
@ 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
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(), 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 1024 of file psd_layer_section.cpp.

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}

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: