Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_layer_record.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2009 Boudewijn Rempt <boud@valdyas.org>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7#include "psd_layer_record.h"
8
9#include <QtEndian>
10
11#include <KoColor.h>
12#include <QBuffer>
13#include <QDataStream>
14#include <QIODevice>
15
16#include "kis_iterator_ng.h"
17#include <algorithm>
18#include <kis_debug.h>
19#include <kis_node.h>
20#include <kis_paint_layer.h>
21
22#include "compression.h"
23#include "psd.h"
24#include "psd_header.h"
25#include "psd_utils.h"
26
27#include <KoColorSpace.h>
28#include <KoColorSpaceMaths.h>
30#include <KoColorSpaceTraits.h>
31
32#include <KoPathShape.h>
33#include <KoPathSegment.h>
34#include <KoPathPoint.h>
35
39
40#include "psd_pixel_utils.h"
41#include <kundo2command.h>
42
43// Just for pretty debug messages
44QString channelIdToChannelType(int channelId, psd_color_mode colormode)
45{
46 switch (channelId) {
47 case -3:
48 return "Real User Supplied Layer Mask (when both a user mask and a vector mask are present";
49 case -2:
50 return "User Supplied Layer Mask";
51 case -1:
52 return "Transparency mask";
53 case 0:
54 switch (colormode) {
55 case Bitmap:
56 case Indexed:
57 return QString("bitmap or indexed: %1").arg(channelId);
58 case Grayscale:
59 case Gray16:
60 return "gray";
61 case RGB:
62 case RGB48:
63 return "red";
64 case Lab:
65 case Lab48:
66 return "L";
67 case CMYK:
68 case CMYK64:
69 return "cyan";
70 case MultiChannel:
72 return QString("multichannel channel %1").arg(channelId);
73 case DuoTone:
74 case Duotone16:
75 return QString("duotone channel %1").arg(channelId);
76 default:
77 return QString("unknown: %1").arg(channelId);
78 };
79 case 1:
80 switch (colormode) {
81 case Bitmap:
82 case Indexed:
83 return QString("WARNING bitmap or indexed: %1").arg(channelId);
84 case Grayscale:
85 case Gray16:
86 return QString("WARNING: %1").arg(channelId);
87 case RGB:
88 case RGB48:
89 return "green";
90 case Lab:
91 case Lab48:
92 return "a";
93 case CMYK:
94 case CMYK64:
95 return "Magenta";
96 case MultiChannel:
98 return QString("multichannel channel %1").arg(channelId);
99 case DuoTone:
100 case Duotone16:
101 return QString("duotone channel %1").arg(channelId);
102 default:
103 return QString("unknown: %1").arg(channelId);
104 };
105 case 2:
106 switch (colormode) {
107 case Bitmap:
108 case Indexed:
109 return QString("WARNING bitmap or indexed: %1").arg(channelId);
110 case Grayscale:
111 case Gray16:
112 return QString("WARNING: %1").arg(channelId);
113 case RGB:
114 case RGB48:
115 return "blue";
116 case Lab:
117 case Lab48:
118 return "b";
119 case CMYK:
120 case CMYK64:
121 return "yellow";
122 case MultiChannel:
123 case DeepMultichannel:
124 return QString("multichannel channel %1").arg(channelId);
125 case DuoTone:
126 case Duotone16:
127 return QString("duotone channel %1").arg(channelId);
128 default:
129 return QString("unknown: %1").arg(channelId);
130 };
131 case 3:
132 switch (colormode) {
133 case Bitmap:
134 case Indexed:
135 return QString("WARNING bitmap or indexed: %1").arg(channelId);
136 case Grayscale:
137 case Gray16:
138 return QString("WARNING: %1").arg(channelId);
139 case RGB:
140 case RGB48:
141 return QString("alpha: %1").arg(channelId);
142 case Lab:
143 case Lab48:
144 return QString("alpha: %1").arg(channelId);
145 case CMYK:
146 case CMYK64:
147 return "Key";
148 case MultiChannel:
149 case DeepMultichannel:
150 return QString("multichannel channel %1").arg(channelId);
151 case DuoTone:
152 case Duotone16:
153 return QString("duotone channel %1").arg(channelId);
154 default:
155 return QString("unknown: %1").arg(channelId);
156 };
157 default:
158 return QString("unknown: %1").arg(channelId);
159 };
160}
161
163 : infoBlocks(header)
164 , m_header(header)
165{
166}
167
168bool PSDLayerRecord::read(QIODevice &io)
169{
170 switch (m_header.byteOrder) {
172 return readImpl<psd_byte_order::psdLittleEndian>(io);
173 default:
174 return readImpl(io);
175 }
176}
177
178template<psd_byte_order byteOrder>
179bool PSDLayerRecord::readImpl(QIODevice &io)
180{
181 dbgFile << "Going to read layer record. Pos:" << io.pos();
182
183 if (!psdread<byteOrder>(io, top) || !psdread<byteOrder>(io, left) || !psdread<byteOrder>(io, bottom) || !psdread<byteOrder>(io, right)
184 || !psdread<byteOrder>(io, nChannels)) {
185 error = "could not read layer record";
186 return false;
187 }
188
189 dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels;
190
191 Q_ASSERT(top <= bottom);
192 Q_ASSERT(left <= right);
193 Q_ASSERT(nChannels > 0);
194
195 if (nChannels < 1) {
196 error = QString("Not enough channels. Got: %1").arg(nChannels);
197 return false;
198 }
199
200 if (nChannels > MAX_CHANNELS) {
201 error = QString("Too many channels. Got: %1").arg(nChannels);
202 return false;
203 }
204
205 for (int i = 0; i < nChannels; ++i) {
206 if (io.atEnd()) {
207 error = "Could not read enough data for channels";
208 return false;
209 }
210
211 ChannelInfo *info = new ChannelInfo;
212
213 if (!psdread<byteOrder>(io, info->channelId)) {
214 error = "could not read channel id";
215 delete info;
216 return false;
217 }
218 bool r;
219 if (m_header.version == 1) {
220 quint32 channelDataLength;
221 r = psdread<byteOrder>(io, channelDataLength);
222 info->channelDataLength = (quint64)channelDataLength;
223 } else {
224 r = psdread<byteOrder>(io, info->channelDataLength);
225 }
226 if (!r) {
227 error = "Could not read length for channel data";
228 delete info;
229 return false;
230 }
231
232 dbgFile << "\tchannel" << i << "id" << channelIdToChannelType(info->channelId, m_header.colormode) << "length" << info->channelDataLength << "start"
233 << info->channelDataStart << "offset" << info->channelOffset << "channelInfoPosition" << info->channelInfoPosition;
234
235 channelInfoRecords << info;
236 }
237
238 if (!psd_read_blendmode<byteOrder>(io, blendModeKey)) {
239 error = QString("Could not read blend mode key. Got: %1").arg(blendModeKey);
240 return false;
241 }
242
243 dbgFile << "\tBlend mode" << blendModeKey << "pos" << io.pos();
244
245 if (!psdread<byteOrder>(io, opacity)) {
246 error = "Could not read opacity";
247 return false;
248 }
249
250 dbgFile << "\tOpacity" << opacity << io.pos();
251
252 if (!psdread<byteOrder>(io, clipping)) {
253 error = "Could not read clipping";
254 return false;
255 }
256
257 dbgFile << "\tclipping" << clipping << io.pos();
258
259 quint8 flags;
260 if (!psdread<byteOrder>(io, flags)) {
261 error = "Could not read flags";
262 return false;
263 }
264 dbgFile << "\tflags" << flags << io.pos();
265
266 transparencyProtected = flags & 1 ? true : false;
267
268 dbgFile << "\ttransparency protected" << transparencyProtected;
269
270 visible = flags & 2 ? false : true;
271
272 dbgFile << "\tvisible" << visible;
273
274 if (flags & 8) {
275 irrelevant = flags & 16 ? true : false;
276 } else {
277 irrelevant = false;
278 }
279
280 dbgFile << "\tirrelevant" << irrelevant;
281
282 dbgFile << "\tfiller at " << io.pos();
283
284 quint8 filler;
285 if (!psdread<byteOrder>(io, filler) || filler != 0) {
286 error = "Could not read padding";
287 return false;
288 }
289
290 dbgFile << "\tGoing to read extra data length" << io.pos();
291
292 quint32 extraDataLength;
293 if (!psdread<byteOrder>(io, extraDataLength) || io.bytesAvailable() < extraDataLength) {
294 error = QString("Could not read extra layer data: %1 at pos %2").arg(extraDataLength).arg(io.pos());
295 return false;
296 }
297
298 dbgFile << "\tExtra data length" << extraDataLength;
299
300 if (extraDataLength > 0) {
301 dbgFile << "Going to read extra data field. Bytes available: " << io.bytesAvailable() << "pos" << io.pos();
302
303 // See https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577409_22582
304 quint32 layerMaskLength = 1; // invalid...
305 if (!psdread<byteOrder>(io, layerMaskLength) || io.bytesAvailable() < layerMaskLength) {
306 error = QString("Could not read layer mask length: %1").arg(layerMaskLength);
307 return false;
308 }
309
310 layerMask = {};
311
312 if (layerMaskLength == 0) {
313 dbgFile << "\tNo layer mask/adjustment layer data. pos" << io.pos();
314 } else {
315 dbgFile << "\tReading layer mask/adjustment layer data. Length of block:" << layerMaskLength << "pos"
316 << io.pos();
317
318 if (!psdread<byteOrder>(io, layerMask.top) || !psdread<byteOrder>(io, layerMask.left)
319 || !psdread<byteOrder>(io, layerMask.bottom) || !psdread<byteOrder>(io, layerMask.right)
320 || !psdread<byteOrder>(io, layerMask.defaultColor) || !psdread<byteOrder>(io, flags)) {
321 error = "could not read common records of layer mask";
322 return false;
323 }
324
325 layerMask.positionedRelativeToLayer = (flags & 1) != 0;
326 layerMask.disabled = (flags & 2) != 0;
327 layerMask.invertLayerMaskWhenBlending = (flags & 4) != 0;
328 const bool hasMaskParameters = (flags & 8) != 0;
329
330 dbgFile << "\tLayer mask info (original): position relative" << layerMask.positionedRelativeToLayer
331 << ", disabled" << layerMask.disabled << ", invert" << layerMask.invertLayerMaskWhenBlending
332 << ", needs to read mask parameters" << hasMaskParameters;
333
334 if (layerMaskLength == 20) {
335 quint16 padding = 0;
336 if (!psdread<byteOrder>(io, padding)) {
337 error = "Could not read layer mask padding";
338 return false;
339 }
340 } else {
341 quint32 remainingBlockLength = layerMaskLength - 18;
342
343 dbgFile << "\tReading selective records from layer mask info. Remaining block length"
344 << remainingBlockLength;
345
346 if (hasMaskParameters) {
347 if (!psdread<byteOrder>(io, flags)) {
348 error = "could not read mask parameters";
349 return false;
350 }
351
352 remainingBlockLength -= 1;
353
354 dbgFile << "\t\tMask parameters" << QString::number(flags, 2) << ". Remaining block length"
355 << remainingBlockLength;
356
357 if (flags & 1) {
358 quint8 dummy = 0;
359 if (!psdread<byteOrder>(io, dummy)) {
360 error = "could not read user mask density";
361 return false;
362 }
363 remainingBlockLength -= sizeof(dummy);
364 }
365
366 if (flags & 2) {
367 double dummy = 0;
368 if (!psdread<byteOrder>(io, dummy)) {
369 error = "could not read user mask feather";
370 return false;
371 }
372 remainingBlockLength -= sizeof(dummy);
373 }
374
375 if (flags & 4) {
376 quint8 dummy = 0;
377 if (!psdread<byteOrder>(io, dummy)) {
378 error = "could not read vector mask density";
379 return false;
380 }
381 remainingBlockLength -= sizeof(dummy);
382 }
383
384 if (flags & 8) {
385 double dummy = 0;
386 if (!psdread<byteOrder>(io, dummy)) {
387 error = "could not read vector mask feather";
388 return false;
389 }
390 remainingBlockLength -= sizeof(dummy);
391 }
392 }
393
394 if (remainingBlockLength >= 1) {
395 if (!psdread<byteOrder>(io, flags)) {
396 error = "could not read 'real' mask record";
397 return false;
398 }
399
400 layerMask.positionedRelativeToLayer = (flags & 1) != 0;
401 layerMask.disabled = (flags & 2) != 0;
402 layerMask.invertLayerMaskWhenBlending = (flags & 4) != 0;
403 const bool hasMaskParameters = (flags & 8) != 0;
404
405 dbgFile << "\t\tLayer mask info (real): position relative" << layerMask.positionedRelativeToLayer
406 << ", disabled" << layerMask.disabled << ", invert" << layerMask.invertLayerMaskWhenBlending
407 << ", needs to read mask parameters" << hasMaskParameters;
408
409 remainingBlockLength -= 1;
410
411 dbgFile << "\t\tRemaining block length" << remainingBlockLength;
412 }
413
414 if (remainingBlockLength >= 1) {
415 if (!psdread<byteOrder>(io, layerMask.defaultColor)) {
416 error = "could not read 'real' default color";
417 return false;
418 }
419 remainingBlockLength -= 1;
420 dbgFile << "\t\tRead 'real' default color. Remaining block length" << remainingBlockLength;
421 }
422
423 if (remainingBlockLength >= 16) {
424 if (!psdread<byteOrder>(io, layerMask.top) || !psdread<byteOrder>(io, layerMask.left)
425 || !psdread<byteOrder>(io, layerMask.bottom) || !psdread<byteOrder>(io, layerMask.right)) {
426 error = "could not read 'real' mask rectangle";
427 return false;
428 }
429 remainingBlockLength -= 16;
430 dbgFile << "\t\tRead 'real' mask rectangle. Remaining block length" << remainingBlockLength;
431 }
432 }
433 }
434
435 // layer blending thingies
436 quint32 blendingDataLength = 0;
437 if (!psdread<byteOrder>(io, blendingDataLength) || io.bytesAvailable() < blendingDataLength) {
438 error = "Could not read extra blending data.";
439 return false;
440 }
441
442 quint32 blendingNchannels = blendingDataLength > 0 ? (blendingDataLength - 8) / 4 / 2 : 0;
443
444 dbgFile << "\tNumber of blending channels:" << blendingNchannels;
445
446 if (blendingDataLength > 0) {
447 if (blendingDataLength > 0) {
448 if (!psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.blackValues[0])
449 || !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.blackValues[1])
450 || !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.whiteValues[0])
451 || !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.first.whiteValues[1])) {
452 error = "Could not read blending black/white values";
453 return false;
454 }
455 }
456 blendingDataLength -= 4;
457
458 if (blendingDataLength > 0) {
459 if (!psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.blackValues[0])
460 || !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.blackValues[1])
461 || !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.whiteValues[0])
462 || !psdread<byteOrder>(io, blendingRanges.compositeGrayRange.second.whiteValues[1])) {
463 error = "Could not read blending black/white values";
464 return false;
465 }
466 }
467 blendingDataLength -= 4;
468
469 dbgFile << "\tBlending ranges:";
470 dbgFile << "\t\tcomposite gray (source) :" << blendingRanges.compositeGrayRange.first;
471 dbgFile << "\t\tcomposite gray (dest):" << blendingRanges.compositeGrayRange.second;
472
473 for (quint32 i = 0; i < blendingNchannels; ++i) {
476 if (!psdread<byteOrder>(io, src.blackValues[0]) || !psdread<byteOrder>(io, src.blackValues[1]) || !psdread<byteOrder>(io, src.whiteValues[0])
477 || !psdread<byteOrder>(io, src.whiteValues[1]) || !psdread<byteOrder>(io, dst.blackValues[0]) || !psdread<byteOrder>(io, dst.blackValues[1])
478 || !psdread<byteOrder>(io, dst.whiteValues[0]) || !psdread<byteOrder>(io, dst.whiteValues[1])) {
479 error = QString("could not read src/dst range for channel %1").arg(i);
480 return false;
481 }
482 dbgFile << "\t\tread range " << src << "to" << dst << "for channel" << i;
483 blendingRanges.sourceDestinationRanges << qMakePair(src, dst);
484 }
485 }
486
487 dbgFile << "\tGoing to read layer name at" << io.pos();
488 quint8 layerNameLength;
489 if (!psdread<byteOrder>(io, layerNameLength)) {
490 error = "Could not read layer name length";
491 return false;
492 }
493
494 dbgFile << "\tlayer name length unpadded" << layerNameLength << "pos" << io.pos();
495 layerNameLength = ((layerNameLength + 1 + 3) & ~0x03) - 1;
496
497 dbgFile << "\tlayer name length padded" << layerNameLength << "pos" << io.pos();
498 // XXX: This should use psdread_pascalstring
499 layerName = io.read(layerNameLength);
500 dbgFile << "\tlayer name" << layerName << io.pos();
501
502 dbgFile << "\tAbout to read additional info blocks at" << io.pos();
503
504 if (!infoBlocks.read(io)) {
506 return false;
507 }
508
509 if (infoBlocks.keys.contains("luni") && !infoBlocks.unicodeLayerName.isEmpty()) {
511 }
512
514 }
515
516 return valid();
517}
518
519void PSDLayerRecord::write(QIODevice &io,
520 KisPaintDeviceSP layerContentDevice,
521 KisNodeSP onlyTransparencyMask,
522 const QRect &maskRect,
523 psd_section_type sectionType,
524 const QDomDocument &stylesXmlDoc,
525 bool useLfxsLayerStyleFormat)
526{
527 switch (m_header.byteOrder) {
529 return writeImpl<psd_byte_order::psdLittleEndian>(io,
530 layerContentDevice,
531 onlyTransparencyMask,
532 maskRect,
533 sectionType,
534 stylesXmlDoc,
535 useLfxsLayerStyleFormat);
536 default:
537 return writeImpl(io, layerContentDevice, onlyTransparencyMask, maskRect, sectionType, stylesXmlDoc, useLfxsLayerStyleFormat);
538 }
539}
540
541template<psd_byte_order byteOrder>
542void PSDLayerRecord::writeImpl(QIODevice &io,
543 KisPaintDeviceSP layerContentDevice,
544 KisNodeSP onlyTransparencyMask,
545 const QRect &maskRect,
546 psd_section_type sectionType,
547 const QDomDocument &stylesXmlDoc,
548 bool useLfxsLayerStyleFormat)
549{
550 dbgFile << "writing layer info record"
551 << "at" << io.pos();
552
553 m_layerContentDevice = layerContentDevice;
554 m_onlyTransparencyMask = onlyTransparencyMask;
556
557 dbgFile << "saving layer record for " << layerName << "at pos" << io.pos();
558 dbgFile << "\ttop" << top << "left" << left << "bottom" << bottom << "right" << right << "number of channels" << nChannels;
559 Q_ASSERT(left <= right);
560 Q_ASSERT(top <= bottom);
561 Q_ASSERT(nChannels > 0);
562
563 try {
564 {
565 const QRect layerRect(left, top, right - left, bottom - top);
566 KisAslWriterUtils::writeRect<byteOrder>(layerRect, io);
567 }
568
569 {
570 quint16 realNumberOfChannels = nChannels + bool(m_onlyTransparencyMask);
571 SAFE_WRITE_EX(byteOrder, io, realNumberOfChannels);
572 }
573
574 Q_FOREACH (ChannelInfo *channel, channelInfoRecords) {
575 SAFE_WRITE_EX(byteOrder, io, (quint16)channel->channelId);
576
577 channel->channelInfoPosition = static_cast<int>(io.pos());
578
579 // to be filled in when we know how big channel block is
580 const quint32 fakeChannelSize = 0;
581 SAFE_WRITE_EX(byteOrder, io, fakeChannelSize);
582 }
583
585 const quint16 userSuppliedMaskChannelId = -2;
586 SAFE_WRITE_EX(byteOrder, io, userSuppliedMaskChannelId);
587
589
590 const quint32 fakeTransparencyMaskSize = 0;
591 SAFE_WRITE_EX(byteOrder, io, fakeTransparencyMaskSize);
592 }
593
594 // blend mode
595 dbgFile << ppVar(blendModeKey) << ppVar(io.pos());
596
597 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
598 KisAslWriterUtils::writeFixedString<byteOrder>(blendModeKey, io);
599
600 SAFE_WRITE_EX(byteOrder, io, opacity);
601 SAFE_WRITE_EX(byteOrder, io, clipping); // unused
602
603 // visibility and protection
604 quint8 flags = 0;
606 flags |= 1;
607 if (!visible)
608 flags |= 2;
609 flags |= (1 << 3);
610 if (irrelevant) {
611 flags |= (1 << 4);
612 }
613
614 SAFE_WRITE_EX(byteOrder, io, flags);
615
616 {
617 quint8 padding = 0;
618 SAFE_WRITE_EX(byteOrder, io, padding);
619 }
620
621 {
622 // extra fields with their own length tag
624
626 {
627 const quint32 layerMaskDataSize = 20; // support simple case only
628 SAFE_WRITE_EX(byteOrder, io, layerMaskDataSize);
629 }
630
631 KisAslWriterUtils::writeRect<byteOrder>(m_onlyTransparencyMaskRect, io);
632
633 {
634 // NOTE: in PSD the default color of the mask is stored in 1 byte value!
635 // Even when the mask is actually 16/32 bit! I have no idea how it is
636 // actually treated in this case.
638 const quint8 defaultPixel = *m_onlyTransparencyMask->paintDevice()->defaultPixel().data();
639 SAFE_WRITE_EX(byteOrder, io, defaultPixel);
640 }
641
642 {
643 quint8 maskFlags = 0; // nothing serious
644 if (!vectorMask.path.subPaths.isEmpty()) {
645 maskFlags |= 8; // bit 3 = indicates that the user mask actually came from rendering other data
646 }
647 SAFE_WRITE_EX(byteOrder, io, maskFlags);
648
649 const quint16 padding = 0; // 2-byte padding
650 SAFE_WRITE_EX(byteOrder, io, padding);
651 }
652 } else {
653 const quint32 nullLayerMaskDataSize = 0;
654 SAFE_WRITE_EX(byteOrder, io, nullLayerMaskDataSize);
655 }
656
657 {
658 // blending ranges are not implemented yet
659 const quint32 nullBlendingRangesSize = 0;
660 SAFE_WRITE_EX(byteOrder, io, nullBlendingRangesSize);
661 }
662
663 // layer name: Pascal string, padded to a multiple of 4 bytes.
664 psdwrite_pascalstring<byteOrder>(io, layerName, 4);
665
666 PsdAdditionalLayerInfoBlock additionalInfoBlock(m_header);
667
668 // write 'luni' data block
669 additionalInfoBlock.writeLuniBlockEx(io, layerName);
670
671 additionalInfoBlock.writeLclrBlockEx(io, psdLabelColor(labelColor));
672
673 // write 'lsct' data block
674 if (sectionType != psd_other) {
675 additionalInfoBlock.writeLsctBlockEx(io, sectionType, isPassThrough, blendModeKey);
676 }
677
678 // write 'lfx2' data block
679 if (!stylesXmlDoc.isNull()) {
680 additionalInfoBlock.writeLfx2BlockEx(io, stylesXmlDoc, useLfxsLayerStyleFormat);
681 }
682
683 // write SoCo, GdFl, PtFl data blocks.
684 if (!fillConfig.isNull()) {
685 additionalInfoBlock.writeFillLayerBlockEx(io, fillConfig, fillType);
686 }
687
688 // write 'vmsk' data block
689 if (!vectorMask.path.subPaths.isEmpty()) {
690 additionalInfoBlock.writeVmskBlockEx(io, vectorMask);
691 }
692
693 // write 'tysh' data block
694 if (!textShape.engineData.isEmpty()) {
695 additionalInfoBlock.writeTypeToolBlockEx(io, textShape);
696 }
697
698 // write 'vstk' data block
699 if (!vectorStroke.isNull()) {
700 additionalInfoBlock.writeVectorStrokeDataEx(io, vectorStroke);
701 }
702
703 if (!vectorOriginationData.isNull()) {
705 }
706
707 }
710 }
711}
712
714{
715 KisPaintDeviceSP result = dev;
716
717 if (m_header.channelDepth == 16) {
718 result = new KisPaintDevice(*dev);
719 result->convertTo(KoColorSpaceRegistry::instance()->alpha16());
720 } else if (m_header.channelDepth == 32) {
721 result = new KisPaintDevice(*dev);
722 result->convertTo(KoColorSpaceRegistry::instance()->alpha32f());
723 }
724 return result;
725}
726
727quint16 PSDLayerRecord::psdLabelColor(int colorLabelIndex)
728{
729 quint16 color = 0;
730 switch (colorLabelIndex) {
731 case 0: // none
732 color = 0;
733 break;
734 case 1: // Blue
735 color = 5;
736 break;
737 case 2: // Green
738 color = 4;
739 break;
740 case 3: // Yellow
741 color = 3;
742 break;
743 case 4: // Orange
744 color = 2;
745 break;
746 case 5: // Brown, don't save.
747 color = 0;
748 break;
749 case 6: // Red
750 color = 1;
751 break;
752 case 7: // Purple
753 color = 6;
754 break;
755 case 8: // Grey
756 color = 7;
757 break;
758 default:
759 color = 0;
760 }
761 return color;
762}
763
765{
766 int color = 0;
767 switch (labelColor) {
768 case 0:
769 color = 0;
770 break;
771 case 1: // red
772 color = 6;
773 break;
774 case 2: // Orange
775 color = 4;
776 break;
777 case 3: // Yellow
778 color = 3;
779 break;
780 case 4: // Green
781 color = 2;
782 break;
783 case 5: // Blue
784 color = 1;
785 break;
786 case 6: // Purple
787 color = 7;
788 break;
789 case 7: // Grey
790 color = 8;
791 break;
792 default:
793 color = 0;
794 }
795 return color;
796}
797
798template<psd_byte_order byteOrder>
800{
803
804 QByteArray buffer(static_cast<int>(device->pixelSize()) * m_onlyTransparencyMaskRect.width() * m_onlyTransparencyMaskRect.height(), 0);
805 device->readBytes((quint8 *)buffer.data(), m_onlyTransparencyMaskRect);
806
808 (quint8 *)buffer.data(),
809 static_cast<int>(device->pixelSize()),
812 -1,
813 true,
814 byteOrder);
815 }
816}
817
818void PSDLayerRecord::writePixelData(QIODevice &io, psd_compression_type compressionType)
819{
820 try {
821 switch (m_header.byteOrder) {
823 writePixelDataImpl<psd_byte_order::psdLittleEndian>(io, compressionType);
824 break;
825 default:
826 writePixelDataImpl(io, compressionType);
827 break;
828 }
831 }
832}
833
834template<psd_byte_order byteOrder>
836{
837 dbgFile << "writing pixel data for layer" << layerName << "at" << io.pos();
838
840 const QRect rc(left, top, right - left, bottom - top);
841
842 if (rc.isEmpty()) {
843 dbgFile << "Layer is empty! Writing placeholder information.";
844
845 for (int i = 0; i < nChannels; i++) {
846 const ChannelInfo *channelInfo = channelInfoRecords[i];
847 KisAslWriterUtils::OffsetStreamPusher<quint32, byteOrder> channelBlockSizeExternalTag(io, 0, channelInfo->channelInfoPosition);
848 SAFE_WRITE_EX(byteOrder, io, static_cast<quint16>(psd_compression_type::Uncompressed));
849 }
850
851 writeTransparencyMaskPixelData<byteOrder>(io);
852
853 return;
854 }
855
856 // now write all the channels in display order
857 dbgFile << "layer" << layerName;
858
859 const int channelSize = m_header.channelDepth / 8;
860 const psd_color_mode colorMode = m_header.colormode;
861
863 Q_FOREACH (const ChannelInfo *channelInfo, channelInfoRecords) {
864 writingInfoList << PsdPixelUtils::ChannelWritingInfo(channelInfo->channelId, channelInfo->channelInfoPosition);
865 }
866
867 PsdPixelUtils::writePixelDataCommon(io, dev, rc, colorMode, channelSize, true, true, writingInfoList, compressionType, byteOrder);
868 writeTransparencyMaskPixelData<byteOrder>(io);
869}
870
872{
873 // XXX: check validity!
874 return true;
875}
876
878{
879 dbgFile << "Reading pixel data for layer" << layerName << "pos" << io.pos();
880
881 const int channelSize = m_header.channelDepth / 8;
882 const QRect layerRect = QRect(left, top, right - left, bottom - top);
883
884 try {
885 // WARNING: Pixel data is ALWAYS in big endian!!!
888 device->clear();
889 error = e.what();
890 return false;
891 }
892
893 return true;
894}
895
897{
898 QRect result;
899
900 if (channel->channelId < -1) {
902 } else {
903 result = QRect(left, top, right - left, bottom - top);
904 }
905
906 return result;
907}
908
909bool PSDLayerRecord::readMask(QIODevice &io, KisPaintDeviceSP dev, ChannelInfo *channelInfo)
910{
911 KIS_ASSERT_RECOVER(channelInfo->channelId < -1)
912 {
913 return false;
914 }
915
916 dbgFile << "Going to read" << channelIdToChannelType(channelInfo->channelId, m_header.colormode) << "mask";
917
918 QRect maskRect = channelRect(channelInfo);
919 if (maskRect.isEmpty()) {
920 dbgFile << "Empty Channel";
921 return true;
922 }
923
924 // the device must be a pixel selection
925 KIS_ASSERT_RECOVER(dev->pixelSize() == 1)
926 {
927 return false;
928 }
929
931
932 const int pixelSize = m_header.channelDepth == 16 ? 2 : m_header.channelDepth == 32 ? 4 : 1;
933
934 QVector<ChannelInfo *> infoRecords;
935 infoRecords << channelInfo;
936 PsdPixelUtils::readAlphaMaskChannels(io, dev, pixelSize, maskRect, infoRecords);
937
938 return true;
939}
940
941KoPathShape *PSDLayerRecord::constructPathShape(psd_path path, double shapeWidth, double shapeHeight)
942{
943 KoPathShape *shape = new KoPathShape();
944
945 // psd paths are stored normalized.
946 QTransform tf = QTransform::fromScale(shapeWidth, shapeHeight);
947
948 QString nodeTypes;
949 Q_FOREACH(psd_path_sub_path subPath, path.subPaths) {
950 for (int i = 0; i < subPath.nodes.size(); i++) {
951 psd_path_node node = subPath.nodes.at(i);
952 if (i == 0) {
953 shape->moveTo(tf.map(node.node));
954 } else {
955 psd_path_node previousNode = subPath.nodes.at(i-1);
956 if (previousNode.node == previousNode.control2 && node.node == node.control1) {
957 shape->lineTo(tf.map(node.node));
958 } else {
959 shape->curveTo(tf.map(previousNode.control2), tf.map(node.control1), tf.map(node.node));
960 }
961 }
962 if (node.isSmooth) {
963 nodeTypes.append("s");
964 } else {
965 nodeTypes.append("c");
966 }
967 }
968 if (subPath.isClosed) {
969 psd_path_node lastNode = subPath.nodes.last();
970 psd_path_node firstNode = subPath.nodes.first();
971 if (lastNode.node == lastNode.control2 && firstNode.node == firstNode.control1) {
972 shape->lineTo(tf.map(firstNode.node));
973 } else {
974 shape->curveTo(tf.map(lastNode.control2), tf.map(firstNode.control1), tf.map(firstNode.node));
975 }
976 shape->closeMerge();
977 }
978
979 }
980 if (shape->pointCount() > 0) {
981 shape->loadNodeTypes(nodeTypes);
982 }
983
984 return shape;
985}
986
987void PSDLayerRecord::addPathShapeToPSDPath(psd_path &path, KoPathShape *shape, double shapeWidth, double shapeHeight)
988{
989 QTransform tf = QTransform::fromScale(shapeWidth, shapeHeight).inverted();
990 tf = shape->absoluteTransformation()*tf;
991
992
993 for (int i = 0; i < shape->subpathCount(); i++) {
994 psd_path_sub_path subPath;
995 subPath.isClosed = shape->isClosedSubpath(i);
996 while(subPath.nodes.size() < shape->subpathPointCount(i)) {
997 const KoPathPoint *point = shape->pointByIndex(KoPathPointIndex(i, subPath.nodes.size()));
998 psd_path_node node;
999 node.node = tf.map(point->point());
1000 node.control1 = point->activeControlPoint1()? tf.map(point->controlPoint1()): node.node;
1001 node.control2 = point->activeControlPoint2()? tf.map(point->controlPoint2()): node.node;
1002
1003 node.isSmooth = (point->properties().testFlag(KoPathPoint::IsSmooth)
1004 || point->properties().testFlag(KoPathPoint::IsSymmetric));
1005 subPath.nodes.append(node);
1006 }
1007
1008 path.subPaths.append(subPath);
1009 }
1010}
1011
1012QDebug operator<<(QDebug dbg, const PSDLayerRecord &layer)
1013{
1014#ifndef NODEBUG
1015 dbg.nospace() << "valid: " << const_cast<PSDLayerRecord *>(&layer)->valid();
1016 dbg.nospace() << ", name: " << layer.layerName;
1017 dbg.nospace() << ", top: " << layer.top;
1018 dbg.nospace() << ", left:" << layer.left;
1019 dbg.nospace() << ", bottom: " << layer.bottom;
1020 dbg.nospace() << ", right: " << layer.right;
1021 dbg.nospace() << ", number of channels: " << layer.nChannels;
1022 dbg.nospace() << ", blendModeKey: " << layer.blendModeKey;
1023 dbg.nospace() << ", opacity: " << layer.opacity;
1024 dbg.nospace() << ", clipping: " << layer.clipping;
1025 dbg.nospace() << ", transparency protected: " << layer.transparencyProtected;
1026 dbg.nospace() << ", visible: " << layer.visible;
1027 dbg.nospace() << ", irrelevant: " << layer.irrelevant << "\n";
1028 Q_FOREACH (const ChannelInfo *channel, layer.channelInfoRecords) {
1029 dbg.space() << channel;
1030 }
1031#endif
1032 return dbg.nospace();
1033}
1034
1035QDebug operator<<(QDebug dbg, const ChannelInfo &channel)
1036{
1037#ifndef NODEBUG
1038 dbg.nospace() << "\tChannel type" << channel.channelId << "size: " << channel.channelDataLength << "compression type" << channel.compressionType << "\n";
1039#endif
1040 return dbg.nospace();
1041}
QPair< int, int > KoPathPointIndex
Definition KoPathShape.h:28
quint32 pixelSize() const
virtual void clear()
void setDefaultPixel(const KoColor &defPixel)
const KoColorSpace * colorSpace() const
KoColor defaultPixel() const
void convertTo(const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent=KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::ConversionFlags conversionFlags=KoColorConversionTransformation::internalConversionFlags(), KUndo2Command *parentCommand=nullptr, KoUpdater *progressUpdater=nullptr)
void readBytes(quint8 *data, qint32 x, qint32 y, qint32 w, qint32 h) const
quint8 * data()
Definition KoColor.h:144
A KoPathPoint represents a point in a path.
PointProperties properties
QPointF point
QPointF controlPoint1
@ IsSmooth
it is smooth, both control points on a line through the point
Definition KoPathPoint.h:41
@ IsSymmetric
it is symmetric, like smooth but control points have same distance to point
Definition KoPathPoint.h:42
bool activeControlPoint1
bool activeControlPoint2
QPointF controlPoint2
The position of a path point within a path shape.
Definition KoPathShape.h:63
int subpathPointCount(int subpathIndex) const
Returns the number of points in a subpath.
bool isClosedSubpath(int subpathIndex) const
Checks if a subpath is closed.
KoPathPoint * lineTo(const QPointF &p)
Adds a new line segment.
void closeMerge()
Closes the current subpath.
KoPathPoint * moveTo(const QPointF &p)
Starts a new Subpath.
int pointCount() const
Returns the number of points in the path.
KoPathPoint * curveTo(const QPointF &c1, const QPointF &c2, const QPointF &p)
Adds a new cubic Bezier curve segment.
int subpathCount() const
Returns the number of subpaths in the path.
void loadNodeTypes(const QString &nodeTypes)
Loads node types.
KoPathPoint * pointByIndex(const KoPathPointIndex &pointIndex) const
Returns the path point specified by a path point index.
QTransform absoluteTransformation() const
Definition KoShape.cpp:382
psd_color_mode colormode
Definition psd_header.h:48
quint16 channelDepth
Definition psd_header.h:47
quint16 version
Definition psd_header.h:43
psd_byte_order byteOrder
Definition psd_header.h:49
bool readImpl(QIODevice &io)
PSDLayerRecord(const PSDHeader &header)
PsdAdditionalLayerInfoBlock infoBlocks
qint64 m_transparencyMaskSizeOffset
KisPaintDeviceSP convertMaskDeviceIfNeeded(KisPaintDeviceSP dev)
QRect m_onlyTransparencyMaskRect
psd_fill_type fillType
void writePixelData(QIODevice &io, psd_compression_type compressionType)
void writeTransparencyMaskPixelData(QIODevice &io)
psd_vector_mask vectorMask
psd_layer_type_shape textShape
bool readPixelData(QIODevice &io, KisPaintDeviceSP device)
void writePixelDataImpl(QIODevice &io, psd_compression_type compressionType)
QDomDocument vectorOriginationData
const PSDHeader m_header
KisPaintDeviceSP m_layerContentDevice
quint16 psdLabelColor(int colorLabelIndex)
LayerBlendingRanges blendingRanges
QVector< ChannelInfo * > channelInfoRecords
QRect channelRect(ChannelInfo *channel) const
QDomDocument vectorStroke
void write(QIODevice &io, KisPaintDeviceSP layerContentDevice, KisNodeSP onlyTransparencyMask, const QRect &maskRect, psd_section_type sectionType, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
KoPathShape * constructPathShape(psd_path path, double shapeWidth, double shapeHeight)
constructPathShape create a KoPathshape based on a psd_path struct, used in vector masks and path res...
void addPathShapeToPSDPath(psd_path &path, KoPathShape *shape, double shapeWidth, double shapeHeight)
addPathShapeToPSDPath add all KoPathShape subpaths to the given psd_path struct.
int kritaColorLabelIndex(quint16 labelColor)
QDomDocument fillConfig
KisNodeSP m_onlyTransparencyMask
LayerMaskData layerMask
void writeImpl(QIODevice &io, KisPaintDeviceSP layerContentDevice, KisNodeSP onlyTransparencyMask, const QRect &maskRect, psd_section_type sectionType, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
bool read(QIODevice &io)
bool readMask(QIODevice &io, KisPaintDeviceSP dev, ChannelInfo *channel)
The PsdAdditionalLayerInfoBlock class implements the Additional Layer Information block.
void writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
void writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
void writeVmskBlockEx(QIODevice &io, psd_vector_mask mask)
void writeVectorOriginationDataEx(QIODevice &io, const QDomDocument &vectorOrigination)
void writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
void writeVectorStrokeDataEx(QIODevice &io, const QDomDocument &vectorStroke)
void writeLuniBlockEx(QIODevice &io, const QString &layerName)
void writeLclrBlockEx(QIODevice &io, const quint16 &labelColor)
void writeTypeToolBlockEx(QIODevice &io, psd_layer_type_shape typeTool)
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define KIS_ASSERT_RECOVER(cond)
Definition kis_assert.h:55
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
#define PREPEND_METHOD(msg)
Definition kis_debug.h:172
#define ppVar(var)
Definition kis_debug.h:155
#define dbgFile
Definition kis_debug.h:53
void writePixelDataCommon(QIODevice &io, KisPaintDeviceSP dev, const QRect &rc, psd_color_mode colorMode, int channelSize, bool alphaFirst, const bool writeCompressionType, QVector< ChannelWritingInfo > &writingInfoList, psd_compression_type compressionType, psd_byte_order byteOrder)
void readChannels(QIODevice &io, KisPaintDeviceSP device, psd_color_mode colorMode, int channelSize, const QRect &layerRect, QVector< ChannelInfo * > infoRecords, psd_byte_order byteOrder)
void writeChannelDataRLE(QIODevice &io, const quint8 *plane, const int channelSize, const QRect &rc, const qint64 sizeFieldOffset, const qint64 rleBlockOffset, const bool writeCompressionType, psd_byte_order byteOrder)
void readAlphaMaskChannels(QIODevice &io, KisPaintDeviceSP device, int channelSize, const QRect &layerRect, QVector< ChannelInfo * > infoRecords, psd_byte_order byteOrder)
psd_compression_type
Definition psd.h:39
@ Uncompressed
Definition psd.h:40
psd_section_type
Definition psd.h:144
@ psd_other
Definition psd.h:144
const int MAX_CHANNELS
Definition psd.h:27
psd_color_mode
Definition psd.h:50
@ CMYK64
Definition psd.h:62
@ Lab
Definition psd.h:58
@ RGB
Definition psd.h:54
@ DuoTone
Definition psd.h:57
@ RGB48
Definition psd.h:60
@ Duotone16
Definition psd.h:64
@ CMYK
Definition psd.h:55
@ Gray16
Definition psd.h:59
@ Indexed
Definition psd.h:53
@ MultiChannel
Definition psd.h:56
@ DeepMultichannel
Definition psd.h:63
@ Grayscale
Definition psd.h:52
@ Bitmap
Definition psd.h:51
@ Lab48
Definition psd.h:61
QString channelIdToChannelType(int channelId, psd_color_mode colormode)
QDebug operator<<(QDebug dbg, const PSDLayerRecord &layer)
psd_compression_type compressionType
quint64 channelDataLength
quint64 channelDataStart
virtual KisPaintDeviceSP paintDevice() const =0
static KoColorSpaceRegistry * instance()
QVector< QPair< LayerBlendingRange, LayerBlendingRange > > sourceDestinationRanges
QPair< LayerBlendingRange, LayerBlendingRange > compositeGrayRange
QList< psd_path_sub_path > subPaths