Krita Source Code Documentation
Loading...
Searching...
No Matches
psd_additional_layer_info_block.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2014 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
9#include "psd.h"
10
11#include <QDomDocument>
12
14
16#include <asl/kis_asl_reader.h>
18#include <asl/kis_asl_writer.h>
20#include <cos/kis_txt2_utls.h>
21
22
24 : m_header(header)
25 , sectionDividerType(psd_other)
26{
27}
28
33
38
40{
41 bool result = true;
42
43 try {
44 switch (m_header.byteOrder) {
46 readImpl<psd_byte_order::psdLittleEndian>(io);
47 break;
48 default:
49 readImpl(io);
50 break;
51 }
53 error = e.what();
54 result = false;
55 }
56
57 return result;
58}
59
60template<psd_byte_order byteOrder>
62{
63 using namespace KisAslReaderUtils;
64
65 QStringList longBlocks;
66 if (m_header.version > 1) {
67 longBlocks << "LMsk"
68 << "Lr16"
69 << "Lr32"
70 << "Layr"
71 << "Mt16"
72 << "Mt32"
73 << "Mtrn"
74 << "Alph"
75 << "FMsk"
76 << "lnk2"
77 << "FEid"
78 << "FXid"
79 << "PxSD";
80 }
81
82 while (!io.atEnd()) {
83 {
84 const std::array<quint8, 4> refSignature1 = {'8', 'B', 'I', 'M'}; // '8BIM' in big-endian
85 const std::array<quint8, 4> refSignature2 = {'8', 'B', '6', '4'}; // '8B64' in big-endian
86
87 if (!TRY_READ_SIGNATURE_2OPS_EX<byteOrder>(io, refSignature1, refSignature2)) {
88 break;
89 }
90 }
91
92 QString key = readFixedString<byteOrder>(io);
93 dbgFile << "found info block with key" << key << "(" << io.pos() << ")";
94
95 quint64 blockSize = GARBAGE_VALUE_MARK;
96 if (longBlocks.contains(key)) {
97 SAFE_READ_EX(byteOrder, io, blockSize);
98 } else {
99 quint32 size32;
100 SAFE_READ_EX(byteOrder, io, size32);
101 blockSize = size32;
102 }
103
104 // Since TIFF headers are padded to multiples of 4,
105 // staving them off here is way easier.
107 if (blockSize % 4U) {
108 dbgFile << "(TIFF) WARNING: current block size is NOT a multiple of 4! Fixing...";
109 blockSize += (4U - blockSize % 4U);
110 }
111 }
112
113 dbgFile << "info block size" << blockSize << "(" << io.pos() << ")";
114
115 if (blockSize == 0)
116 continue;
117
118 // offset verifier will correct the position on the exit from
119 // current namespace, including 'continue', 'return' and
120 // exceptions.
121 SETUP_OFFSET_VERIFIER(infoBlockEndVerifier, io, blockSize, 0);
122
123 if (keys.contains(key)) {
124 error = "Found duplicate entry for key ";
125 continue;
126 }
127 keys << key;
128
129 // TODO: Loading of 32 bit files is not supported yet
130 if (key == "Lr16" /* || key == "Lr32"*/) {
132 int offset = m_header.version > 1 ? 8 : 4;
133 dbgFile << "Offset for block handler: " << io.pos() << offset;
134 io.seek(io.pos() - offset);
136 }
137 } else if (key == "Layr") {
139 int offset = m_header.version > 1 ? 8 : 4;
140 dbgFile << "(TIFF) Offset for block handler: " << io.pos() << offset;
141 io.seek(io.pos() - offset);
143 }
144 } else if (key == "SoCo") {
145 // Solid Color
148 } else if (key == "GdFl") {
149 // Gradient Fill
152 } else if (key == "PtFl") {
153 // Pattern Fill
156 } else if (key == "brit") {
157 } else if (key == "levl") {
158 } else if (key == "curv") {
159 } else if (key == "expA") {
160 } else if (key == "vibA") {
161 } else if (key == "hue") {
162 } else if (key == "hue2") {
163 } else if (key == "blnc") {
164 } else if (key == "blwh") {
165 } else if (key == "phfl") {
166 } else if (key == "mixr") {
167 } else if (key == "clrL") {
168 } else if (key == "nvrt") {
169 } else if (key == "post") {
170 } else if (key == "thrs") {
171 } else if (key == "selc") {
172 } else if (key == "lrFX") {
173 // deprecated! use lfx2 instead!
174 } else if (key == "tySh") {
175 } else if (key == "luni") {
176 // get the unicode layer name
177 unicodeLayerName = readUnicodeString<byteOrder>(io);
178 dbgFile << "\t" << "unicodeLayerName" << unicodeLayerName;
179 } else if (key == "lyid") {
180 quint32 id;
181 psdread<byteOrder>(io, id);
182 dbgFile << "\t" << "layer ID:" << id;
183 } else if (key == "lfx2" || key == "lfxs") {
184 // lfxs is a special variant of layer styles for group layers
186 } else if (key == "Patt" || key == "Pat2" || key == "Pat3") {
187 QDomDocument pattern = KisAslReader::readPsdSectionPattern(io, blockSize, byteOrder);
188 embeddedPatterns << pattern;
189 } else if (key == "Anno") {
190 } else if (key == "clbl") {
191 } else if (key == "infx") {
192 } else if (key == "knko") {
193 } else if (key == "lspf") {
194 // Protected setting. Only the (undocumented) complete
195 // lock flag is supported. Other flags include
196 // transparency, composite and position (bits 0-2).
197 quint32 protection;
198 psdread<byteOrder>(io, protection);
199 userLock = protection & (1 << 31)? true: false;
200 } else if (key == "lclr") {
201 // layer label color.
202 quint16 col1 = 0;
203 quint16 col2 = 0;
204 quint16 col3 = 0;
205 quint16 col4 = 0;
206 psdread<byteOrder>(io, col1);
207 psdread<byteOrder>(io, col2);
208 psdread<byteOrder>(io, col3);
209 psdread<byteOrder>(io, col4);
210 dbgFile << "\t" << "layer color:" << col1 << col2 << col3 << col4;
211 labelColor = col1;
212 } else if (key == "fxrp") {
213 } else if (key == "grdm") {
214 } else if (key == "lsct") {
215 quint32 dividerType = GARBAGE_VALUE_MARK;
216 SAFE_READ_EX(byteOrder, io, dividerType);
217 this->sectionDividerType = (psd_section_type)dividerType;
218
219 dbgFile << "Reading \"lsct\" block:";
220 dbgFile << ppVar(blockSize);
221 dbgFile << ppVar(dividerType);
222
223 if (blockSize >= 12) {
224 quint32 lsctSignature = GARBAGE_VALUE_MARK;
225 const quint32 refSignature1 = 0x3842494D; // '8BIM' in little-endian
226 SAFE_READ_SIGNATURE_EX(byteOrder, io, lsctSignature, refSignature1);
227
228 this->sectionDividerBlendMode = readFixedString<byteOrder>(io);
229
231 }
232
233 // Animation
234 if (blockSize >= 14) {
239 }
240
241 } else if (key == "brst") {
242 } else if (key == "vmsk" || key == "vsms") { // If key is "vsms" then we are writing for (Photoshop CS6) and the document will have a "vscg" key
243 quint32 version; // ( = 3 for Photoshop 6.0)
244 psdread<byteOrder>(io, version);
245
246 quint32 flags;
247 psdread<byteOrder>(io, flags);
248 // read the flags.
249 vectorMask.invert = flags & 1? true: false;
250 vectorMask.notLink = flags & 2? true: false;
251 vectorMask.disable = flags & 4? true: false;
252
253 quint64 currentPos = 8;
254 psd_path_sub_path currentPath;
255 bool firstPath = true;
256
257 while (currentPos < blockSize) {
258 quint16 recordType;
259 psdread<byteOrder>(io, recordType);
260
261 if (recordType == 6) {
262 io.skip(24);
263 dbgFile << "\trecord" << recordType;
264 } else if (recordType == 7) {
265 QRectF bounds;
266 // unsure if there can be multiple clipboard records...
267 bounds.setTop(psdreadFixedPoint<byteOrder>(io));
268 bounds.setLeft(psdreadFixedPoint<byteOrder>(io));
269 bounds.setBottom(psdreadFixedPoint<byteOrder>(io));
270 bounds.setRight(psdreadFixedPoint<byteOrder>(io));
272 vectorMask.path.clipBoardResolution = psdreadFixedPoint<byteOrder>(io);
273 dbgFile << "\trecord" << recordType << "top" << bounds << "res" << vectorMask.path.clipBoardResolution;
274 io.skip(4);
275 } else if (recordType == 0 || recordType == 3) {
276 quint16 length;
277 psdread<byteOrder>(io, length);
278 dbgFile << "\trecord" << recordType << "length" << length;
279 if (firstPath) {
280 currentPath.isClosed = (recordType == 0);
281 } else {
282 vectorMask.path.subPaths.append(currentPath);
283 currentPath = psd_path_sub_path();
284 currentPath.isClosed = (recordType == 0);
285 }
286 firstPath = false;
287 io.skip(22);
288 } else if (recordType == 8) {
289 quint16 length;
290 psdread<byteOrder>(io, length);
291 dbgFile << "\trecord" << recordType << "length" << length;
293 io.skip(22);
294 } else {
295 psd_path_node node;
296 node.control1.setY(psdreadFixedPoint<byteOrder>(io));
297 node.control1.setX(psdreadFixedPoint<byteOrder>(io));
298 node.node.setY(psdreadFixedPoint<byteOrder>(io));
299 node.node.setX(psdreadFixedPoint<byteOrder>(io));
300 node.control2.setY(psdreadFixedPoint<byteOrder>(io));
301 node.control2.setX(psdreadFixedPoint<byteOrder>(io));
302 node.isSmooth = (recordType == 1 || recordType == 4);
303 dbgFile << "\trecord" << recordType << "c1" << node.control1
304 << "node" << node.node << "c2" << node.control2;
305 currentPath.nodes.append(node);
306 }
307
308 currentPos += 26;
309 }
310 if (!currentPath.nodes.isEmpty()) {
311 vectorMask.path.subPaths.append(currentPath);
312 }
313
314 } else if (key == "TySh") {
316 } else if (key == "ffxi") {
317 } else if (key == "lnsr") {
318 } else if (key == "shpa") {
319 } else if (key == "shmd") {
320 } else if (key == "lyvr") {
321 } else if (key == "tsly") {
322 } else if (key == "lmgm") {
323 } else if (key == "vmgm") {
324 } else if (key == "plLd") { // Replaced by SoLd in CS3
325
326 } else if (key == "linkD" || key == "lnk2" || key == "lnk3") {
327 } else if (key == "CgEd") {
328 } else if (key == "Txt2") { // global text data, basically the same as an Illustrator text object.
329 // Docs say "first 4 are length", this is not true for this particular block, only when in ASL is first 4 length.
330 QByteArray ba = io.read(blockSize);
332 txt2Data = KisTxt2Utils::uncompressKeys(p.parseCosToJson(&ba));
333 } else if (key == "pths") {
334 } else if (key == "anFX") {
335 } else if (key == "FMsk") {
336 } else if (key == "SoLd") {
337 } else if (key == "vstk") { // vector stroke info
339 } else if (key == "vscg") {
340 if (blockSize > 4) {
341 QString vscgKey = readFixedString<byteOrder>(io);
343 if (vscgKey == "SoCo") {
345 } else if (vscgKey == "GdFl") {
346 // Gradient Fill
348 } else if (vscgKey == "PtFl") {
349 // Pattern Fill
351 }
352 }
353 } else if (key == "sn2P") {
354 } else if (key == "vogk") { // Live path shapes, these are similar to parametric shapes.
356 } else if (key == "Mtrn" || key == "Mt16" || key == "Mt32") { // There is no data associated with these keys.
357
358 } else if (key == "LMsk") {
359 // TIFFs store the global mask here.
361 int offset = m_header.version > 1 ? 8 : 4;
362 dbgFile << "(TIFF) Offset for block handler: " << io.pos() << offset;
363 io.seek(io.pos() - offset);
365 }
366 } else if (key == "FXid") {
367 } else if (key == "FEid") {
368 }
369 }
370}
371
372bool PsdAdditionalLayerInfoBlock::write(QIODevice & /*io*/, KisNodeSP /*node*/)
373{
374 return true;
375}
376
378{
379 return true;
380}
381
382void PsdAdditionalLayerInfoBlock::writeLuniBlockEx(QIODevice &io, const QString &layerName)
383{
384 switch (m_header.byteOrder) {
386 writeLuniBlockExImpl<psd_byte_order::psdLittleEndian>(io, layerName);
387 break;
388 default:
389 writeLuniBlockExImpl(io, layerName);
390 break;
391 }
392}
393
394template<psd_byte_order byteOrder>
395void PsdAdditionalLayerInfoBlock::writeLuniBlockExImpl(QIODevice &io, const QString &layerName)
396{
397 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
398 KisAslWriterUtils::writeFixedString<byteOrder>("luni", io);
400 KisAslWriterUtils::writeUnicodeString<byteOrder>(layerName, io);
401}
402
403void PsdAdditionalLayerInfoBlock::writeLsctBlockEx(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
404{
405 switch (m_header.byteOrder) {
407 writeLsctBlockExImpl<psd_byte_order::psdLittleEndian>(io, sectionType, isPassThrough, blendModeKey);
408 break;
409 default:
410 writeLsctBlockExImpl(io, sectionType, isPassThrough, blendModeKey);
411 break;
412 }
413}
414
415template<psd_byte_order byteOrder>
416void PsdAdditionalLayerInfoBlock::writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
417{
418 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
419 KisAslWriterUtils::writeFixedString<byteOrder>("lsct", io);
421 SAFE_WRITE_EX(byteOrder, io, (quint32)sectionType);
422
423 QString realBlendModeKey = isPassThrough ? QString("pass") : blendModeKey;
424
425 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
426 KisAslWriterUtils::writeFixedString<byteOrder>(realBlendModeKey, io);
427}
428
429void PsdAdditionalLayerInfoBlock::writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
430{
431 switch (m_header.byteOrder) {
433 writeLfx2BlockExImpl<psd_byte_order::psdLittleEndian>(io, stylesXmlDoc, useLfxsLayerStyleFormat);
434 break;
435 default:
436 writeLfx2BlockExImpl(io, stylesXmlDoc, useLfxsLayerStyleFormat);
437 break;
438 }
439}
440
441template<psd_byte_order byteOrder>
442void PsdAdditionalLayerInfoBlock::writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
443{
444 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
445 // 'lfxs' format is used for Group layers in PS
446 KisAslWriterUtils::writeFixedString<byteOrder>(!useLfxsLayerStyleFormat ? "lfx2" : "lfxs", io);
448
449 try {
450 KisAslWriter writer(byteOrder);
451 writer.writePsdLfx2SectionEx(io, stylesXmlDoc);
452
454 warnKrita << "WARNING: Couldn't save layer style lfx2 block:" << PREPEND_METHOD(e.what());
455
456 // TODO: make this error recoverable!
457 throw e;
458 }
459}
460
461void PsdAdditionalLayerInfoBlock::writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc)
462{
463 switch (m_header.byteOrder) {
465 writePattBlockExImpl<psd_byte_order::psdLittleEndian>(io, patternsXmlDoc);
466 break;
467 default:
468 writePattBlockExImpl(io, patternsXmlDoc);
469 break;
470 }
471}
472
473void PsdAdditionalLayerInfoBlock::writeLspfBlockEx(QIODevice &io, const bool lock)
474{
475 switch (m_header.byteOrder) {
477 writeLspfBlockExImpl<psd_byte_order::psdLittleEndian>(io, lock);
478 break;
479 default:
480 writeLspfBlockExImpl(io, lock);
481 break;
482 }
483}
484
485void PsdAdditionalLayerInfoBlock::writeLclrBlockEx(QIODevice &io, const quint16 &labelColor)
486{
487 switch (m_header.byteOrder) {
489 writeLclrBlockExImpl<psd_byte_order::psdLittleEndian>(io, labelColor);
490 break;
491 default:
493 break;
494 }
495}
496
497void PsdAdditionalLayerInfoBlock::writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
498{
499 switch (m_header.byteOrder) {
501 writeFillLayerBlockExImpl<psd_byte_order::psdLittleEndian>(io, fillConfig, type);
502 break;
503 default:
505 break;
506 }
507}
508
510{
511 switch (m_header.byteOrder) {
513 writeVectorMaskImpl<psd_byte_order::psdLittleEndian>(io, mask);
514 break;
515 default:
516 writeVectorMaskImpl(io, mask);
517 break;
518 }
519}
520
522{
523 switch (m_header.byteOrder) {
525 writeTypeToolImpl<psd_byte_order::psdLittleEndian>(io, typeTool);
526 break;
527 default:
528 writeTypeToolImpl(io, typeTool);
529 break;
530 }
531}
532
533void PsdAdditionalLayerInfoBlock::writeVectorStrokeDataEx(QIODevice &io, const QDomDocument &vectorStroke)
534{
535 switch (m_header.byteOrder) {
537 writeVectorStrokeDataImpl<psd_byte_order::psdLittleEndian>(io, vectorStroke);
538 break;
539 default:
541 break;
542 }
543}
544
545void PsdAdditionalLayerInfoBlock::writeVectorOriginationDataEx(QIODevice &io, const QDomDocument &vectorOrigination)
546{
547 switch (m_header.byteOrder) {
549 writeVectorOriginationDataImpl<psd_byte_order::psdLittleEndian>(io, vectorOrigination);
550 break;
551 default:
552 writeVectorOriginationDataImpl(io, vectorOrigination);
553 break;
554 }
555}
556
557template<psd_byte_order byteOrder>
558void PsdAdditionalLayerInfoBlock::writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc)
559{
560 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
561 KisAslWriterUtils::writeFixedString<byteOrder>("Patt", io);
562 const quint32 padding = m_header.tiffStyleLayerBlock ? 4 : 2;
564
565 try {
566 KisAslPatternsWriter writer(patternsXmlDoc, io, byteOrder);
567 writer.writePatterns();
568
570 warnKrita << "WARNING: Couldn't save layer style patterns block:" << PREPEND_METHOD(e.what());
571
572 // TODO: make this error recoverable!
573 throw e;
574 }
575}
576
577template<psd_byte_order byteOrder>
578void PsdAdditionalLayerInfoBlock::writeLspfBlockExImpl(QIODevice &io, const bool &lock)
579{
580 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
581 KisAslWriterUtils::writeFixedString<byteOrder>("lspf", io);
582 const quint32 len = 4;
583 SAFE_WRITE_EX(byteOrder, io, len);
584
585 quint32 flags = 0;
586 if (lock) {
587 flags |= (1 << 31);
588 }
589 SAFE_WRITE_EX(byteOrder, io, flags);
590}
591
592template<psd_byte_order byteOrder>
593void PsdAdditionalLayerInfoBlock::writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr)
594{
595 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
596 KisAslWriterUtils::writeFixedString<byteOrder>("lclr", io);
597 // 4x2 quint16
598 const quint32 len = 8;
599 SAFE_WRITE_EX(byteOrder, io, len);
600 quint16 zero = 0;
601 SAFE_WRITE_EX(byteOrder, io, lclr);
602 SAFE_WRITE_EX(byteOrder, io, zero);
603 SAFE_WRITE_EX(byteOrder, io, zero);
604 SAFE_WRITE_EX(byteOrder, io, zero);
605
606
607}
608
609template<psd_byte_order byteOrder>
610void PsdAdditionalLayerInfoBlock::writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
611{
612 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
613 if (type == psd_fill_solid_color) {
614 KisAslWriterUtils::writeFixedString<byteOrder>("SoCo", io);
615 } else if (type == psd_fill_gradient) {
616 KisAslWriterUtils::writeFixedString<byteOrder>("GdFl", io);
617 } else {
618 KisAslWriterUtils::writeFixedString<byteOrder>("PtFl", io);
619 }
621
622 try {
623 KisAslWriter writer(byteOrder);
624
626
628 warnKrita << "WARNING: Couldn't save fill layer block:" << PREPEND_METHOD(e.what());
629
630 // TODO: make this error recoverable!
631 throw e;
632 }
633}
634
635template<psd_byte_order byteOrder>
637{
638 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
639 KisAslWriterUtils::writeFixedString<byteOrder>("vmsk", io);
640
641 quint32 len = 8; //, 4 version, 4 flags
642 len += 52; // 26 path record, 26 initial fill rule.
643 Q_FOREACH(psd_path_sub_path subPath, mask.path.subPaths) {
644 len += 26; // subpath record;
645 len += subPath.nodes.size() * 26; //and one for each node.
646 }
647
648 SAFE_WRITE_EX(byteOrder, io, len);
649
650 quint32 version = 3;
651 SAFE_WRITE_EX(byteOrder, io, version);
652 quint32 flags = 0;
653 if (mask.invert) {
654 flags |= 1;
655 }
656 if (mask.notLink) {
657 flags |= 2;
658 }
659 if (mask.disable) {
660 flags |= 4;
661 }
662 SAFE_WRITE_EX(byteOrder, io, flags);
663
664 // start path records
665 quint16 recordType = 6;
666 quint32 zero = 0;
667 SAFE_WRITE_EX(byteOrder, io, recordType);
668 // 24 empty bits
669 SAFE_WRITE_EX(byteOrder, io, zero);
670 SAFE_WRITE_EX(byteOrder, io, zero);
671 SAFE_WRITE_EX(byteOrder, io, zero);
672 SAFE_WRITE_EX(byteOrder, io, zero);
673 SAFE_WRITE_EX(byteOrder, io, zero);
674 SAFE_WRITE_EX(byteOrder, io, zero);
675 // initial fill rule record
676 recordType = 8;
677 SAFE_WRITE_EX(byteOrder, io, recordType);
678 quint16 fillType = mask.path.initialFillRecord? 1: 0;
679 SAFE_WRITE_EX(byteOrder, io, fillType);
680 // 22 empty bits
681 const quint16 halfZero = 0;
682 SAFE_WRITE_EX(byteOrder, io, halfZero);
683 SAFE_WRITE_EX(byteOrder, io, zero);
684 SAFE_WRITE_EX(byteOrder, io, zero);
685 SAFE_WRITE_EX(byteOrder, io, zero);
686 SAFE_WRITE_EX(byteOrder, io, zero);
687 SAFE_WRITE_EX(byteOrder, io, zero);
688 // write the subpaths.
689 Q_FOREACH(psd_path_sub_path subPath, mask.path.subPaths) {
690 recordType = subPath.isClosed? 0: 3;
691 quint16 length = subPath.nodes.size();
692 dbgFile << "writing subpath" << subPath.nodes.size();
693 SAFE_WRITE_EX(byteOrder, io, recordType);
694 SAFE_WRITE_EX(byteOrder, io, length);
695 // 22 empty bits
696 SAFE_WRITE_EX(byteOrder, io, halfZero);
697 SAFE_WRITE_EX(byteOrder, io, zero);
698 SAFE_WRITE_EX(byteOrder, io, zero);
699 SAFE_WRITE_EX(byteOrder, io, zero);
700 SAFE_WRITE_EX(byteOrder, io, zero);
701 SAFE_WRITE_EX(byteOrder, io, zero);
702
703 Q_FOREACH(psd_path_node node, subPath.nodes) {
704 if (subPath.isClosed) {
705 recordType = node.isSmooth? 1: 2;
706 } else {
707 recordType = node.isSmooth? 4: 5;
708 }
709 SAFE_WRITE_EX(byteOrder, io, recordType);
710 psdwriteFixedPoint<byteOrder>(io, node.control1.y());
711 psdwriteFixedPoint<byteOrder>(io, node.control1.x());
712 psdwriteFixedPoint<byteOrder>(io, node.node.y());
713 psdwriteFixedPoint<byteOrder>(io, node.node.x());
714 psdwriteFixedPoint<byteOrder>(io, node.control2.y());
715 psdwriteFixedPoint<byteOrder>(io, node.control2.x());
716 }
717 }
718}
719
720template<psd_byte_order byteOrder>
722{
723 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
724 KisAslWriterUtils::writeFixedString<byteOrder>("TySh", io);
725
727
728 try {
729 KisAslWriter writer(byteOrder);
730
731 writer.writeTypeToolObjectSettings(io, tool.textDataASLXML(), tool.textWarpXML(), tool.transform, tool.boundingBox);
732
734 warnKrita << "WARNING: Couldn't save text layer block:" << PREPEND_METHOD(e.what());
735
736 // TODO: make this error recoverable!
737 throw e;
738 }
739}
740
741template<psd_byte_order byteOrder>
742void PsdAdditionalLayerInfoBlock::writeVectorStrokeDataImpl(QIODevice &io, const QDomDocument &vectorStroke)
743{
744 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
745 KisAslWriterUtils::writeFixedString<byteOrder>("vstk", io);
747 try {
748 KisAslWriter writer(byteOrder);
749
751
753 warnKrita << "WARNING: Couldn't save vector stroke layer block:" << PREPEND_METHOD(e.what());
754
755 // TODO: make this error recoverable!
756 throw e;
757 }
758}
759
760void PsdAdditionalLayerInfoBlock::writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash)
761{
762 switch (m_header.byteOrder) {
764 writeTxt2BlockExImpl<psd_byte_order::psdLittleEndian>(io, txt2Hash);
765 break;
766 default:
767 writeTxt2BlockExImpl(io, txt2Hash);
768 break;
769 }
770}
771
772template<psd_byte_order byteOrder>
773void PsdAdditionalLayerInfoBlock::writeTxt2BlockExImpl(QIODevice &io, const QVariantHash txt2Hash)
774{
775 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
776 KisAslWriterUtils::writeFixedString<byteOrder>("Txt2", io);
777
778 QByteArray ba = KisCosWriter::writeTxt2FromVariantHash(txt2Hash);
779 quint32 length = ba.size();
780 SAFE_WRITE_EX(byteOrder, io, length);
781 io.write(ba);
782}
783
784template<psd_byte_order byteOrder>
785void PsdAdditionalLayerInfoBlock::writeVectorOriginationDataImpl(QIODevice &io, const QDomDocument &vectorOrigination)
786{
787 KisAslWriterUtils::writeFixedString<byteOrder>("8BIM", io);
788 KisAslWriterUtils::writeFixedString<byteOrder>("vogk", io);
790 try {
791 KisAslWriter writer(byteOrder);
792
793 writer.writeVectorOriginationDataEx(io, vectorOrigination);
794
796 warnKrita << "WARNING: Couldn't save vector stroke layer block:" << PREPEND_METHOD(e.what());
797
798 // TODO: make this error recoverable!
799 throw e;
800 }
801}
qreal length(const QPointF &vec)
Definition Ellipse.cc:82
const Params2D p
static QDomDocument readLfx2PsdSection(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readPsdSectionPattern(QIODevice &device, qint64 bytesLeft, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readTypeToolObjectSettings(QIODevice &device, QTransform &transform, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readVectorOriginationData(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readVectorStroke(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
static QDomDocument readFillLayer(QIODevice &device, psd_byte_order byteOrder=psd_byte_order::psdBigEndian)
void writeFillLayerSectionEx(QIODevice &device, const QDomDocument &doc)
void writePsdLfx2SectionEx(QIODevice &device, const QDomDocument &doc)
void writeVectorStrokeDataEx(QIODevice &device, const QDomDocument &doc)
void writeVectorOriginationDataEx(QIODevice &device, const QDomDocument &doc)
void writeTypeToolObjectSettings(QIODevice &device, const QDomDocument &doc, const QDomDocument &warpDoc, const QTransform tf, const QRectF bounds)
The KisCosParser class.
static QByteArray writeTxt2FromVariantHash(const QVariantHash doc)
static QVariantHash uncompressKeys(QVariantHash doc)
uncompressKeys One of the problems with the Txt2 block is that older versions of photoshop (cs2-cs3 e...
quint16 version
Definition psd_header.h:43
bool tiffStyleLayerBlock
Definition psd_header.h:50
psd_byte_order byteOrder
Definition psd_header.h:49
void writeLfx2BlockEx(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
void writeVectorOriginationDataImpl(QIODevice &io, const QDomDocument &vectorOrigination)
void writeFillLayerBlockEx(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
ExtraLayerInfoBlockHandler m_layerInfoBlockHandler
void writeVmskBlockEx(QIODevice &io, psd_vector_mask mask)
void writeVectorOriginationDataEx(QIODevice &io, const QDomDocument &vectorOrigination)
void writeVectorStrokeDataImpl(QIODevice &io, const QDomDocument &vectorStroke)
void writeLspfBlockEx(QIODevice &io, const bool lock)
void writeVectorMaskImpl(QIODevice &io, psd_vector_mask mask)
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 writeFillLayerBlockExImpl(QIODevice &io, const QDomDocument &fillConfig, psd_fill_type type)
void writeLfx2BlockExImpl(QIODevice &io, const QDomDocument &stylesXmlDoc, bool useLfxsLayerStyleFormat)
PsdAdditionalLayerInfoBlock(const PSDHeader &header)
void writeLclrBlockExImpl(QIODevice &io, const quint16 &lclr)
bool write(QIODevice &io, KisNodeSP node)
void writeLspfBlockExImpl(QIODevice &io, const bool &lock)
void writeLsctBlockExImpl(QIODevice &io, psd_section_type sectionType, bool isPassThrough, const QString &blendModeKey)
void writeLclrBlockEx(QIODevice &io, const quint16 &labelColor)
void writeTxt2BlockEx(QIODevice &io, const QVariantHash txt2Hash)
std::function< bool(QIODevice &)> ExtraLayerInfoBlockHandler
void writePattBlockExImpl(QIODevice &io, const QDomDocument &patternsXmlDoc)
void writePattBlockEx(QIODevice &io, const QDomDocument &patternsXmlDoc)
void writeTxt2BlockExImpl(QIODevice &io, const QVariantHash txt2Hash)
void setExtraLayerInfoBlockHandler(ExtraLayerInfoBlockHandler handler)
void setUserMaskInfoBlockHandler(UserMaskInfoBlockHandler handler)
void writeTypeToolImpl(QIODevice &io, psd_layer_type_shape tool)
void writeLuniBlockExImpl(QIODevice &io, const QString &layerName)
void writeTypeToolBlockEx(QIODevice &io, psd_layer_type_shape typeTool)
std::function< bool(QIODevice &)> UserMaskInfoBlockHandler
#define GARBAGE_VALUE_MARK
#define SAFE_READ_EX(byteOrder, device, varname)
#define SAFE_READ_SIGNATURE_EX(byteOrder, device, varname, expected)
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define bounds(x, a, b)
#define PREPEND_METHOD(msg)
Definition kis_debug.h:180
#define warnKrita
Definition kis_debug.h:94
#define ppVar(var)
Definition kis_debug.h:163
#define dbgFile
Definition kis_debug.h:57
#define SETUP_OFFSET_VERIFIER(name, device, expectedOffset, maxPadding)
psd_section_type
Definition psd.h:144
@ psd_other
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
QList< psd_path_sub_path > subPaths