1 
2 
3 #pragma warning(disable : 4533)
4 
5 #include "tiio_std.h"
6 
7 #include "tlevel.h"
8 #include "trasterimage.h"
9 #include "ttoonzimage.h"
10 #include "trastercm.h"
11 #include "tnzimage.h"
12 #include "tsystem.h"
13 #include "trop.h"
14 #include "toonz/fill.h"
15 #include "toonz/autoclose.h"
16 #include "tenv.h"
17 #include "convert2tlv.h"
18 #include "tstream.h"
19 
20 #include <map>
21 
22 #include "toonz/toonzfolders.h"
23 
24 // gmt, 14/11/2013 removed a commented out blocks of code (void buildInks1(),
25 // void buildPalette() )
26 
27 extern TEnv::DoubleVar AutocloseDistance;
28 extern TEnv::DoubleVar AutocloseAngle;
29 extern TEnv::IntVar AutocloseOpacity;
30 
31 namespace {
32 //----------------------------------------------
33 
unmultiply(const TPixel & in)34 inline TPixel unmultiply(const TPixel &in) {
35   if (in.r == 255) return in;
36   TPixel out;
37   int val = in.r * 255 / in.m;
38   out.r   = tcrop(val, 0, 255);
39   val     = in.g * 255 / in.m;
40   out.g   = tcrop(val, 0, 255);
41   val     = in.b * 255 / in.m;
42   out.b   = tcrop(val, 0, 255);
43   out.m   = 255;
44   return out;
45 }
46 
47 //----------------------------------------------
48 
distance(const TPixel & c1,const TPixel & c2)49 inline int distance(const TPixel &c1, const TPixel &c2) {
50   return (c1.r - c2.r) * (c1.r - c2.r) + (c1.g - c2.g) * (c1.g - c2.g) +
51          (c1.b - c2.b) * (c1.b - c2.b);
52 }
53 
54 //----------------------------------------------
55 
findClosest(const std::map<TPixel,int> & colorMap,TPixel & curPixColor)56 int findClosest(const std::map<TPixel, int> &colorMap, TPixel &curPixColor) {
57   std::map<TPixel, int>::const_iterator it = colorMap.begin();
58   int minDistance                          = 1000000000;
59   int index                                = -1;
60   for (; it != colorMap.end(); ++it) {
61     int dist = distance(it->first, curPixColor);
62     if (dist < minDistance) {
63       minDistance = dist;
64       index       = it->second;
65     }
66   }
67   assert(index != -1);
68   return index;
69 }
70 
71 //----------------------------------------------
72 
73 #define CHECKCOLOR(r, x, y, tone)                                              \
74   {                                                                            \
75     TPixelCM32 color = *((TPixelCM32 *)r->pixels(y) + (x));                    \
76     if (color.getTone() == tone /*&& color.getPaint()!=0*/)                    \
77       return TPoint(x, y);                                                     \
78   }
79 
80 // cerca in quadrati concentrici di raggio rad intorno al pixel in  (x, y)
81 // il primo pixel di paint puro e ritorna il suo indice di paint
82 
getClosestToneValue(const TRasterCM32P & r,int y,int x,int tone)83 TPoint getClosestToneValue(const TRasterCM32P &r, int y, int x, int tone) {
84   int maxRad = std::min({x, r->getLx() - x - 1, y, r->getLy() - y - 1});
85 
86   for (int rad = 1; rad < maxRad; rad++) {
87     CHECKCOLOR(r, x, y - rad, tone)
88     CHECKCOLOR(r, x, y + rad, tone)
89     CHECKCOLOR(r, x - rad, y, tone)
90     CHECKCOLOR(r, x + rad, y, tone)
91     for (int j = 1; j <= rad; j++) {
92       CHECKCOLOR(r, x - j, y - rad, tone)
93       CHECKCOLOR(r, x + j, y - rad, tone)
94       CHECKCOLOR(r, x - j, y + rad, tone)
95       CHECKCOLOR(r, x + j, y + rad, tone)
96 
97       CHECKCOLOR(r, x - rad, y - j, tone)
98       CHECKCOLOR(r, x - rad, y + j, tone)
99       CHECKCOLOR(r, x + rad, y - j, tone)
100       CHECKCOLOR(r, x + rad, y + j, tone)
101     }
102   }
103   return TPoint(-1, -1);
104 }
105 
106 //--------------------------------------------------
107 
getClosestPurePaint(const TRasterCM32P & r,int y,int x)108 TPoint getClosestPurePaint(const TRasterCM32P &r, int y, int x) {
109   return getClosestToneValue(r, y, x, 255);
110 }
111 
112 //----------------------------------------------------
113 
getClosestPureInk(const TRasterCM32P & r,int y,int x)114 TPoint getClosestPureInk(const TRasterCM32P &r, int y, int x) {
115   return getClosestToneValue(r, y, x, 0);
116 }
117 
118 //----------------------------------------------------
119 
firstIsUnpainted(const TRaster32P & r1,const TRaster32P & r2)120 bool firstIsUnpainted(const TRaster32P &r1, const TRaster32P &r2) {
121   for (int i = 0; i < r1->getLy(); i++) {
122     TPixel32 *pix1 = r1->pixels(i);
123     TPixel32 *pix2 = r2->pixels(i);
124     for (int j = 0; j < r1->getLx(); j++, pix1++, pix2++) {
125       if (pix1->m == 255 && pix2->m == 0)
126         return false;
127       else if (pix1->m == 0 && pix2->m == 255)
128         return true;
129     }
130   }
131   return true;
132 }
133 
134 //----------------------------------------------
135 // ritorna -1 se non ha il canale di matte (tutti i pixel a 255)
136 
getMaxMatte(const TRaster32P & r)137 int getMaxMatte(const TRaster32P &r) {
138   int maxMatte   = -1;
139   bool withMatte = false;
140   for (int i = 0; i < r->getLy(); i++) {
141     TPixel32 *pix = r->pixels(i);
142     for (int j = 0; j < r->getLx(); j++, pix++) {
143       maxMatte = std::max(maxMatte, (int)pix->m);
144       if (pix->m != 255) withMatte = true;
145     }
146   }
147   return withMatte ? maxMatte : -1;
148 }
149 
150 //----------------------------------------------
151 
normalize(const TRaster32P & r,int maxMatte)152 void normalize(const TRaster32P &r, int maxMatte) {
153   int val;
154 
155   for (int i = 0; i < r->getLy(); i++) {
156     TPixel32 *pix = r->pixels(i);
157     for (int j = 0; j < r->getLx(); j++, pix++) {
158       val    = pix->r * 255 / maxMatte;
159       pix->r = tcrop(val, 0, 255);
160       val    = pix->g * 255 / maxMatte;
161       pix->g = tcrop(val, 0, 255);
162       val    = pix->b * 255 / maxMatte;
163       pix->b = tcrop(val, 0, 255);
164       val    = pix->m * 255 / maxMatte;
165       pix->m = tcrop(val, 0, 255);
166     }
167   }
168 }
169 
170 //----------------------------------------------------
171 
getFramesCount(const TLevelP & l,int from,int to)172 int getFramesCount(const TLevelP &l, int from, int to) {
173   if (from == -1) return l->getFrameCount();
174 
175   int count           = 0;
176   TLevel::Iterator it = l->begin();
177   while (it != l->end() && it->first.getNumber() < from) it++;
178   while (it != l->end() && it->first.getNumber() <= to) it++, count++;
179   return count;
180 }
181 }  // namespace
182 // namespace
183 
findNearestColor(const TPixel & color)184 std::map<TPixel, int>::const_iterator Convert2Tlv::findNearestColor(
185     const TPixel &color) {
186   // assert((int)colorMap.size()>toIndex);
187   // assert((int)colorMap.size()>fromIndex);
188   std::map<TPixel, int>::const_iterator ret = m_colorMap.end(),
189                                         it  = m_colorMap.begin();
190   // std::advance(it, fromIndex);
191 
192   int mindist = 1000;
193   for (; it != m_colorMap.end(); ++it) {
194     const TPixel &curr = it->first;
195     int dr             = abs(curr.r - color.r);
196     if (dr > m_colorTolerance) continue;
197     int dg = abs(curr.g - color.g);
198     if (dg > m_colorTolerance) continue;
199     int db = abs(curr.b - color.b);
200     if (db > m_colorTolerance) continue;
201     int dist = dr + dg + db;
202     if (dist < mindist) {
203       mindist = dist;
204       ret     = it;
205     }
206   }
207   return ret;
208 }
209 
210 //-------------------------------------------------------------------
211 
buildInks(TRasterCM32P & rout,const TRaster32P & rin)212 void Convert2Tlv::buildInks(TRasterCM32P &rout, const TRaster32P &rin) {
213   std::map<TPixel, int>::const_iterator it;
214   TPixel curColor = TPixel::Transparent;
215   int i, j;
216   int curIndex;
217 
218   // prima passata: identifico i colori di inchiostro e metto in rout i pixel di
219   // inchiostro puro
220   for (i = 0; i < rin->getLy(); i++) {
221     TPixel *pixin      = rin->pixels(i);
222     TPixelCM32 *pixout = rout->pixels(i);
223     for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) {
224       TPixel colorIn;
225 
226       if (pixin->m != 255) continue;
227 
228       if (curColor != *pixin) {
229         curColor = *pixin;
230         if ((it = m_colorMap.find(curColor)) == m_colorMap.end()) {
231           if (m_colorTolerance > 0) it = findNearestColor(curColor);
232           // if (it==colorMap.end() && (int)colorMap.size()>origColorCount)
233           //	it  = findNearestColor(curColor, colorMap, colorTolerance,
234           // origColorCount, colorMap.size()-1);
235           if (it == m_colorMap.end() && m_lastIndex < 4095) {
236             m_colorMap[curColor] = ++m_lastIndex;
237             curIndex             = m_lastIndex;
238           } else if (it != m_colorMap.end()) {
239             m_colorMap[curColor] = it->second;
240             curIndex             = it->second;
241           }
242         } else
243           curIndex = it->second;
244       }
245       *pixout = TPixelCM32(curIndex, 0, 0);
246     }
247   }
248 
249   // seconda  passata: metto gli inchiostri di antialiasing
250   curColor = TPixel::Transparent;
251 
252   for (i = 0; i < rin->getLy(); i++) {
253     TPixel *pixin      = rin->pixels(i);
254     TPixelCM32 *pixout = rout->pixels(i);
255     for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) {
256       TPixel colorIn;
257       if (pixin->m == 255)  // gia' messo nel ciclo precedente
258         continue;
259       if (pixin->m == 0) continue;
260 
261       colorIn = unmultiply(*pixin);  // findClosestOpaque(rin, i, j);
262 
263       if (curColor != colorIn) {
264         curColor = colorIn;
265         if ((it = m_colorMap.find(curColor)) != m_colorMap.end())
266           curIndex = it->second;
267         else
268           curIndex = findClosest(m_colorMap, curColor);
269       }
270       *pixout = TPixelCM32(curIndex, 0, 255 - pixin->m);
271     }
272   }
273 }
274 
275 //----------------------------------------------
276 
removeAntialias(TRasterCM32P & r)277 void Convert2Tlv::removeAntialias(TRasterCM32P &r) {
278   int threshold = (int)(m_antialiasValue * 255.0 / 100.0);
279   int tone;
280   for (int i = 0; i < r->getLy(); i++) {
281     TPixelCM32 *pix = r->pixels(i);
282     for (int j = 0; j < r->getLx(); j++, pix++)
283       if ((tone = pix->getTone()) !=
284           0xff)  // tone==ff e tone==0 non vanno toccati mai
285         pix->setTone(tone > threshold ? 0xff : 0);
286   }
287 }
288 
289 //------------------------------------------------------------------
290 
buildInksFromGrayTones(TRasterCM32P & rout,const TRasterP & rin)291 void Convert2Tlv::buildInksFromGrayTones(TRasterCM32P &rout,
292                                          const TRasterP &rin) {
293   int i, j;
294 
295   TRasterGR8P r8 = (TRasterGR8P)rin;
296   TRaster32P r32 = (TRaster32P)rin;
297   if (r8)
298     for (i = 0; i < rin->getLy(); i++) {
299       TPixelGR8 *pixin   = r8->pixels(i);
300       TPixelCM32 *pixout = rout->pixels(i);
301       for (j = 0; j < rin->getLx(); j++, pixin++, pixout++)
302         *pixout = TPixelCM32(1, 0, pixin->value);
303     }
304   else
305     for (i = 0; i < rin->getLy(); i++) {
306       TPixel *pixin      = r32->pixels(i);
307       TPixelCM32 *pixout = rout->pixels(i);
308       for (j = 0; j < rin->getLx(); j++, pixin++, pixout++)
309         *pixout = TPixelCM32(1, 0, TPixelGR8::from(*pixin).value);
310     }
311 }
312 
313 //----------------------------------------------------------------------
314 
buildInksForNAAImage(TRasterCM32P & rout,const TRaster32P & rin)315 void Convert2Tlv::buildInksForNAAImage(TRasterCM32P &rout,
316                                        const TRaster32P &rin) {
317   std::map<TPixel, int>::iterator it;
318   TPixel curColor = TPixel::Transparent;
319   int i, j;
320   int curIndex;
321 
322   // prima passata: identifico i colori di inchiostro e metto in rout i pixel di
323   // inchiostro puro
324   for (i = 0; i < rin->getLy(); i++) {
325     TPixel *pixin      = rin->pixels(i);
326     TPixelCM32 *pixout = rout->pixels(i);
327     for (j = 0; j < rin->getLx(); j++, pixin++, pixout++) {
328       TPixel colorIn;
329 
330       /*- treat white/transparent pixels as transparent -*/
331       if (*pixin == TPixel(255, 255, 255) || *pixin == TPixel::Transparent) {
332         *pixout = TPixelCM32(0, 0, 255);
333         continue;
334       }
335 
336       if (curColor != *pixin) {
337         curColor = *pixin;
338         if ((it = m_colorMap.find(curColor)) == m_colorMap.end()) {
339           if (m_lastIndex < 4095) m_colorMap[curColor] = ++m_lastIndex;
340           curIndex = m_lastIndex;
341         } else
342           curIndex = it->second;
343       }
344       *pixout = TPixelCM32(curIndex, 0, 0);
345     }
346   }
347 
348   if (m_colorMap.empty()) m_colorMap[TPixel::Black] = ++m_lastIndex;
349 }
350 
351 //----------------------------------------------
352 
doFill(TRasterCM32P & rout,const TRaster32P & rin)353 void Convert2Tlv::doFill(TRasterCM32P &rout, const TRaster32P &rin) {
354   // prima passata: si filla  solo partendo da pixel senza inchiostro, senza
355   // antialiasing(tone==255)
356   for (int i = 0; i < rin->getLy(); i++) {
357     TPixel *pixin      = rin->pixels(i);
358     TPixelCM32 *pixout = rout->pixels(i);
359     for (int j = 0; j < rin->getLx(); j++, pixin++, pixout++) {
360       if (!(pixout->getTone() == 255 && pixout->getPaint() == 0 &&
361             pixin->m == 255))
362         continue;
363 
364       std::map<TPixel, int>::const_iterator it;
365       int paintIndex;
366       if ((it = m_colorMap.find(*pixin)) == m_colorMap.end()) {
367         if (m_colorTolerance > 0) it = findNearestColor(*pixin);
368         // if (it==colorMap.end() && (int)colorMap.size()>origColorCount) //se
369         // non l'ho trovato tra i colori origari, lo cerco in quelli nuovi, ma
370         // in questo caso deve essere esattamente uguale(tolerance = 0)
371         //	 it  = findNearestColor(*pixin, colorMap, colorTolerance,
372         // origColorCount, colorMap.size()-1);
373 
374         if (it == m_colorMap.end() && m_lastIndex < 4096) {
375           m_colorMap[*pixin] = ++m_lastIndex;
376           paintIndex         = m_lastIndex;
377         } else if (it != m_colorMap.end()) {
378           m_colorMap[*pixin] = it->second;
379           paintIndex         = it->second;
380         }
381       } else
382         paintIndex = it->second;
383       FillParameters params;
384       params.m_p         = TPoint(j, i);
385       params.m_styleId   = paintIndex;
386       params.m_emptyOnly = true;
387       fill(rout, params);
388       // if (*((ULONG *)rout->getRawData())!=0xff)
389       //  {
390       //  int cavolo=0;
391       //  }
392     }
393   }
394 
395   // seconda passata: se son rimasti pixel antialiasati non fillati, si fillano,
396   // cercando nelle vicinanze un pixel di paint puro per capire il colore da
397   // usare
398   for (int i = 0; i < rin->getLy(); i++) {
399     TPixel *pixin      = rin->pixels(i);
400     TPixelCM32 *pixout = rout->pixels(i);
401     for (int j = 0; j < rin->getLx(); j++, pixin++, pixout++) {
402       if (!(pixout->getTone() > 0 && pixout->getTone() < 255 &&
403             pixout->getPaint() == 0 && pixin->m == 255))
404         continue;
405 
406       TPoint p = getClosestPurePaint(rout, i, j);
407       if (p.x == -1) continue;
408 
409       // pixout->setPaint( paintIndex);
410       FillParameters params;
411       params.m_p         = TPoint(j, i);
412       params.m_styleId   = (rout->pixels(p.y) + p.x)->getPaint();
413       params.m_emptyOnly = true;
414 
415       fill(rout, params);
416     }
417   }
418 
419   // infine, si filla di trasparente lo sfondo, percorrendo il bordo, nel caso
420   // di trasbordamenti di colore
421   TPixelCM32 *pixCm;
422   TPixel *pix;
423 
424   pixCm = rout->pixels(0);
425   pix   = rin->pixels(0);
426   FillParameters params;
427   params.m_styleId = 0;
428 
429   for (int i = 0; i < rout->getLx(); i++, pixCm++, pix++)
430     if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) {
431       params.m_p = TPoint(i, 0);
432       fill(rout, params);
433     }
434 
435   pixCm = rout->pixels(rout->getLy() - 1);
436   pix   = rin->pixels(rout->getLy() - 1);
437   for (int i = 0; i < rout->getLx(); i++, pixCm++, pix++)
438     if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) {
439       params.m_p = TPoint(i, rout->getLy() - 1);
440       fill(rout, params);
441     }
442   int wrapCM = rout->getWrap();
443   int wrap   = rin->getWrap();
444 
445   pixCm = rout->pixels(0);
446   pix   = rin->pixels(0);
447   for (int i = 0; i < rin->getLy(); i++, pixCm += wrapCM, pix += wrap)
448     if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) {
449       params.m_p = TPoint(0, i);
450       fill(rout, params);
451     }
452   pixCm = rout->pixels(0) + rout->getLx() - 1;
453   pix   = rin->pixels(0) + rin->getLx() - 1;
454   for (int i = 0; i < rin->getLy(); i++, pixCm += wrapCM, pix += wrap)
455     if (pixCm->getTone() == 255 && pixCm->getPaint() != 0 && pix->m == 0) {
456       params.m_p = TPoint(rout->getLx() - 1, i);
457       fill(rout, params);
458     }
459 }
460 
461 //----------------------------------------------
462 
buildToonzRaster(TRasterCM32P & rout,const TRasterP & rin1,const TRasterP & rin2)463 void Convert2Tlv::buildToonzRaster(TRasterCM32P &rout, const TRasterP &rin1,
464                                    const TRasterP &rin2) {
465   if (rin2) assert(rin1->getSize() == rin2->getSize());
466 
467   rout->clear();
468 
469   std::cout << "      computing inks...\n";
470   TRaster32P r1    = (TRaster32P)rin1;
471   TRasterGR8P r1gr = (TRasterGR8P)rin1;
472   TRaster32P r2    = (TRaster32P)rin2;
473   TRasterGR8P r2gr = (TRasterGR8P)rin2;
474   TRasterP rU, rP;
475 
476   if (r1gr) {
477     rU = r1gr;
478     rP = r2;
479   } else if (r2gr) {
480     rU = r2gr;
481     rP = r1;
482   } else if (!r1)
483     rU = r2;
484   else if (!r2)
485     rU = r1;
486   else if (firstIsUnpainted(r1, r2)) {
487     rU = r1;
488     rP = r2;
489   } else {
490     rU = r2;
491     rP = r1;
492   }
493 
494   TRasterCM32P r;
495   if (rout->getSize() != rU->getSize()) {
496     int dx = rout->getLx() - rU->getLx();
497     int dy = rout->getLy() - rU->getLy();
498     assert(dx >= 0 && dy >= 0);
499 
500     r = rout->extract(dx / 2, dy / 2, dx / 2 + rU->getLx() - 1,
501                       dy / 2 + rU->getLy() - 1);
502   } else
503     r = rout;
504 
505   if ((TRasterGR8P)rU)
506     buildInksFromGrayTones(r, rU);
507   else if (m_isUnpaintedFromNAA)
508     buildInksForNAAImage(r, (TRaster32P)rU);
509   else {
510     int maxMatte = getMaxMatte((TRaster32P)rU);
511     if (maxMatte == -1)
512       buildInksFromGrayTones(r, rU);
513     else if (maxMatte == 0)  // empty frame doesn't need further computation
514       return;
515     else {
516       if (maxMatte < 255) normalize(rU, maxMatte);
517       buildInks(r, (TRaster32P)rU /*rP,*/);
518     }
519   }
520 
521   if (m_autoclose)
522     TAutocloser(r, AutocloseDistance, AutocloseAngle, 1, AutocloseOpacity)
523         .exec();
524 
525   if (rP) {
526     std::cout << "      computing paints...\n";
527     doFill(r, rP);
528   }
529   if (m_antialiasType == 2)  // remove antialias
530     removeAntialias(r);
531   else if (m_antialiasType == 1)  // add antialias
532   {
533     TRasterCM32P raux(r->getSize());
534     TRop::antialias(r, raux, 10, m_antialiasValue);
535     rout = raux;
536   }
537 }
538 
539 //----------------------------------------------
540 
buildPalette()541 TPalette *Convert2Tlv::buildPalette() {
542   std::map<TPixel, int>::const_iterator it = m_colorMap.begin();
543   TPalette::Page *page                     = m_palette->getPage(0);
544 
545   QList<int> stylesToBeAddedToPage;
546 
547   for (; it != m_colorMap.end(); ++it) {
548     if (it->second >
549         m_maxPaletteIndex)  // colore nuovo da aggiungere alla paletta)
550     {
551       if (m_palette->getStyleCount() > it->second)
552         m_palette->setStyle(it->second, it->first);
553       else {
554         while (m_palette->getStyleCount() < it->second)
555           m_palette->addStyle(TPixel::Transparent);
556         int id = m_palette->addStyle(it->first);
557         assert(id == it->second);
558       }
559     }
560     if (!m_palette->getStylePage(it->second))
561       stylesToBeAddedToPage.push_back(it->second);
562   }
563 
564   /*- インデックス順にページに格納する -*/
565   if (!stylesToBeAddedToPage.isEmpty()) {
566     std::sort(stylesToBeAddedToPage.begin(), stylesToBeAddedToPage.end());
567     for (int s = 0; s < stylesToBeAddedToPage.size(); s++)
568       page->addStyle(stylesToBeAddedToPage.at(s));
569   }
570 
571   /*
572     If the palette path is empty, an initial palette with four colors are set in
573     the palette here.
574     ( see Convert2Tlv::init() ) So here I make the latter three styles in the
575     initial palette to set
576     "AutoPaint" options.
577   */
578   if (m_palettePath.isEmpty()) {
579     assert(m_palette->getStyleCount() >= 5);
580     for (int id = 2; id <= 4; id++) m_palette->getStyle(id)->setFlags(1);
581   }
582 
583   if (!m_appendDefaultPalette) return m_palette;
584 
585   /*-- Adding styles in the default palette to the result palette, starts here
586    * --*/
587   TFilePath palettePath =
588       ToonzFolder::getStudioPaletteFolder() + "cleanup_default.tpl";
589   TFileStatus pfs(palettePath);
590 
591   if (!pfs.doesExist() || !pfs.isReadable()) return m_palette;
592 
593   TIStream is(palettePath);
594   if (!is) return m_palette;
595 
596   std::string tagName;
597   if (!is.matchTag(tagName) || tagName != "palette") return m_palette;
598 
599   std::string gname;
600   is.getTagParam("name", gname);
601   TPalette *defaultPalette = new TPalette();
602   defaultPalette->loadData(is);
603 
604   m_palette->setIsCleanupPalette(false);
605 
606   TPalette::Page *dstPage = m_palette->getPage(0);
607   TPalette::Page *srcPage = defaultPalette->getPage(0);
608 
609   for (int srcIndexInPage = 0; srcIndexInPage < srcPage->getStyleCount();
610        srcIndexInPage++) {
611     int id = srcPage->getStyleId(srcIndexInPage);
612 
613     bool isUsedInDstPalette = false;
614 
615     for (int dstIndexInPage = 0; dstIndexInPage < dstPage->getStyleCount();
616          dstIndexInPage++) {
617       if (dstPage->getStyleId(dstIndexInPage) == id) {
618         isUsedInDstPalette = true;
619         break;
620       }
621     }
622 
623     if (isUsedInDstPalette)
624       continue;
625     else {
626       int addedId =
627           m_palette->addStyle(srcPage->getStyle(srcIndexInPage)->clone());
628       dstPage->addStyle(addedId);
629       /*-- StudioPalette由来のDefaultPaletteの場合、GrobalNameを消去する --*/
630       m_palette->getStyle(addedId)->setGlobalName(L"");
631       m_palette->getStyle(addedId)->setOriginalName(L"");
632     }
633   }
634   delete defaultPalette;
635   /*-- Adding styles in the default palette to the result palette, ends here
636    * --*/
637 
638   return m_palette;
639 }
640 
641 //------------------------------------------------------------------------------
642 
Convert2Tlv(const TFilePath & filepath1,const TFilePath & filepath2,const TFilePath & outFolder,const QString & outName,int from,int to,bool doAutoclose,const TFilePath & palettePath,int colorTolerance,int antialiasType,int antialiasValue,bool isUnpaintedFromNAA,bool appendDefaultPalette,double dpi)643 Convert2Tlv::Convert2Tlv(const TFilePath &filepath1, const TFilePath &filepath2,
644                          const TFilePath &outFolder, const QString &outName,
645                          int from, int to, bool doAutoclose,
646                          const TFilePath &palettePath, int colorTolerance,
647                          int antialiasType, int antialiasValue,
648                          bool isUnpaintedFromNAA, bool appendDefaultPalette,
649                          double dpi)
650     : m_size(0, 0)
651     , m_level1()
652     , m_levelIn1()
653     , m_levelIn2()
654     , m_levelOut()
655     , m_autoclose(doAutoclose)
656     , m_premultiply(false)
657     , m_count(0)
658     , m_from(from)
659     , m_to(to)
660     , m_palettePath(palettePath)
661     , m_colorTolerance(colorTolerance)
662     , m_palette(0)
663     , m_antialiasType(antialiasType)
664     , m_antialiasValue(antialiasValue)
665     , m_isUnpaintedFromNAA(isUnpaintedFromNAA)
666     , m_appendDefaultPalette(appendDefaultPalette)
667     , m_dpi(dpi) {
668   if (filepath1 != TFilePath()) {
669     m_levelIn1 = filepath1.getParentDir() + filepath1.getLevelName();
670     if (outFolder != TFilePath())
671       m_levelOut =
672           m_levelIn1.withParentDir(outFolder).withNoFrame().withType("tlv");
673     else
674       m_levelOut = m_levelIn1.withNoFrame().withType(
675           "tlv");  // filePaths[0].getParentDir() +
676                    // TFilePath(filePaths[0].getWideName() + L".tlv");
677 
678     if (outName != "") m_levelOut = m_levelOut.withName(outName.toStdString());
679   }
680 
681   if (filepath2 != TFilePath())
682     m_levelIn2 = filepath2.getParentDir() + filepath2.getLevelName();
683 }
684 
685 //-------------------------------------------------------------------------------------
686 
getFramesToConvertCount()687 int Convert2Tlv::getFramesToConvertCount() {
688   if (m_level1 && m_level1->getFrameCount() > 0)
689     return getFramesCount(m_level1, m_from,
690                           m_to);  // m_level1->getFrameCount();
691   else {
692     try {
693       TLevelReaderP lr = TLevelReaderP(m_levelIn1);
694       if (lr) {
695         TLevelP l = lr->loadInfo();
696         if (l) {
697           return getFramesCount(l, m_from, m_to);
698         }
699       }
700     } catch (...) {
701       return 0;
702     }
703   }
704   return 0;
705 }
706 
707 //---------------------------------------------
708 
init(std::string & errorMessage)709 bool Convert2Tlv::init(std::string &errorMessage) {
710   m_lastIndex = m_maxPaletteIndex = 0;
711   m_colorMap.clear();
712 
713   try {
714     m_lr1 = TLevelReaderP(m_levelIn1);
715     if (m_lr1) m_level1 = m_lr1->loadInfo();
716   } catch (...) {
717     errorMessage =
718         "Error: can't read level " + ::to_string(m_levelIn1.getWideString());
719     return false;
720   }
721 
722   if (m_level1->getFrameCount() == 0) {
723     errorMessage =
724         "Error: can't find level " + ::to_string(m_levelIn1.getWideString());
725     return false;
726   }
727 
728   TLevelP level2;
729 
730   if (m_levelIn2 != TFilePath()) {
731     try {
732       m_lr2 = TLevelReaderP(m_levelIn2);
733       if (m_lr2) level2 = m_lr2->loadInfo();
734     } catch (...) {
735       errorMessage =
736           "Error: can't read level " + ::to_string(m_levelIn2.getWideString());
737       return false;
738     }
739 
740     if (level2->getFrameCount() == 0) {
741       errorMessage =
742           "Error: can't find level " + ::to_string(m_levelIn2.getWideString());
743       return false;
744     }
745 
746     if (m_level1->getFrameCount() != level2->getFrameCount()) {
747       errorMessage = "Error: the two input levels must have same frame number";
748       return false;
749     }
750   }
751 
752   m_size = TDimension();
753 
754   m_lw = TLevelWriterP(m_levelOut);
755   m_it = m_level1->begin();
756 
757   TLevel::Iterator it2;
758 
759   if (level2->getFrameCount() > 0) it2 = level2->begin();
760 
761   for (; m_it != m_level1->end(); ++m_it) {
762     TImageReaderP ir1       = m_lr1->getFrameReader(m_it->first);
763     const TImageInfo *info1 = ir1->getImageInfo();
764     if (!info1) {
765       errorMessage = "Error: can't read frame " +
766                      std::to_string(m_it->first.getNumber()) + " of level  " +
767                      ::to_string(m_levelIn1.getWideString());
768       return false;
769     }
770 
771     if (info1->m_bitsPerSample != 8) {
772       errorMessage = "Error: all frames must have 8 bits per channel!\n";
773       return false;
774     }
775     m_size.lx = std::max(m_size.lx, info1->m_lx);
776     m_size.ly = std::max(m_size.ly, info1->m_ly);
777 
778     if (m_lr2 != TLevelReaderP()) {
779       TImageReaderP ir2 = m_lr2->getFrameReader(it2->first);
780 
781       if (ir2) {
782         const TImageInfo *info2 = ir2->getImageInfo();
783         if (!info1) {
784           errorMessage = "Error: can't read frame " +
785                          std::to_string(it2->first.getNumber()) +
786                          " of level  " +
787                          ::to_string(m_levelIn2.getWideString());
788           ;
789           return false;
790         }
791 
792         if (info1->m_lx != info2->m_lx || info1->m_ly != info2->m_ly) {
793           errorMessage =
794               "Error: painted frames must have same resolution of matching "
795               "unpainted frames!\n";
796           return false;
797         }
798         if (info2->m_bitsPerSample != 8) {
799           errorMessage = "Error: all frames must have 8 bits per channel!\n";
800           return false;
801         }
802       }
803       ++it2;
804     }
805   }
806 
807   m_palette = new TPalette();
808 
809   if (m_palettePath != TFilePath()) {
810     TIStream is(m_palettePath);
811     is >> m_palette;
812     if (m_palette->getStyleInPagesCount() == 0) {
813       errorMessage = "Error: invalid palette!\n";
814 
815       return false;
816     }
817     for (int i = 0; i < m_palette->getStyleCount(); i++)
818       if (m_palette->getStylePage(i)) {
819         m_colorMap[m_palette->getStyle(i)->getMainColor()] = i;
820         if (i > m_lastIndex) m_lastIndex = i;
821       }
822     assert(m_colorMap.size() == m_palette->getStyleInPagesCount());
823   }
824 
825   m_maxPaletteIndex = m_lastIndex;
826 
827   m_it = m_level1->begin();
828 
829   /*-
830   If the palette is empty, make an initial palette with black, red, blue and
831   green styles.
832   For the latter three styles the "autopaint" option should be set.
833   -*/
834   if (m_lastIndex == 0) {
835     m_colorMap[TPixel::Black] = ++m_lastIndex;
836     m_colorMap[TPixel::Red]   = ++m_lastIndex;
837     m_colorMap[TPixel::Blue]  = ++m_lastIndex;
838     m_colorMap[TPixel::Green] = ++m_lastIndex;
839   }
840 
841   return true;
842 }
843 
844 //----------------------------------------------------------------------------------------
845 
convertNext(std::string & errorMessage)846 bool Convert2Tlv::convertNext(std::string &errorMessage) {
847   if (m_count == 0 && m_from != -1)
848     while (m_it != m_level1->end() && m_it->first.getNumber() < m_from) m_it++;
849 
850   std::cout << "Processing image " << ++m_count << " of "
851             << getFramesCount(m_level1, m_from, m_to) << "...\n";
852   std::cout << "      Loading frame " << m_it->first.getNumber() << "...\n";
853   TImageReaderP ir1    = m_lr1->getFrameReader(m_it->first);
854   TRasterImageP imgIn1 = (TRasterImageP)ir1->load();
855   if (!imgIn1) {
856     errorMessage = "Error: cannot read frame" +
857                    std::to_string(m_it->first.getNumber()) + " of " +
858                    ::to_string(m_levelIn1.getWideString()) + "!";
859     return false;
860   }
861   TRasterP rin1 = imgIn1->getRaster();
862 
863   assert((TRaster32P)rin1 || (TRasterGR8P)rin1);
864 
865   TRasterP rin2;
866   TRasterImageP imgIn2;
867 
868   if (m_lr2) {
869     TImageReaderP ir2 = m_lr2->getFrameReader(m_it->first);
870     imgIn2            = (TRasterImageP)ir2->load();
871     if (!imgIn2) {
872       errorMessage = "Error: cannot read frame " +
873                      std::to_string(m_it->first.getNumber()) + " of " +
874                      ::to_string(m_levelIn2.getWideString()) + "!";
875       return false;
876     }
877     rin2 = imgIn2->getRaster();
878     assert((TRaster32P)rin2 || (TRasterGR8P)rin2);
879   }
880 
881   TRasterCM32P rout(m_size);
882   buildToonzRaster(rout, rin1, rin2);
883 
884   std::cout << "      saving frame in level \'" << m_levelOut.getLevelName()
885             << "\'...\n\n";
886   TImageWriterP iw  = m_lw->getFrameWriter(m_it->first);
887   TToonzImageP timg = TToonzImageP(rout, rout->getBounds());
888 
889   TRect bbox;
890   TRop::computeBBox(rout, bbox);
891   timg->setSavebox(bbox);
892 
893   if (m_dpi > 0.0)  // specify dpi in the convert popup
894     timg->setDpi(m_dpi, m_dpi);
895   else {
896     double dpix, dpiy;
897     imgIn1->getDpi(dpix, dpiy);
898     timg->setDpi(dpix, dpiy);
899   }
900 
901   TLevel::Iterator itaux = m_it;
902   itaux++;
903   if (itaux == m_level1->end() ||
904       (m_to != -1 &&
905        itaux->first.getNumber() > m_to))  // ultimo frame da scrivere.
906     timg->setPalette(buildPalette());
907 
908   iw->save(timg);
909 
910   ++m_it;
911   return true;
912 }
913 
914 //----------------------------------------------------------------------------------------------
915 
abort()916 bool Convert2Tlv::abort() {
917   try {
918     m_lr1    = TLevelReaderP();
919     m_lr2    = TLevelReaderP();
920     m_lw     = TLevelWriterP();
921     m_level1 = TLevelP();
922 
923     std::cout << "No output generated\n";
924     TSystem::deleteFile(m_levelOut);
925     TSystem::deleteFile(m_levelOut.withType("tpl"));
926     return false;
927   } catch (...) {
928     return false;
929   }
930 }
931 
932 //==============================================================================================
933 //
934 // RasterToToonzRasterConverter
935 //
936 //----------------------------------------------------------------------------------------------
937 
RasterToToonzRasterConverter()938 RasterToToonzRasterConverter::RasterToToonzRasterConverter() {
939   m_palette = new TPalette();
940 }
941 
~RasterToToonzRasterConverter()942 RasterToToonzRasterConverter::~RasterToToonzRasterConverter() {}
943 
setPalette(const TPaletteP & palette)944 void RasterToToonzRasterConverter::setPalette(const TPaletteP &palette) {
945   m_palette = palette;
946 }
947 
convert(const TRasterP & inputRaster)948 TRasterCM32P RasterToToonzRasterConverter::convert(
949     const TRasterP &inputRaster) {
950   int lx = inputRaster->getLx();
951   int ly = inputRaster->getLy();
952 
953   TRaster32P r = inputRaster;
954   /*
955 TRasterGR8P r1gr = (TRasterGR8P)inputRaster;
956 TRasterP rU, rP;
957 */
958 
959   TRasterCM32P rout(lx, ly);
960 
961   for (int y = 0; y < ly; y++) {
962     TPixel32 *pixin    = r->pixels(y);
963     TPixel32 *pixinEnd = pixin + lx;
964     TPixelCM32 *pixout = rout->pixels(y);
965     while (pixin < pixinEnd) {
966       int v = (pixin->r + pixin->g + pixin->b) / 3;
967       ++pixin;
968       *pixout++ = TPixelCM32(1, 0, v);
969     }
970   }
971   return rout;
972 }
973 
convert(const TRasterP & inksInputRaster,const TRasterP & paintInputRaster)974 TRasterCM32P RasterToToonzRasterConverter::convert(
975     const TRasterP &inksInputRaster, const TRasterP &paintInputRaster) {
976   return TRasterCM32P();
977 }
978 
convert(const TRasterImageP & ri)979 TToonzImageP RasterToToonzRasterConverter::convert(const TRasterImageP &ri) {
980   TRasterCM32P ras = convert(ri->getRaster());
981   if (ras)
982     return TToonzImageP(ras, TRect(ras->getBounds()));
983   else
984     return TToonzImageP();
985 }
986