1
2
3 // TnzCore includes
4 #include "tsimplecolorstyles.h"
5 #include "timage_io.h"
6 #include "tconvert.h"
7 #include "tvectorimage.h"
8 #include "tpixelutils.h"
9 #include "tsystem.h"
10 #include "tstream.h"
11
12 // Qt includes
13 #include <QMutexLocker>
14
15 #include "tpalette.h"
16
17 #include <memory>
18 #include <sstream>
19
20 PERSIST_IDENTIFIER(TPalette, "palette")
21
22 TPersistDeclarationT<TPalette> auxPaletteDeclaration("vectorpalette");
23
24 DEFINE_CLASS_CODE(TPalette, 30)
25
26 //*************************************************************************************
27 // Local namespace stuff
28 //*************************************************************************************
29
30 namespace {
31
pointToString(const TColorStyle::PickedPosition & point)32 const std::string pointToString(const TColorStyle::PickedPosition &point) {
33 if (point.frame == 0)
34 return std::to_string(point.pos.x) + "," + std::to_string(point.pos.y);
35 else
36 return std::to_string(point.pos.x) + "," + std::to_string(point.pos.y) +
37 "," + std::to_string(point.frame);
38 }
39
40 // splitting string with ','
stringToPoint(const std::string & string)41 const TColorStyle::PickedPosition stringToPoint(const std::string &string) {
42 std::string buffer;
43 std::stringstream ss(string);
44 std::vector<std::string> result;
45 while (std::getline(ss, buffer, ',')) // split with comma
46 result.push_back(buffer);
47
48 int x = std::stoi(result[0]);
49 int y = std::stoi(result[1]);
50 int frame = 0;
51 if (result.size() == 3) // getting the third part of string - if any.
52 frame = std::stoi(result[2]);
53
54 return {TPoint(x, y), frame};
55 }
56
57 // convert refLevelFids to string for saving
fidsToString(const std::vector<TFrameId> & fids)58 std::string fidsToString(const std::vector<TFrameId> &fids) {
59 std::string str;
60 QList<int> numList;
61
62 for (const auto fid : fids) {
63 int num = fid.getNumber();
64 if (numList.isEmpty() || num == numList.last() + 1) {
65 numList.push_back(num);
66 continue;
67 }
68 // print
69 if (numList.count() == 1)
70 str += std::to_string(numList[0]) + ",";
71 else if (numList.count() == 2)
72 str +=
73 std::to_string(numList[0]) + "," + std::to_string(numList[1]) + ",";
74 else
75 str += std::to_string(numList[0]) + "-" + std::to_string(numList.last()) +
76 ",";
77
78 numList.clear();
79 numList.push_back(num);
80 }
81 if (numList.count() == 1)
82 str += std::to_string(numList[0]);
83 else if (numList.count() == 2)
84 str += std::to_string(numList[0]) + "," + std::to_string(numList[1]);
85 else
86 str += std::to_string(numList[0]) + "-" + std::to_string(numList.last());
87 return str;
88 }
89
90 // convert loaded string to refLevelFids
strToFids(std::string fidsStr)91 std::vector<TFrameId> strToFids(std::string fidsStr) {
92 std::vector<TFrameId> ret;
93 QString str = QString::fromStdString(fidsStr);
94 QStringList chunks = str.split(',', QString::SkipEmptyParts);
95 for (const auto &chunk : chunks) {
96 QStringList nums = chunk.split('-', QString::SkipEmptyParts);
97 assert(nums.count() > 0 && nums.count() <= 2);
98 if (nums.count() == 1)
99 ret.push_back(TFrameId(nums[0].toInt()));
100 else { // nums.count() == 2
101 assert(nums[0].toInt() < nums[1].toInt());
102 for (int i = nums[0].toInt(); i <= nums[1].toInt(); i++)
103 ret.push_back(TFrameId(i));
104 }
105 }
106 return ret;
107 }
108
109 } // namespace
110
111 //===================================================================
112 //
113 // TPalette::Page
114 //
115 //-------------------------------------------------------------------
116
Page(std::wstring name)117 TPalette::Page::Page(std::wstring name)
118 : m_name(name), m_index(-1), m_palette(0) {}
119
120 //-------------------------------------------------------------------
121
getStyle(int index) const122 TColorStyle *TPalette::Page::getStyle(int index) const {
123 assert(m_palette);
124 if (0 <= index && index < getStyleCount())
125 return m_palette->getStyle(m_styleIds[index]);
126 else
127 return 0;
128 }
129
130 //-------------------------------------------------------------------
131
getStyleId(int index) const132 int TPalette::Page::getStyleId(int index) const {
133 assert(m_palette);
134 if (0 <= index && index < getStyleCount())
135 return m_styleIds[index];
136 else
137 return -1;
138 }
139
140 //-------------------------------------------------------------------
141
addStyle(int styleId)142 int TPalette::Page::addStyle(int styleId) {
143 assert(m_palette);
144 if (styleId < 0 || styleId >= m_palette->getStyleCount()) return -1;
145 if (m_palette->m_styles[styleId].first != 0) return -1;
146 m_palette->m_styles[styleId].first = this;
147 int indexInPage = int(m_styleIds.size());
148 m_styleIds.push_back(styleId);
149 return indexInPage;
150 }
151
152 //-------------------------------------------------------------------
153
addStyle(TColorStyle * style)154 int TPalette::Page::addStyle(TColorStyle *style) {
155 assert(m_palette);
156 int stylesCount = int(m_palette->m_styles.size());
157 int styleId;
158 for (styleId = 0; styleId < stylesCount; styleId++)
159 if (m_palette->m_styles[styleId].first == 0) break;
160 if (styleId >= stylesCount - 1) return addStyle(m_palette->addStyle(style));
161 m_palette->setStyle(styleId, style);
162 return addStyle(styleId);
163 }
164
165 //-------------------------------------------------------------------
166
addStyle(TPixel32 color)167 int TPalette::Page::addStyle(TPixel32 color) {
168 return addStyle(new TSolidColorStyle(color));
169 }
170
171 //-------------------------------------------------------------------
172
insertStyle(int indexInPage,int styleId)173 void TPalette::Page::insertStyle(int indexInPage, int styleId) {
174 assert(m_palette);
175 if (styleId < 0 || styleId >= m_palette->getStyleCount()) return;
176 if (m_palette->m_styles[styleId].first != 0) return;
177 m_palette->m_styles[styleId].first = this;
178 if (indexInPage < 0)
179 indexInPage = 0;
180 else if (indexInPage > getStyleCount())
181 indexInPage = getStyleCount();
182 m_styleIds.insert(m_styleIds.begin() + indexInPage, styleId);
183 }
184
185 //-------------------------------------------------------------------
186
insertStyle(int indexInPage,TColorStyle * style)187 void TPalette::Page::insertStyle(int indexInPage, TColorStyle *style) {
188 assert(m_palette);
189 int styleId = m_palette->addStyle(style);
190 if (styleId >= 0) insertStyle(indexInPage, styleId);
191 }
192
193 //-------------------------------------------------------------------
194
insertStyle(int indexInPage,TPixel32 color)195 void TPalette::Page::insertStyle(int indexInPage, TPixel32 color) {
196 assert(m_palette);
197 int styleId = m_palette->addStyle(color);
198 if (styleId >= 0) insertStyle(indexInPage, styleId);
199 }
200
201 //-------------------------------------------------------------------
202
removeStyle(int indexInPage)203 void TPalette::Page::removeStyle(int indexInPage) {
204 if (indexInPage < 0 || indexInPage >= getStyleCount()) return;
205 assert(m_palette);
206 int styleId = getStyleId(indexInPage);
207 assert(0 <= styleId && styleId < m_palette->getStyleCount());
208 assert(m_palette->m_styles[styleId].first == this);
209 m_palette->m_styles[styleId].first = 0;
210 m_styleIds.erase(m_styleIds.begin() + indexInPage);
211 }
212
213 //-------------------------------------------------------------------
214
search(int styleId) const215 int TPalette::Page::search(int styleId) const {
216 std::vector<int>::const_iterator it =
217 std::find(m_styleIds.begin(), m_styleIds.end(), styleId);
218 if (it == m_styleIds.end())
219 return -1;
220 else
221 return it - m_styleIds.begin();
222 }
223
224 //-------------------------------------------------------------------
225
search(TColorStyle * style) const226 int TPalette::Page::search(TColorStyle *style) const {
227 assert(style);
228 assert(m_palette);
229 for (int i = 0; i < getStyleCount(); i++)
230 if (m_palette->getStyle(m_styleIds[i]) == style) return i;
231 return -1;
232 }
233
234 //===================================================================
235 //
236 // TPalette
237 //
238 //-------------------------------------------------------------------
239
TPalette()240 TPalette::TPalette()
241 : m_version(0)
242 , m_isCleanupPalette(false)
243 , m_currentFrame(-1)
244 , m_dirtyFlag(false)
245 , m_mutex(QMutex::Recursive)
246 , m_isLocked(false)
247 , m_askOverwriteFlag(false)
248 , m_shortcutScopeIndex(0)
249 , m_currentStyleId(1) {
250 QString tempName(QObject::tr("colors"));
251 std::wstring pageName = tempName.toStdWString();
252 Page *page = addPage(pageName);
253 page->addStyle(TPixel32(255, 255, 255, 0));
254 page->addStyle(TPixel32(0, 0, 0, 255));
255 getStyle(0)->setName(L"color_0");
256 getStyle(1)->setName(L"color_1");
257
258 for (int i = 0; i < 10; i++) m_shortcuts['0' + i] = i;
259 }
260
261 //-------------------------------------------------------------------
262
~TPalette()263 TPalette::~TPalette() {
264 std::set<TColorStyle *> table;
265 int i = 0;
266 for (i = 0; i < getStyleCount(); i++) {
267 assert(table.find(getStyle(i)) == table.end());
268 table.insert(getStyle(i));
269 }
270 clearPointerContainer(m_pages);
271 }
272
273 //-------------------------------------------------------------------
274
clone() const275 TPalette *TPalette::clone() const {
276 TPalette *palette = new TPalette;
277 palette->assign(this);
278 return palette;
279 }
280
281 //-------------------------------------------------------------------
282
getStyle(int index) const283 TColorStyle *TPalette::getStyle(int index) const {
284 if (0 <= index && index < getStyleCount())
285 return m_styles[index].second.getPointer();
286 else {
287 static TSolidColorStyle *ss = new TSolidColorStyle(TPixel32::Red);
288 ss->addRef();
289 return ss;
290 }
291 }
292
293 //-------------------------------------------------------------------
294
getStyleInPagesCount() const295 int TPalette::getStyleInPagesCount() const {
296 int styleInPagesCount = 0;
297 for (int i = 0; i < getStyleCount(); i++)
298 if (m_styles[i].first != 0) styleInPagesCount++;
299 return styleInPagesCount;
300 }
301
302 //-------------------------------------------------------------------
303
getFirstUnpagedStyle() const304 int TPalette::getFirstUnpagedStyle() const {
305 for (int i = 0; i < getStyleCount(); i++)
306 if (m_styles[i].first == 0) return i;
307 return -1;
308 }
309
310 //-------------------------------------------------------------------
311 /*! Adding style with new styleId. Even if there are deleted styles in the
312 * palette, the new style will be appended to the end of the list.
313 */
addStyle(TColorStyle * style)314 int TPalette::addStyle(TColorStyle *style) {
315 // limit the number of cleanup style to 7
316 if (isCleanupPalette() && getStyleInPagesCount() >= 8) return -1;
317
318 int styleId = int(m_styles.size());
319 if (styleId < 4096) {
320 // checking if the style is overlapped
321 int i = 0;
322 for (i = 0; i < styleId; i++)
323 if (getStyle(i) == style) break;
324 if (i == styleId) {
325 m_styles.push_back(std::make_pair((Page *)0, style));
326 return styleId;
327 }
328 }
329 delete style;
330 return -1;
331 }
332
333 //-------------------------------------------------------------------
334
addStyle(const TPixel32 & color)335 int TPalette::addStyle(const TPixel32 &color) {
336 return addStyle(new TSolidColorStyle(color));
337 }
338
339 //-------------------------------------------------------------------
340
setStyle(int styleId,TColorStyle * style)341 void TPalette::setStyle(int styleId, TColorStyle *style) {
342 std::unique_ptr<TColorStyle> styleOwner(style);
343
344 int styleCount = getStyleCount();
345
346 if (0 <= styleId && styleId < styleCount) {
347 // Find out if the supplied style is already in the palette
348 // with a different style id. In that case, bail out as a noop.
349 for (int i = 0; i < styleCount; ++i)
350 if (style == getStyle(i)) return;
351
352 // Substitution can take place
353 if (typeid(*m_styles[styleId].second.getPointer()) != typeid(*style))
354 m_styleAnimationTable.erase(styleId);
355
356 m_styles[styleId].second = styleOwner.release();
357 }
358 }
359
360 //-------------------------------------------------------------------
361
setStyle(int styleId,const TPixelRGBM32 & color)362 void TPalette::setStyle(int styleId, const TPixelRGBM32 &color) {
363 setStyle(styleId, new TSolidColorStyle(color));
364 }
365
366 //-------------------------------------------------------------------
367
getPageCount() const368 int TPalette::getPageCount() const { return int(m_pages.size()); }
369
370 //-------------------------------------------------------------------
371
getPage(int pageIndex)372 TPalette::Page *TPalette::getPage(int pageIndex) {
373 if (0 <= pageIndex && pageIndex < getPageCount()) {
374 Page *page = m_pages[pageIndex];
375 assert(page->getIndex() == pageIndex);
376 assert(page->m_palette == this);
377 return page;
378 } else
379 return 0;
380 }
381
382 //-------------------------------------------------------------------
383
getPage(int pageIndex) const384 const TPalette::Page *TPalette::getPage(int pageIndex) const {
385 if (0 <= pageIndex && pageIndex < getPageCount()) {
386 Page *page = m_pages[pageIndex];
387 assert(page->getIndex() == pageIndex);
388 assert(page->m_palette == this);
389 return page;
390 } else
391 return 0;
392 }
393
394 //-------------------------------------------------------------------
395
addPage(std::wstring name)396 TPalette::Page *TPalette::addPage(std::wstring name) {
397 Page *page = new Page(name);
398 page->m_index = getPageCount();
399 page->m_palette = this;
400 m_pages.push_back(page);
401 return page;
402 }
403
404 //-------------------------------------------------------------------
405
erasePage(int index)406 void TPalette::erasePage(int index) {
407 Page *page = getPage(index);
408 if (!page) return;
409 m_pages.erase(m_pages.begin() + index);
410 int i;
411 for (i = 0; i < getPageCount(); i++) m_pages[i]->m_index = i;
412 for (i = 0; i < page->getStyleCount(); i++)
413 m_styles[page->getStyleId(i)].first = 0;
414 page->m_palette = 0;
415 delete page;
416 }
417
418 //-------------------------------------------------------------------
419
movePage(Page * page,int dstPageIndex)420 void TPalette::movePage(Page *page, int dstPageIndex) {
421 assert(page);
422 assert(page->m_palette == this);
423 dstPageIndex = tcrop(dstPageIndex, 0, getPageCount() - 1);
424 if (dstPageIndex == page->getIndex()) return;
425 m_pages.erase(m_pages.begin() + page->getIndex());
426 m_pages.insert(m_pages.begin() + dstPageIndex, page);
427 for (int i = 0; i < getPageCount(); i++) m_pages[i]->m_index = i;
428 assert(page->getIndex() == dstPageIndex);
429 }
430
431 //-------------------------------------------------------------------
432
getStylePage(int styleId) const433 TPalette::Page *TPalette::getStylePage(int styleId) const {
434 if (0 <= styleId && styleId < getStyleCount())
435 return m_styles[styleId].first;
436 else
437 return 0;
438 }
439
440 //-------------------------------------------------------------------
441
getClosestStyle(const TPixel32 & color) const442 int TPalette::getClosestStyle(const TPixel32 &color) const {
443 struct locals {
444 static inline int getDistance2(const TPixel32 &a, const TPixel32 &b) {
445 return (a.r - b.r) * (a.r - b.r) + (a.g - b.g) * (a.g - b.g) +
446 (a.b - b.b) * (a.b - b.b) + (a.m - b.m) * (a.m - b.m);
447 }
448 }; // locals
449
450 if (color == TPixel32::Transparent) return 0;
451 int bestIndex = -1;
452 int bestDistance = 255 * 255 * 4 + 1;
453 for (int i = 1; i < (int)m_styles.size(); i++) {
454 // if(i==FirstUserStyle+2) continue;
455 TSolidColorStyle *scs =
456 dynamic_cast<TSolidColorStyle *>(m_styles[i].second.getPointer());
457 if (scs) {
458 int d = locals::getDistance2(scs->getMainColor(), color);
459 if (d < bestDistance) {
460 bestIndex = i;
461 bestDistance = d;
462 }
463 }
464 }
465 return bestIndex;
466 }
467
468 //-------------------------------------------------------------------
469
getFxRects(const TRect & rect,TRect & rectIn,TRect & rectOut)470 bool TPalette::getFxRects(const TRect &rect, TRect &rectIn, TRect &rectOut) {
471 int i;
472 bool ret = false;
473 int borderIn, borderOut, fullBorderIn = 0, fullBorderOut = 0;
474
475 for (i = 0; i < (int)m_styles.size(); i++)
476 if (m_styles[i].second->isRasterStyle()) {
477 m_styles[i].second->getRasterStyleFx()->getEnlargement(borderIn,
478 borderOut);
479 fullBorderIn = std::max(fullBorderIn, borderIn);
480 fullBorderOut = std::max(fullBorderOut, borderOut);
481 ret = true;
482 }
483
484 rectIn = rect.enlarge(fullBorderIn);
485 rectOut = rect.enlarge(fullBorderOut);
486 return ret;
487 }
488
489 //===================================================================
490 //
491 // I/O
492 //
493 //-------------------------------------------------------------------
494
495 namespace {
496
497 class StyleWriter final : public TOutputStreamInterface {
498 TOStream &m_os;
499 int m_index;
500
501 public:
502 static TFilePath m_rootDir;
StyleWriter(TOStream & os,int index)503 StyleWriter(TOStream &os, int index) : m_os(os), m_index(index) {}
setRootDir(const TFilePath & fp)504 static void setRootDir(const TFilePath &fp) { m_rootDir = fp; }
505
operator <<(double x)506 TOutputStreamInterface &operator<<(double x) override {
507 m_os << x;
508 return *this;
509 };
operator <<(int x)510 TOutputStreamInterface &operator<<(int x) override {
511 m_os << x;
512 return *this;
513 };
operator <<(std::string x)514 TOutputStreamInterface &operator<<(std::string x) override {
515 m_os << x;
516 return *this;
517 };
operator <<(UCHAR x)518 TOutputStreamInterface &operator<<(UCHAR x) override {
519 m_os << (int)x;
520 return *this;
521 };
operator <<(USHORT x)522 TOutputStreamInterface &operator<<(USHORT x) override {
523 m_os << (int)x;
524 return *this;
525 };
operator <<(const TPixel32 & x)526 TOutputStreamInterface &operator<<(const TPixel32 &x) override {
527 m_os << x;
528 return *this;
529 };
operator <<(const TRaster32P & ras)530 TOutputStreamInterface &operator<<(const TRaster32P &ras) override {
531 assert(m_rootDir != TFilePath());
532
533 std::string name = "texture_" + std::to_string(m_index);
534 m_os << name;
535 TFilePath filename = ((m_rootDir + "textures") + name).withType("bmp");
536 if (!TFileStatus(m_rootDir + "textures").doesExist()) {
537 try {
538 TSystem::mkDir(m_rootDir + "textures");
539 } catch (...) {
540 }
541 }
542
543 TImageWriter::save(filename, ras);
544 return *this;
545 };
546 };
547
548 //-------------------------------------------------------------------
549
550 class StyleReader final : public TInputStreamInterface {
551 TIStream &m_is; //!< Wrapped input stream.
552 VersionNumber m_version; //!< Palette version number (overrides m_is's one).
553
554 public:
555 static TFilePath m_rootDir;
556
557 public:
StyleReader(TIStream & is,const VersionNumber & version)558 StyleReader(TIStream &is, const VersionNumber &version)
559 : m_is(is), m_version(version) {}
560
setRootDir(const TFilePath & fp)561 static void setRootDir(const TFilePath &fp) { m_rootDir = fp; }
562
operator >>(double & x)563 TInputStreamInterface &operator>>(double &x) override {
564 m_is >> x;
565 return *this;
566 }
567
operator >>(int & x)568 TInputStreamInterface &operator>>(int &x) override {
569 m_is >> x;
570 return *this;
571 }
572
operator >>(std::string & x)573 TInputStreamInterface &operator>>(std::string &x) override {
574 m_is >> x;
575 return *this;
576 }
577
operator >>(UCHAR & x)578 TInputStreamInterface &operator>>(UCHAR &x) override {
579 int v;
580 m_is >> v;
581 x = v;
582 return *this;
583 }
584
operator >>(USHORT & x)585 TInputStreamInterface &operator>>(USHORT &x) override {
586 int v;
587 m_is >> v;
588 x = v;
589 return *this;
590 }
591
operator >>(TRaster32P & x)592 TInputStreamInterface &operator>>(TRaster32P &x) override {
593 assert(m_rootDir != TFilePath());
594 std::string name;
595 m_is >> name;
596 TFilePath filename = ((m_rootDir + "textures") + name).withType("bmp");
597 TRasterP ras;
598 if (TImageReader::load(filename, ras)) {
599 x = ras;
600 }
601 return *this;
602 }
603
operator >>(TPixel32 & x)604 TInputStreamInterface &operator>>(TPixel32 &x) override {
605 m_is >> x;
606 return *this;
607 }
608
609 /*!
610 \details Explicitly ovverrides the stream's version, returning m_version.
611 This is necessary since palettes have their \a own version number,
612 which is \a not the TIStream's file one.
613 */
versionNumber() const614 VersionNumber versionNumber() const override {
615 return m_version;
616 } //!< Returns the palette's version number.
617 };
618
619 TFilePath StyleWriter::m_rootDir = TFilePath();
620 TFilePath StyleReader::m_rootDir = TFilePath();
621
622 } // namespace
623
624 //===================================================================
625
setRootDir(const TFilePath & fp)626 void TPalette::setRootDir(const TFilePath &fp) {
627 StyleWriter::setRootDir(fp);
628 StyleReader::setRootDir(fp);
629 }
630
631 //-------------------------------------------------------------------
632
saveData(TOStream & os)633 void TPalette::saveData(TOStream &os) {
634 os.child("version") << 71 << 0; // Inserting the version tag at this level.
635 // This is necessary to support the tpl format
636 if (m_refImgPath !=
637 TFilePath()) { // since it performs *untagged* stream output
638 if (m_areRefLevelFidsSpecified) {
639 std::map<std::string, std::string> attr;
640 attr["fids"] = fidsToString(m_refLevelFids);
641 os.openChild("refImgPath", attr);
642 } else
643 os.openChild("refImgPath");
644 os << m_refImgPath; // (the palette is streamed directly).
645 os.closeChild();
646 }
647
648 os.openChild("styles");
649 {
650 for (int i = 0; i < getStyleCount(); ++i) {
651 TColorStyleP style = m_styles[i].second;
652 if (style->getPickedPosition().pos == TPoint())
653 os.openChild("style");
654 else {
655 std::map<std::string, std::string> attr;
656 attr["pickedpos"] = pointToString(style->getPickedPosition());
657 os.openChild("style", attr);
658 }
659 {
660 StyleWriter w(os, i);
661 style->save(w);
662 }
663 os.closeChild();
664 }
665 }
666 os.closeChild();
667
668 os.openChild("stylepages");
669 {
670 for (int i = 0; i < getPageCount(); ++i) {
671 Page *page = getPage(i);
672 os.openChild("page");
673 {
674 os.child("name") << page->getName();
675
676 os.openChild("indices");
677 {
678 int m = page->getStyleCount();
679
680 for (int j = 0; j < m; ++j) os << page->getStyleId(j);
681 }
682 os.closeChild();
683 }
684 os.closeChild();
685 }
686 }
687 os.closeChild();
688
689 if (isAnimated()) {
690 os.openChild("animation");
691 {
692 StyleAnimationTable::iterator sat, saEnd = m_styleAnimationTable.end();
693 for (sat = m_styleAnimationTable.begin(); sat != saEnd; ++sat) {
694 int styleId = sat->first;
695 StyleAnimation &animation = sat->second;
696
697 std::map<std::string, std::string> attributes;
698 attributes["id"] = std::to_string(styleId);
699
700 os.openChild("style", attributes);
701 {
702 StyleAnimation::iterator kt, kEnd = animation.end();
703 for (kt = animation.begin(); kt != kEnd; ++kt) {
704 int frame = kt->first;
705
706 TColorStyle *cs = kt->second.getPointer();
707 assert(cs);
708
709 attributes.clear();
710 attributes["frame"] = std::to_string(frame);
711
712 /*os.openChild("keycolor", attributes); // Up
713 to Toonz 7.0, animations saved os << cs->getMainColor(); // the main
714 color only os.closeChild();*/ //
715
716 os.openChild("keyframe", attributes);
717 {
718 StyleWriter w(os, sat->first);
719 kt->second->save(w);
720 }
721 os.closeChild();
722 }
723 }
724 os.closeChild();
725 }
726 }
727 os.closeChild();
728 }
729
730 // salvo gli shortcuts solo se sono non standard
731 int i;
732 for (i = 0; i < 10; ++i)
733 if (getShortcutValue('0' + i) != i) break;
734
735 if (i < 10) {
736 os.openChild("shortcuts");
737 {
738 for (i = 0; i < 10; i++) os << getShortcutValue('0' + i);
739 }
740 os.closeChild();
741 }
742 if (isLocked()) {
743 os.openChild("lock");
744 os << 1;
745 os.closeChild();
746 }
747 }
748
749 //-------------------------------------------------------------------
750
loadData(TIStream & is)751 void TPalette::loadData(TIStream &is) {
752 m_styles.clear();
753 clearPointerContainer(m_pages);
754
755 VersionNumber version = is.getVersion();
756
757 std::string tagName;
758 while (is.openChild(tagName)) {
759 if (tagName == "version") {
760 is >> version.first >> version.second;
761 if (version > VersionNumber(71, 0))
762 throw TException("palette, unsupported version number");
763 } else if (tagName == "styles") {
764 while (!is.eos()) // I think while(is.openChild(tagName))
765 { // would be better. However, I don't trust
766 if (!is.openChild(tagName) ||
767 tagName !=
768 "style") // TIStream's implementation very much. Keeping it
769 throw TException(
770 "palette, expected tag <style>"); // like this for now.
771 {
772 StyleReader r(is, version);
773 TColorStyle *cs = TColorStyle::load(r);
774
775 std::string pickedPosStr;
776 if (is.getTagParam("pickedpos", pickedPosStr))
777 cs->setPickedPosition(stringToPoint(pickedPosStr));
778
779 addStyle(cs);
780 }
781
782 is.closeChild();
783 }
784 } else if (tagName == "stylepages") {
785 while (!is.eos()) {
786 if (!is.openChild(tagName) || tagName != "page")
787 throw TException("palette, expected tag <stylepage>");
788 {
789 std::wstring pageName;
790
791 if (!is.openChild(tagName) || tagName != "name")
792 throw TException("palette, expected tag <name>");
793 { is >> pageName; }
794 is.closeChild();
795
796 Page *page = addPage(pageName);
797
798 if (!is.openChild(tagName) || tagName != "indices")
799 throw TException("palette, expected tag <indices>");
800 {
801 while (!is.eos()) {
802 int index;
803 is >> index;
804 page->addStyle(index);
805 }
806 }
807 is.closeChild();
808 }
809 is.closeChild();
810 }
811 } else if (tagName == "refImgPath") {
812 std::string fidsStr;
813 if (is.getTagParam("fids", fidsStr)) {
814 m_areRefLevelFidsSpecified = true;
815 m_refLevelFids = strToFids(fidsStr);
816 } else {
817 m_areRefLevelFidsSpecified = false;
818 m_refLevelFids.clear();
819 }
820 is >> m_refImgPath;
821 } else if (tagName == "animation") {
822 while (!is.eos()) {
823 if (!is.openChild(tagName) || tagName != "style")
824 throw TException("palette, expected tag <style>");
825 {
826 int styleId = 0;
827 if (!is.getTagParam("id", styleId))
828 throw TException("palette, missing id attribute in tag <style>");
829
830 StyleAnimation animation;
831
832 TColorStyle *style = getStyle(styleId);
833 assert(style);
834
835 while (is.matchTag(tagName)) {
836 TColorStyle *cs = 0;
837 int frame = 0;
838
839 if (tagName == "keycolor") {
840 if (!is.getTagParam("frame", frame))
841 throw TException(
842 "palette, missing frame attribute in tag <keycolor>");
843
844 TPixel32 color;
845 is >> color;
846
847 cs = style->clone();
848 cs->setMainColor(color);
849 } else if (tagName == "keyframe") {
850 if (!is.getTagParam("frame", frame))
851 throw TException(
852 "palette, missing frame attribute in tag <keyframe>");
853
854 StyleReader r(is, version);
855 cs = TColorStyle::load(r);
856 } else
857 throw TException("palette, expected <keyframe> tag");
858
859 animation[frame] = cs;
860 is.closeChild();
861 }
862
863 m_styleAnimationTable[styleId] = animation;
864 }
865 is.closeChild();
866 }
867 } else if (tagName == "stylepages") {
868 int key = '0';
869 while (!is.eos()) {
870 int styleId = 0;
871 is >> styleId;
872
873 if (key <= '9') setShortcutValue(key, styleId);
874 }
875 } else if (tagName == "shortcuts") {
876 for (int i = 0; i < 10; ++i) {
877 int v;
878 is >> v;
879
880 setShortcutValue('0' + i, v);
881 }
882 } else if (tagName == "lock") {
883 int lockValue;
884 is >> lockValue;
885 m_isLocked = (bool)lockValue;
886 } else
887 throw TException("palette, unknown tag: " + tagName);
888
889 is.closeChild();
890 }
891 }
892
893 //===================================================================
894
895 /*! if the palette is copied from studio palette, this function will modify the
896 * original names.
897 */
assign(const TPalette * src,bool isFromStudioPalette)898 void TPalette::assign(const TPalette *src, bool isFromStudioPalette) {
899 if (src == this) return;
900 int i;
901 m_isCleanupPalette = src->isCleanupPalette();
902 // for(i=0;i<getStyleCount();i++) delete getStyle(i);
903 m_styles.clear();
904 clearPointerContainer(m_pages);
905
906 for (i = 0; i < src->getStyleCount(); i++) {
907 TColorStyle *srcStyle = src->getStyle(i);
908 TColorStyle *dstStyle = srcStyle->clone();
909 dstStyle->setName(
910 srcStyle->getName()); // per un baco del TColorStyle::clone()
911 dstStyle->setGlobalName(
912 srcStyle->getGlobalName()); // per un baco del TColorStyle::clone()
913
914 // if the style is copied from studio palette, put its name to the original
915 // name.
916 // check if the style has the global name (i.e. it comes from studio
917 // palette)
918 if (isFromStudioPalette && srcStyle->getGlobalName() != L"") {
919 // If the original style has no original name (i.e. if the style is copied
920 // from the studio palette)
921 if (srcStyle->getOriginalName() == L"") {
922 // put the original style name to the "original name" of the pasted
923 // style.
924 dstStyle->setOriginalName(srcStyle->getName());
925 }
926 }
927
928 int j = addStyle(dstStyle);
929 assert(i == j);
930 }
931
932 for (i = 0; i < src->getPageCount(); i++) {
933 const Page *srcPage = src->getPage(i);
934 Page *dstPage = addPage(srcPage->getName());
935 for (int j = 0; j < srcPage->getStyleCount(); j++)
936 dstPage->addStyle(srcPage->getStyleId(j));
937 }
938 m_refImg = !!src->m_refImg ? src->m_refImg->cloneImage() : TImageP();
939 m_refImgPath = src->m_refImgPath;
940
941 StyleAnimationTable::iterator it;
942 StyleAnimation::iterator j;
943 for (it = m_styleAnimationTable.begin(); it != m_styleAnimationTable.end();
944 ++it) {
945 // for(j = it->second.begin(); j != it->second.end(); ++j)
946 // delete j->second;
947 it->second.clear();
948 }
949 m_styleAnimationTable.clear();
950 StyleAnimationTable::const_iterator cit;
951 for (cit = src->m_styleAnimationTable.begin();
952 cit != src->m_styleAnimationTable.end(); ++cit) {
953 StyleAnimation animation = cit->second;
954 for (j = animation.begin(); j != animation.end(); j++)
955 j->second = j->second->clone();
956 m_styleAnimationTable[cit->first] = cit->second;
957 }
958 m_globalName = src->getGlobalName();
959 m_shortcuts = src->m_shortcuts;
960 m_currentFrame = src->m_currentFrame;
961 m_shortcutScopeIndex = src->m_shortcutScopeIndex;
962 // setDirtyFlag(true);
963 }
964
965 //-------------------------------------------------------------------
966 /*!if the palette is merged from studio palette, this function will modify the
967 * original names.
968 */
merge(const TPalette * src,bool isFromStudioPalette)969 void TPalette::merge(const TPalette *src, bool isFromStudioPalette) {
970 std::map<int, int> table;
971 int i;
972 for (i = 1; i < src->getStyleCount(); i++) {
973 TColorStyle *srcStyle = src->getStyle(i);
974 TColorStyle *dstStyle = srcStyle->clone();
975 dstStyle->setName(srcStyle->getName());
976 dstStyle->setGlobalName(srcStyle->getGlobalName());
977
978 // if the style is copied from studio palette, put its name to the original
979 // name.
980 // check if the style has the global name (i.e. it comes from studio
981 // palette)
982 if (isFromStudioPalette && srcStyle->getGlobalName() != L"") {
983 // If the original style has no original name (i.e. if the style is copied
984 // from the studio palette)
985 if (srcStyle->getOriginalName() == L"") {
986 // put the original style name to the "original name" of the pasted
987 // style.
988 dstStyle->setOriginalName(srcStyle->getName());
989 }
990 }
991
992 int j = addStyle(dstStyle);
993 table[i] = j;
994 }
995
996 int pageCount = src->getPageCount();
997 for (i = 0; i < pageCount; i++) {
998 const Page *srcPage = src->getPage(i);
999 std::wstring pageName = srcPage->getName();
1000 if (pageName == L"colors" && src->getPaletteName() != L"")
1001 pageName = src->getPaletteName();
1002 Page *dstPage = addPage(pageName); //;
1003 for (int j = 0; j < srcPage->getStyleCount(); j++) {
1004 int styleId = srcPage->getStyleId(j);
1005 if (styleId == 0) continue;
1006 assert(table.find(styleId) != table.end());
1007 dstPage->addStyle(table[styleId]);
1008 }
1009 assert(dstPage->m_palette == this);
1010 }
1011 }
1012
1013 //-------------------------------------------------------------------
1014
setIsCleanupPalette(bool on)1015 void TPalette::setIsCleanupPalette(bool on) { m_isCleanupPalette = on; }
1016
1017 //-------------------------------------------------------------------
1018
setRefImg(const TImageP & img)1019 void TPalette::setRefImg(const TImageP &img) {
1020 m_refImg = img;
1021 if (img) {
1022 assert(img->getPalette() == 0);
1023 }
1024 }
1025
1026 //-------------------------------------------------------------------
1027
setRefLevelFids(const std::vector<TFrameId> fids,bool specified)1028 void TPalette::setRefLevelFids(const std::vector<TFrameId> fids,
1029 bool specified) {
1030 m_refLevelFids = fids;
1031 m_areRefLevelFidsSpecified = specified;
1032 }
1033
1034 //-------------------------------------------------------------------
1035
getRefLevelFids()1036 std::vector<TFrameId> TPalette::getRefLevelFids() { return m_refLevelFids; }
1037
1038 //-------------------------------------------------------------------
1039
setRefImgPath(const TFilePath & refImgPath)1040 void TPalette::setRefImgPath(const TFilePath &refImgPath) {
1041 m_refImgPath = refImgPath;
1042 }
1043
1044 //===================================================================
1045
isAnimated() const1046 bool TPalette::isAnimated() const { return !m_styleAnimationTable.empty(); }
1047
1048 //-------------------------------------------------------------------
1049
getFrame() const1050 int TPalette::getFrame() const { return m_currentFrame; }
1051
1052 //-------------------------------------------------------------------
1053
setFrame(int frame)1054 void TPalette::setFrame(int frame) {
1055 QMutexLocker muLock(&m_mutex);
1056
1057 if (m_currentFrame == frame) return;
1058
1059 m_currentFrame = frame;
1060
1061 StyleAnimationTable::iterator sat, saEnd = m_styleAnimationTable.end();
1062 for (sat = m_styleAnimationTable.begin(); sat != saEnd; ++sat) {
1063 StyleAnimation &animation = sat->second;
1064 assert(!animation.empty());
1065
1066 // Retrieve the associated style to interpolate
1067 int styleId = sat->first;
1068 assert(0 <= styleId && styleId < getStyleCount());
1069
1070 TColorStyle *cs = getStyle(styleId);
1071 assert(cs);
1072
1073 // Buid the keyframes interval containing frame
1074 StyleAnimation::iterator j0, j1;
1075
1076 j1 = animation.lower_bound(
1077 frame); // j1 is the first element: j1->first >= frame
1078 if (j1 == animation.begin())
1079 cs->copy(*j1->second);
1080 else {
1081 j0 = j1, --j0;
1082 assert(j0->first <= frame);
1083
1084 if (j1 == animation.end())
1085 cs->copy(*j0->second);
1086 else {
1087 assert(frame <= j1->first);
1088
1089 cs->assignBlend(*j0->second, *j1->second,
1090 (frame - j0->first) / double(j1->first - j0->first));
1091 }
1092 }
1093 }
1094 }
1095
1096 //-------------------------------------------------------------------
1097
isKeyframe(int styleId,int frame) const1098 bool TPalette::isKeyframe(int styleId, int frame) const {
1099 StyleAnimationTable::const_iterator it = m_styleAnimationTable.find(styleId);
1100 if (it == m_styleAnimationTable.end()) return false;
1101 return it->second.count(frame) > 0;
1102 }
1103
1104 //-------------------------------------------------------------------
1105
getKeyframeCount(int styleId) const1106 int TPalette::getKeyframeCount(int styleId) const {
1107 StyleAnimationTable::const_iterator it = m_styleAnimationTable.find(styleId);
1108 if (it == m_styleAnimationTable.end()) return 0;
1109 return int(it->second.size());
1110 }
1111
1112 //-------------------------------------------------------------------
1113
getKeyframe(int styleId,int index) const1114 int TPalette::getKeyframe(int styleId, int index) const {
1115 StyleAnimationTable::const_iterator it = m_styleAnimationTable.find(styleId);
1116 if (it == m_styleAnimationTable.end()) return -1;
1117 const StyleAnimation &animation = it->second;
1118 if (index < 0 || index >= (int)animation.size()) return -1;
1119 StyleAnimation::const_iterator j = animation.begin();
1120 std::advance(j, index);
1121 return j->first;
1122 }
1123
1124 //-------------------------------------------------------------------
1125
setKeyframe(int styleId,int frame)1126 void TPalette::setKeyframe(int styleId, int frame) {
1127 assert(styleId >= 0 && styleId < getStyleCount());
1128 assert(frame >= 0);
1129
1130 StyleAnimationTable::iterator sat = m_styleAnimationTable.find(styleId);
1131
1132 if (sat == m_styleAnimationTable.end())
1133 sat =
1134 m_styleAnimationTable.insert(std::make_pair(styleId, StyleAnimation()))
1135 .first;
1136
1137 assert(sat != m_styleAnimationTable.end());
1138
1139 StyleAnimation &animation = sat->second;
1140 animation[frame] = getStyle(styleId)->clone();
1141 }
1142
1143 //-------------------------------------------------------------------
1144
clearKeyframe(int styleId,int frame)1145 void TPalette::clearKeyframe(int styleId, int frame) {
1146 assert(0 <= styleId && styleId < getStyleCount());
1147 assert(0 <= frame);
1148 StyleAnimationTable::iterator it = m_styleAnimationTable.find(styleId);
1149 if (it == m_styleAnimationTable.end()) return;
1150 StyleAnimation &animation = it->second;
1151 StyleAnimation::iterator j = animation.find(frame);
1152 if (j == animation.end()) return;
1153 // j->second->release();
1154 animation.erase(j);
1155 if (animation.empty()) {
1156 m_styleAnimationTable.erase(styleId);
1157 }
1158 }
1159
1160 //-------------------------------------------------------------------
1161
getShortcutValue(int key) const1162 int TPalette::getShortcutValue(int key) const {
1163 assert(Qt::Key_0 <= key && key <= Qt::Key_9);
1164
1165 int shortcutIndex = (key == Qt::Key_0) ? 9 : key - Qt::Key_1;
1166 int indexInPage = m_shortcutScopeIndex * 10 + shortcutIndex;
1167 // shortcut is available only in the first page
1168 return getPage(0)->getStyleId(indexInPage);
1169 }
1170
1171 //-------------------------------------------------------------------
1172
getStyleShortcut(int styleId) const1173 int TPalette::getStyleShortcut(int styleId) const {
1174 assert(0 <= styleId && styleId < getStyleCount());
1175
1176 Page *page = getStylePage(styleId);
1177 // shortcut is available only in the first page
1178 if (!page || page->getIndex() != 0) return -1;
1179 int indexInPage = page->search(styleId);
1180 int shortcutIndex = indexInPage - m_shortcutScopeIndex * 10;
1181 if (shortcutIndex < 0 || shortcutIndex > 9) return -1;
1182 return (shortcutIndex == 9) ? Qt::Key_0 : Qt::Key_1 + shortcutIndex;
1183 }
1184
1185 //-------------------------------------------------------------------
1186
setShortcutValue(int key,int styleId)1187 void TPalette::setShortcutValue(int key, int styleId) {
1188 assert('0' <= key && key <= '9');
1189 assert(styleId == -1 || 0 <= styleId && styleId < getStyleCount());
1190 if (styleId == -1)
1191 m_shortcuts.erase(key);
1192 else {
1193 std::map<int, int>::iterator it;
1194 for (it = m_shortcuts.begin(); it != m_shortcuts.end(); ++it)
1195 if (it->second == styleId) {
1196 m_shortcuts.erase(it);
1197 break;
1198 }
1199 m_shortcuts[key] = styleId;
1200 }
1201 }
1202
1203 //-------------------------------------------------------------------
1204 // Returns true if there is at least one style with picked pos value
1205 //-------------------------------------------------------------------
1206
hasPickedPosStyle()1207 bool TPalette::hasPickedPosStyle() {
1208 for (int i = 0; i < getStyleCount(); ++i) {
1209 TColorStyleP style = m_styles[i].second;
1210 if (style->getPickedPosition().pos != TPoint()) return true;
1211 }
1212 return false;
1213 }
1214
1215 //-------------------------------------------------------------------
1216
nextShortcutScope(bool invert)1217 void TPalette::nextShortcutScope(bool invert) {
1218 if (invert) {
1219 if (m_shortcutScopeIndex > 0)
1220 m_shortcutScopeIndex -= 1;
1221 else
1222 m_shortcutScopeIndex = getPage(0)->getStyleCount() / 10;
1223 } else {
1224 if ((m_shortcutScopeIndex + 1) * 10 < getPage(0)->getStyleCount())
1225 m_shortcutScopeIndex += 1;
1226 else
1227 m_shortcutScopeIndex = 0;
1228 }
1229 }
1230