Krita Source Code Documentation
Loading...
Searching...
No Matches
kis_asl_patterns_writer.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2015 Dmitry Kazakov <dimula73@gmail.com>
3 * SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 */
7
9
10#include <functional>
11
12#include <compression.h>
13#include <kis_debug.h>
14#include <resources/KoPattern.h>
15
18#include "kis_asl_xml_parser.h"
19
20KisAslPatternsWriter::KisAslPatternsWriter(const QDomDocument &doc, QIODevice &device, psd_byte_order byteOrder)
21 : m_doc(doc)
22 , m_device(device)
23 , m_numPatternsWritten(0)
24 , m_byteOrder(byteOrder)
25{
26}
27
29{
31 c.subscribePattern("/Patterns/KisPattern", std::bind(&KisAslPatternsWriter::addPattern, this, std::placeholders::_1));
32 c.subscribePattern("/patterns/KisPattern", std::bind(&KisAslPatternsWriter::addPattern, this, std::placeholders::_1));
33
34 KisAslXmlParser parser;
35 parser.parseXML(m_doc, c);
36}
37
38void sliceQImage(const QImage &image, QVector<QVector<QByteArray>> *dstPlanes, bool *isCompressed)
39{
40 KIS_ASSERT_RECOVER_NOOP(image.format() == QImage::Format_ARGB32);
41
42 QVector<QVector<QByteArray>> uncompressedRows;
43 QVector<QVector<QByteArray>> compressedRows;
44
45 uncompressedRows.resize(3);
46 compressedRows.resize(3);
47
48 int compressedSize = 0;
49
50 for (int i = 0; i < 3; i++) {
51 const int srcRowOffset = 2 - i;
52 const int srcStep = 4;
53 const int dstStep = 1;
54
55 for (int row = 0; row < image.height(); row++) {
56 uncompressedRows[i].append(QByteArray(image.width(), '\0'));
57 quint8 *dstPtr = (quint8 *)uncompressedRows[i].last().data();
58
59 const quint8 *srcPtr = image.constScanLine(row) + srcRowOffset;
60
61 for (int col = 0; col < image.width(); col++) {
62 *dstPtr = *srcPtr;
63
64 srcPtr += srcStep;
65 dstPtr += dstStep;
66 }
67
68 compressedRows[i].append(Compression::compress(uncompressedRows[i].last(), psd_compression_type::RLE));
69 if (compressedRows[i].last().isEmpty()) {
70 throw KisAslWriterUtils::ASLWriteException("Failed to compress pattern plane");
71 }
72
73 compressedSize += compressedRows[i].last().size() + 2; // two bytes for offset tag
74 }
75 }
76
77 if (compressedSize < image.width() * image.height() * 3) {
78 *dstPlanes = compressedRows;
79 *isCompressed = true;
80 } else {
81 *dstPlanes = uncompressedRows;
82 *isCompressed = false;
83 }
84}
85
87{
88 KoPatternSP effectivePattern = pattern;
89
90 if (effectivePattern->hasAlpha()) {
91 effectivePattern = pattern->cloneWithoutAlpha();
92 }
93
94 switch (m_byteOrder) {
96 addPatternImpl<psd_byte_order::psdLittleEndian>(effectivePattern);
97 break;
98 default:
99 addPatternImpl(effectivePattern);
100 break;
101 }
102}
103
104template<psd_byte_order byteOrder>
106{
107 {
109
110 {
111 const quint32 patternVersion = 1;
112 SAFE_WRITE_EX(byteOrder, m_device, patternVersion);
113 }
114
115 {
116 const quint32 patternImageMode = 3;
117 SAFE_WRITE_EX(byteOrder, m_device, patternImageMode);
118 }
119
120 {
121 const quint16 patternHeight = static_cast<quint16>(pattern->height());
122 SAFE_WRITE_EX(byteOrder, m_device, patternHeight);
123 }
124
125 {
126 const quint16 patternWidth = static_cast<quint16>(pattern->width());
127 SAFE_WRITE_EX(byteOrder, m_device, patternWidth);
128 }
129
130 KisAslWriterUtils::writeUnicodeString<byteOrder>(pattern->name(), m_device);
131 KisAslWriterUtils::writePascalString<byteOrder>(KisAslWriterUtils::getPatternUuidLazy(pattern), m_device);
132
133 // Write "Virtual Memory Array List"
134
135 const QRect patternRect(0, 0, pattern->width(), pattern->height());
136
137 {
138 {
139 const quint32 arrayVersion = 3;
140 SAFE_WRITE_EX(byteOrder, m_device, arrayVersion);
141 }
142
144
145 KisAslWriterUtils::writeRect<byteOrder>(patternRect, m_device);
146
147 {
148 // don't ask me why it is called this way...
149 const quint32 numberOfChannels = 24;
150 SAFE_WRITE_EX(byteOrder, m_device, numberOfChannels);
151 }
152
153 KIS_ASSERT_RECOVER_RETURN(patternRect.size() == pattern->pattern().size());
154
155 QVector<QVector<QByteArray>> imagePlanes;
156 bool isCompressed;
157 sliceQImage(pattern->pattern(), &imagePlanes, &isCompressed);
158
159 for (int i = 0; i < 3; i++) {
160 {
161 const quint32 planeIsWritten = 1;
162 SAFE_WRITE_EX(byteOrder, m_device, planeIsWritten);
163 }
164
166
167 {
168 const quint32 pixelDepth1 = 8;
169 SAFE_WRITE_EX(byteOrder, m_device, pixelDepth1);
170 }
171
172 KisAslWriterUtils::writeRect<byteOrder>(patternRect, m_device);
173
174 {
175 // why twice? who knows...
176 const quint16 pixelDepth2 = 8;
177 SAFE_WRITE_EX(byteOrder, m_device, pixelDepth2);
178 }
179
180 {
181 // compress with RLE
182 const quint8 compressionMethod = isCompressed;
183 SAFE_WRITE_EX(byteOrder, m_device, compressionMethod);
184 }
185
186 KIS_ASSERT_RECOVER_RETURN(imagePlanes[i].size() == pattern->pattern().height());
187
188 if (isCompressed) {
189 Q_FOREACH (const QByteArray &compressedRow, imagePlanes[i]) {
190 const quint16 compressionRowSize = static_cast<quint16>(compressedRow.size());
191 SAFE_WRITE_EX(byteOrder, m_device, compressionRowSize);
192 }
193 }
194
195 Q_FOREACH (const QByteArray &rowData, imagePlanes[i]) {
196 const qint64 bytesWritten = m_device.write(rowData);
197 if (bytesWritten != rowData.size()) {
198 throw KisAslWriterUtils::ASLWriteException("Failed to write a compressed pattern plane");
199 }
200 }
201 }
202 }
203 }
204
205 const qint64 currentPos = m_device.pos();
206 const qint64 alignedPos = KisAslWriterUtils::alignOffsetCeil(currentPos, 4);
207
208 if (currentPos != alignedPos) {
209 m_device.seek(alignedPos);
210 }
211
213}
static QByteArray compress(QByteArray bytes, psd_compression_type compressionType, int row_size=0, int color_depth=0)
void subscribePattern(const QString &path, ASLCallbackPattern callback)
void addPattern(const KoPatternSP pattern)
const QDomDocument & m_doc
KisAslPatternsWriter(const QDomDocument &doc, QIODevice &device, psd_byte_order byteOrder)
void addPatternImpl(const KoPatternSP pattern)
void parseXML(const QDomDocument &doc, KisAslObjectCatcher &catcher)
void sliceQImage(const QImage &image, QVector< QVector< QByteArray > > *dstPlanes, bool *isCompressed)
#define SAFE_WRITE_EX(byteOrder, device, varname)
#define KIS_ASSERT_RECOVER_RETURN(cond)
Definition kis_assert.h:75
#define KIS_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:97
QString getPatternUuidLazy(const KoPatternSP pattern)
qint64 alignOffsetCeil(qint64 pos, qint64 alignment)
psd_byte_order
Definition psd.h:33
@ RLE
Definition psd.h:41