1 
2 
3 #include "vectorizerpopup.h"
4 
5 // Tnz6 includes
6 #include "tapp.h"
7 #include "fileselection.h"
8 #include "castselection.h"
9 #include "cellselection.h"
10 #include "overwritepopup.h"
11 #include "vectorizerswatch.h"
12 #include "filebrowserpopup.h"
13 #include "menubarcommandids.h"
14 
15 // TnzQt includes
16 #include "toonzqt/menubarcommand.h"
17 #include "toonzqt/intfield.h"
18 #include "toonzqt/colorfield.h"
19 #include "toonzqt/checkbox.h"
20 #include "toonzqt/gutil.h"
21 
22 // TnzLib includes
23 #include "toonz/namebuilder.h"
24 #include "toonz/txshsimplelevel.h"
25 #include "toonz/txshleveltypes.h"
26 #include "toonz/txsheet.h"
27 #include "toonz/txshcell.h"
28 #include "toonz/toonzscene.h"
29 #include "toonz/tcenterlinevectorizer.h"
30 #include "toonz/dpiscale.h"
31 #include "toonz/txshchildlevel.h"
32 #include "toonz/levelset.h"
33 #include "toonz/tscenehandle.h"
34 #include "toonz/txsheethandle.h"
35 #include "toonz/txshlevelhandle.h"
36 #include "toonz/sceneproperties.h"
37 #include "toonz/imagemanager.h"
38 #include "toonz/Naa2TlvConverter.h"
39 
40 // TnzCore includes
41 #include "tsystem.h"
42 #include "tconvert.h"
43 #include "tpalette.h"
44 #include "trasterimage.h"
45 #include "ttoonzimage.h"
46 #include "tcolorstyles.h"
47 #include "tstroke.h"
48 #include "tpersistset.h"
49 #include "columncommand.h"
50 
51 // Qt includes
52 #include <QFrame>
53 #include <QComboBox>
54 #include <QCoreApplication>
55 #include <QPushButton>
56 #include <QHBoxLayout>
57 #include <QVBoxLayout>
58 #include <QString>
59 #include <QLabel>
60 #include <QSplitter>
61 #include <QGridLayout>
62 #include <QScrollArea>
63 #include <QAction>
64 #include <QMainWindow>
65 #include <QToolButton>
66 
67 using namespace DVGui;
68 
69 //********************************************************************************
70 //    Local namespace  classes
71 //********************************************************************************
72 
73 namespace {
74 
75 struct Param {
76   QString m_name;
77   int m_bit;
78 
Param__anon0ff2ca300111::Param79   Param(const QString &name, int bit) : m_name(name), m_bit(bit) {}
80 };
81 
82 struct ParamGroup {
83   int m_startRow, m_separatorRow;
84   std::vector<Param> m_params;
85 
ParamGroup__anon0ff2ca300111::ParamGroup86   ParamGroup(int startRow, int separatorRow)
87       : m_startRow(startRow), m_separatorRow(separatorRow) {}
88 };
89 
90 }  // namespace
91 
92 //********************************************************************************
93 //    Local namespace stuff
94 //********************************************************************************
95 
96 namespace {
97 
98 std::vector<ParamGroup> l_centerlineParamGroups;
99 std::vector<ParamGroup> l_outlineParamGroups;
100 
101 bool l_quitLoop = false;
102 
103 //=============================================================================
104 
getCurrentVectorizerParameters()105 VectorizerParameters *getCurrentVectorizerParameters() {
106   return TApp::instance()
107       ->getCurrentScene()
108       ->getScene()
109       ->getProperties()
110       ->getVectorizerParameters();
111 }
112 
113 //-----------------------------------------------------------------------------
114 
getSelectedLevels(std::set<TXshLevel * > & levels,int & r0,int & c0,int & r1,int & c1)115 bool getSelectedLevels(std::set<TXshLevel *> &levels, int &r0, int &c0, int &r1,
116                        int &c1) {
117   TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
118 
119   CastSelection *castSelection =
120       dynamic_cast<CastSelection *>(TSelection::getCurrent());
121   TCellSelection *cellSelection =
122       dynamic_cast<TCellSelection *>(TSelection::getCurrent());
123 
124   if (castSelection) {
125     std::vector<TXshLevel *> selectedLevels;
126     castSelection->getSelectedLevels(selectedLevels);
127 
128     for (int i = 0; i < (int)selectedLevels.size(); ++i)
129       levels.insert(selectedLevels[i]);
130 
131     return false;
132   } else if (cellSelection) {
133     cellSelection->getSelectedCells(r0, c0, r1, c1);
134 
135     for (int c = c0; c <= c1; ++c) {
136       for (int r = r0; r <= r1; ++r) {
137         TXshCell cell = xsheet->getCell(r, c);
138 
139         if (TXshLevel *level = cell.isEmpty() ? 0 : cell.getSimpleLevel())
140           levels.insert(level);
141       }
142     }
143 
144     return true;
145   }
146 
147   return false;
148 }
149 
150 //-----------------------------------------------------------------------------
151 
getSelectedLevel()152 TXshLevel *getSelectedLevel() {
153   std::set<TXshLevel *> levels;
154   int r0, c0, r1, c1;
155 
156   getSelectedLevels(levels, r0, c0, r1, c1);
157 
158   return (levels.size() == 1) ? *levels.begin() : (TXshLevel *)0;
159 }
160 
161 //-----------------------------------------------------------------------------
162 
getSelectedLevelPath()163 TFilePath getSelectedLevelPath() {
164   TXshLevel *level = getSelectedLevel();
165 
166   return level
167              ? TApp::instance()->getCurrentScene()->getScene()->decodeFilePath(
168                    level->getPath())
169              : TFilePath();
170 }
171 
172 //-----------------------------------------------------------------------------
173 
getSelectedFids(std::vector<TFrameId> & fids,TXshSimpleLevel * level,int r0,int c0,int r1,int c1)174 void getSelectedFids(std::vector<TFrameId> &fids, TXshSimpleLevel *level,
175                      int r0, int c0, int r1, int c1) {
176   TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
177 
178   std::set<TFrameId> fidsSet;
179   for (int c = c0; c <= c1; ++c) {
180     for (int r = r0; r <= r1; ++r) {
181       TXshCell cell = xsheet->getCell(r, c);
182 
183       TXshSimpleLevel *curLevel = cell.isEmpty() ? 0 : cell.getSimpleLevel();
184       if (curLevel != level) continue;
185 
186       fidsSet.insert(cell.getFrameId());
187     }
188   }
189 
190   std::set<TFrameId>::iterator fst, fsEnd = fidsSet.end();
191   for (fst = fidsSet.begin(); fst != fsEnd; ++fst) fids.push_back(*fst);
192 }
193 
194 // Toonz Raster Level may have palette including MyPaint styles,
195 // which cannot be rendered in vector levels.
196 // In such case replace MyPaint styles by solid color styles.
replaceMyPaintBrushStyles(TPalette * palette)197 void replaceMyPaintBrushStyles(TPalette *palette) {
198   for (int s = 0; s < palette->getStyleCount(); s++) {
199     TColorStyle *style = palette->getStyle(s);
200     if (style->getTagId() == 4001)  // TMyPaintBrushStyle
201       palette->setStyle(s, style->getMainColor());
202   }
203 }
204 
205 }  // namespace
206 
207 //*****************************************************************************
208 //    Vectorizer implementation
209 //*****************************************************************************
210 
Vectorizer()211 Vectorizer::Vectorizer()
212     : m_dialog(new OverwriteDialog)
213     , m_isCanceled(false)
214     , m_dialogShown(false) {}
215 
216 //-----------------------------------------------------------------------------
217 
~Vectorizer()218 Vectorizer::~Vectorizer() {
219   // DO NOT REMOVE - DESTRUCTS INCOMPLETE TYPES IN THE HEADER FILE
220 }
221 
222 //-----------------------------------------------------------------------------
223 
doVectorize(TImageP img,TPalette * palette,const VectorizerConfiguration & conf)224 TVectorImageP Vectorizer::doVectorize(TImageP img, TPalette *palette,
225                                       const VectorizerConfiguration &conf) {
226   TToonzImageP ti  = img;
227   TRasterImageP ri = img;
228 
229   if (!ti && !ri) return TVectorImageP();
230 
231   VectorizerCore vCore;
232   connect(&vCore, SIGNAL(partialDone(int, int)), this,
233           SIGNAL(partialDone(int, int)));
234   connect(this, SIGNAL(transmitCancel()), &vCore, SLOT(onCancel()),
235           Qt::DirectConnection);  // Direct connection *must* be
236                                   // established for child cancels
237   return vCore.vectorize(img, conf, palette);
238 }
239 
240 //-----------------------------------------------------------------------------
241 
setLevel(const TXshSimpleLevelP & level)242 void Vectorizer::setLevel(const TXshSimpleLevelP &level) {
243   m_level = level;
244 
245   // Creo il livello pli
246   TXshSimpleLevel *sl = m_level.getPointer();
247   if (!sl) return;
248 
249   int rowCount = sl->getFrameCount();
250   if (rowCount <= 0 || sl->isEmpty()) return;
251 
252   TXshLevel *xl;
253   ToonzScene *scene = TApp::instance()->getCurrentScene()->getScene();
254 
255   // Build the new level name
256   std::wstring levelName = sl->getName() + L"v";
257   {
258     std::unique_ptr<NameBuilder> nameBuilder(
259         NameBuilder::getBuilder(levelName));
260 
261     for (;;) {
262       levelName = nameBuilder->getNext();
263       if (scene->getLevelSet()->getLevel(levelName) == 0) break;
264     }
265   }
266 
267   TFilePath dstPath = sl->getPath().withName(levelName).withType("pli");
268   dstPath           = scene->decodeFilePath(dstPath);
269 
270   bool overWrite = false;
271   if (TSystem::doesExistFileOrLevel(dstPath)) {
272     m_dialogShown = true;
273 
274     std::wstring name = m_dialog->execute(scene, dstPath, true);
275     if (m_dialog->cancelPressed()) return;
276 
277     switch (m_dialog->getChoice()) {
278     case OverwriteDialog::KEEP_OLD:
279       xl = scene->getLevelSet()->getLevel(levelName);
280       if (!xl) xl = scene->loadLevel(dstPath);
281 
282       m_vLevel = xl->getSimpleLevel();
283       return;
284     case OverwriteDialog::OVERWRITE:
285       overWrite = true;
286       break;
287     default:
288       levelName = name;
289       break;
290     }
291   }
292 
293   xl = scene->createNewLevel(PLI_XSHLEVEL, levelName);
294 
295   TXshSimpleLevel *vl = xl->getSimpleLevel();
296   assert(vl);
297 
298   if (overWrite) {
299     vl->setPath(scene->codeFilePath(dstPath));
300     vl->setName(levelName);
301   }
302 
303   TPalette *palette = 0;
304   if (sl->getType() == TZP_XSHLEVEL) {
305     palette = sl->getPalette()->clone();
306     replaceMyPaintBrushStyles(palette);
307   } else
308     palette = new TPalette;
309 
310   palette->setPaletteName(vl->getName());
311   vl->setPalette(palette);
312 
313   m_vLevel = vl;
314 }
315 
316 //-----------------------------------------------------------------------------
317 
doVectorize()318 int Vectorizer::doVectorize() {
319   struct {
320     Vectorizer *m_this;
321 
322     CenterlineConfiguration m_cConf;
323     NewOutlineConfiguration m_oConf;
324 
325     void updateConfig(double weight) {
326       if (m_this->m_params.m_isOutline)
327         m_oConf = m_this->m_params.getOutlineConfiguration(weight);
328       else
329         m_cConf = m_this->m_params.getCenterlineConfiguration(weight);
330     }
331 
332   } locals = {this};
333 
334   VectorizerConfiguration &configuration =
335       m_params.m_isOutline
336           ? static_cast<VectorizerConfiguration &>(locals.m_oConf)
337           : static_cast<VectorizerConfiguration &>(locals.m_cConf);
338 
339   if (!m_vLevel) return 0;
340 
341   if (m_dialog->getChoice() == OverwriteDialog::KEEP_OLD && m_dialogShown)
342     return m_fids.size();
343 
344   TXshSimpleLevel *sl = m_level.getPointer();
345   if (!sl) return 0;
346 
347   int rowCount = sl->getFrameCount();
348   if (rowCount <= 0 || sl->isEmpty()) return 0;
349 
350   double frameRange[2] = {static_cast<double>(m_fids.front().getNumber()) - 1,
351                           static_cast<double>(m_fids.back().getNumber()) - 1};
352 
353   int count = 0;
354 
355   std::vector<TFrameId>::const_iterator ft, fEnd = m_fids.end();
356   for (ft = m_fids.begin(); ft != fEnd; ++ft) {
357     // Retrieve the image to be vectorized
358     TImageP img;
359     if (sl->getType() == OVL_XSHLEVEL || sl->getType() == TZP_XSHLEVEL ||
360         sl->getType() == TZI_XSHLEVEL)
361       img = sl->getFullsampledFrame(*ft, ImageManager::dontPutInCache);
362 
363     if (!img) continue;
364 
365     // Build image-toonz coordinate transformation
366     TAffine dpiAff = getDpiAffine(sl, *ft, true);
367     double factor  = norm(dpiAff * TPointD(1, 0));
368 
369     TPointD center;
370     if (TToonzImageP ti = img)
371       center = ti->getRaster()->getCenterD();
372     else if (TRasterImageP ri = img)
373       center = ri->getRaster()->getCenterD();
374 
375     // Build vectorizer configuration
376     double weight = (ft->getNumber() - 1 - frameRange[0]) /
377                     std::max(frameRange[1] - frameRange[0], 1.0);
378     weight = tcrop(weight, 0.0, 1.0);
379 
380     locals.updateConfig(weight);  // TEMPORARY
381 
382     configuration.m_affine     = dpiAff * TTranslation(-center);
383     configuration.m_thickScale = factor;
384 
385     // Build vectorization label to be displayed
386     QString labelName = QString::fromStdWString(sl->getShortName());
387     labelName.push_back(' ');
388     labelName.append(QString::fromStdString(ft->expand(TFrameId::NO_PAD)));
389 
390     emit frameName(labelName);
391 
392     // Perform vectorization
393     if (TVectorImageP vi =
394             doVectorize(img, m_vLevel->getPalette(), configuration)) {
395       TFrameId fid = *ft;
396 
397       if (fid.getNumber() < 0) fid = TFrameId(1, ft->getLetter());
398 
399       m_vLevel->setFrame(fid, vi);
400       vi->setPalette(m_vLevel->getPalette());
401 
402       emit frameDone(++count);
403     }
404 
405     // Stop if canceled
406     if (m_isCanceled) break;
407   }
408 
409   m_dialogShown = false;
410 
411   return count;
412 }
413 
414 //-----------------------------------------------------------------------------
415 
run()416 void Vectorizer::run() { doVectorize(); }
417 
418 //*****************************************************************************
419 //    VectorizerPopup implentation
420 //*****************************************************************************
421 
422 #if QT_VERSION >= 0x050500
VectorizerPopup(QWidget * parent,Qt::WindowFlags flags)423 VectorizerPopup::VectorizerPopup(QWidget *parent, Qt::WindowFlags flags)
424 #else
425 VectorizerPopup::VectorizerPopup(QWidget *parent, Qt::WFlags flags)
426 #endif
427     : Dialog(TApp::instance()->getMainWindow(), true, false, "Vectorizer")
428     , m_sceneHandle(TApp::instance()->getCurrentScene()) {
429   struct Locals {
430     int m_bit;
431 
432     Locals() : m_bit() {}
433 
434     static void addParameterGroup(std::vector<ParamGroup> &paramGroups,
435                                   int group, int startRow,
436                                   int separatorRow = -1) {
437       assert(group <= paramGroups.size());
438 
439       if (group == paramGroups.size())
440         paramGroups.push_back(ParamGroup(startRow, separatorRow));
441     }
442 
443     void addParameter(std::vector<ParamGroup> &paramGroups,
444                       const QString &paramName) {
445       paramGroups.back().m_params.push_back(Param(paramName, m_bit++));
446     }
447 
448   } locals;
449 
450   // Su MAC i dialog modali non hanno bottoni di chiusura nella titleBar
451   setModal(false);
452   setWindowTitle(tr("Convert-to-Vector Settings"));
453 
454   setLabelWidth(125);
455 
456   setTopMargin(0);
457   setTopSpacing(0);
458 
459   // Build vertical layout
460   beginVLayout();
461 
462   QSplitter *splitter = new QSplitter(Qt::Vertical, this);
463   splitter->setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding,
464                                       QSizePolicy::MinimumExpanding));
465   addWidget(splitter);
466 
467   QToolBar *leftToolBar = new QToolBar, *rightToolBar = new QToolBar;
468   leftToolBar->setObjectName("MediumPaddingToolBar");
469   rightToolBar->setObjectName("MediumPaddingToolBar");
470   leftToolBar->setIconSize(QSize(17, 17));
471   rightToolBar->setIconSize(QSize(17, 17));
472   {
473     QWidget *toolbarsContainer = new QWidget(this);
474     toolbarsContainer->setFixedHeight(22);
475     addWidget(toolbarsContainer);
476 
477     QHBoxLayout *toolbarsLayout = new QHBoxLayout(toolbarsContainer);
478     toolbarsContainer->setLayout(toolbarsLayout);
479 
480     toolbarsLayout->setMargin(0);
481     toolbarsLayout->setSpacing(0);
482 
483     QToolBar *spacingToolBar = new QToolBar(
484         toolbarsContainer);  // The spacer object must be a toolbar.
485     spacingToolBar->setFixedHeight(
486         22);  // It's related to qss choices... I know it's stinky
487 
488     toolbarsLayout->addWidget(leftToolBar, 0, Qt::AlignLeft);
489     toolbarsLayout->addWidget(spacingToolBar, 1);
490     toolbarsLayout->addWidget(rightToolBar, 0, Qt::AlignRight);
491   }
492 
493   endVLayout();
494 
495   // Build parameters area
496   QScrollArea *paramsArea = new QScrollArea(splitter);
497   paramsArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
498   paramsArea->setWidgetResizable(true);
499   splitter->addWidget(paramsArea);
500   splitter->setStretchFactor(0, 1);
501 
502   m_paramsWidget = new QFrame(paramsArea);
503   paramsArea->setWidget(m_paramsWidget);
504 
505   m_paramsLayout = new QGridLayout;
506   m_paramsWidget->setLayout(m_paramsLayout);
507 
508   int group = 0, row = 0;
509 
510   locals.addParameterGroup(::l_centerlineParamGroups, group, row);
511   locals.addParameterGroup(::l_outlineParamGroups, group++, row);
512 
513   // Vectorization mode
514   m_typeMenu = new QComboBox(this);
515   m_typeMenu->setFixedSize(245, WidgetHeight);
516   QStringList formats;
517   formats << tr("Centerline") << tr("Outline");
518   m_typeMenu->addItems(formats);
519   m_typeMenu->setMinimumHeight(WidgetHeight);
520   bool isOutline = m_sceneHandle->getScene()
521                        ->getProperties()
522                        ->getVectorizerParameters()
523                        ->m_isOutline;
524   m_typeMenu->setCurrentIndex(isOutline ? 1 : 0);
525   connect(m_typeMenu, SIGNAL(currentIndexChanged(int)), this,
526           SLOT(onTypeChange(int)));
527 
528   m_paramsLayout->addWidget(new QLabel(tr("Mode")), row, 0, Qt::AlignRight);
529   m_paramsLayout->addWidget(m_typeMenu, row++, 1);
530 
531   locals.addParameter(l_centerlineParamGroups, tr("Mode"));
532   locals.addParameter(l_outlineParamGroups, tr("Mode"));
533 
534   //-------------------- Parameters area - Centerline ------------------------
535 
536   locals.addParameterGroup(l_centerlineParamGroups, group++, row);
537 
538   // Threshold
539   m_cThresholdLabel = new QLabel(tr("Threshold"));
540   m_cThreshold      = new IntField(this);
541 
542   m_paramsLayout->addWidget(m_cThresholdLabel, row, 0, Qt::AlignRight);
543   m_paramsLayout->addWidget(m_cThreshold, row++, 1);
544 
545   locals.addParameter(l_centerlineParamGroups, tr("Threshold"));
546 
547   // Accuracy
548   m_cAccuracyLabel = new QLabel(tr("Accuracy"));
549   m_cAccuracy      = new IntField(this);
550 
551   m_paramsLayout->addWidget(m_cAccuracyLabel, row, 0, Qt::AlignRight);
552   m_paramsLayout->addWidget(m_cAccuracy, row++, 1);
553 
554   locals.addParameter(l_centerlineParamGroups, tr("Accuracy"));
555 
556   // Despeckling
557   m_cDespecklingLabel = new QLabel(tr("Despeckling"));
558   m_cDespeckling      = new IntField(this);
559 
560   m_paramsLayout->addWidget(m_cDespecklingLabel, row, 0, Qt::AlignRight);
561   m_paramsLayout->addWidget(m_cDespeckling, row++, 1);
562 
563   locals.addParameter(l_centerlineParamGroups, tr("Despeckling"));
564 
565   // Max Thickness
566   m_cMaxThicknessLabel = new QLabel(tr("Max Thickness"));
567   m_paramsLayout->addWidget(m_cMaxThicknessLabel, row, 0, Qt::AlignRight);
568 
569   m_cMaxThickness = new IntField(this);
570   m_cMaxThickness->enableSlider(false);
571   m_paramsLayout->addWidget(m_cMaxThickness, row++, 1, Qt::AlignLeft);
572 
573   locals.addParameter(l_centerlineParamGroups, tr("Max Thickness"));
574 
575   // Thickness Calibration
576   m_cThicknessRatioLabel = new QLabel(tr("Thickness Calibration"));
577   m_paramsLayout->addWidget(m_cThicknessRatioLabel, row, 0, Qt::AlignRight);
578 
579   /*m_cThicknessRatio = new IntField(this);
580 paramsLayout->addWidget(m_cThicknessRatio, row++, 1);*/
581 
582   QHBoxLayout *cThicknessRatioLayout = new QHBoxLayout;
583 
584   cThicknessRatioLayout->addSpacing(20);
585 
586   m_cThicknessRatioFirstLabel = new QLabel(tr("Start:"));
587   cThicknessRatioLayout->addWidget(m_cThicknessRatioFirstLabel);
588 
589   m_cThicknessRatioFirst = new MeasuredDoubleLineEdit(this);
590   m_cThicknessRatioFirst->setMeasure("percentage");
591   cThicknessRatioLayout->addWidget(m_cThicknessRatioFirst);
592 
593   m_cThicknessRatioLastLabel = new QLabel(tr("End:"));
594   cThicknessRatioLayout->addWidget(m_cThicknessRatioLastLabel);
595 
596   m_cThicknessRatioLast = new MeasuredDoubleLineEdit(this);
597   m_cThicknessRatioLast->setMeasure("percentage");
598   cThicknessRatioLayout->addWidget(m_cThicknessRatioLast);
599 
600   cThicknessRatioLayout->addStretch(1);
601 
602   m_paramsLayout->addLayout(cThicknessRatioLayout, row++, 1);
603 
604   locals.addParameter(l_centerlineParamGroups, tr("Thickness Calibration"));
605 
606   // Checkboxes
607   {
608     static const QString name = tr("Preserve Painted Areas");
609     locals.addParameter(l_centerlineParamGroups, name);
610 
611     m_cPaintFill = new CheckBox(name, this);
612     m_cPaintFill->setFixedHeight(WidgetHeight);
613     m_paramsLayout->addWidget(m_cPaintFill, row++, 1);
614   }
615 
616   {
617     static const QString name = tr("Align Boundary Strokes Direction");
618     locals.addParameter(l_centerlineParamGroups, name);
619 
620     m_cAlignBoundaryStrokesDirection = new CheckBox(name, this);
621     m_cAlignBoundaryStrokesDirection->setFixedHeight(WidgetHeight);
622     m_cAlignBoundaryStrokesDirection->setToolTip(
623         tr("Align boundary strokes direction to be the same.\n(clockwise, i.e. "
624            "left to right as viewed from inside of the shape)"));
625     m_paramsLayout->addWidget(m_cAlignBoundaryStrokesDirection, row++, 1);
626   }
627 
628   {
629     static const QString name = tr("Add Border");
630     locals.addParameter(l_centerlineParamGroups, name);
631 
632     m_cMakeFrame = new CheckBox(name, this);
633     m_cMakeFrame->setFixedHeight(WidgetHeight);
634     m_paramsLayout->addWidget(m_cMakeFrame, row++, 1);
635   }
636 
637   locals.addParameterGroup(l_centerlineParamGroups, group++, row + 1, row);
638 
639   m_cNaaSourceSeparator = new Separator(tr("Full color non-AA images"));
640   m_paramsLayout->addWidget(m_cNaaSourceSeparator, row++, 0, 1, 2);
641 
642   {
643     static const QString name = tr("Enhanced ink recognition");
644     locals.addParameter(l_centerlineParamGroups, name);
645 
646     m_cNaaSource = new CheckBox(name, this);
647     m_cNaaSource->setFixedHeight(WidgetHeight);
648     m_paramsLayout->addWidget(m_cNaaSource, row++, 1);
649   }
650 
651   //-------------------- Parameters area - Outline ------------------------
652 
653   group = 1;
654   locals.addParameterGroup(l_outlineParamGroups, group++, row);
655 
656   // Accuracy
657   m_oAccuracyLabel = new QLabel(tr("Accuracy"));
658   m_oAccuracy      = new IntField(this);
659 
660   m_paramsLayout->addWidget(m_oAccuracyLabel, row, 0, Qt::AlignRight);
661   m_paramsLayout->addWidget(m_oAccuracy, row++, 1);
662 
663   locals.addParameter(l_outlineParamGroups, tr("Accuracy"));
664 
665   // Despeckling
666   m_oDespecklingLabel = new QLabel(tr("Despeckling"));
667   m_oDespeckling      = new IntField(this);
668 
669   m_paramsLayout->addWidget(m_oDespecklingLabel, row, 0, Qt::AlignRight);
670   m_paramsLayout->addWidget(m_oDespeckling, row++, 1);
671 
672   locals.addParameter(l_outlineParamGroups, tr("Despeckling"));
673 
674   // Paint Fill
675   {
676     static const QString name = tr("Preserve Painted Areas");
677     locals.addParameter(l_outlineParamGroups, name);
678 
679     m_oPaintFill = new CheckBox(name, this);
680     m_oPaintFill->setFixedHeight(WidgetHeight);
681     m_paramsLayout->addWidget(m_oPaintFill, row++, 1);
682   }
683 
684   {
685     static const QString name = tr("Align Boundary Strokes Direction");
686     locals.addParameter(l_outlineParamGroups, name);
687 
688     m_oAlignBoundaryStrokesDirection = new CheckBox(name, this);
689     m_oAlignBoundaryStrokesDirection->setFixedHeight(WidgetHeight);
690     m_oAlignBoundaryStrokesDirection->setToolTip(
691         tr("Align boundary strokes direction to be the same.\n(clockwise, i.e. "
692            "left to right as viewed from inside of the shape)"));
693     m_paramsLayout->addWidget(m_oAlignBoundaryStrokesDirection, row++, 1);
694   }
695   locals.addParameterGroup(l_outlineParamGroups, group++, row + 1, row);
696 
697   m_oCornersSeparator = new Separator(tr("Corners"));
698   m_paramsLayout->addWidget(m_oCornersSeparator, row++, 0, 1, 2);
699 
700   // Adherence
701   m_oAdherenceLabel = new QLabel(tr("Adherence"));
702   m_oAdherence      = new IntField(this);
703 
704   m_paramsLayout->addWidget(m_oAdherenceLabel, row, 0, Qt::AlignRight);
705   m_paramsLayout->addWidget(m_oAdherence, row++, 1);
706 
707   locals.addParameter(l_outlineParamGroups, tr("Adherence"));
708 
709   // Angle
710   m_oAngleLabel = new QLabel(tr("Angle"));
711   m_oAngle      = new IntField(this);
712 
713   m_paramsLayout->addWidget(m_oAngleLabel, row, 0, Qt::AlignRight);
714   m_paramsLayout->addWidget(m_oAngle, row++, 1);
715 
716   locals.addParameter(l_outlineParamGroups, tr("Angle"));
717 
718   // Relative
719   m_oRelativeLabel = new QLabel(tr("Curve Radius"));
720   m_oRelative      = new IntField(this);
721 
722   m_paramsLayout->addWidget(m_oRelativeLabel, row, 0, Qt::AlignRight);
723   m_paramsLayout->addWidget(m_oRelative, row++, 1);
724 
725   locals.addParameter(l_outlineParamGroups, tr("Curve Radius"));
726 
727   locals.addParameterGroup(l_outlineParamGroups, group++, row + 1, row);
728 
729   m_oFullColorSeparator = new Separator(tr("Raster Levels"));
730   m_paramsLayout->addWidget(m_oFullColorSeparator, row++, 0, 1, 2);
731 
732   // Max Colors
733   m_oMaxColorsLabel = new QLabel(tr("Max Colors"));
734   m_oMaxColors      = new IntField(this);
735 
736   m_paramsLayout->addWidget(m_oMaxColorsLabel, row, 0, Qt::AlignRight);
737   m_paramsLayout->addWidget(m_oMaxColors, row++, 1);
738 
739   locals.addParameter(l_outlineParamGroups, tr("Max Colors"));
740 
741   // Transparent Color
742   m_oTransparentColorLabel = new QLabel(tr("Transparent Color"), this);
743   m_oTransparentColor = new ColorField(this, true, TPixel32::Transparent, 48);
744   m_paramsLayout->addWidget(m_oTransparentColorLabel, row, 0, Qt::AlignRight);
745   m_paramsLayout->addWidget(m_oTransparentColor, row++, 1);
746 
747   locals.addParameter(l_outlineParamGroups, tr("Transparent Color"));
748 
749   locals.addParameterGroup(l_outlineParamGroups, group++, row + 1, row);
750 
751   m_oTlvSeparator = new Separator(tr("TLV Levels"));
752   m_paramsLayout->addWidget(m_oTlvSeparator, row++, 0, 1, 2);
753 
754   // Tone Threshold
755   m_oToneThresholdLabel = new QLabel(tr("Tone Threshold"));
756   m_oToneThreshold      = new IntField(this);
757 
758   m_paramsLayout->addWidget(m_oToneThresholdLabel, row, 0, Qt::AlignRight);
759   m_paramsLayout->addWidget(m_oToneThreshold, row++, 1);
760 
761   locals.addParameter(l_outlineParamGroups, tr("Tone Threshold"));
762 
763   m_paramsLayout->setRowStretch(row, 1);
764 
765   //-------------------- Swatch area ------------------------
766 
767   m_swatchArea = new VectorizerSwatchArea(this);
768   splitter->addWidget(m_swatchArea);
769   m_swatchArea->setEnabled(false);  // Initally not enabled
770 
771   connect(this, SIGNAL(valuesChanged()), m_swatchArea,
772           SLOT(invalidateContents()));
773 
774   //---------------------- Toolbar --------------------------
775 
776   QAction *swatchAct =
777       new QAction(createQIcon("preview"), tr("Toggle Swatch Preview"), this);
778   swatchAct->setCheckable(true);
779   leftToolBar->addAction(swatchAct);
780 
781   QAction *centerlineAct = new QAction(createQIcon("centerline"),
782                                        tr("Toggle Centerlines Check"), this);
783   centerlineAct->setCheckable(true);
784   leftToolBar->addAction(centerlineAct);
785 
786   QToolButton *visibilityButton = new QToolButton(this);
787   visibilityButton->setIcon(createQIcon("menu"));
788   visibilityButton->setText(tr("Options"));
789   visibilityButton->setPopupMode(QToolButton::InstantPopup);
790 
791   QMenu *visibilityMenu = new QMenu(visibilityButton);
792   visibilityButton->setMenu(visibilityMenu);
793 
794   rightToolBar->addWidget(visibilityButton);
795   rightToolBar->addSeparator();
796 
797   QAction *saveAct =
798       new QAction(createQIcon("save"), tr("Save Settings"), this);
799   rightToolBar->addAction(saveAct);
800   QAction *loadAct =
801       new QAction(createQIcon("load"), tr("Load Settings"), this);
802   rightToolBar->addAction(loadAct);
803   rightToolBar->addSeparator();
804 
805   QAction *resetAct =
806       new QAction(createQIcon("settings_reset"), tr("Reset Settings"), this);
807   rightToolBar->addAction(resetAct);
808 
809   connect(swatchAct, SIGNAL(triggered(bool)), m_swatchArea,
810           SLOT(enablePreview(bool)));
811   connect(centerlineAct, SIGNAL(triggered(bool)), m_swatchArea,
812           SLOT(enableDrawCenterlines(bool)));
813   connect(visibilityMenu, SIGNAL(aboutToShow()), this,
814           SLOT(populateVisibilityMenu()));
815   connect(saveAct, SIGNAL(triggered()), this, SLOT(saveParameters()));
816   connect(loadAct, SIGNAL(triggered()), this, SLOT(loadParameters()));
817   connect(resetAct, SIGNAL(triggered()), this, SLOT(resetParameters()));
818 
819   //------------------- Convert Button ----------------------
820 
821   // Convert Button
822   m_okBtn = new QPushButton(QString(tr("Convert")), this);
823   connect(m_okBtn, SIGNAL(clicked()), this, SLOT(onOk()));
824 
825   addButtonBarWidget(m_okBtn);
826 
827   // All detailed signals convey to the unique valuesChanged() signal. That
828   // makes it easier
829   // to disconnect update notifications whenever we loadConfiguration(..).
830   connect(this, SIGNAL(valuesChanged()), this, SLOT(updateSceneSettings()));
831 
832   // Connect value changes to update the global
833   // VectorizerPopUpSettingsContainer.
834   // connect(m_typeMenu,SIGNAL(currentIndexChanged(const QString
835   // &)),this,SLOT(updateSceneSettings()));
836   connect(m_cThreshold, SIGNAL(valueChanged(bool)), this,
837           SLOT(onValueEdited(bool)));
838   connect(m_cAccuracy, SIGNAL(valueChanged(bool)), this,
839           SLOT(onValueEdited(bool)));
840   connect(m_cDespeckling, SIGNAL(valueChanged(bool)), this,
841           SLOT(onValueEdited(bool)));
842   connect(m_cMaxThickness, SIGNAL(valueChanged(bool)), this,
843           SLOT(onValueEdited(bool)));
844   // connect(m_cThicknessRatio,SIGNAL(valueChanged(bool)),this,SLOT(onValueEdited(bool)));
845   connect(m_cThicknessRatioFirst, SIGNAL(valueChanged()), this,
846           SLOT(onValueEdited()));
847   connect(m_cThicknessRatioLast, SIGNAL(valueChanged()), this,
848           SLOT(onValueEdited()));
849   connect(m_cMakeFrame, SIGNAL(stateChanged(int)), this, SLOT(onValueEdited()));
850   connect(m_cPaintFill, SIGNAL(stateChanged(int)), this, SLOT(onValueEdited()));
851   connect(m_cAlignBoundaryStrokesDirection, SIGNAL(stateChanged(int)), this,
852           SLOT(onValueEdited()));
853   connect(m_cNaaSource, SIGNAL(stateChanged(int)), this, SLOT(onValueEdited()));
854 
855   connect(m_oAccuracy, SIGNAL(valueChanged(bool)), this,
856           SLOT(onValueEdited(bool)));
857   connect(m_oDespeckling, SIGNAL(valueChanged(bool)), this,
858           SLOT(onValueEdited(bool)));
859   connect(m_oPaintFill, SIGNAL(stateChanged(int)), this, SLOT(onValueEdited()));
860   connect(m_oAlignBoundaryStrokesDirection, SIGNAL(stateChanged(int)), this,
861           SLOT(onValueEdited()));
862   connect(m_oAdherence, SIGNAL(valueChanged(bool)), this,
863           SLOT(onValueEdited(bool)));
864   connect(m_oAngle, SIGNAL(valueChanged(bool)), this,
865           SLOT(onValueEdited(bool)));
866   connect(m_oRelative, SIGNAL(valueChanged(bool)), this,
867           SLOT(onValueEdited(bool)));
868   connect(m_oDespeckling, SIGNAL(valueChanged(bool)), this,
869           SLOT(onValueEdited(bool)));
870   connect(m_oMaxColors, SIGNAL(valueChanged(bool)), this,
871           SLOT(onValueEdited(bool)));
872   connect(m_oTransparentColor, SIGNAL(colorChanged(const TPixel32 &, bool)),
873           this, SLOT(onValueEdited(const TPixel32 &, bool)));
874   connect(m_oToneThreshold, SIGNAL(valueChanged(bool)), this,
875           SLOT(onValueEdited(bool)));
876 
877   refreshPopup();
878 
879   // Non e' corretto: manca la possibilita' di aggiornare la selezione del
880   // livello corrente
881   //  connect(TApp::instance()->getCurrentLevel(), SIGNAL(xshLevelChanged()),
882   //                                         this, SLOT(updateValues()));
883 }
884 
885 //-----------------------------------------------------------------------------
886 
getParameters() const887 VectorizerParameters *VectorizerPopup::getParameters() const {
888   assert(m_sceneHandle);
889 
890   ToonzScene *scene = m_sceneHandle->getScene();
891   assert(scene);
892 
893   TSceneProperties *sceneProp = scene->getProperties();
894   assert(sceneProp);
895 
896   assert(sceneProp->getVectorizerParameters());
897   return sceneProp->getVectorizerParameters();
898 }
899 
900 //-----------------------------------------------------------------------------
901 
onValueEdited(bool isDrag)902 void VectorizerPopup::onValueEdited(bool isDrag) {
903   if (!isDrag) emit valuesChanged();
904 }
905 
906 //-----------------------------------------------------------------------------
907 
isLevelToConvert(TXshSimpleLevel * sl)908 bool VectorizerPopup::isLevelToConvert(TXshSimpleLevel *sl) {
909   return (sl->getPath().getType() != "pli");
910 }
911 
912 //-----------------------------------------------------------------------------
913 
apply()914 bool VectorizerPopup::apply() {
915   std::set<TXshLevel *> levels;
916 
917   ToonzScene *scene = m_sceneHandle->getScene();
918   if (!scene) {
919     assert(scene);
920     return false;
921   }
922 
923   TSceneProperties *sceneProp = scene->getProperties();
924   if (!sceneProp) return false;
925 
926   VectorizerParameters *vectorizerParameters =
927       sceneProp->getVectorizerParameters();
928   if (!vectorizerParameters) return false;
929 
930   int r0               = 0;
931   int c0               = 0;
932   int r1               = 0;
933   int c1               = 0;
934   bool isCellSelection = getSelectedLevels(levels, r0, c0, r1, c1);
935   if (c0 < 0) c0 = 0;
936   if (levels.empty()) {
937     error(tr("The current selection is invalid."));
938     return false;
939   }
940 
941   // Initialize Progress bar
942   m_progressDialog = new DVGui::ProgressDialog("", "Cancel", 0, 1);
943   m_progressDialog->setWindowFlags(
944       Qt::Dialog | Qt::WindowTitleHint);  // Don't show ? and X buttons
945   m_progressDialog->setWindowTitle(QString("Convert To Vector..."));
946   m_progressDialog->setAttribute(Qt::WA_DeleteOnClose);
947   m_progressDialog->setWindowModality(
948       Qt::WindowModal);  // No user interaction is allowed during vectorization
949   m_progressDialog->setFixedSize(200, 100);
950 
951   // Initialize vectorizer
952   m_vectorizer = new Vectorizer;
953 
954   m_vectorizer->setParameters(*vectorizerParameters);
955 
956   connect(m_vectorizer, SIGNAL(frameName(QString)), this,
957           SLOT(onFrameName(QString)), Qt::QueuedConnection);
958   connect(m_vectorizer, SIGNAL(frameDone(int)), this, SLOT(onFrameDone(int)),
959           Qt::QueuedConnection);
960   connect(m_vectorizer, SIGNAL(partialDone(int, int)), this,
961           SLOT(onPartialDone(int, int)), Qt::QueuedConnection);
962   // We DON'T want the progress bar to be hidden at cancel press - since its
963   // modal
964   // behavior prevents the user to interfere with a possibly still active
965   // vectorization.
966   disconnect(m_progressDialog, SIGNAL(canceled()), m_progressDialog,
967              SLOT(onCancel()));
968   // We first inform the vectorizer of a cancel press;
969   bool ret = connect(m_progressDialog, SIGNAL(canceled()), m_vectorizer,
970                      SLOT(cancel()));
971   // which eventually transmits the command to vectorization core, allowing
972   // full-time cancels
973   ret = ret && connect(m_progressDialog, SIGNAL(canceled()), m_vectorizer,
974                        SIGNAL(transmitCancel()));
975   // Only after the vectorizer has terminated its process - or got cancelled, we
976   // are allowed
977   // to proceed here.
978   ret = ret && connect(m_vectorizer, SIGNAL(finished()), this,
979                        SLOT(onFinished()), Qt::QueuedConnection);
980   assert(ret);
981 
982   std::set<int> newColumnIndices;
983   int newIndexColumn = c1 + 1;
984   for (auto const level : levels) {
985     TXshSimpleLevel *sl = dynamic_cast<TXshSimpleLevel *>(level);
986     if (!sl || !sl->getSimpleLevel() || !isLevelToConvert(sl)) {
987       QString levelName = tr(::to_string(sl->getName()).c_str());
988       QString errorMsg =
989           tr("Cannot convert to vector the current selection.") + levelName;
990       error(errorMsg);
991       continue;
992     }
993 
994     std::vector<TFrameId> fids;
995 
996     if (isCellSelection)
997       getSelectedFids(fids, sl, r0, c0, r1, c1);
998     else
999       sl->getFids(fids);
1000     assert(fids.size() > 0);
1001 
1002     close();
1003 
1004     // Re-initialize progress Bar
1005     m_progressDialog->setMaximum(fids.size() * 100);
1006     m_progressDialog->setValue(0);
1007     m_currFrame = 0;
1008 
1009     // Re-initialize vectorizer
1010     m_vectorizer->setLevel(sl);
1011     m_vectorizer->setFids(fids);
1012 
1013     // Start vectorizing
1014     m_vectorizer->start();
1015     m_progressDialog->show();
1016 
1017     // Wait the vectorizer...
1018     while (!l_quitLoop)
1019       QCoreApplication::processEvents(QEventLoop::AllEvents |
1020                                       QEventLoop::WaitForMoreEvents);
1021 
1022     l_quitLoop = false;
1023 
1024     // Assign output X-sheet cells
1025     TXshSimpleLevel *vl = m_vectorizer->getVectorizedLevel();
1026     if (isCellSelection && vl) {
1027       TXsheet *xsheet = TApp::instance()->getCurrentXsheet()->getXsheet();
1028       xsheet->insertColumn(newIndexColumn);
1029 
1030       int r, c;
1031       for (c = c0; c <= c1; c++) {
1032         for (r = r0; r <= r1; r++) {
1033           TXshCell cell = xsheet->getCell(r, c);
1034           TXshSimpleLevel *level =
1035               (!cell.isEmpty()) ? cell.getSimpleLevel() : 0;
1036           if (level != sl) continue;
1037           TFrameId curFid = cell.getFrameId();
1038           std::vector<TFrameId> newFids;
1039           vl->getFids(newFids);
1040           for (auto const &fid : newFids) {
1041             if (fid.getNumber() ==
1042                     curFid.getNumber() ||  // Hanno stesso numero di frame
1043                 (fid.getNumber() == 1 &&
1044                  curFid.getNumber() ==
1045                      -2))  // La vecchia cella non ha numero di frame
1046               xsheet->setCell(r, newIndexColumn, TXshCell(vl, fid));
1047           }
1048         }
1049       }
1050       newColumnIndices.insert(newIndexColumn);
1051       newIndexColumn += 1;
1052     } else if (vl) {
1053       std::vector<TFrameId> gomi;
1054       newColumnIndices.insert(scene->getXsheet()->getFirstFreeColumnIndex());
1055       scene->getXsheet()->exposeLevel(
1056           0, scene->getXsheet()->getFirstFreeColumnIndex(), vl, gomi);
1057     }
1058 
1059     if (m_vectorizer->isCanceled()) break;
1060   }
1061 
1062   // Add undo object
1063   if (!m_vectorizer->isCanceled())
1064     ColumnCmd::addConvertToVectorUndo(newColumnIndices);
1065 
1066   m_progressDialog->close();
1067   delete m_vectorizer;
1068 
1069   TApp::instance()->getCurrentScene()->notifyCastChange();
1070   TApp::instance()->getCurrentXsheet()->notifyXsheetChanged();
1071 
1072   return true;
1073 }
1074 
1075 //-----------------------------------------------------------------------------
1076 
onFinished()1077 void VectorizerPopup::onFinished() { l_quitLoop = true; }
1078 
1079 //-----------------------------------------------------------------------------
1080 
onFrameName(QString frameName)1081 void VectorizerPopup::onFrameName(QString frameName) {
1082   QString label = tr("Conversion in progress: ") + frameName;
1083   m_progressDialog->setLabelText(label);
1084 }
1085 
1086 //-----------------------------------------------------------------------------
1087 
onFrameDone(int frameCount)1088 void VectorizerPopup::onFrameDone(int frameCount) {
1089   m_progressDialog->setValue(
1090       frameCount * 100);  // 100 multiplier stands for partial progresses
1091   m_currFrame = frameCount;
1092 }
1093 
1094 //-----------------------------------------------------------------------------
1095 
onPartialDone(int partial,int total)1096 void VectorizerPopup::onPartialDone(int partial, int total) {
1097   int value = (m_currFrame + partial / (double)total) * 100.0;
1098 
1099   // NOTA: Puo' essere che la seguente non sia vera - dipende dall'ordine di
1100   // esecuzione dei segnali
1101   // onFrameDone e onPartialDone - se i primi si fanno in massa prima... Puo'
1102   // generare uno stack overflow...
1103   // NOTA: Non va ancora bene. Cosi' si attenua largamente il problema, ma a
1104   // volte puo' ancora succedere.
1105   if (value > m_progressDialog->value() + 5 &&
1106       value < m_progressDialog->maximum()) {
1107     // qDebug("Partial %d of %d;  Value %d of %d", partial, total, value,
1108     // m_progressDialog->maximum());
1109     m_progressDialog->setValue(value);
1110   }
1111   /*else
1112 {
1113 if(value != m_progressDialog->value())
1114 qDebug("ERRORE: VALORE PB= %d; Valore: %d",m_progressDialog->value(),value);
1115 }*/
1116 }
1117 
1118 //-----------------------------------------------------------------------------
1119 
onOk()1120 void VectorizerPopup::onOk() { apply(); }
1121 
1122 //-----------------------------------------------------------------------------
1123 
1124 //! Copies the pop-up settings into scene settings.
updateSceneSettings()1125 void VectorizerPopup::updateSceneSettings() {
1126   VectorizerParameters *vParams = getParameters();
1127   assert(vParams);
1128 
1129   bool outline         = (m_typeMenu->currentIndex() == 1);
1130   vParams->m_isOutline = outline;
1131 
1132   if (outline) {
1133     vParams->m_oDespeckling      = m_oDespeckling->getValue();
1134     vParams->m_oAccuracy         = m_oAccuracy->getValue();
1135     vParams->m_oAdherence        = m_oAdherence->getValue();
1136     vParams->m_oAngle            = m_oAngle->getValue();
1137     vParams->m_oRelative         = m_oRelative->getValue();
1138     vParams->m_oMaxColors        = m_oMaxColors->getValue();
1139     vParams->m_oToneThreshold    = m_oToneThreshold->getValue();
1140     vParams->m_oTransparentColor = m_oTransparentColor->getColor();
1141     vParams->m_oPaintFill        = m_oPaintFill->isChecked();
1142     vParams->m_oAlignBoundaryStrokesDirection =
1143         m_oAlignBoundaryStrokesDirection->isChecked();
1144   } else {
1145     vParams->m_cThreshold    = m_cThreshold->getValue();
1146     vParams->m_cAccuracy     = m_cAccuracy->getValue();
1147     vParams->m_cDespeckling  = m_cDespeckling->getValue();
1148     vParams->m_cMaxThickness = m_cMaxThickness->getValue();
1149     vParams->m_cThicknessRatioFirst =
1150         m_cThicknessRatioFirst->getValue() * 100.0;
1151     vParams->m_cThicknessRatioLast = m_cThicknessRatioLast->getValue() * 100.0;
1152     vParams->m_cMakeFrame          = m_cMakeFrame->isChecked();
1153     vParams->m_cPaintFill          = m_cPaintFill->isChecked();
1154     vParams->m_cAlignBoundaryStrokesDirection =
1155         m_cAlignBoundaryStrokesDirection->isChecked();
1156     vParams->m_cNaaSource = m_cNaaSource->isChecked();
1157   }
1158 }
1159 
1160 //-----------------------------------------------------------------------------
1161 
refreshPopup()1162 void VectorizerPopup::refreshPopup() { setType(getParameters()->m_isOutline); }
1163 
1164 //-----------------------------------------------------------------------------
1165 
updateVisibility()1166 void VectorizerPopup::updateVisibility() {
1167   struct Locals {
1168     QGridLayout *const m_paramsLayout;
1169 
1170     void setVisible(QLayoutItem *item, bool visible) {
1171       if (item) {
1172         if (QWidget *w = item->widget())
1173           w->setVisible(visible);
1174         else if (QLayout *l = item->layout()) {
1175           int i, iCount = l->count();
1176           for (i = 0; i != iCount; ++i) setVisible(l->itemAt(i), visible);
1177         }
1178       }
1179     }
1180 
1181     void setVisible(int row, bool visible) {
1182       int c, cCount = m_paramsLayout->columnCount();
1183       for (c = 0; c != cCount; ++c)
1184         setVisible(m_paramsLayout->itemAtPosition(row, c), visible);
1185     }
1186 
1187     void setVisible(const std::vector<ParamGroup> &paramGroups,
1188                     int visibilityBits) {
1189       // Iterate parameter groups
1190       std::vector<ParamGroup>::const_iterator pgt, pgEnd = paramGroups.end();
1191       for (pgt = paramGroups.begin(); pgt != pgEnd; ++pgt) {
1192         bool groupVisible = false;
1193 
1194         // Iterate parameters
1195         int r, rCount = int(pgt->m_params.size());
1196         for (r = 0; r != rCount; ++r) {
1197           bool visible = (visibilityBits >> pgt->m_params[r].m_bit) & 1;
1198 
1199           setVisible(pgt->m_startRow + r, visible);
1200           groupVisible = visible | groupVisible;
1201         }
1202 
1203         // Finally, set group header's visibility
1204         if (pgt->m_separatorRow >= 0)
1205           setVisible(pgt->m_separatorRow, groupVisible);
1206       }
1207     }
1208 
1209   } locals = {m_paramsLayout};
1210 
1211   VectorizerParameters *vParams = getParameters();
1212   assert(vParams);
1213 
1214   locals.setVisible(
1215       vParams->m_isOutline ? l_outlineParamGroups : l_centerlineParamGroups,
1216       vParams->m_visibilityBits);
1217 }
1218 
1219 //-----------------------------------------------------------------------------
1220 
onTypeChange(int indexType)1221 void VectorizerPopup::onTypeChange(int indexType) {
1222   ToonzScene *scene = m_sceneHandle->getScene();
1223   if (!scene) return;
1224   TSceneProperties *sceneProp = scene->getProperties();
1225   if (!sceneProp) return;
1226   VectorizerParameters *vectorizerParameters =
1227       sceneProp->getVectorizerParameters();
1228   if (!vectorizerParameters) return;
1229   bool isOutline        = vectorizerParameters->m_isOutline;
1230   bool isNewTypeOutline = (indexType == 0) ? false : true;
1231   if (isNewTypeOutline == isOutline) return;
1232 
1233   vectorizerParameters->m_isOutline = isNewTypeOutline;
1234   setType(isNewTypeOutline);
1235 
1236   m_swatchArea->invalidateContents();
1237 }
1238 
1239 //-----------------------------------------------------------------------------
1240 
setType(bool outline)1241 void VectorizerPopup::setType(bool outline) {
1242   disconnect(m_typeMenu, SIGNAL(currentIndexChanged(int)), this,
1243              SLOT(onTypeChange(int)));
1244 
1245   // Setting child visibility alot invokes several layout updates - causing
1246   // extensive flickering
1247   m_paramsWidget->layout()->setEnabled(false);
1248 
1249   bool centerline = !outline;
1250   m_typeMenu->setCurrentIndex((int)outline);
1251 
1252   m_cThresholdLabel->setVisible(centerline);
1253   m_cThreshold->setVisible(centerline);
1254   m_cAccuracyLabel->setVisible(centerline);
1255   m_cAccuracy->setVisible(centerline);
1256   m_cDespecklingLabel->setVisible(centerline);
1257   m_cDespeckling->setVisible(centerline);
1258   m_cMaxThicknessLabel->setVisible(centerline);
1259   m_cMaxThickness->setVisible(centerline);
1260   // m_cThicknessRatio->setVisible(centerline);
1261   m_cThicknessRatioLabel->setVisible(centerline);
1262   m_cThicknessRatioFirstLabel->setVisible(centerline);
1263   m_cThicknessRatioFirst->setVisible(centerline);
1264   m_cThicknessRatioLastLabel->setVisible(centerline);
1265   m_cThicknessRatioLast->setVisible(centerline);
1266 
1267   m_cPaintFill->setVisible(centerline);
1268   m_cAlignBoundaryStrokesDirection->setVisible(centerline);
1269   m_cMakeFrame->setVisible(centerline);
1270   m_cNaaSourceSeparator->setVisible(centerline);
1271   m_cNaaSource->setVisible(centerline);
1272 
1273   m_oAccuracyLabel->setVisible(outline);
1274   m_oAccuracy->setVisible(outline);
1275   m_oDespecklingLabel->setVisible(outline);
1276   m_oDespeckling->setVisible(outline);
1277   m_oPaintFill->setVisible(outline);
1278   m_oAlignBoundaryStrokesDirection->setVisible(outline);
1279   m_oCornersSeparator->setVisible(outline);
1280   m_oAngleLabel->setVisible(outline);
1281   m_oAngle->setVisible(outline);
1282   m_oAdherenceLabel->setVisible(outline);
1283   m_oAdherence->setVisible(outline);
1284   m_oRelativeLabel->setVisible(outline);
1285   m_oRelative->setVisible(outline);
1286   m_oFullColorSeparator->setVisible(outline);
1287   m_oMaxColorsLabel->setVisible(outline);
1288   m_oMaxColors->setVisible(outline);
1289   m_oTransparentColorLabel->setVisible(outline);
1290   m_oTransparentColor->setVisible(outline);
1291   m_oTlvSeparator->setVisible(outline);
1292   m_oToneThresholdLabel->setVisible(outline);
1293   m_oToneThreshold->setVisible(outline);
1294 
1295   m_paramsWidget->layout()->setEnabled(true);
1296 
1297   loadConfiguration(outline);
1298 
1299   connect(m_typeMenu, SIGNAL(currentIndexChanged(int)), this,
1300           SLOT(onTypeChange(int)));
1301 
1302   updateVisibility();
1303 }
1304 
1305 //-----------------------------------------------------------------------------
1306 
1307 // This is essentially the inverse of the previous one.
loadConfiguration(bool isOutline)1308 void VectorizerPopup::loadConfiguration(bool isOutline) {
1309   disconnect(SIGNAL(valuesChanged()));  // Avoid notifications for value changes
1310 
1311   ToonzScene *scene = m_sceneHandle->getScene();
1312   assert(scene);
1313 
1314   TSceneProperties *sceneProp = scene->getProperties();
1315   assert(sceneProp);
1316 
1317   VectorizerParameters *vParams = sceneProp->getVectorizerParameters();
1318   assert(vParams);
1319 
1320   loadRanges(isOutline);
1321 
1322   if (isOutline) {
1323     m_oDespeckling->setValue(vParams->m_oDespeckling);
1324     m_oAdherence->setValue(vParams->m_oAdherence);
1325     m_oAngle->setValue(vParams->m_oAngle);
1326     m_oRelative->setValue(vParams->m_oRelative);
1327     m_oAccuracy->setValue(vParams->m_oAccuracy);
1328     m_oPaintFill->setChecked(vParams->m_oPaintFill);
1329     m_oAlignBoundaryStrokesDirection->setChecked(
1330         vParams->m_oAlignBoundaryStrokesDirection);
1331     m_oMaxColors->setValue(vParams->m_oMaxColors);
1332     m_oTransparentColor->setColor(vParams->m_oTransparentColor);
1333     m_oToneThreshold->setValue(vParams->m_oToneThreshold);
1334   } else {
1335     m_cThreshold->setValue(vParams->m_cThreshold);
1336     m_cDespeckling->setValue(vParams->m_cDespeckling);
1337     m_cPaintFill->setChecked(vParams->m_cPaintFill);
1338     m_cAlignBoundaryStrokesDirection->setChecked(
1339         vParams->m_cAlignBoundaryStrokesDirection);
1340     m_cMakeFrame->setChecked(vParams->m_cMakeFrame);
1341     m_cNaaSource->setChecked(vParams->m_cNaaSource);
1342     m_cMaxThickness->setValue(vParams->m_cMaxThickness);
1343     m_cAccuracy->setValue(vParams->m_cAccuracy);
1344     // m_cThicknessRatio->setValue(vParams->m_cThicknessRatio);
1345     m_cThicknessRatioFirst->setValue(vParams->m_cThicknessRatioFirst / 100.0);
1346     m_cThicknessRatioLast->setValue(vParams->m_cThicknessRatioLast / 100.0);
1347   }
1348 
1349   // Reconnect changes update
1350   connect(this, SIGNAL(valuesChanged()), this, SLOT(updateSceneSettings()));
1351   connect(this, SIGNAL(valuesChanged()), m_swatchArea,
1352           SLOT(invalidateContents()));
1353 
1354   m_swatchArea->updateContents();
1355 }
1356 
1357 //-----------------------------------------------------------------------------
1358 
loadRanges(int outline)1359 void VectorizerPopup::loadRanges(int outline) {
1360   if (outline) {
1361     m_oAccuracy->setRange(0, 10);
1362     m_oDespeckling->setRange(0, 10);
1363     m_oAdherence->setRange(0, 100);
1364     m_oAngle->setRange(0, 180);
1365     m_oRelative->setRange(0, 100);
1366     m_oMaxColors->setRange(1, 256);
1367     m_oToneThreshold->setRange(0, 255);
1368   } else {
1369     m_cThreshold->setRange(1, 10);
1370     m_cAccuracy->setRange(1, 10);
1371     m_cDespeckling->setRange(1, 10);
1372     m_cMaxThickness->setRange(0, (std::numeric_limits<int>::max)());
1373     // m_cThicknessRatio->setRange(0,100);
1374     m_cThicknessRatioFirst->setRange(0, 1.0);
1375     m_cThicknessRatioLast->setRange(0, 1.0);
1376   }
1377 }
1378 
1379 //-----------------------------------------------------------------------------
1380 
showEvent(QShowEvent * se)1381 void VectorizerPopup::showEvent(QShowEvent *se) {
1382   refreshPopup();
1383   connect(m_sceneHandle, SIGNAL(sceneSwitched()), SLOT(refreshPopup()));
1384 }
1385 
1386 //-----------------------------------------------------------------------------
1387 
hideEvent(QHideEvent * he)1388 void VectorizerPopup::hideEvent(QHideEvent *he) {
1389   refreshPopup();
1390   disconnect(m_sceneHandle, SIGNAL(sceneSwitched()), this,
1391              SLOT(refreshPopup()));
1392   Dialog::hideEvent(he);
1393 }
1394 
1395 //-----------------------------------------------------------------------------
1396 
populateVisibilityMenu()1397 void VectorizerPopup::populateVisibilityMenu() {
1398   struct Locals {
1399     VectorizerPopup *m_this;
1400 
1401     void addActions(QMenu *menu, const std::vector<ParamGroup> &paramGroups,
1402                     int visibilityBits) {
1403       std::vector<ParamGroup>::const_iterator gt, gEnd = paramGroups.end();
1404 
1405       for (gt = paramGroups.begin(); gt != gEnd; ++gt) {
1406         if (gt->m_separatorRow >= 0) menu->addSeparator();
1407 
1408         std::vector<Param>::const_iterator pt, pEnd = gt->m_params.end();
1409         for (pt = gt->m_params.begin(); pt != pEnd; ++pt) {
1410           QAction *visibleParam = menu->addAction(pt->m_name);
1411           visibleParam->setCheckable(true);
1412           visibleParam->setChecked(visibilityBits & (1 << pt->m_bit));
1413           visibleParam->setData(pt->m_bit);
1414 
1415           bool ret = connect(visibleParam, SIGNAL(toggled(bool)), m_this,
1416                              SLOT(visibilityToggled()));
1417           assert(ret);
1418         }
1419       }
1420     }
1421 
1422   } locals = {this};
1423 
1424   QMenu *menu = qobject_cast<QMenu *>(sender());
1425   menu->clear();
1426 
1427   VectorizerParameters *vParams = getParameters();
1428   locals.addActions(
1429       menu,
1430       vParams->m_isOutline ? l_outlineParamGroups : l_centerlineParamGroups,
1431       vParams->m_visibilityBits);
1432 }
1433 
1434 //-----------------------------------------------------------------------------
1435 
visibilityToggled()1436 void VectorizerPopup::visibilityToggled() {
1437   QAction *action = qobject_cast<QAction *>(sender());
1438   assert(action);
1439 
1440   const QVariant &data = action->data();
1441   assert(data.canConvert<int>());
1442 
1443   int row = action->data().toInt();
1444 
1445   VectorizerParameters *vParams = getParameters();
1446   vParams->m_visibilityBits ^= (1 << row);
1447 
1448   updateVisibility();
1449 }
1450 
1451 //-----------------------------------------------------------------------------
1452 
saveParameters()1453 void VectorizerPopup::saveParameters() {
1454   struct {
1455     VectorizerPopup *m_this;
1456 
1457     static bool vectorizerType(TPersist *persist) {
1458       return (dynamic_cast<VectorizerParameters *>(persist) != 0);
1459     }
1460 
1461     void saveParams(const TFilePath &fp)  // May throw due to I/O failure
1462     {
1463       // Read the complete file first
1464       TPersistSet levelSettings;
1465 
1466       if (TSystem::doesExistFileOrLevel(fp)) {
1467         TIStream is(fp);
1468 
1469         if (!is)
1470           throw TException(
1471               tr("File could not be opened for read").toStdWString());
1472 
1473         is >> levelSettings;
1474       }
1475 
1476       // Replace data to be saved
1477       VectorizerParameters *params = getCurrentVectorizerParameters();
1478 
1479       levelSettings.insert(
1480           std::unique_ptr<TPersist>(new VectorizerParameters(*params)));
1481 
1482       // Save the new settings
1483       TOStream os(fp);
1484 
1485       if (!os)
1486         throw TException(
1487             tr("File could not be opened for write").toStdWString());
1488 
1489       os << levelSettings;
1490     }
1491 
1492   } locals = {this};
1493 
1494   // Retrieve current level path
1495   TFilePath folder, fileName;
1496 
1497   const TFilePath &levelPath = getSelectedLevelPath();
1498   if (!levelPath.isEmpty()) {
1499     folder   = levelPath.getParentDir();
1500     fileName = TFilePath(levelPath.getWideName()).withType("tnzsettings");
1501   }
1502 
1503   // Open save popup with defaulted path
1504   static GenericSaveFilePopup *popup =
1505       new GenericSaveFilePopup(tr("Save Vectorizer Parameters"));
1506 
1507   popup->setFilterTypes(QStringList("tnzsettings"));
1508   popup->setFolder(folder);
1509   popup->setFilename(fileName);
1510 
1511   fileName = popup->getPath();
1512   if (!fileName.isEmpty()) {
1513     try {
1514       locals.saveParams(fileName);
1515     } catch (const TException &e) {
1516       DVGui::error(QString::fromStdWString(e.getMessage()));
1517     }
1518   }
1519 }
1520 
1521 //-----------------------------------------------------------------------------
1522 
loadParameters()1523 void VectorizerPopup::loadParameters() {
1524   struct {
1525     VectorizerPopup *m_this;
1526 
1527     void loadParams(const TFilePath &fp)  // May throw due to I/O failure
1528     {
1529       TIStream is(fp);
1530 
1531       if (!is)
1532         throw TException(
1533             tr("File could not be opened for read").toStdWString());
1534 
1535       VectorizerParameters *vParams = getCurrentVectorizerParameters();
1536       const std::string &vParamsTag = vParams->getStreamTag();
1537 
1538       std::string tagName;
1539       while (is.matchTag(tagName)) {
1540         if (tagName == vParamsTag)
1541           is >> *vParams, is.matchEndTag();
1542         else
1543           is.skipCurrentTag();
1544       }
1545     }
1546 
1547   } locals = {this};
1548 
1549   // Retrieve current level path
1550   TFilePath folder, fileName;
1551 
1552   const TFilePath &levelPath = getSelectedLevelPath();
1553   if (!levelPath.isEmpty()) {
1554     folder   = levelPath.getParentDir();
1555     fileName = TFilePath(levelPath.getWideName()).withType("tnzsettings");
1556   }
1557 
1558   // Open load popup with defaulted path
1559   static GenericLoadFilePopup *popup =
1560       new GenericLoadFilePopup(tr("Load Vectorizer Parameters"));
1561 
1562   popup->setFilterTypes(QStringList("tnzsettings"));
1563   popup->setFolder(folder);
1564   popup->setFilename(fileName);
1565 
1566   fileName = popup->getPath();
1567   if (!fileName.isEmpty()) {
1568     try {
1569       locals.loadParams(fileName);
1570       refreshPopup();  // Update GUI to reflect changes
1571     } catch (const TException &e) {
1572       DVGui::error(QString::fromStdWString(e.getMessage()));
1573     }
1574   }
1575 }
1576 
1577 //-----------------------------------------------------------------------------
1578 
resetParameters()1579 void VectorizerPopup::resetParameters() {
1580   *getCurrentVectorizerParameters() = VectorizerParameters();
1581   refreshPopup();
1582 }
1583 
1584 //*****************************************************************************
1585 //    VectorizerPopupCommand instantiation
1586 //*****************************************************************************
1587 
1588 OpenPopupCommandHandler<VectorizerPopup> openVectorizerPopup(
1589     MI_ConvertToVectors);
1590