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