Changeset View
Changeset View
Standalone View
Standalone View
libs/pigment/resources/KoColorSet.cpp
Context not available. | |||||
20 | #include <resources/KoColorSet.h> | 20 | #include <resources/KoColorSet.h> | ||
---|---|---|---|---|---|
21 | 21 | | |||
22 | #include <sys/types.h> | 22 | #include <sys/types.h> | ||
23 | #include <QtEndian> // qFromLittleEndian | | |||
24 | 23 | | |||
25 | #include <QImage> | | |||
26 | #include <QPoint> | | |||
27 | #include <QVector> | | |||
28 | #include <QFile> | 24 | #include <QFile> | ||
29 | #include <QTextStream> | | |||
30 | #include <QFileInfo> | 25 | #include <QFileInfo> | ||
31 | #include <QBuffer> | 26 | #include <QBuffer> | ||
27 | #include <QVector> | ||||
28 | #include <QTextStream> | ||||
29 | #include <QTextCodec> | ||||
30 | #include <QHash> | ||||
31 | #include <QList> | ||||
32 | #include <QByteArray> | 32 | #include <QByteArray> | ||
33 | #include <QPainter> | | |||
34 | #include <QDomDocument> | 33 | #include <QDomDocument> | ||
35 | #include <QDomElement> | 34 | #include <QDomElement> | ||
36 | #include <QDomNodeList> | 35 | #include <QDomNodeList> | ||
37 | #include <QXmlStreamReader> | 36 | #include <QString> | ||
38 | #include <QTextCodec> | | |||
39 | #include <QMap> | | |||
40 | #include <QHash> | | |||
41 | #include <QStringList> | 37 | #include <QStringList> | ||
38 | #include <QImage> | ||||
39 | #include <QPainter> | ||||
40 | #include <QByteArray> | ||||
41 | #include <QXmlStreamReader> | ||||
42 | #include <QXmlStreamAttributes> | ||||
43 | #include <QtEndian> // qFromLittleEndian | ||||
42 | 44 | | |||
43 | #include <DebugPigment.h> | 45 | #include <DebugPigment.h> | ||
44 | #include <klocalizedstring.h> | 46 | #include <klocalizedstring.h> | ||
45 | 47 | | |||
46 | #include <KoStore.h> | 48 | #include <KoStore.h> | ||
47 | #include "KoColor.h" | 49 | #include <KoColor.h> | ||
48 | #include "KoColorSetEntry.h" | 50 | #include <KoColorSpace.h> | ||
49 | #include "KoColorProfile.h" | 51 | #include <KoColorSpaceRegistry.h> | ||
50 | #include "KoColorSpaceRegistry.h" | 52 | #include <KoColorProfile.h> | ||
51 | #include "KoColorModelStandardIds.h" | 53 | #include <KoColorProfile.h> | ||
52 | 54 | #include <KoColorModelStandardIds.h> | |||
53 | 55 | #include "KisSwatch.h" | |||
54 | struct KoColorSet::Private { | 56 | | ||
55 | KoColorSet::PaletteType paletteType; | 57 | #include "KoColorSet.h" | ||
56 | QByteArray data; | 58 | #include "KoColorSet_p.h" | ||
57 | QString comment; | 59 | | ||
58 | qint32 columns {0}; // Set the default value that the GIMP uses... | 60 | const QString KoColorSet::GLOBAL_GROUP_NAME = QString(); | ||
59 | QVector<KoColorSetEntry> colors; //ungrouped colors | 61 | const QString KoColorSet::KPL_VERSION_ATTR = "version"; | ||
60 | QStringList groupNames; //names of the groups, this is used to determine the order they are in. | 62 | const QString KoColorSet::KPL_GROUP_ROW_COUNT_ATTR = "rows"; | ||
61 | QMap<QString, QVector<KoColorSetEntry>> groups; //grouped colors. | 63 | const QString KoColorSet::KPL_PALETTE_COLUMN_COUNT_ATTR = "columns"; | ||
62 | }; | 64 | const QString KoColorSet::KPL_PALETTE_NAME_ATTR = "name"; | ||
63 | 65 | const QString KoColorSet::KPL_PALETTE_COMMENT_ATTR = "comment"; | |||
64 | KoColorSet::PaletteType detectFormat(const QString &fileName, const QByteArray &ba) { | 66 | const QString KoColorSet::KPL_PALETTE_FILENAME_ATTR = "filename"; | ||
65 | 67 | const QString KoColorSet::KPL_PALETTE_READONLY_ATTR = "readonly"; | |||
66 | QFileInfo fi(fileName); | 68 | const QString KoColorSet::KPL_COLOR_MODEL_ID_ATTR = "colorModelId"; | ||
67 | 69 | const QString KoColorSet::KPL_COLOR_DEPTH_ID_ATTR = "colorDepthId"; | |||
68 | // .pal | 70 | const QString KoColorSet::KPL_GROUP_NAME_ATTR = "name"; | ||
69 | if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { | 71 | const QString KoColorSet::KPL_SWATCH_ROW_ATTR = "row"; | ||
70 | return KoColorSet::RIFF_PAL; | 72 | const QString KoColorSet::KPL_SWATCH_COL_ATTR = "column"; | ||
71 | } | 73 | const QString KoColorSet::KPL_SWATCH_NAME_ATTR = "name"; | ||
72 | // .gpl | 74 | const QString KoColorSet::KPL_SWATCH_ID_ATTR = "id"; | ||
73 | else if (ba.startsWith("GIMP Palette")) { | 75 | const QString KoColorSet::KPL_SWATCH_SPOT_ATTR = "spot"; | ||
74 | return KoColorSet::GPL; | 76 | const QString KoColorSet::KPL_SWATCH_BITDEPTH_ATTR = "bitdepth"; | ||
75 | } | 77 | const QString KoColorSet::KPL_PALETTE_PROFILE_TAG = "Profile"; | ||
76 | // .pal | 78 | const QString KoColorSet::KPL_SWATCH_POS_TAG = "Position"; | ||
77 | else if (ba.startsWith("JASC-PAL")) { | 79 | const QString KoColorSet::KPL_SWATCH_TAG = "ColorSetEntry"; | ||
78 | return KoColorSet::PSP_PAL; | 80 | const QString KoColorSet::KPL_GROUP_TAG = "Group"; | ||
79 | } | 81 | const QString KoColorSet::KPL_PALETTE_TAG = "ColorSet"; | ||
80 | else if (fi.suffix().toLower() == "aco") { | | |||
81 | return KoColorSet::ACO; | | |||
82 | } | | |||
83 | else if (fi.suffix().toLower() == "act") { | | |||
84 | return KoColorSet::ACT; | | |||
85 | } | | |||
86 | else if (fi.suffix().toLower() == "xml") { | | |||
87 | return KoColorSet::XML; | | |||
88 | } | | |||
89 | else if (fi.suffix().toLower() == "kpl") { | | |||
90 | return KoColorSet::KPL; | | |||
91 | } | | |||
92 | else if (fi.suffix().toLower() == "sbz") { | | |||
93 | return KoColorSet::SBZ; | | |||
94 | } | | |||
95 | return KoColorSet::UNKNOWN; | | |||
96 | } | | |||
97 | 82 | | |||
98 | KoColorSet::KoColorSet(const QString& filename) | 83 | KoColorSet::KoColorSet(const QString& filename) | ||
99 | : KoResource(filename) | 84 | : KoResource(filename) | ||
100 | , d(new Private()) | 85 | , d(new Private(this)) | ||
101 | { | 86 | { | ||
102 | } | 87 | if (!filename.isEmpty()) { | ||
103 | 88 | QFileInfo f(filename); | |||
104 | KoColorSet::KoColorSet() | 89 | setIsEditable(f.isWritable()); | ||
105 | : KoResource(QString()) | 90 | } | ||
106 | , d(new Private()) | | |||
107 | { | | |||
108 | | ||||
109 | } | 91 | } | ||
110 | 92 | | |||
111 | /// Create an copied palette | 93 | /// Create an copied palette | ||
112 | KoColorSet::KoColorSet(const KoColorSet& rhs) | 94 | KoColorSet::KoColorSet(const KoColorSet& rhs) | ||
113 | : QObject(0) | 95 | : QObject(Q_NULLPTR) | ||
114 | , KoResource(QString()) | 96 | , KoResource(rhs) | ||
115 | , d(new Private()) | 97 | , d(new Private(this)) | ||
116 | { | 98 | { | ||
117 | setFilename(rhs.filename()); | 99 | d->paletteType = rhs.d->paletteType; | ||
100 | d->data = rhs.d->data; | ||||
118 | d->comment = rhs.d->comment; | 101 | d->comment = rhs.d->comment; | ||
119 | d->columns = rhs.d->columns; | | |||
120 | d->colors = rhs.d->colors; | | |||
121 | d->groupNames = rhs.d->groupNames; | 102 | d->groupNames = rhs.d->groupNames; | ||
122 | d->groups = rhs.d->groups; | 103 | d->groups = rhs.d->groups; | ||
123 | setValid(true); | 104 | d->isGlobal = rhs.d->isGlobal; | ||
105 | d->isEditable = rhs.d->isEditable; | ||||
124 | } | 106 | } | ||
125 | 107 | | |||
126 | KoColorSet::~KoColorSet() | 108 | KoColorSet::~KoColorSet() | ||
127 | { | 109 | { } | ||
128 | } | | |||
129 | 110 | | |||
130 | bool KoColorSet::load() | 111 | bool KoColorSet::load() | ||
131 | { | 112 | { | ||
Context not available. | |||||
135 | warnPigment << "Can't open file " << filename(); | 116 | warnPigment << "Can't open file " << filename(); | ||
136 | return false; | 117 | return false; | ||
137 | } | 118 | } | ||
138 | bool res = loadFromDevice(&file); | 119 | bool res = loadFromDevice(&file); | ||
139 | file.close(); | 120 | file.close(); | ||
121 | if (!QFileInfo(filename()).isWritable()) { | ||||
122 | setIsEditable(false); | ||||
123 | } | ||||
140 | return res; | 124 | return res; | ||
141 | } | 125 | } | ||
142 | 126 | | |||
Context not available. | |||||
148 | 132 | | |||
149 | Q_ASSERT(d->data.size() != 0); | 133 | Q_ASSERT(d->data.size() != 0); | ||
150 | 134 | | |||
151 | return init(); | 135 | return d->init(); | ||
152 | } | 136 | } | ||
153 | 137 | | |||
154 | 138 | | |||
155 | bool KoColorSet::save() | 139 | bool KoColorSet::save() | ||
156 | { | 140 | { | ||
157 | QFile file(filename()); | 141 | if (d->isGlobal) { | ||
158 | if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { | 142 | // save to resource dir | ||
159 | return false; | 143 | QFile file(filename()); | ||
144 | if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { | ||||
145 | return false; | ||||
146 | } | ||||
147 | saveToDevice(&file); | ||||
148 | file.close(); | ||||
149 | return true; | ||||
150 | } else { | ||||
151 | return true; // palette is not global, but still indicate that it's saved | ||||
160 | } | 152 | } | ||
161 | saveToDevice(&file); | | |||
162 | file.close(); | | |||
163 | return true; | | |||
164 | } | 153 | } | ||
165 | 154 | | |||
166 | bool KoColorSet::saveToDevice(QIODevice *dev) const | 155 | bool KoColorSet::saveToDevice(QIODevice *dev) const | ||
Context not available. | |||||
168 | bool res; | 157 | bool res; | ||
169 | switch(d->paletteType) { | 158 | switch(d->paletteType) { | ||
170 | case GPL: | 159 | case GPL: | ||
171 | res = saveGpl(dev); | 160 | res = d->saveGpl(dev); | ||
172 | break; | 161 | break; | ||
173 | default: | 162 | default: | ||
174 | res = saveKpl(dev); | 163 | res = d->saveKpl(dev); | ||
175 | } | 164 | } | ||
176 | if (res) { | 165 | if (res) { | ||
177 | KoResource::saveToDevice(dev); | 166 | KoResource::saveToDevice(dev); | ||
Context not available. | |||||
179 | return res; | 168 | return res; | ||
180 | } | 169 | } | ||
181 | 170 | | |||
182 | bool KoColorSet::init() | 171 | QByteArray KoColorSet::toByteArray() const | ||
183 | { | 172 | { | ||
184 | d->colors.clear(); // just in case this is a reload (eg by KoEditColorSetDialog), | 173 | QBuffer s; | ||
185 | d->groups.clear(); | 174 | s.open(QIODevice::WriteOnly); | ||
186 | d->groupNames.clear(); | 175 | if (!saveToDevice(&s)) { | ||
187 | 176 | warnPigment << "saving palette failed:" << name(); | |||
188 | if (filename().isNull()) { | 177 | return QByteArray(); | ||
189 | warnPigment << "Cannot load palette" << name() << "there is no filename set"; | | |||
190 | return false; | | |||
191 | } | 178 | } | ||
192 | if (d->data.isNull()) { | 179 | s.close(); | ||
193 | QFile file(filename()); | 180 | s.open(QIODevice::ReadOnly); | ||
194 | if (file.size() == 0) { | 181 | QByteArray res = s.readAll(); | ||
195 | warnPigment << "Cannot load palette" << name() << "there is no data available"; | 182 | s.close(); | ||
196 | return false; | | |||
197 | } | | |||
198 | file.open(QIODevice::ReadOnly); | | |||
199 | d->data = file.readAll(); | | |||
200 | file.close(); | | |||
201 | } | | |||
202 | | ||||
203 | bool res = false; | | |||
204 | d->paletteType = detectFormat(filename(), d->data); | | |||
205 | switch(d->paletteType) { | | |||
206 | case GPL: | | |||
207 | res = loadGpl(); | | |||
208 | break; | | |||
209 | case ACT: | | |||
210 | res = loadAct(); | | |||
211 | break; | | |||
212 | case RIFF_PAL: | | |||
213 | res = loadRiff(); | | |||
214 | break; | | |||
215 | case PSP_PAL: | | |||
216 | res = loadPsp(); | | |||
217 | break; | | |||
218 | case ACO: | | |||
219 | res = loadAco(); | | |||
220 | break; | | |||
221 | case XML: | | |||
222 | res = loadXml(); | | |||
223 | break; | | |||
224 | case KPL: | | |||
225 | res = loadKpl(); | | |||
226 | break; | | |||
227 | case SBZ: | | |||
228 | res = loadSbz(); | | |||
229 | break; | | |||
230 | default: | | |||
231 | res = false; | | |||
232 | } | | |||
233 | setValid(res); | | |||
234 | | ||||
235 | if (d->columns == 0) { | | |||
236 | d->columns = 10; | | |||
237 | } | | |||
238 | | ||||
239 | QImage img(d->columns * 4, (d->colors.size() / d->columns) * 4, QImage::Format_ARGB32); | | |||
240 | QPainter gc(&img); | | |||
241 | gc.fillRect(img.rect(), Qt::darkGray); | | |||
242 | int counter = 0; | | |||
243 | for(int i = 0; i < d->columns; ++i) { | | |||
244 | for (int j = 0; j < (d->colors.size() / d->columns); ++j) { | | |||
245 | if (counter < d->colors.size()) { | | |||
246 | QColor c = d->colors.at(counter).color().toQColor(); | | |||
247 | gc.fillRect(i * 4, j * 4, 4, 4, c); | | |||
248 | counter++; | | |||
249 | } | | |||
250 | else { | | |||
251 | break; | | |||
252 | } | | |||
253 | } | | |||
254 | } | | |||
255 | setImage(img); | | |||
256 | | ||||
257 | // save some memory | | |||
258 | d->data.clear(); | | |||
259 | return res; | 183 | return res; | ||
260 | } | 184 | } | ||
261 | 185 | | |||
262 | bool KoColorSet::saveGpl(QIODevice *dev) const | 186 | bool KoColorSet::fromByteArray(QByteArray &data) | ||
263 | { | 187 | { | ||
264 | QTextStream stream(dev); | 188 | QBuffer buf(&data); | ||
265 | stream << "GIMP Palette\nName: " << name() << "\nColumns: " << d->columns << "\n#\n"; | 189 | buf.open(QIODevice::ReadOnly); | ||
266 | 190 | return loadFromDevice(&buf); | |||
267 | for (int i = 0; i < d->colors.size(); i++) { | | |||
268 | const KoColorSetEntry& entry = d->colors.at(i); | | |||
269 | QColor c = entry.color().toQColor(); | | |||
270 | stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; | | |||
271 | if (entry.name().isEmpty()) | | |||
272 | stream << "Untitled\n"; | | |||
273 | else | | |||
274 | stream << entry.name() << "\n"; | | |||
275 | } | | |||
276 | | ||||
277 | return true; | | |||
278 | } | 191 | } | ||
279 | 192 | | |||
280 | quint32 KoColorSet::nColors() | 193 | KoColorSet::PaletteType KoColorSet::paletteType() const | ||
281 | { | 194 | { | ||
282 | if (d->colors.isEmpty()) return 0; | 195 | return d->paletteType; | ||
283 | | ||||
284 | quint32 total = d->colors.size(); | | |||
285 | if (!d->groups.empty()) { | | |||
286 | Q_FOREACH (const QVector<KoColorSetEntry> &group, d->groups.values()) { | | |||
287 | total += group.size(); | | |||
288 | } | | |||
289 | } | | |||
290 | return total; | | |||
291 | } | 196 | } | ||
292 | 197 | | |||
293 | quint32 KoColorSet::nColorsGroup(QString groupName) { | 198 | void KoColorSet::setPaletteType(PaletteType paletteType) | ||
294 | if (d->groups.contains(groupName)) { | | |||
295 | return d->groups.value(groupName).size(); | | |||
296 | } else if (groupName.isEmpty() && !d->colors.isEmpty()){ | | |||
297 | return d->colors.size(); | | |||
298 | } else { | | |||
299 | return 0; | | |||
300 | } | | |||
301 | } | | |||
302 | | ||||
303 | quint32 KoColorSet::getIndexClosestColor(const KoColor color, bool useGivenColorSpace) | | |||
304 | { | 199 | { | ||
305 | quint32 closestIndex = 0; | 200 | d->paletteType = paletteType; | ||
306 | quint8 highestPercentage = 0; | | |||
307 | quint8 testPercentage = 0; | | |||
308 | KoColor compare = color; | | |||
309 | for (quint32 i=0; i<nColors(); i++) { | | |||
310 | KoColor entry = getColorGlobal(i).color(); | | |||
311 | if (useGivenColorSpace == true && compare.colorSpace() != entry.colorSpace()) { | | |||
312 | entry.convertTo(compare.colorSpace()); | | |||
313 | | ||||
314 | } else if(compare.colorSpace()!=entry.colorSpace()) { | | |||
315 | compare.convertTo(entry.colorSpace()); | | |||
316 | } | | |||
317 | testPercentage = (255 - compare.colorSpace()->difference(compare.data(), entry.data())); | | |||
318 | if (testPercentage>highestPercentage) | | |||
319 | { | | |||
320 | closestIndex = i; | | |||
321 | highestPercentage = testPercentage; | | |||
322 | } | | |||
323 | } | | |||
324 | return closestIndex; | | |||
325 | } | 201 | } | ||
326 | 202 | | |||
327 | QString KoColorSet::closestColorName(const KoColor color, bool useGivenColorSpace) | | |||
328 | { | | |||
329 | int i = getIndexClosestColor(color, useGivenColorSpace); | | |||
330 | return getColorGlobal(i).name(); | | |||
331 | } | | |||
332 | 203 | | |||
333 | void KoColorSet::add(const KoColorSetEntry & c, QString groupName) | 204 | quint32 KoColorSet::colorCount() const | ||
334 | { | 205 | { | ||
335 | if (d->groups.contains(groupName) || d->groupNames.contains(groupName)) { | 206 | return d->groups[GLOBAL_GROUP_NAME].colorCount(); | ||
336 | d->groups[groupName].push_back(c); | | |||
337 | } else { | | |||
338 | d->colors.push_back(c); | | |||
339 | } | | |||
340 | } | 207 | } | ||
341 | 208 | | |||
342 | quint32 KoColorSet::insertBefore(const KoColorSetEntry &c, qint32 index, const QString &groupName) | 209 | void KoColorSet::add(const KisSwatch &c, const QString &groupName) | ||
343 | { | 210 | { | ||
344 | quint32 newIndex = index; | 211 | KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) | ||
345 | if (d->groups.contains(groupName)) { | 212 | ? d->groups[groupName] : d->global(); | ||
346 | d->groups[groupName].insert(index, c); | 213 | modifiedGroup.addEntry(c); | ||
347 | } else if (groupName.isEmpty()){ | | |||
348 | d->colors.insert(index, c); | | |||
349 | } else { | | |||
350 | warnPigment << "Couldn't find group to insert to"; | | |||
351 | } | | |||
352 | return newIndex; | | |||
353 | } | 214 | } | ||
354 | 215 | | |||
355 | void KoColorSet::removeAt(quint32 index, QString groupName) | 216 | void KoColorSet::setEntry(const KisSwatch &e, int x, int y, const QString &groupName) | ||
356 | { | 217 | { | ||
357 | if (d->groups.contains(groupName)){ | 218 | KisSwatchGroup &modifiedGroup = d->groups.contains(groupName) | ||
358 | if ((quint32)d->groups.value(groupName).size()>index) { | 219 | ? d->groups[groupName] : d->global(); | ||
359 | d->groups[groupName].remove(index); | 220 | modifiedGroup.setEntry(e, x, y); | ||
360 | } | | |||
361 | } else { | | |||
362 | if ((quint32)d->colors.size()>index) { | | |||
363 | d->colors.remove(index); | | |||
364 | } | | |||
365 | } | | |||
366 | } | 221 | } | ||
367 | 222 | | |||
368 | void KoColorSet::clear() | 223 | void KoColorSet::clear() | ||
369 | { | 224 | { | ||
370 | d->colors.clear(); | | |||
371 | d->groups.clear(); | 225 | d->groups.clear(); | ||
226 | d->groupNames.clear(); | ||||
227 | d->groups[GLOBAL_GROUP_NAME] = KisSwatchGroup(); | ||||
228 | d->groupNames.append(GLOBAL_GROUP_NAME); | ||||
372 | } | 229 | } | ||
373 | 230 | | |||
374 | KoColorSetEntry KoColorSet::getColorGlobal(quint32 index) | 231 | KisSwatch KoColorSet::getColorGlobal(quint32 x, quint32 y) const | ||
375 | { | | |||
376 | KoColorSetEntry e; | | |||
377 | quint32 groupIndex = index; | | |||
378 | QString groupName = findGroupByGlobalIndex(index, &groupIndex); | | |||
379 | e = getColorGroup(groupIndex, groupName); | | |||
380 | return e; | | |||
381 | } | | |||
382 | | ||||
383 | KoColorSetEntry KoColorSet::getColorGroup(quint32 index, QString groupName) | | |||
384 | { | 232 | { | ||
385 | KoColorSetEntry e; | 233 | int yInGroup = y; | ||
386 | if (d->groups.contains(groupName)) { | 234 | QString nameGroupFoundIn; | ||
387 | if (nColorsGroup(groupName)>index) { | 235 | for (const QString &groupName : d->groupNames) { | ||
388 | e = d->groups.value(groupName).at(index); | 236 | if (yInGroup < d->groups[groupName].rowCount()) { | ||
389 | } else { | 237 | nameGroupFoundIn = groupName; | ||
390 | warnPigment<<index<<"is above"<<nColorsGroup(groupName)<<"of"<<groupName; | 238 | break; | ||
391 | } | | |||
392 | } else if (groupName.isEmpty() || groupName == QString()) { | | |||
393 | if (nColorsGroup(groupName)>index) { | | |||
394 | e = d->colors.at(index); | | |||
395 | } else { | 239 | } else { | ||
396 | warnPigment<<index<<"is above the size of the default group:"<<nColorsGroup(groupName); | 240 | yInGroup -= d->groups[groupName].rowCount(); | ||
397 | } | | |||
398 | } else { | | |||
399 | warnPigment << "Color group "<<groupName<<" not found"; | | |||
400 | } | | |||
401 | return e; | | |||
402 | } | | |||
403 | | ||||
404 | QString KoColorSet::findGroupByGlobalIndex(quint32 globalIndex, quint32 *index) | | |||
405 | { | | |||
406 | *index = globalIndex; | | |||
407 | QString groupName = QString(); | | |||
408 | if ((quint32)d->colors.size()<=*index) { | | |||
409 | *index -= (quint32)d->colors.size(); | | |||
410 | if (!d->groups.empty() || !d->groupNames.empty()) { | | |||
411 | QStringList groupNames = getGroupNames(); | | |||
412 | Q_FOREACH (QString name, groupNames) { | | |||
413 | quint32 size = (quint32)d->groups.value(name).size(); | | |||
414 | if (size<=*index) { | | |||
415 | *index -= size; | | |||
416 | } else { | | |||
417 | groupName = name; | | |||
418 | return groupName; | | |||
419 | } | | |||
420 | } | | |||
421 | | ||||
422 | } | 241 | } | ||
423 | } | 242 | } | ||
424 | return groupName; | 243 | const KisSwatchGroup &groupFoundIn = nameGroupFoundIn == GLOBAL_GROUP_NAME | ||
244 | ? d->global() : d->groups[nameGroupFoundIn]; | ||||
245 | Q_ASSERT(groupFoundIn.checkEntry(x, yInGroup)); | ||||
246 | return groupFoundIn.getEntry(x, yInGroup); | ||||
425 | } | 247 | } | ||
426 | 248 | | |||
427 | QString KoColorSet::findGroupByColorName(const QString &name, quint32 *index) | 249 | KisSwatch KoColorSet::getColorGroup(quint32 x, quint32 y, QString groupName) | ||
428 | { | 250 | { | ||
429 | *index = 0; | 251 | KisSwatch e; | ||
430 | QString groupName = QString(); | 252 | const KisSwatchGroup &sourceGroup = groupName == QString() | ||
431 | for (int i = 0; i<d->colors.size(); i++) { | 253 | ? d->global() : d->groups[groupName]; | ||
432 | if(d->colors.at(i).name() == name) { | 254 | if (sourceGroup.checkEntry(x, y)) { | ||
433 | *index = (quint32)i; | 255 | e = sourceGroup.getEntry(x, y); | ||
434 | return groupName; | | |||
435 | } | | |||
436 | } | | |||
437 | QStringList groupNames = getGroupNames(); | | |||
438 | Q_FOREACH (QString name, groupNames) { | | |||
439 | for (int i=0; i<d->groups[name].size(); i++) { | | |||
440 | if(d->groups[name].at(i).name() == name) { | | |||
441 | *index = (quint32)i; | | |||
442 | groupName = name; | | |||
443 | return groupName; | | |||
444 | } | | |||
445 | } | | |||
446 | } | | |||
447 | return groupName; | | |||
448 | } | | |||
449 | | ||||
450 | QString KoColorSet::findGroupByID(const QString &id, quint32 *index) { | | |||
451 | *index = 0; | | |||
452 | QString groupName = QString(); | | |||
453 | for (int i = 0; i<d->colors.size(); i++) { | | |||
454 | if(d->colors.at(i).id() == id) { | | |||
455 | *index = (quint32)i; | | |||
456 | return groupName; | | |||
457 | } | | |||
458 | } | | |||
459 | QStringList groupNames = getGroupNames(); | | |||
460 | Q_FOREACH (QString name, groupNames) { | | |||
461 | for (int i=0; i<d->groups[name].size(); i++) { | | |||
462 | if(d->groups[name].at(i).id() == id) { | | |||
463 | *index = (quint32)i; | | |||
464 | groupName = name; | | |||
465 | return groupName; | | |||
466 | } | | |||
467 | } | | |||
468 | } | 256 | } | ||
469 | return groupName; | 257 | return e; | ||
470 | } | 258 | } | ||
471 | 259 | | |||
472 | QStringList KoColorSet::getGroupNames() | 260 | QStringList KoColorSet::getGroupNames() | ||
473 | { | 261 | { | ||
474 | if (d->groupNames.size()<d->groups.size()) { | 262 | if (d->groupNames.size() != d->groups.size()) { | ||
475 | warnPigment << "mismatch between groups and the groupnames list."; | 263 | warnPigment << "mismatch between groups and the groupnames list."; | ||
476 | return QStringList(d->groups.keys()); | 264 | return QStringList(d->groups.keys()); | ||
477 | } | 265 | } | ||
478 | return d->groupNames; | 266 | return d->groupNames; | ||
479 | } | 267 | } | ||
480 | 268 | | |||
481 | bool KoColorSet::changeGroupName(QString oldGroupName, QString newGroupName) | 269 | bool KoColorSet::changeGroupName(const QString &oldGroupName, const QString &newGroupName) | ||
482 | { | 270 | { | ||
483 | if (d->groupNames.contains(oldGroupName)==false) { | 271 | if (!d->groups.contains(oldGroupName)) { | ||
484 | return false; | 272 | return false; | ||
485 | } | 273 | } | ||
486 | QVector<KoColorSetEntry> dummyList = d->groups.value(oldGroupName); | 274 | if (oldGroupName == newGroupName) { | ||
275 | return true; | ||||
276 | } | ||||
277 | d->groups[newGroupName] = d->groups[oldGroupName]; | ||||
487 | d->groups.remove(oldGroupName); | 278 | d->groups.remove(oldGroupName); | ||
488 | d->groups[newGroupName] = dummyList; | 279 | d->groups[newGroupName].setName(newGroupName); | ||
489 | //rename the string in the stringlist; | 280 | //rename the string in the stringlist; | ||
490 | int index = d->groupNames.indexOf(oldGroupName); | 281 | int index = d->groupNames.indexOf(oldGroupName); | ||
491 | d->groupNames.replace(index, newGroupName); | 282 | d->groupNames.replace(index, newGroupName); | ||
492 | return true; | 283 | return true; | ||
493 | } | 284 | } | ||
494 | 285 | | |||
495 | bool KoColorSet::changeColorSetEntry(KoColorSetEntry entry, QString groupName, quint32 index) | | |||
496 | { | | |||
497 | if (index>=nColorsGroup(groupName) || (d->groupNames.contains(groupName)==false && groupName.size()>0)) { | | |||
498 | return false; | | |||
499 | } | | |||
500 | | ||||
501 | if (groupName==QString()) { | | |||
502 | d->colors[index] = entry; | | |||
503 | } else { | | |||
504 | d->groups[groupName][index] = entry; | | |||
505 | } | | |||
506 | return true; | | |||
507 | } | | |||
508 | | ||||
509 | void KoColorSet::setColumnCount(int columns) | 286 | void KoColorSet::setColumnCount(int columns) | ||
510 | { | 287 | { | ||
511 | d->columns = columns; | 288 | d->groups[GLOBAL_GROUP_NAME].setColumnCount(columns); | ||
289 | for (KisSwatchGroup &g : d->groups.values()) { | ||||
290 | g.setColumnCount(columns); | ||||
291 | } | ||||
512 | } | 292 | } | ||
513 | 293 | | |||
514 | int KoColorSet::columnCount() | 294 | int KoColorSet::columnCount() const | ||
515 | { | 295 | { | ||
516 | return d->columns; | 296 | return d->groups[GLOBAL_GROUP_NAME].columnCount(); | ||
517 | } | 297 | } | ||
518 | 298 | | |||
519 | QString KoColorSet::comment() | 299 | QString KoColorSet::comment() | ||
Context not available. | |||||
532 | return false; | 312 | return false; | ||
533 | } | 313 | } | ||
534 | d->groupNames.append(groupName); | 314 | d->groupNames.append(groupName); | ||
535 | d->groups[groupName] = QVector<KoColorSetEntry>(); | 315 | d->groups[groupName] = KisSwatchGroup(); | ||
316 | d->groups[groupName].setName(groupName); | ||||
536 | return true; | 317 | return true; | ||
537 | } | 318 | } | ||
538 | 319 | | |||
Context not available. | |||||
541 | if (d->groupNames.contains(groupName)==false || d->groupNames.contains(groupNameInsertBefore)==false) { | 322 | if (d->groupNames.contains(groupName)==false || d->groupNames.contains(groupNameInsertBefore)==false) { | ||
542 | return false; | 323 | return false; | ||
543 | } | 324 | } | ||
544 | d->groupNames.removeAt(d->groupNames.indexOf(groupName)); | 325 | if (groupNameInsertBefore != GLOBAL_GROUP_NAME && groupName != GLOBAL_GROUP_NAME) { | ||
545 | int index = d->groupNames.size(); | 326 | d->groupNames.removeAt(d->groupNames.indexOf(groupName)); | ||
546 | if (groupNameInsertBefore!=QString()) { | 327 | int index = d->groupNames.indexOf(groupNameInsertBefore); | ||
547 | index = d->groupNames.indexOf(groupNameInsertBefore); | 328 | d->groupNames.insert(index, groupName); | ||
548 | } | 329 | } | ||
549 | d->groupNames.insert(index, groupName); | | |||
550 | return true; | 330 | return true; | ||
551 | } | 331 | } | ||
552 | 332 | | |||
Context not available. | |||||
555 | if (!d->groups.contains(groupName)) { | 335 | if (!d->groups.contains(groupName)) { | ||
556 | return false; | 336 | return false; | ||
557 | } | 337 | } | ||
558 | if (keepColors) { | 338 | | ||
559 | for (int i = 0; i<d->groups.value(groupName).size(); i++) { | 339 | if (groupName == GLOBAL_GROUP_NAME) { | ||
560 | d->colors.append(d->groups.value(groupName).at(i)); | 340 | return false; | ||
561 | } | | |||
562 | } | 341 | } | ||
563 | for(int n = 0; n<d->groupNames.size(); n++) { | 342 | | ||
564 | if (d->groupNames.at(n) == groupName) { | 343 | if (keepColors) { | ||
565 | d->groupNames.removeAt(n); | 344 | // put all colors directly below global | ||
345 | int startingRow = d->groups[GLOBAL_GROUP_NAME].rowCount(); | ||||
346 | for (const KisSwatchGroup::SwatchInfo &info : d->groups[groupName].infoList()) { | ||||
347 | d->groups[GLOBAL_GROUP_NAME].setEntry(info.swatch, | ||||
348 | info.column, | ||||
349 | info.row + startingRow); | ||||
566 | } | 350 | } | ||
567 | } | 351 | } | ||
568 | 352 | | |||
353 | d->groupNames.removeAt(d->groupNames.indexOf(groupName)); | ||||
569 | d->groups.remove(groupName); | 354 | d->groups.remove(groupName); | ||
570 | return true; | 355 | return true; | ||
571 | } | 356 | } | ||
Context not available. | |||||
576 | } | 361 | } | ||
577 | 362 | | |||
578 | 363 | | |||
579 | bool KoColorSet::loadGpl() | 364 | int KoColorSet::rowCount() const | ||
580 | { | 365 | { | ||
581 | QString s = QString::fromUtf8(d->data.data(), d->data.count()); | 366 | int res = 0; | ||
582 | 367 | for (const QString &name : d->groupNames) { | |||
583 | if (s.isEmpty() || s.isNull() || s.length() < 50) { | 368 | res += d->groups[name].rowCount(); | ||
584 | warnPigment << "Illegal Gimp palette file: " << filename(); | | |||
585 | return false; | | |||
586 | } | 369 | } | ||
587 | 370 | return res; | |||
588 | quint32 index = 0; | | |||
589 | | ||||
590 | QStringList lines = s.split('\n', QString::SkipEmptyParts); | | |||
591 | | ||||
592 | if (lines.size() < 3) { | | |||
593 | warnPigment << "Not enough lines in palette file: " << filename(); | | |||
594 | return false; | | |||
595 | } | | |||
596 | | ||||
597 | QString columns; | | |||
598 | qint32 r, g, b; | | |||
599 | KoColorSetEntry e; | | |||
600 | | ||||
601 | // Read name | | |||
602 | | ||||
603 | | ||||
604 | if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { | | |||
605 | warnPigment << "Illegal Gimp palette file: " << filename(); | | |||
606 | return false; | | |||
607 | } | | |||
608 | | ||||
609 | setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); | | |||
610 | | ||||
611 | index = 2; | | |||
612 | | ||||
613 | // Read columns | | |||
614 | if (lines[index].toLower().contains("columns")) { | | |||
615 | columns = lines[index].split(":")[1].trimmed(); | | |||
616 | d->columns = columns.toInt(); | | |||
617 | index = 3; | | |||
618 | } | | |||
619 | | ||||
620 | | ||||
621 | for (qint32 i = index; i < lines.size(); i++) { | | |||
622 | | ||||
623 | if (lines[i].startsWith('#')) { | | |||
624 | d->comment += lines[i].mid(1).trimmed() + ' '; | | |||
625 | } else if (!lines[i].isEmpty()) { | | |||
626 | QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); | | |||
627 | | ||||
628 | if (a.count() < 3) { | | |||
629 | break; | | |||
630 | } | | |||
631 | | ||||
632 | r = a[0].toInt(); | | |||
633 | a.pop_front(); | | |||
634 | g = a[0].toInt(); | | |||
635 | a.pop_front(); | | |||
636 | b = a[0].toInt(); | | |||
637 | a.pop_front(); | | |||
638 | | ||||
639 | r = qBound(0, r, 255); | | |||
640 | g = qBound(0, g, 255); | | |||
641 | b = qBound(0, b, 255); | | |||
642 | | ||||
643 | e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); | | |||
644 | | ||||
645 | QString name = a.join(" "); | | |||
646 | e.setName(name.isEmpty() ? i18n("Untitled") : name); | | |||
647 | | ||||
648 | add(e); | | |||
649 | } | | |||
650 | } | | |||
651 | return true; | | |||
652 | } | 371 | } | ||
653 | 372 | | |||
654 | bool KoColorSet::loadAct() | 373 | KisSwatchGroup *KoColorSet::getGroup(const QString &name) | ||
655 | { | 374 | { | ||
656 | QFileInfo info(filename()); | 375 | if (!d->groups.contains(name)) { | ||
657 | setName(info.baseName()); | 376 | return Q_NULLPTR; | ||
658 | KoColorSetEntry e; | | |||
659 | for (int i = 0; i < d->data.size(); i += 3) { | | |||
660 | quint8 r = d->data[i]; | | |||
661 | quint8 g = d->data[i+1]; | | |||
662 | quint8 b = d->data[i+2]; | | |||
663 | e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); | | |||
664 | add(e); | | |||
665 | } | 377 | } | ||
666 | return true; | 378 | return &(d->groups[name]); | ||
667 | } | 379 | } | ||
668 | 380 | | |||
669 | struct RiffHeader { | 381 | KisSwatchGroup *KoColorSet::getGlobalGroup() | ||
670 | quint32 riff; | | |||
671 | quint32 size; | | |||
672 | quint32 signature; | | |||
673 | quint32 data; | | |||
674 | quint32 datasize; | | |||
675 | quint16 version; | | |||
676 | quint16 colorcount; | | |||
677 | }; | | |||
678 | | ||||
679 | | ||||
680 | bool KoColorSet::loadRiff() | | |||
681 | { | 382 | { | ||
682 | // http://worms2d.info/Palette_file | 383 | return getGroup(GLOBAL_GROUP_NAME); | ||
683 | QFileInfo info(filename()); | 384 | } | ||
684 | setName(info.baseName()); | | |||
685 | KoColorSetEntry e; | | |||
686 | 385 | | |||
687 | RiffHeader header; | 386 | bool KoColorSet::isGlobal() const | ||
688 | memcpy(&header, d->data.constData(), sizeof(RiffHeader)); | 387 | { | ||
689 | header.colorcount = qFromBigEndian(header.colorcount); | 388 | return d->isGlobal; | ||
389 | } | ||||
690 | 390 | | |||
691 | for (int i = sizeof(RiffHeader); | 391 | void KoColorSet::setIsGlobal(bool isGlobal) | ||
692 | (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < d->data.size()); | 392 | { | ||
693 | i += 4) { | 393 | d->isGlobal = isGlobal; | ||
694 | quint8 r = d->data[i]; | | |||
695 | quint8 g = d->data[i+1]; | | |||
696 | quint8 b = d->data[i+2]; | | |||
697 | e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); | | |||
698 | add(e); | | |||
699 | } | | |||
700 | return true; | | |||
701 | } | 394 | } | ||
702 | 395 | | |||
396 | bool KoColorSet::isEditable() const | ||||
397 | { | ||||
398 | return d->isEditable; | ||||
399 | } | ||||
703 | 400 | | |||
704 | bool KoColorSet::loadPsp() | 401 | void KoColorSet::setIsEditable(bool isEditable) | ||
705 | { | 402 | { | ||
706 | QFileInfo info(filename()); | 403 | d->isEditable = isEditable; | ||
707 | setName(info.baseName()); | 404 | } | ||
708 | KoColorSetEntry e; | | |||
709 | qint32 r, g, b; | | |||
710 | 405 | | |||
711 | QString s = QString::fromUtf8(d->data.data(), d->data.count()); | 406 | KisSwatchGroup::SwatchInfo KoColorSet::getClosestColorInfo(KoColor compare, bool useGivenColorSpace) | ||
712 | QStringList l = s.split('\n', QString::SkipEmptyParts); | 407 | { | ||
713 | if (l.size() < 4) return false; | 408 | KisSwatchGroup::SwatchInfo res; | ||
714 | if (l[0] != "JASC-PAL") return false; | | |||
715 | if (l[1] != "0100") return false; | | |||
716 | 409 | | |||
717 | int entries = l[2].toInt(); | 410 | quint8 highestPercentage = 0; | ||
411 | quint8 testPercentage = 0; | ||||
718 | 412 | | |||
719 | for (int i = 0; i < entries; ++i) { | 413 | for (const QString &groupName : getGroupNames()) { | ||
414 | KisSwatchGroup *group = getGroup(groupName); | ||||
415 | for (const KisSwatchGroup::SwatchInfo &currInfo : group->infoList()) { | ||||
416 | KoColor color = currInfo.swatch.color(); | ||||
417 | if (useGivenColorSpace == true && compare.colorSpace() != color.colorSpace()) { | ||||
418 | color.convertTo(compare.colorSpace()); | ||||
720 | 419 | | |||
721 | QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); | 420 | } else if (compare.colorSpace() != color.colorSpace()) { | ||
722 | 421 | compare.convertTo(color.colorSpace()); | |||
723 | if (a.count() != 3) { | 422 | } | ||
724 | continue; | 423 | testPercentage = (255 - compare.colorSpace()->difference(compare.data(), color.data())); | ||
424 | if (testPercentage > highestPercentage) | ||||
425 | { | ||||
426 | highestPercentage = testPercentage; | ||||
427 | res = currInfo; | ||||
428 | } | ||||
725 | } | 429 | } | ||
430 | } | ||||
431 | return res; | ||||
432 | } | ||||
726 | 433 | | |||
727 | r = a[0].toInt(); | 434 | /********************************KoColorSet::Private**************************/ | ||
728 | a.pop_front(); | | |||
729 | g = a[0].toInt(); | | |||
730 | a.pop_front(); | | |||
731 | b = a[0].toInt(); | | |||
732 | a.pop_front(); | | |||
733 | | ||||
734 | r = qBound(0, r, 255); | | |||
735 | g = qBound(0, g, 255); | | |||
736 | b = qBound(0, b, 255); | | |||
737 | 435 | | |||
738 | e.setColor(KoColor(QColor(r, g, b), | 436 | KoColorSet::Private::Private(KoColorSet *a_colorSet) | ||
739 | KoColorSpaceRegistry::instance()->rgb8())); | 437 | : colorSet(a_colorSet) | ||
438 | , isGlobal(true) | ||||
439 | , isEditable(false) | ||||
440 | { | ||||
441 | groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); | ||||
442 | groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); | ||||
443 | } | ||||
740 | 444 | | |||
741 | QString name = a.join(" "); | 445 | KoColorSet::PaletteType KoColorSet::Private::detectFormat(const QString &fileName, const QByteArray &ba) | ||
742 | e.setName(name.isEmpty() ? i18n("Untitled") : name); | 446 | { | ||
447 | QFileInfo fi(fileName); | ||||
743 | 448 | | |||
744 | add(e); | 449 | // .pal | ||
450 | if (ba.startsWith("RIFF") && ba.indexOf("PAL data", 8)) { | ||||
451 | return KoColorSet::RIFF_PAL; | ||||
745 | } | 452 | } | ||
746 | return true; | 453 | // .gpl | ||
454 | else if (ba.startsWith("GIMP Palette")) { | ||||
455 | return KoColorSet::GPL; | ||||
456 | } | ||||
457 | // .pal | ||||
458 | else if (ba.startsWith("JASC-PAL")) { | ||||
459 | return KoColorSet::PSP_PAL; | ||||
460 | } | ||||
461 | else if (fi.suffix().toLower() == "aco") { | ||||
462 | return KoColorSet::ACO; | ||||
463 | } | ||||
464 | else if (fi.suffix().toLower() == "act") { | ||||
465 | return KoColorSet::ACT; | ||||
466 | } | ||||
467 | else if (fi.suffix().toLower() == "xml") { | ||||
468 | return KoColorSet::XML; | ||||
469 | } | ||||
470 | else if (fi.suffix().toLower() == "kpl") { | ||||
471 | return KoColorSet::KPL; | ||||
472 | } | ||||
473 | else if (fi.suffix().toLower() == "sbz") { | ||||
474 | return KoColorSet::SBZ; | ||||
475 | } | ||||
476 | return KoColorSet::UNKNOWN; | ||||
747 | } | 477 | } | ||
748 | 478 | | |||
749 | void scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) | 479 | void KoColorSet::Private::scribusParseColor(KoColorSet *set, QXmlStreamReader *xml) | ||
750 | { | 480 | { | ||
751 | KoColorSetEntry colorEntry; | 481 | KisSwatch colorEntry; | ||
752 | // It's a color, retrieve it | 482 | // It's a color, retrieve it | ||
753 | QXmlStreamAttributes colorProperties = xml->attributes(); | 483 | QXmlStreamAttributes colorProperties = xml->attributes(); | ||
754 | 484 | | |||
Context not available. | |||||
817 | quint8 y = cmyk >> 8 & 0xff; | 547 | quint8 y = cmyk >> 8 & 0xff; | ||
818 | quint8 k = cmyk & 0xff; | 548 | quint8 k = cmyk & 0xff; | ||
819 | 549 | | |||
820 | dbgPigment << "Color parsed: "<< c << m << y << k; | 550 | dbgPigment << "Color parsed: "<< c << m << y << k; | ||
551 | | ||||
552 | currentColor.data()[0] = c; | ||||
553 | currentColor.data()[1] = m; | ||||
554 | currentColor.data()[2] = y; | ||||
555 | currentColor.data()[3] = k; | ||||
556 | currentColor.setOpacity(OPACITY_OPAQUE_U8); | ||||
557 | colorEntry.setColor(currentColor); | ||||
558 | | ||||
559 | set->add(colorEntry); | ||||
560 | | ||||
561 | while(xml->readNextStartElement()) { | ||||
562 | //ignore - these are all unknown or the /> element tag | ||||
563 | xml->skipCurrentElement(); | ||||
564 | } | ||||
565 | return; | ||||
566 | } | ||||
567 | } | ||||
568 | else { | ||||
569 | xml->raiseError("Unknown color space for color " + colorEntry.name()); | ||||
570 | } | ||||
571 | } | ||||
572 | | ||||
573 | bool KoColorSet::Private::loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) | ||||
574 | { | ||||
575 | | ||||
576 | //1. Get name | ||||
577 | QXmlStreamAttributes paletteProperties = xml->attributes(); | ||||
578 | QStringRef paletteName = paletteProperties.value("Name"); | ||||
579 | dbgPigment << "Processed name of palette:" << paletteName; | ||||
580 | set->setName(paletteName.toString()); | ||||
581 | | ||||
582 | //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them | ||||
583 | | ||||
584 | while(xml->readNextStartElement()) { | ||||
585 | QStringRef currentElement = xml->name(); | ||||
586 | if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { | ||||
587 | scribusParseColor(set, xml); | ||||
588 | } | ||||
589 | else { | ||||
590 | xml->skipCurrentElement(); | ||||
591 | } | ||||
592 | } | ||||
593 | | ||||
594 | if(xml->hasError()) { | ||||
595 | return false; | ||||
596 | } | ||||
597 | | ||||
598 | return true; | ||||
599 | } | ||||
600 | | ||||
601 | quint16 KoColorSet::Private::readShort(QIODevice *io) { | ||||
602 | quint16 val; | ||||
603 | quint64 read = io->read((char*)&val, 2); | ||||
604 | if (read != 2) return false; | ||||
605 | return qFromBigEndian(val); | ||||
606 | } | ||||
607 | | ||||
608 | bool KoColorSet::Private::init() | ||||
609 | { | ||||
610 | // just in case this is a reload (eg by KoEditColorSetDialog), | ||||
611 | groupNames.clear(); | ||||
612 | groups.clear(); | ||||
613 | groupNames.append(KoColorSet::GLOBAL_GROUP_NAME); | ||||
614 | groups[KoColorSet::GLOBAL_GROUP_NAME] = KisSwatchGroup(); | ||||
615 | | ||||
616 | if (colorSet->filename().isNull()) { | ||||
617 | warnPigment << "Cannot load palette" << colorSet->name() << "there is no filename set"; | ||||
618 | return false; | ||||
619 | } | ||||
620 | if (data.isNull()) { | ||||
621 | QFile file(colorSet->filename()); | ||||
622 | if (file.size() == 0) { | ||||
623 | warnPigment << "Cannot load palette" << colorSet->name() << "there is no data available"; | ||||
624 | return false; | ||||
625 | } | ||||
626 | file.open(QIODevice::ReadOnly); | ||||
627 | data = file.readAll(); | ||||
628 | file.close(); | ||||
629 | } | ||||
630 | | ||||
631 | bool res = false; | ||||
632 | paletteType = detectFormat(colorSet->filename(), data); | ||||
633 | switch(paletteType) { | ||||
634 | case GPL: | ||||
635 | res = loadGpl(); | ||||
636 | break; | ||||
637 | case ACT: | ||||
638 | res = loadAct(); | ||||
639 | break; | ||||
640 | case RIFF_PAL: | ||||
641 | res = loadRiff(); | ||||
642 | break; | ||||
643 | case PSP_PAL: | ||||
644 | res = loadPsp(); | ||||
645 | break; | ||||
646 | case ACO: | ||||
647 | res = loadAco(); | ||||
648 | break; | ||||
649 | case XML: | ||||
650 | res = loadXml(); | ||||
651 | break; | ||||
652 | case KPL: | ||||
653 | res = loadKpl(); | ||||
654 | break; | ||||
655 | case SBZ: | ||||
656 | res = loadSbz(); | ||||
657 | break; | ||||
658 | default: | ||||
659 | res = false; | ||||
660 | } | ||||
661 | colorSet->setValid(res); | ||||
662 | | ||||
663 | QImage img(global().columnCount() * 4, global().rowCount() * 4, QImage::Format_ARGB32); | ||||
664 | QPainter gc(&img); | ||||
665 | gc.fillRect(img.rect(), Qt::darkGray); | ||||
666 | for (const KisSwatchGroup::SwatchInfo &info : global().infoList()) { | ||||
667 | QColor c = info.swatch.color().toQColor(); | ||||
668 | gc.fillRect(info.column * 4, info.row * 4, 4, 4, c); | ||||
669 | } | ||||
670 | colorSet->setImage(img); | ||||
671 | colorSet->setValid(res); | ||||
821 | 672 | | |||
822 | currentColor.data()[0] = c; | 673 | data.clear(); | ||
823 | currentColor.data()[1] = m; | 674 | return res; | ||
824 | currentColor.data()[2] = y; | 675 | } | ||
825 | currentColor.data()[3] = k; | | |||
826 | currentColor.setOpacity(OPACITY_OPAQUE_U8); | | |||
827 | colorEntry.setColor(currentColor); | | |||
828 | 676 | | |||
829 | set->add(colorEntry); | 677 | bool KoColorSet::Private::saveGpl(QIODevice *dev) const | ||
678 | { | ||||
679 | Q_ASSERT(dev->isOpen()); | ||||
680 | Q_ASSERT(dev->isWritable()); | ||||
830 | 681 | | |||
831 | while(xml->readNextStartElement()) { | 682 | QTextStream stream(dev); | ||
832 | //ignore - these are all unknown or the /> element tag | 683 | stream << "GIMP Palette\nName: " << colorSet->name() << "\nColumns: " << colorSet->columnCount() << "\n#\n"; | ||
833 | xml->skipCurrentElement(); | 684 | | ||
685 | /* | ||||
686 | * Qt doesn't provide an interface to get a const reference to a QHash, that is | ||||
687 | * the underlying data structure of groups. Therefore, directly use | ||||
688 | * groups[KoColorSet::GLOBAL_GROUP_NAME] so that saveGpl can stay const | ||||
689 | */ | ||||
690 | | ||||
691 | for (int y = 0; y < groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount(); y++) { | ||||
692 | for (int x = 0; x < colorSet->columnCount(); x++) { | ||||
693 | if (!groups[KoColorSet::GLOBAL_GROUP_NAME].checkEntry(x, y)) { | ||||
694 | continue; | ||||
834 | } | 695 | } | ||
835 | return; | 696 | const KisSwatch& entry = groups[KoColorSet::GLOBAL_GROUP_NAME].getEntry(x, y); | ||
697 | QColor c = entry.color().toQColor(); | ||||
698 | stream << c.red() << " " << c.green() << " " << c.blue() << "\t"; | ||||
699 | if (entry.name().isEmpty()) | ||||
700 | stream << "Untitled\n"; | ||||
701 | else | ||||
702 | stream << entry.name() << "\n"; | ||||
836 | } | 703 | } | ||
837 | } | 704 | } | ||
838 | else { | 705 | | ||
839 | xml->raiseError("Unknown color space for color " + colorEntry.name()); | 706 | return true; | ||
840 | } | | |||
841 | } | 707 | } | ||
842 | 708 | | |||
843 | bool loadScribusXmlPalette(KoColorSet *set, QXmlStreamReader *xml) | 709 | bool KoColorSet::Private::loadGpl() | ||
844 | { | 710 | { | ||
711 | QString s = QString::fromUtf8(data.data(), data.count()); | ||||
845 | 712 | | |||
846 | //1. Get name | 713 | if (s.isEmpty() || s.isNull() || s.length() < 50) { | ||
847 | QXmlStreamAttributes paletteProperties = xml->attributes(); | 714 | warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); | ||
848 | QStringRef paletteName = paletteProperties.value("Name"); | 715 | return false; | ||
849 | dbgPigment << "Processed name of palette:" << paletteName; | 716 | } | ||
850 | set->setName(paletteName.toString()); | | |||
851 | 717 | | |||
852 | //2. Inside the SCRIBUSCOLORS, there are lots of colors. Retrieve them | 718 | quint32 index = 0; | ||
853 | 719 | | |||
854 | while(xml->readNextStartElement()) { | 720 | QStringList lines = s.split('\n', QString::SkipEmptyParts); | ||
855 | QStringRef currentElement = xml->name(); | 721 | | ||
856 | if(QStringRef::compare(currentElement, "COLOR", Qt::CaseInsensitive) == 0) { | 722 | if (lines.size() < 3) { | ||
857 | scribusParseColor(set, xml); | 723 | warnPigment << "Not enough lines in palette file: " << colorSet->filename(); | ||
858 | } | 724 | return false; | ||
859 | else { | | |||
860 | xml->skipCurrentElement(); | | |||
861 | } | | |||
862 | } | 725 | } | ||
863 | 726 | | |||
864 | if(xml->hasError()) { | 727 | QString columnsText; | ||
728 | qint32 r, g, b; | ||||
729 | KisSwatch e; | ||||
730 | | ||||
731 | // Read name | ||||
732 | if (!lines[0].startsWith("GIMP") || !lines[1].toLower().contains("name")) { | ||||
733 | warnPigment << "Illegal Gimp palette file: " << colorSet->filename(); | ||||
865 | return false; | 734 | return false; | ||
866 | } | 735 | } | ||
867 | 736 | | |||
868 | return true; | 737 | colorSet->setName(i18n(lines[1].split(":")[1].trimmed().toLatin1())); | ||
869 | } | | |||
870 | 738 | | |||
871 | bool KoColorSet::loadXml() { | 739 | index = 2; | ||
872 | bool res = false; | | |||
873 | 740 | | |||
874 | QXmlStreamReader *xml = new QXmlStreamReader(d->data); | 741 | // Read columns | ||
742 | int columns = 0; | ||||
743 | if (lines[index].toLower().contains("columns")) { | ||||
744 | columnsText = lines[index].split(":")[1].trimmed(); | ||||
745 | columns = columnsText.toInt(); | ||||
746 | global().setColumnCount(columns); | ||||
747 | index = 3; | ||||
748 | } | ||||
875 | 749 | | |||
876 | if (xml->readNextStartElement()) { | 750 | | ||
877 | QStringRef paletteId = xml->name(); | 751 | for (qint32 i = index; i < lines.size(); i++) { | ||
878 | if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus | 752 | if (lines[i].startsWith('#')) { | ||
879 | dbgPigment << "XML palette: " << filename() << ", Scribus format"; | 753 | comment += lines[i].mid(1).trimmed() + ' '; | ||
880 | res = loadScribusXmlPalette(this, xml); | 754 | } else if (!lines[i].isEmpty()) { | ||
881 | } | 755 | QStringList a = lines[i].replace('\t', ' ').split(' ', QString::SkipEmptyParts); | ||
882 | else { | 756 | | ||
883 | // Unknown XML format | 757 | if (a.count() < 3) { | ||
884 | xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); | 758 | break; | ||
759 | } | ||||
760 | | ||||
761 | r = qBound(0, a[0].toInt(), 255); | ||||
762 | g = qBound(0, a[1].toInt(), 255); | ||||
763 | b = qBound(0, a[2].toInt(), 255); | ||||
764 | | ||||
765 | e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); | ||||
766 | | ||||
767 | for (int i = 0; i != 3; i++) { | ||||
768 | a.pop_front(); | ||||
769 | } | ||||
770 | QString name = a.join(" "); | ||||
771 | e.setName(name.isEmpty() ? i18n("Untitled") : name); | ||||
772 | | ||||
773 | global().addEntry(e); | ||||
885 | } | 774 | } | ||
886 | } | 775 | } | ||
776 | return true; | ||||
777 | } | ||||
887 | 778 | | |||
888 | // If there is any error (it should be returned through the stream) | 779 | bool KoColorSet::Private::loadAct() | ||
889 | if (xml->hasError() || !res) { | 780 | { | ||
890 | warnPigment << "Illegal XML palette:" << filename(); | 781 | QFileInfo info(colorSet->filename()); | ||
891 | warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); | 782 | colorSet->setName(info.baseName()); | ||
892 | return false; | 783 | KisSwatch e; | ||
784 | for (int i = 0; i < data.size(); i += 3) { | ||||
785 | quint8 r = data[i]; | ||||
786 | quint8 g = data[i+1]; | ||||
787 | quint8 b = data[i+2]; | ||||
788 | e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); | ||||
789 | global().addEntry(e); | ||||
893 | } | 790 | } | ||
894 | else { | 791 | return true; | ||
895 | dbgPigment << "XML palette parsed successfully:" << filename(); | 792 | } | ||
896 | return true; | 793 | | ||
794 | bool KoColorSet::Private::loadRiff() | ||||
795 | { | ||||
796 | // http://worms2d.info/Palette_file | ||||
797 | QFileInfo info(colorSet->filename()); | ||||
798 | colorSet->setName(info.baseName()); | ||||
799 | KisSwatch e; | ||||
800 | | ||||
801 | RiffHeader header; | ||||
802 | memcpy(&header, data.constData(), sizeof(RiffHeader)); | ||||
803 | header.colorcount = qFromBigEndian(header.colorcount); | ||||
804 | | ||||
805 | for (int i = sizeof(RiffHeader); | ||||
806 | (i < (int)(sizeof(RiffHeader) + header.colorcount) && i < data.size()); | ||||
807 | i += 4) { | ||||
808 | quint8 r = data[i]; | ||||
809 | quint8 g = data[i+1]; | ||||
810 | quint8 b = data[i+2]; | ||||
811 | e.setColor(KoColor(QColor(r, g, b), KoColorSpaceRegistry::instance()->rgb8())); | ||||
812 | groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); | ||||
897 | } | 813 | } | ||
814 | return true; | ||||
898 | } | 815 | } | ||
899 | 816 | | |||
900 | bool KoColorSet::saveKpl(QIODevice *dev) const | 817 | | ||
818 | bool KoColorSet::Private::loadPsp() | ||||
901 | { | 819 | { | ||
902 | QScopedPointer<KoStore> store(KoStore::createStore(dev, KoStore::Write, "application/x-krita-palette", KoStore::Zip)); | 820 | QFileInfo info(colorSet->filename()); | ||
903 | if (!store || store->bad()) return false; | 821 | colorSet->setName(info.baseName()); | ||
822 | KisSwatch e; | ||||
823 | qint32 r, g, b; | ||||
904 | 824 | | |||
905 | QSet<const KoColorProfile *> profiles; | 825 | QString s = QString::fromUtf8(data.data(), data.count()); | ||
906 | QMap<const KoColorProfile*, const KoColorSpace*> profileMap; | 826 | QStringList l = s.split('\n', QString::SkipEmptyParts); | ||
827 | if (l.size() < 4) return false; | ||||
828 | if (l[0] != "JASC-PAL") return false; | ||||
829 | if (l[1] != "0100") return false; | ||||
907 | 830 | | |||
908 | { | 831 | int entries = l[2].toInt(); | ||
909 | QDomDocument doc; | | |||
910 | QDomElement root = doc.createElement("Colorset"); | | |||
911 | root.setAttribute("version", "1.0"); | | |||
912 | root.setAttribute("name", name()); | | |||
913 | root.setAttribute("comment", d->comment); | | |||
914 | root.setAttribute("columns", d->columns); | | |||
915 | Q_FOREACH(const KoColorSetEntry &entry, d->colors) { | | |||
916 | | ||||
917 | // Only save non-builtin profiles.= | | |||
918 | const KoColorProfile *profile = entry.color().colorSpace()->profile(); | | |||
919 | if (!profile->fileName().isEmpty()) { | | |||
920 | profiles << profile; | | |||
921 | profileMap[profile] = entry.color().colorSpace(); | | |||
922 | } | | |||
923 | QDomElement el = doc.createElement("ColorSetEntry"); | | |||
924 | el.setAttribute("name", entry.name()); | | |||
925 | el.setAttribute("id", entry.id()); | | |||
926 | el.setAttribute("spot", entry.spotColor() ? "true" : "false"); | | |||
927 | el.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id()); | | |||
928 | entry.color().toXML(doc, el); | | |||
929 | root.appendChild(el); | | |||
930 | } | | |||
931 | Q_FOREACH(const QString &groupName, d->groupNames) { | | |||
932 | QDomElement gl = doc.createElement("Group"); | | |||
933 | gl.setAttribute("name", groupName); | | |||
934 | root.appendChild(gl); | | |||
935 | Q_FOREACH(const KoColorSetEntry &entry, d->groups.value(groupName)) { | | |||
936 | 832 | | |||
937 | // Only save non-builtin profiles.= | 833 | for (int i = 0; i < entries; ++i) { | ||
938 | const KoColorProfile *profile = entry.color().colorSpace()->profile(); | 834 | | ||
939 | if (!profile->fileName().isEmpty()) { | 835 | QStringList a = l[i + 3].replace('\t', ' ').split(' ', QString::SkipEmptyParts); | ||
940 | profiles << profile; | 836 | | ||
941 | profileMap[profile] = entry.color().colorSpace(); | 837 | if (a.count() != 3) { | ||
942 | } | 838 | continue; | ||
943 | QDomElement el = doc.createElement("ColorSetEntry"); | | |||
944 | el.setAttribute("name", entry.name()); | | |||
945 | el.setAttribute("id", entry.id()); | | |||
946 | el.setAttribute("spot", entry.spotColor() ? "true" : "false"); | | |||
947 | el.setAttribute("bitdepth", entry.color().colorSpace()->colorDepthId().id()); | | |||
948 | entry.color().toXML(doc, el); | | |||
949 | gl.appendChild(el); | | |||
950 | } | | |||
951 | } | 839 | } | ||
952 | 840 | | |||
953 | doc.appendChild(root); | 841 | r = qBound(0, a[0].toInt(), 255); | ||
954 | if (!store->open("colorset.xml")) { return false; } | 842 | g = qBound(0, a[1].toInt(), 255); | ||
955 | QByteArray ba = doc.toByteArray(); | 843 | b = qBound(0, a[2].toInt(), 255); | ||
956 | if (store->write(ba) != ba.size()) { return false; } | | |||
957 | if (!store->close()) { return false; } | | |||
958 | } | | |||
959 | 844 | | |||
960 | QDomDocument doc; | 845 | e.setColor(KoColor(QColor(r, g, b), | ||
961 | QDomElement profileElement = doc.createElement("Profiles"); | 846 | KoColorSpaceRegistry::instance()->rgb8())); | ||
962 | 847 | | |||
963 | Q_FOREACH(const KoColorProfile *profile, profiles) { | 848 | QString name = a.join(" "); | ||
964 | QString fn = QFileInfo(profile->fileName()).fileName(); | 849 | e.setName(name.isEmpty() ? i18n("Untitled") : name); | ||
965 | if (!store->open(fn)) { return false; } | | |||
966 | QByteArray profileRawData = profile->rawData(); | | |||
967 | if (!store->write(profileRawData)) { return false; } | | |||
968 | if (!store->close()) { return false; } | | |||
969 | QDomElement el = doc.createElement("Profile"); | | |||
970 | el.setAttribute("filename", fn); | | |||
971 | el.setAttribute("name", profile->name()); | | |||
972 | el.setAttribute("colorModelId", profileMap[profile]->colorModelId().id()); | | |||
973 | el.setAttribute("colorDepthId", profileMap[profile]->colorDepthId().id()); | | |||
974 | profileElement.appendChild(el); | | |||
975 | 850 | | |||
851 | groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); | ||||
976 | } | 852 | } | ||
977 | doc.appendChild(profileElement); | 853 | return true; | ||
978 | if (!store->open("profiles.xml")) { return false; } | | |||
979 | QByteArray ba = doc.toByteArray(); | | |||
980 | if (store->write(ba) != ba.size()) { return false; } | | |||
981 | if (!store->close()) { return false; } | | |||
982 | | ||||
983 | return store->finalize(); | | |||
984 | } | 854 | } | ||
985 | 855 | | |||
986 | bool KoColorSet::loadKpl() | 856 | bool KoColorSet::Private::loadKpl() | ||
987 | { | 857 | { | ||
988 | QBuffer buf(&d->data); | 858 | QBuffer buf(&data); | ||
989 | buf.open(QBuffer::ReadOnly); | 859 | buf.open(QBuffer::ReadOnly); | ||
990 | 860 | | |||
991 | QScopedPointer<KoStore> store(KoStore::createStore(&buf, KoStore::Read, "application/x-krita-palette", KoStore::Zip)); | 861 | QScopedPointer<KoStore> store(KoStore::createStore(&buf, KoStore::Read, "krita/x-colorset", KoStore::Zip)); | ||
992 | if (!store || store->bad()) return false; | 862 | if (!store || store->bad()) { return false; } | ||
993 | 863 | | |||
994 | if (store->hasFile("profiles.xml")) { | 864 | if (store->hasFile("profiles.xml")) { | ||
995 | | ||||
996 | if (!store->open("profiles.xml")) { return false; } | 865 | if (!store->open("profiles.xml")) { return false; } | ||
997 | QByteArray data; | 866 | QByteArray data; | ||
998 | data.resize(store->size()); | 867 | data.resize(store->size()); | ||
Context not available. | |||||
1002 | QDomDocument doc; | 871 | QDomDocument doc; | ||
1003 | doc.setContent(ba); | 872 | doc.setContent(ba); | ||
1004 | QDomElement e = doc.documentElement(); | 873 | QDomElement e = doc.documentElement(); | ||
1005 | QDomElement c = e.firstChildElement("Profiles"); | 874 | QDomElement c = e.firstChildElement(KPL_PALETTE_PROFILE_TAG); | ||
1006 | while (!c.isNull()) { | 875 | while (!c.isNull()) { | ||
1007 | 876 | QString name = c.attribute(KPL_PALETTE_NAME_ATTR); | |||
1008 | QString name = c.attribute("name"); | 877 | QString filename = c.attribute(KPL_PALETTE_FILENAME_ATTR); | ||
1009 | QString filename = c.attribute("filename"); | 878 | QString colorModelId = c.attribute(KPL_COLOR_MODEL_ID_ATTR); | ||
1010 | QString colorModelId = c.attribute("colorModelId"); | 879 | QString colorDepthId = c.attribute(KPL_COLOR_DEPTH_ID_ATTR); | ||
1011 | QString colorDepthId = c.attribute("colorDepthId"); | | |||
1012 | if (!KoColorSpaceRegistry::instance()->profileByName(name)) { | 880 | if (!KoColorSpaceRegistry::instance()->profileByName(name)) { | ||
1013 | store->open(filename); | 881 | store->open(filename); | ||
1014 | QByteArray data; | 882 | QByteArray data; | ||
Context not available. | |||||
1023 | } | 891 | } | ||
1024 | 892 | | |||
1025 | c = c.nextSiblingElement(); | 893 | c = c.nextSiblingElement(); | ||
1026 | | ||||
1027 | } | 894 | } | ||
1028 | } | 895 | } | ||
1029 | 896 | | |||
Context not available. | |||||
1037 | QDomDocument doc; | 904 | QDomDocument doc; | ||
1038 | doc.setContent(ba); | 905 | doc.setContent(ba); | ||
1039 | QDomElement e = doc.documentElement(); | 906 | QDomElement e = doc.documentElement(); | ||
1040 | setName(e.attribute("name")); | 907 | colorSet->setName(e.attribute(KPL_PALETTE_NAME_ATTR)); | ||
1041 | d->comment = e.attribute("comment"); | 908 | colorSet->setColumnCount(e.attribute(KPL_PALETTE_COLUMN_COUNT_ATTR).toInt()); | ||
1042 | d->columns = e.attribute("columns").toInt(); | 909 | colorSet->setIsEditable(e.attribute(KPL_PALETTE_READONLY_ATTR) != "true"); | ||
1043 | 910 | comment = e.attribute(KPL_PALETTE_COMMENT_ATTR); | |||
1044 | QDomElement c = e.firstChildElement("ColorSetEntry"); | | |||
1045 | while (!c.isNull()) { | | |||
1046 | QString colorDepthId = c.attribute("bitdepth", Integer8BitsColorDepthID.id()); | | |||
1047 | KoColorSetEntry entry; | | |||
1048 | 911 | | |||
912 | loadKplGroup(doc, e, colorSet->getGlobalGroup()); | ||||
1049 | 913 | | |||
1050 | entry.setColor(KoColor::fromXML(c.firstChildElement(), colorDepthId)); | 914 | QDomElement g = e.firstChildElement(KPL_GROUP_TAG); | ||
1051 | entry.setName(c.attribute("name")); | | |||
1052 | entry.setId(c.attribute("id")); | | |||
1053 | entry.setSpotColor(c.attribute("spot", "false") == "true" ? true : false); | | |||
1054 | d->colors << entry; | | |||
1055 | | ||||
1056 | c = c.nextSiblingElement("ColorSetEntry"); | | |||
1057 | | ||||
1058 | } | | |||
1059 | QDomElement g = e.firstChildElement("Group"); | | |||
1060 | while (!g.isNull()) { | 915 | while (!g.isNull()) { | ||
1061 | QString groupName = g.attribute("name"); | 916 | QString groupName = g.attribute(KPL_GROUP_NAME_ATTR); | ||
1062 | addGroup(groupName); | 917 | colorSet->addGroup(groupName); | ||
1063 | QDomElement cg = g.firstChildElement("ColorSetEntry"); | 918 | loadKplGroup(doc, g, colorSet->getGroup(groupName)); | ||
1064 | while (!cg.isNull()) { | 919 | g = g.nextSiblingElement(KPL_GROUP_TAG); | ||
1065 | QString colorDepthId = cg.attribute("bitdepth", Integer8BitsColorDepthID.id()); | | |||
1066 | KoColorSetEntry entry; | | |||
1067 | | ||||
1068 | | ||||
1069 | entry.setColor(KoColor::fromXML(cg.firstChildElement(), colorDepthId)); | | |||
1070 | entry.setName(cg.attribute("name")); | | |||
1071 | entry.setId(cg.attribute("id")); | | |||
1072 | entry.setSpotColor(cg.attribute("spot", "false") == "true" ? true : false); | | |||
1073 | add(entry, groupName); | | |||
1074 | | ||||
1075 | cg = cg.nextSiblingElement("ColorSetEntry"); | | |||
1076 | | ||||
1077 | } | | |||
1078 | g = g.nextSiblingElement("Group"); | | |||
1079 | } | 920 | } | ||
1080 | | ||||
1081 | } | 921 | } | ||
1082 | 922 | | |||
1083 | | ||||
1084 | buf.close(); | 923 | buf.close(); | ||
1085 | return true; | 924 | return true; | ||
1086 | } | 925 | } | ||
1087 | 926 | | |||
1088 | quint16 readShort(QIODevice *io) { | 927 | bool KoColorSet::Private::loadAco() | ||
1089 | quint16 val; | | |||
1090 | quint64 read = io->read((char*)&val, 2); | | |||
1091 | if (read != 2) return false; | | |||
1092 | return qFromBigEndian(val); | | |||
1093 | } | | |||
1094 | | ||||
1095 | bool KoColorSet::loadAco() | | |||
1096 | { | 928 | { | ||
1097 | QFileInfo info(filename()); | 929 | QFileInfo info(colorSet->filename()); | ||
1098 | setName(info.baseName()); | 930 | colorSet->setName(info.baseName()); | ||
1099 | 931 | | |||
1100 | QBuffer buf(&d->data); | 932 | QBuffer buf(&data); | ||
1101 | buf.open(QBuffer::ReadOnly); | 933 | buf.open(QBuffer::ReadOnly); | ||
1102 | 934 | | |||
1103 | quint16 version = readShort(&buf); | 935 | quint16 version = readShort(&buf); | ||
1104 | quint16 numColors = readShort(&buf); | 936 | quint16 numColors = readShort(&buf); | ||
1105 | KoColorSetEntry e; | 937 | KisSwatch e; | ||
1106 | 938 | | |||
1107 | if (version == 1 && buf.size() > 4+numColors*10) { | 939 | if (version == 1 && buf.size() > 4+numColors*10) { | ||
1108 | buf.seek(4+numColors*10); | 940 | buf.seek(4+numColors*10); | ||
Context not available. | |||||
1161 | e.setColor(c); | 993 | e.setColor(c); | ||
1162 | } | 994 | } | ||
1163 | else { | 995 | else { | ||
1164 | warnPigment << "Unsupported colorspace in palette" << filename() << "(" << colorSpace << ")"; | 996 | warnPigment << "Unsupported colorspace in palette" << colorSet->filename() << "(" << colorSpace << ")"; | ||
1165 | skip = true; | 997 | skip = true; | ||
1166 | } | 998 | } | ||
1167 | if (version == 2) { | 999 | if (version == 2) { | ||
Context not available. | |||||
1174 | QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); | 1006 | QTextCodec *Utf16Codec = QTextCodec::codecForName("UTF-16BE"); | ||
1175 | e.setName(Utf16Codec->toUnicode(ba)); | 1007 | e.setName(Utf16Codec->toUnicode(ba)); | ||
1176 | } else { | 1008 | } else { | ||
1177 | warnPigment << "Version 2 name block is the wrong size" << filename(); | 1009 | warnPigment << "Version 2 name block is the wrong size" << colorSet->filename(); | ||
1178 | } | 1010 | } | ||
1179 | } | 1011 | } | ||
1180 | v2 = readShort(&buf); //end marker also needs to be skipped. | 1012 | v2 = readShort(&buf); //end marker also needs to be skipped. | ||
1181 | Q_UNUSED(v2); | 1013 | Q_UNUSED(v2); | ||
1182 | } | 1014 | } | ||
1183 | if (!skip) { | 1015 | if (!skip) { | ||
1184 | add(e); | 1016 | groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(e); | ||
1185 | } | 1017 | } | ||
1186 | } | 1018 | } | ||
1187 | return true; | 1019 | return true; | ||
1188 | } | 1020 | } | ||
1189 | 1021 | | |||
1190 | bool KoColorSet::loadSbz() { | 1022 | bool KoColorSet::Private::loadSbz() { | ||
1191 | QBuffer buf(&d->data); | 1023 | QBuffer buf(&data); | ||
1192 | buf.open(QBuffer::ReadOnly); | 1024 | buf.open(QBuffer::ReadOnly); | ||
1193 | 1025 | | |||
1194 | // &buf is a subclass of QIODevice | 1026 | // &buf is a subclass of QIODevice | ||
Context not available. | |||||
1203 | QByteArray ba = store->read(store->size()); | 1035 | QByteArray ba = store->read(store->size()); | ||
1204 | store->close(); | 1036 | store->close(); | ||
1205 | 1037 | | |||
1206 | dbgPigment << "XML palette: " << filename() << ", SwatchBooker format"; | 1038 | dbgPigment << "XML palette: " << colorSet->filename() << ", SwatchBooker format"; | ||
1207 | 1039 | | |||
1208 | QDomDocument doc; | 1040 | QDomDocument doc; | ||
1209 | int errorLine, errorColumn; | 1041 | int errorLine, errorColumn; | ||
1210 | QString errorMessage; | 1042 | QString errorMessage; | ||
1211 | bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); | 1043 | bool status = doc.setContent(ba, &errorMessage, &errorLine, &errorColumn); | ||
1212 | if (!status) { | 1044 | if (!status) { | ||
1213 | warnPigment << "Illegal XML palette:" << filename(); | 1045 | warnPigment << "Illegal XML palette:" << colorSet->filename(); | ||
1214 | warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; | 1046 | warnPigment << "Error (line" << errorLine << ", column" << errorColumn << "):" << errorMessage; | ||
1215 | return false; | 1047 | return false; | ||
1216 | } | 1048 | } | ||
Context not available. | |||||
1228 | QDomElement title = metadata.firstChildElement("dc:title"); | 1060 | QDomElement title = metadata.firstChildElement("dc:title"); | ||
1229 | QString colorName = title.text(); | 1061 | QString colorName = title.text(); | ||
1230 | colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; | 1062 | colorName = colorName.isEmpty() ? i18n("Untitled") : colorName; | ||
1231 | setName(colorName); | 1063 | colorSet->setName(colorName); | ||
1232 | dbgPigment << "Processed name of palette:" << name(); | 1064 | dbgPigment << "Processed name of palette:" << colorSet->name(); | ||
1233 | // End reading properties | 1065 | // End reading properties | ||
1234 | 1066 | | |||
1235 | // Now read colors... | 1067 | // Now read colors... | ||
Context not available. | |||||
1260 | 1092 | | |||
1261 | // We'll store colors here, and as we process swatches | 1093 | // We'll store colors here, and as we process swatches | ||
1262 | // we'll add them to the palette | 1094 | // we'll add them to the palette | ||
1263 | QHash<QString, KoColorSetEntry> materialsBook; | 1095 | QHash<QString, KisSwatch> materialsBook; | ||
1264 | QHash<QString, const KoColorSpace*> fileColorSpaces; | 1096 | QHash<QString, const KoColorSpace*> fileColorSpaces; | ||
1265 | 1097 | | |||
1266 | // Color processing | 1098 | // Color processing | ||
1267 | for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) | 1099 | for(; !colorElement.isNull(); colorElement = colorElement.nextSiblingElement("color")) | ||
1268 | { | 1100 | { | ||
1269 | KoColorSetEntry currentEntry; | 1101 | KisSwatch currentEntry; | ||
1270 | // Set if color is spot | 1102 | // Set if color is spot | ||
1271 | currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); | 1103 | currentEntry.setSpotColor(colorElement.attribute("usage") == "spot"); | ||
1272 | 1104 | | |||
Context not available. | |||||
1530 | return false; | 1362 | return false; | ||
1531 | } | 1363 | } | ||
1532 | if (materialsBook.contains(id)) { | 1364 | if (materialsBook.contains(id)) { | ||
1533 | add(materialsBook.value(id)); | 1365 | groups[KoColorSet::GLOBAL_GROUP_NAME].addEntry(materialsBook.value(id)); | ||
1534 | } | 1366 | } | ||
1535 | else { | 1367 | else { | ||
1536 | warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; | 1368 | warnPigment << "Invalid swatch definition (material not found) (line" << swatch.lineNumber() << ", column" << swatch.columnNumber() << ")"; | ||
Context not available. | |||||
1559 | return false; | 1391 | return false; | ||
1560 | } | 1392 | } | ||
1561 | if (materialsBook.contains(id)) { | 1393 | if (materialsBook.contains(id)) { | ||
1562 | add(materialsBook.value(id), currentGroupName); | 1394 | groups[currentGroupName].addEntry(materialsBook.value(id)); | ||
1563 | } | 1395 | } | ||
1564 | else { | 1396 | else { | ||
1565 | warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; | 1397 | warnPigment << "Invalid swatch definition (material not found) (line" << groupSwatch.lineNumber() << ", column" << groupSwatch.columnNumber() << ")"; | ||
Context not available. | |||||
1575 | buf.close(); | 1407 | buf.close(); | ||
1576 | return true; | 1408 | return true; | ||
1577 | } | 1409 | } | ||
1410 | | ||||
1411 | bool KoColorSet::Private::loadXml() { | ||||
1412 | bool res = false; | ||||
1413 | | ||||
1414 | QXmlStreamReader *xml = new QXmlStreamReader(data); | ||||
1415 | | ||||
1416 | if (xml->readNextStartElement()) { | ||||
1417 | QStringRef paletteId = xml->name(); | ||||
1418 | if (QStringRef::compare(paletteId, "SCRIBUSCOLORS", Qt::CaseInsensitive) == 0) { // Scribus | ||||
1419 | dbgPigment << "XML palette: " << colorSet->filename() << ", Scribus format"; | ||||
1420 | res = loadScribusXmlPalette(colorSet, xml); | ||||
1421 | } | ||||
1422 | else { | ||||
1423 | // Unknown XML format | ||||
1424 | xml->raiseError("Unknown XML palette format. Expected SCRIBUSCOLORS, found " + paletteId); | ||||
1425 | } | ||||
1426 | } | ||||
1427 | | ||||
1428 | // If there is any error (it should be returned through the stream) | ||||
1429 | if (xml->hasError() || !res) { | ||||
1430 | warnPigment << "Illegal XML palette:" << colorSet->filename(); | ||||
1431 | warnPigment << "Error (line"<< xml->lineNumber() << ", column" << xml->columnNumber() << "):" << xml->errorString(); | ||||
1432 | return false; | ||||
1433 | } | ||||
1434 | else { | ||||
1435 | dbgPigment << "XML palette parsed successfully:" << colorSet->filename(); | ||||
1436 | return true; | ||||
1437 | } | ||||
1438 | } | ||||
1439 | | ||||
1440 | bool KoColorSet::Private::saveKpl(QIODevice *dev) const | ||||
1441 | { | ||||
1442 | QScopedPointer<KoStore> store(KoStore::createStore(dev, KoStore::Write, "krita/x-colorset", KoStore::Zip)); | ||||
1443 | if (!store || store->bad()) return false; | ||||
1444 | | ||||
1445 | QSet<const KoColorSpace *> colorSpaces; | ||||
1446 | | ||||
1447 | { | ||||
1448 | QDomDocument doc; | ||||
1449 | QDomElement root = doc.createElement(KPL_PALETTE_TAG); | ||||
1450 | root.setAttribute(KPL_VERSION_ATTR, "1.0"); | ||||
1451 | root.setAttribute(KPL_PALETTE_NAME_ATTR, colorSet->name()); | ||||
1452 | root.setAttribute(KPL_PALETTE_COMMENT_ATTR, comment); | ||||
1453 | root.setAttribute(KPL_PALETTE_READONLY_ATTR, | ||||
1454 | (colorSet->isEditable() || !colorSet->isGlobal()) ? "false" : "true"); | ||||
1455 | root.setAttribute(KPL_PALETTE_COLUMN_COUNT_ATTR, colorSet->columnCount()); | ||||
1456 | root.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, groups[KoColorSet::GLOBAL_GROUP_NAME].rowCount()); | ||||
1457 | | ||||
1458 | saveKplGroup(doc, root, colorSet->getGroup(KoColorSet::GLOBAL_GROUP_NAME), colorSpaces); | ||||
1459 | | ||||
1460 | for (const QString &groupName : groupNames) { | ||||
1461 | if (groupName == KoColorSet::GLOBAL_GROUP_NAME) { continue; } | ||||
1462 | QDomElement gl = doc.createElement(KPL_GROUP_TAG); | ||||
1463 | gl.setAttribute(KPL_GROUP_NAME_ATTR, groupName); | ||||
1464 | root.appendChild(gl); | ||||
1465 | saveKplGroup(doc, gl, colorSet->getGroup(groupName), colorSpaces); | ||||
1466 | } | ||||
1467 | | ||||
1468 | doc.appendChild(root); | ||||
1469 | if (!store->open("colorset.xml")) { return false; } | ||||
1470 | QByteArray ba = doc.toByteArray(); | ||||
1471 | if (store->write(ba) != ba.size()) { return false; } | ||||
1472 | if (!store->close()) { return false; } | ||||
1473 | } | ||||
1474 | | ||||
1475 | QDomDocument doc; | ||||
1476 | QDomElement profileElement = doc.createElement("Profiles"); | ||||
1477 | | ||||
1478 | for (const KoColorSpace *colorSpace : colorSpaces) { | ||||
1479 | QString fn = QFileInfo(colorSpace->profile()->fileName()).fileName(); | ||||
1480 | if (!store->open(fn)) { return false; } | ||||
1481 | QByteArray profileRawData = colorSpace->profile()->rawData(); | ||||
1482 | if (!store->write(profileRawData)) { return false; } | ||||
1483 | if (!store->close()) { return false; } | ||||
1484 | QDomElement el = doc.createElement(KPL_PALETTE_PROFILE_TAG); | ||||
1485 | el.setAttribute(KPL_PALETTE_FILENAME_ATTR, fn); | ||||
1486 | el.setAttribute(KPL_PALETTE_NAME_ATTR, colorSpace->profile()->name()); | ||||
1487 | el.setAttribute(KPL_COLOR_MODEL_ID_ATTR, colorSpace->colorModelId().id()); | ||||
1488 | el.setAttribute(KPL_COLOR_DEPTH_ID_ATTR, colorSpace->colorDepthId().id()); | ||||
1489 | profileElement.appendChild(el); | ||||
1490 | | ||||
1491 | } | ||||
1492 | doc.appendChild(profileElement); | ||||
1493 | if (!store->open("profiles.xml")) { return false; } | ||||
1494 | QByteArray ba = doc.toByteArray(); | ||||
1495 | if (store->write(ba) != ba.size()) { return false; } | ||||
1496 | if (!store->close()) { return false; } | ||||
1497 | | ||||
1498 | return store->finalize(); | ||||
1499 | } | ||||
1500 | | ||||
1501 | void KoColorSet::Private::saveKplGroup(QDomDocument &doc, | ||||
1502 | QDomElement &groupEle, | ||||
1503 | const KisSwatchGroup *group, | ||||
1504 | QSet<const KoColorSpace *> &colorSetSet) const | ||||
1505 | { | ||||
1506 | groupEle.setAttribute(KPL_GROUP_ROW_COUNT_ATTR, QString::number(group->rowCount())); | ||||
1507 | | ||||
1508 | for (const SwatchInfoType &info : group->infoList()) { | ||||
1509 | const KoColorProfile *profile = info.swatch.color().colorSpace()->profile(); | ||||
1510 | // Only save non-builtin profiles.= | ||||
1511 | if (!profile->fileName().isEmpty()) { | ||||
1512 | colorSetSet.insert(info.swatch.color().colorSpace()); | ||||
1513 | } | ||||
1514 | QDomElement swatchEle = doc.createElement(KPL_SWATCH_TAG); | ||||
1515 | swatchEle.setAttribute(KPL_SWATCH_NAME_ATTR, info.swatch.name()); | ||||
1516 | swatchEle.setAttribute(KPL_SWATCH_ID_ATTR, info.swatch.id()); | ||||
1517 | swatchEle.setAttribute(KPL_SWATCH_SPOT_ATTR, info.swatch.spotColor() ? "true" : "false"); | ||||
1518 | swatchEle.setAttribute(KPL_SWATCH_BITDEPTH_ATTR, info.swatch.color().colorSpace()->colorDepthId().id()); | ||||
1519 | info.swatch.color().toXML(doc, swatchEle); | ||||
1520 | | ||||
1521 | QDomElement positionEle = doc.createElement(KPL_SWATCH_POS_TAG); | ||||
1522 | positionEle.setAttribute(KPL_SWATCH_ROW_ATTR, info.row); | ||||
1523 | positionEle.setAttribute(KPL_SWATCH_COL_ATTR, info.column); | ||||
1524 | swatchEle.appendChild(positionEle); | ||||
1525 | | ||||
1526 | groupEle.appendChild(swatchEle); | ||||
1527 | } | ||||
1528 | } | ||||
1529 | | ||||
1530 | void KoColorSet::Private::loadKplGroup(const QDomDocument &doc, const QDomElement &parentEle, KisSwatchGroup *group) | ||||
1531 | { | ||||
1532 | Q_UNUSED(doc); | ||||
1533 | if (!parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).isNull()) { | ||||
1534 | group->setRowCount(parentEle.attribute(KPL_GROUP_ROW_COUNT_ATTR).toInt()); | ||||
1535 | } | ||||
1536 | | ||||
1537 | for (QDomElement swatchEle = parentEle.firstChildElement(KPL_SWATCH_TAG); | ||||
1538 | !swatchEle.isNull(); | ||||
1539 | swatchEle = swatchEle.nextSiblingElement(KPL_SWATCH_TAG)) { | ||||
1540 | QString colorDepthId = swatchEle.attribute(KPL_SWATCH_BITDEPTH_ATTR, Integer8BitsColorDepthID.id()); | ||||
1541 | KisSwatch entry; | ||||
1542 | | ||||
1543 | entry.setColor(KoColor::fromXML(swatchEle.firstChildElement(), colorDepthId)); | ||||
1544 | entry.setName(swatchEle.attribute(KPL_SWATCH_NAME_ATTR)); | ||||
1545 | entry.setId(swatchEle.attribute(KPL_SWATCH_ID_ATTR)); | ||||
1546 | entry.setSpotColor(swatchEle.attribute(KPL_SWATCH_SPOT_ATTR, "false") == "true" ? true : false); | ||||
1547 | QDomElement positionEle = swatchEle.firstChildElement(KPL_SWATCH_POS_TAG); | ||||
1548 | if (!positionEle.isNull()) { | ||||
1549 | int rowNumber = positionEle.attribute(KPL_SWATCH_ROW_ATTR).toInt(); | ||||
1550 | int columnNumber = positionEle.attribute(KPL_SWATCH_COL_ATTR).toInt(); | ||||
1551 | if (columnNumber < 0 || | ||||
1552 | columnNumber >= colorSet->columnCount() || | ||||
1553 | rowNumber < 0 | ||||
1554 | ) { | ||||
1555 | warnPigment << "Swatch" << entry.name() | ||||
1556 | << "of palette" << colorSet->name() | ||||
1557 | << "has invalid position."; | ||||
1558 | continue; | ||||
1559 | } | ||||
1560 | group->setEntry(entry, columnNumber, rowNumber); | ||||
1561 | } else { | ||||
1562 | group->addEntry(entry); | ||||
1563 | } | ||||
1564 | } | ||||
1565 | } | ||||
Context not available. |