1 
2 
3 #include "toonz/tcenterlinevectorizer.h"
4 
5 // TnzCore includes
6 #include "tsystem.h"
7 #include "tstopwatch.h"
8 #include "tpalette.h"
9 #include "trastercm.h"
10 #include "ttoonzimage.h"
11 #include "tregion.h"
12 #include "tstroke.h"
13 #include "trasterimage.h"
14 #include "tmathutil.h"
15 
16 // tcg includes
17 #include "tcg/tcg_numeric_ops.h"
18 
19 // STD includes
20 #include <cmath>
21 #include <functional>
22 
23 #undef DEBUG
24 
25 //---------------------------------------------------------
26 
27 struct ControlPoint {
28   TStroke *m_stroke;
29   int m_index;
ControlPointControlPoint30   ControlPoint(TStroke *stroke, int index) : m_stroke(stroke), m_index(index) {}
getPointControlPoint31   TPointD getPoint() const { return m_stroke->getControlPoint(m_index); }
setPointControlPoint32   void setPoint(const TPointD &p) {
33     TThickPoint point = m_stroke->getControlPoint(m_index);
34     point.x           = p.x;
35     point.y           = p.y;
36     m_stroke->setControlPoint(m_index, point);
37   }
38 };
39 
40 //---------------------------------------------------------
41 
42 class Node;
43 
44 class DataPixel {
45 public:
46   TPoint m_pos;
47   int m_value;
48   bool m_ink;
49   Node *m_node;
DataPixel()50   DataPixel() : m_value(0), m_ink(false), m_node(0) {}
51 };
52 
53 //---------------------------------------------------------
54 #ifdef _WIN32
55 template class DV_EXPORT_API TSmartPointerT<TRasterT<DataPixel>>;
56 #endif
57 typedef TRasterPT<DataPixel> DataRasterP;
58 //---------------------------------------------------------
59 
60 class Junction;
61 
62 class Node {
63 public:
64   Node *m_other;
65   DataPixel *m_pixel;
66   Node *m_prev, *m_next;
67   Junction *m_junction;
68 #ifdef DEBUG
69   bool m_flag;
70 #endif
71   bool m_visited;
Node()72   Node()
73       : m_pixel(0)
74       , m_prev(0)
75       , m_next(0)
76       , m_junction(0)
77       ,
78 #ifdef DEBUG
79       m_flag(false)
80       ,
81 #endif
82       m_visited(false) {
83   }
84 };
85 
86 //---------------------------------------------------------
87 
88 class ProtoStroke;
89 
90 class Junction {
91 public:
92   TThickPoint m_center;
93   std::deque<Node *> m_nodes;
94   int m_junctionOrder;
95   std::vector<ProtoStroke *> m_protoStrokes;
96   bool m_locked;
Junction()97   Junction()
98       : m_center()
99       , m_nodes()
100       , m_junctionOrder(0)
101       , m_protoStrokes()
102       , m_locked(false) {}
103   bool isConvex();
104 };
105 
106 //---------------------------------------------------------
107 
108 class ProtoStroke {
109 public:
110   TPointD m_startDirection, m_endDirection;
111   Junction *m_startJunction, *m_endJunction;
112   std::deque<TThickPoint> m_points;
ProtoStroke()113   ProtoStroke()
114       : m_points()
115       , m_startDirection()
116       , m_endDirection()
117       , m_startJunction(0)
118       , m_endJunction(0) {}
ProtoStroke(std::deque<TThickPoint>::iterator it_b,std::deque<TThickPoint>::iterator it_e)119   ProtoStroke(std::deque<TThickPoint>::iterator it_b,
120               std::deque<TThickPoint>::iterator it_e)
121       : m_points(it_b, it_e)
122       , m_startDirection()
123       , m_endDirection()
124       , m_startJunction(0)
125       , m_endJunction(0) {}
126 };
127 
128 //---------------------------------------------------------
129 
computeDistance2(Node * na,Node * nb)130 static double computeDistance2(Node *na, Node *nb) {
131   assert(na->m_pixel);
132   assert(nb->m_pixel);
133   TPointD d = convert(na->m_pixel->m_pos - nb->m_pixel->m_pos);
134   return d * d;
135 }
136 
137 //---------------------------------------------------------
138 
renormalizeImage(TVectorImage * vi)139 static void renormalizeImage(TVectorImage *vi) {
140   int i, j;
141   int n = vi->getStrokeCount();
142   std::vector<ControlPoint> points;
143   points.reserve(n * 2);
144   for (i = 0; i < n; i++) {
145     TStroke *stroke = vi->getStroke(i);
146     int m           = stroke->getControlPointCount();
147     if (m > 0) {
148       if (m == 1)
149         points.push_back(ControlPoint(stroke, 0));
150       else {
151         points.push_back(ControlPoint(stroke, 0));
152         points.push_back(ControlPoint(stroke, m - 1));
153       }
154     }
155   }
156   int count = points.size();
157   for (i = 0; i < count; i++) {
158     ControlPoint &pi = points[i];
159     TPointD posi     = pi.getPoint();
160     TPointD center   = posi;
161     std::vector<int> neighbours;
162     neighbours.push_back(i);
163     for (j = i + 1; j < count; j++) {
164       TPointD posj = points[j].getPoint();
165       double d     = tdistance(posj, posi);
166       if (d < 0.01) {
167         neighbours.push_back(j);
168         center += posj;
169       }
170     }
171     int m = neighbours.size();
172     if (m == 1) continue;
173     center = center * (1.0 / m);
174     for (j = 0; j < m; j++) points[neighbours[j]].setPoint(center);
175   }
176 }
177 
178 //---------------------------------------------------------
179 
180 class OutlineVectorizer {
181   TPalette *m_palette;
182 
183 public:
184   TRasterP m_src;
185 
186   OutlineConfiguration m_configuration;
187   DataRasterP m_dataRaster;
188   std::vector<std::pair<int, DataRasterP>> m_dataRasterArray;
189   TVectorImageP m_vimage;
190   std::vector<Node *> m_nodes;
191   std::list<std::vector<TThickPoint>> m_protoOutlines;
192 
193   std::vector<Junction *> m_junctions;
194 
OutlineVectorizer(const OutlineConfiguration & configuration,TPalette * palette)195   OutlineVectorizer(const OutlineConfiguration &configuration,
196                     TPalette *palette)
197       : m_configuration(configuration), m_palette(palette) {}
198 
199   ~OutlineVectorizer();
200 
201   void traceOutline(Node *initialNode);
202   void createOutlineStrokes();
203   void makeDataRaster(const TRasterP &src);
204 
205   Node *findOtherSide(Node *node);
206 
207   void clearNodes();
208   Node *createNode(DataPixel *pix);
209 
210   void clearJunctions();
211 
212   void init();
213 
214   void link(DataPixel *pix, DataPixel *from, DataPixel *to);
215 
computeGradient(DataPixel * pix)216   TPoint computeGradient(DataPixel *pix) {
217     assert(m_dataRaster);
218     const int wrap = m_dataRaster->getWrap();
219 
220     TPoint g(0, 0);
221     int n, s, w, e, nw, sw, ne, se;
222 
223     w  = pix[-1].m_value;
224     nw = pix[-1 + wrap].m_value;
225     sw = pix[-1 - wrap].m_value;
226 
227     e  = pix[+1].m_value;
228     ne = pix[+1 + wrap].m_value;
229     se = pix[+1 - wrap].m_value;
230 
231     n = pix[+wrap].m_value;
232     s = pix[-wrap].m_value;
233 
234     g.y = -sw + ne - se + nw + 2 * (n - s);
235     g.x = -sw + ne + se - nw + 2 * (e - w);
236     return g;
237   }
238 
239 private:
240   // not implemented
241   OutlineVectorizer(const OutlineVectorizer &);
242   OutlineVectorizer &operator=(const OutlineVectorizer &);
243 };
244 
245 //---------------------------------------------------------
246 
~OutlineVectorizer()247 OutlineVectorizer::~OutlineVectorizer() {
248   m_protoOutlines.clear();
249   clearNodes();
250   clearJunctions();
251 }
252 
253 //---------------------------------------------------------
254 
init()255 void OutlineVectorizer::init() {
256   int y;
257 
258   DataRasterP dataRaster = m_dataRaster;
259   const int wrap         = dataRaster->getWrap();
260   const int delta[]      = {-wrap - 1, -wrap, -wrap + 1, 1,
261                        wrap + 1,  wrap,  wrap - 1,  -1};
262 
263   for (y = 1; y < dataRaster->getLy() - 1; y++) {
264     DataPixel *pix    = dataRaster->pixels(y);
265     DataPixel *endPix = pix + dataRaster->getLx() - 1;
266     pix++;
267     for (pix++; pix < endPix; ++pix) {
268       if ((pix->m_ink == false) || (pix[-wrap].m_ink && pix[wrap].m_ink &&
269                                     pix[-1].m_ink && pix[1].m_ink))
270         continue;
271       int i;
272       for (i = 0; i < 8; i++)
273         if (pix[delta[i]].m_ink && pix[delta[(i + 1) & 0x7]].m_ink == false)
274           break;
275       int start = i;
276       if (i == 8) continue;  // punto isolato
277       for (;;) {
278         int j = (i + 1) & 0x7;
279         assert(i < 8 && pix[delta[i]].m_ink);
280         assert(j < 8 && pix[delta[j]].m_ink == false);
281         do
282           j = (j + 1) & 0x7;
283         while (pix[delta[j]].m_ink == false);
284         assert(j < 8 && pix[delta[j]].m_ink);
285         if (((i + 2) & 0x7) != j || (i & 1) == 0) {
286           // il bianco comprende anche un fianco
287           link(pix, pix + delta[i], pix + delta[j]);
288         }
289         i = j;
290         assert(i < 8);
291         while (pix[delta[(i + 1) & 0x7]].m_ink) i = (i + 1) & 0x7;
292         assert(i < 8 && pix[delta[i]].m_ink);
293         assert(pix[delta[(i + 1) & 0x7]].m_ink == false);
294         if (i == start) break;
295       }
296     }
297   }
298 }
299 
300 //---------------------------------------------------------
301 
createNode(DataPixel * pix)302 Node *OutlineVectorizer::createNode(DataPixel *pix) {
303   Node *node    = new Node();
304   node->m_pixel = pix;
305   node->m_other = pix->m_node;
306   pix->m_node   = node;
307   m_nodes.push_back(node);
308   return node;
309 }
310 
311 //---------------------------------------------------------
312 
clearNodes()313 void OutlineVectorizer::clearNodes() {
314   int i;
315   for (i = 0; i < (int)m_nodes.size(); i++) delete m_nodes[i];
316   m_nodes.clear();
317 }
318 
319 //---------------------------------------------------------
320 
clearJunctions()321 void OutlineVectorizer::clearJunctions() {
322   int i;
323   for (i = 0; i < (int)m_junctions.size(); i++) delete m_junctions[i];
324   m_junctions.clear();
325 }
326 
327 //---------------------------------------------------------
328 
link(DataPixel * pix,DataPixel * srcPix,DataPixel * dstPix)329 void OutlineVectorizer::link(DataPixel *pix, DataPixel *srcPix,
330                              DataPixel *dstPix) {
331   Node *srcNode = 0, *dstNode = 0, *node = 0;
332   Node *tmp;
333   for (tmp = pix->m_node; tmp; tmp = tmp->m_other) {
334     if (tmp->m_pixel == 0) continue;
335     if (tmp->m_prev && tmp->m_prev->m_pixel == srcPix) {
336       assert(srcNode == 0);
337       if (node) {
338         assert(node->m_next->m_pixel == dstPix);
339         assert(node->m_prev == 0);
340         node->m_prev        = tmp->m_prev;
341         tmp->m_prev->m_next = node;
342         tmp->m_next = tmp->m_prev = 0;
343         tmp->m_pixel              = 0;
344         return;
345       }
346       assert(tmp->m_next == 0);
347       srcNode = tmp->m_prev;
348       node    = tmp;
349     }
350     if (tmp->m_next && tmp->m_next->m_pixel == dstPix) {
351       assert(dstNode == 0);
352       if (node) {
353         assert(node->m_prev->m_pixel == srcPix);
354         assert(node->m_next == 0);
355         node->m_next        = tmp->m_next;
356         tmp->m_next->m_prev = node;
357         tmp->m_next = tmp->m_prev = 0;
358         tmp->m_pixel              = 0;
359         return;
360       }
361       assert(tmp->m_prev == 0);
362       dstNode = tmp->m_next;
363       node    = tmp;
364     }
365   }
366   if (!node) node = createNode(pix);
367   if (!srcNode) srcNode = createNode(srcPix);
368   if (!dstNode) dstNode = createNode(dstPix);
369 
370   if (!node->m_next) {
371     node->m_next = dstNode;
372     assert(dstNode->m_prev == 0);
373     dstNode->m_prev = node;
374   }
375   if (!node->m_prev) {
376     node->m_prev = srcNode;
377     assert(srcNode->m_next == 0);
378     srcNode->m_next = node;
379   }
380 
381   assert(node->m_next == dstNode);
382   assert(node->m_prev == srcNode);
383   assert(dstNode->m_prev == node);
384   assert(srcNode->m_next == node);
385 }
386 
387 //---------------------------------------------------------
388 
traceOutline(Node * initialNode)389 void OutlineVectorizer::traceOutline(Node *initialNode) {
390   Node *startNode = initialNode;
391   Node *node;
392   do {
393     if (!startNode) break;
394     node = findOtherSide(startNode);
395     if (!node) break;
396 
397     double startDist2 = computeDistance2(startNode, node);
398     if (startDist2 > 0.1) break;
399 
400     startNode = startNode->m_next;
401   } while (startNode != initialNode);
402 
403   if (!startNode) return;
404   node = startNode;
405   std::vector<TThickPoint> points;
406   do {
407     node = node->m_next;
408     if (!node) break;
409     node->m_visited = true;
410     points.push_back(TThickPoint(convert(node->m_pixel->m_pos), 0));
411   } while (node != startNode);
412   m_protoOutlines.push_back(points);
413 }
414 
415 //---------------------------------------------------------
416 
findOtherSide(Node * node)417 Node *OutlineVectorizer::findOtherSide(Node *node) {
418   DataPixel *pix = node->m_pixel;
419 
420   TPoint dir = -computeGradient(pix);
421   if (dir == TPoint(0, 0)) return 0;
422   TPoint d1(tsign(dir.x), 0), d2(0, tsign(dir.y));
423   int num = abs(dir.y), den = abs(dir.x);
424   if (num > den) {
425     std::swap(d1, d2);
426     std::swap(num, den);
427   }
428   TPoint pos = pix->m_pos;
429   int i;
430   for (i = 0;; i++) {
431     TPoint q(pos.x + d1.x * i + d2.x * num * i / den,
432              pos.y + d1.y * i + d2.y * num * i / den);
433     DataPixel *nextPix = m_dataRaster->pixels(q.y) + q.x;
434     if (nextPix->m_ink == false) break;
435     pix = nextPix;
436   }
437   assert(pix);
438   if (!pix->m_node) {
439     const int wrap = m_dataRaster->getWrap();
440     if (pix[-1].m_node)
441       pix--;
442     else if (pix[1].m_node)
443       pix++;
444     else if (pix[wrap].m_node)
445       pix += wrap;
446     else if (pix[-wrap].m_node)
447       pix -= wrap;
448     else {
449       assert(0);
450     }
451   }
452   if (!pix->m_node) return 0;
453   Node *q = pix->m_node;
454   while (q->m_pixel == 0 && q->m_other) q = q->m_other;
455   assert(q && q->m_pixel == pix);
456 
457   for (i = 0; i < 5; i++) {
458     if (!q->m_prev) break;
459     q = q->m_prev;
460   }
461 
462   Node *best       = q;
463   double bestDist2 = computeDistance2(q, node);
464   for (i = 0; i < 10; i++) {
465     q = q->m_next;
466     if (!q) break;
467     double dist2 = computeDistance2(q, node);
468     if (dist2 < bestDist2) {
469       bestDist2 = dist2;
470       best      = q;
471     }
472   }
473 
474   return best;
475 }
476 
477 //---------------------------------------------------------
478 
createOutlineStrokes()479 void OutlineVectorizer::createOutlineStrokes() {
480   m_vimage->enableRegionComputing(true, false);
481   int j;
482 
483   for (j = 0; j < (int)m_nodes.size(); j++) {
484     Node *node = m_nodes[j];
485     if (node->m_pixel == 0 || node->m_visited) continue;
486     traceOutline(node);
487   }
488 
489 #ifdef DEBUG
490   for (j = 0; j < (int)m_nodes.size(); j++) {
491     Node *node = m_nodes[j];
492     if (node->m_pixel == 0 || node->m_flag) continue;
493     outputNodes(node);
494   }
495 #endif
496 
497   std::list<std::vector<TThickPoint>>::iterator it_outlines =
498       m_protoOutlines.begin();
499   for (; it_outlines != m_protoOutlines.end(); it_outlines++) {
500     if (it_outlines->size() > 3) {
501       std::vector<TThickPoint> points;
502       std::vector<TThickPoint>::iterator it;
503 
504       if (it_outlines->size() > 10) {
505         it = it_outlines->begin() + 1;
506         for (;;) {
507           // Baco: Ricontrolla l'if seguente - in alcuni casi va fuori bounds...
508           if ((int)it_outlines->size() <= m_configuration.m_smoothness + 1)
509             break;
510           if (it >= it_outlines->end() - (m_configuration.m_smoothness + 1))
511             break;
512           for (j = 0; j < m_configuration.m_smoothness; j++)
513             it = it_outlines->erase(it);
514           ++it;
515         }
516       }
517 
518       points.push_back(it_outlines->front());
519       it              = it_outlines->begin();
520       TThickPoint old = *it;
521       ++it;
522       for (; it != it_outlines->end(); ++it) {
523         TThickPoint point((1 / 2.0) * (*it + old));
524         points.push_back(point);
525         old = *it;
526       }
527 
528       points.push_back(it_outlines->back());
529       points.push_back(it_outlines->front());
530 
531       TStroke *stroke =
532           TStroke::interpolate(points, m_configuration.m_interpolationError);
533       stroke->setStyle(m_configuration.m_strokeStyleId);
534       stroke->setSelfLoop();
535       m_vimage->addStroke(stroke);
536     }
537   }
538 }
539 
540 //---------------------------------------------------------
541 
colorDistance2(const TPixel32 & c0,const TPixel32 & c1)542 inline int colorDistance2(const TPixel32 &c0, const TPixel32 &c1) {
543   return ((c0.r - c1.r) * (c0.r - c1.r) + (c0.g - c1.g) * (c0.g - c1.g) +
544           (c0.b - c1.b) * (c0.b - c1.b));
545 }
546 
547 //---------------------------------------------------------
548 #define MAX_TOLERANCE 20
549 
550 #include "tcolorstyles.h"
551 
makeDataRaster(const TRasterP & src)552 void OutlineVectorizer::makeDataRaster(const TRasterP &src) {
553   m_vimage = new TVectorImage();
554   if (!src) return;
555   m_src = src;
556 
557   clearNodes();
558   clearJunctions();
559 
560   int x, y, ii = 0;
561   TRaster32P srcRGBM = (TRaster32P)m_src;
562   TRasterCM32P srcCM = (TRasterCM32P)m_src;
563   TRasterGR8P srcGR  = (TRasterGR8P)m_src;
564 
565   // Inizializzo DataRasterP per i casi in cui si ha un TRaster32P, un
566   // TRasterGR8P o un TRasterCM32P molto grande
567   DataRasterP dataRaster(m_src->getSize().lx + 2, m_src->getSize().ly + 2);
568   if (srcRGBM || srcGR ||
569       (srcCM && srcCM->getLx() * srcCM->getLy() > 5000000)) {
570     int ly              = dataRaster->getLy();
571     int lx              = dataRaster->getLx();
572     int wrap            = dataRaster->getWrap();
573     DataPixel *dataPix0 = dataRaster->pixels(0);
574     DataPixel *dataPix1 = dataRaster->pixels(0) + m_src->getLx() + 1;
575     for (y = 0; y < ly; y++, dataPix0 += wrap, dataPix1 += wrap) {
576       dataPix0->m_pos.x = 0;
577       dataPix1->m_pos.x = lx - 1;
578       dataPix0->m_pos.y = dataPix1->m_pos.y = y;
579       dataPix0->m_value = dataPix1->m_value = 0;
580       dataPix0->m_ink = dataPix1->m_ink = false;
581       dataPix0->m_node = dataPix1->m_node = 0;
582     }
583     dataPix0 = dataRaster->pixels(0);
584     dataPix1 = dataRaster->pixels(ly - 1);
585     for (x = 0; x < lx; x++, dataPix0++, dataPix1++) {
586       dataPix0->m_pos.x = dataPix1->m_pos.x = x;
587       dataPix0->m_pos.y                     = 0;
588       dataPix1->m_pos.y                     = ly - 1;
589       dataPix0->m_value = dataPix1->m_value = 0;
590       dataPix0->m_ink = dataPix1->m_ink = false;
591       dataPix0->m_node = dataPix1->m_node = 0;
592     }
593   }
594 
595   if (srcRGBM) {
596     assert(m_palette);
597     int inkId = m_palette->getClosestStyle(m_configuration.m_inkColor);
598     if (!inkId || m_configuration.m_inkColor !=
599                       m_palette->getStyle(inkId)->getMainColor()) {
600       inkId = m_palette->getStyleCount();
601       m_palette->getStylePage(1)->insertStyle(1, m_configuration.m_inkColor);
602       m_palette->setStyle(inkId, m_configuration.m_inkColor);
603     }
604     assert(inkId);
605 
606     m_dataRasterArray.push_back(std::pair<int, DataRasterP>(inkId, dataRaster));
607     int maxDistance2 =
608         m_configuration.m_threshold * m_configuration.m_threshold;
609 
610     for (y = 0; y < m_src->getLy(); y++) {
611       TPixel32 *inPix    = srcRGBM->pixels(y);
612       TPixel32 *inEndPix = inPix + srcRGBM->getLx();
613       DataPixel *dataPix = dataRaster->pixels(y + 1) + 1;
614       x                  = 0;
615       while (inPix < inEndPix) {
616         *dataPix      = DataPixel();
617         int distance2 = colorDistance2(m_configuration.m_inkColor, *inPix);
618 
619         if (y == 0 || y == m_src->getLy() - 1 || x == 0 ||
620             x == m_src->getLx() - 1 || inPix->m == 0) {
621           dataPix->m_value = 255;
622           dataPix->m_ink   = false;
623         } else {
624           dataPix->m_value = (inPix->r + 2 * inPix->g + inPix->b) >> 2;
625           dataPix->m_ink   = (distance2 < maxDistance2);
626         }
627         dataPix->m_pos.x = x++;
628         dataPix->m_pos.y = y;
629         dataPix->m_node  = 0;
630         inPix++;
631         dataPix++;
632       }
633     }
634   } else if (srcGR) {
635     assert(m_palette);
636     int inkId = m_palette->getClosestStyle(m_configuration.m_inkColor);
637     if (!inkId || m_configuration.m_inkColor !=
638                       m_palette->getStyle(inkId)->getMainColor()) {
639       inkId = m_palette->getStyleCount();
640       m_palette->getStylePage(1)->insertStyle(1, m_configuration.m_inkColor);
641       m_palette->setStyle(inkId, m_configuration.m_inkColor);
642     }
643     assert(inkId);
644 
645     m_dataRasterArray.push_back(std::pair<int, DataRasterP>(inkId, dataRaster));
646     int threshold = m_configuration.m_threshold;
647 
648     for (y = 0; y < m_src->getLy(); y++) {
649       TPixelGR8 *inPix    = srcGR->pixels(y);
650       TPixelGR8 *inEndPix = inPix + srcGR->getLx();
651       DataPixel *dataPix  = dataRaster->pixels(y + 1) + 1;
652       x                   = 0;
653       while (inPix < inEndPix) {
654         *dataPix = DataPixel();
655         if (y == 0 || y == m_src->getLy() - 1 || x == 0 ||
656             x == m_src->getLx() - 1) {
657           dataPix->m_value = 255;
658           dataPix->m_ink   = false;
659         } else {
660           dataPix->m_value = inPix->value;
661           dataPix->m_ink   = (inPix->value < threshold);
662         }
663         dataPix->m_pos.x = x++;
664         dataPix->m_pos.y = y;
665         dataPix->m_node  = 0;
666         inPix++;
667         dataPix++;
668       }
669     }
670   }
671 
672   else if (srcCM) {
673     int currInk, nextInk = 0;
674 
675     if (srcCM->getLx() * srcCM->getLy() > 5000000) {
676       int threshold = m_configuration.m_threshold;
677       int inkId     = m_palette->getClosestStyle(TPixel::Black);
678 
679       if (TPixel::Black != m_palette->getStyle(inkId)->getMainColor()) {
680         inkId = m_palette->getStyleCount();
681         m_palette->getStylePage(1)->insertStyle(1, m_configuration.m_inkColor);
682         m_palette->setStyle(inkId, m_configuration.m_inkColor);
683       }
684       assert(inkId);
685 
686       m_dataRasterArray.push_back(
687           std::pair<int, DataRasterP>(inkId, dataRaster));
688 
689       // inizializza la parte centrale
690       for (y = 0; y < m_src->getLy(); y++) {
691         TPixelCM32 *inPix    = srcCM->pixels(y);
692         TPixelCM32 *inEndPix = inPix + m_src->getLx();
693         DataPixel *dataPix   = dataRaster->pixels(y + 1) + 1;
694         x                    = 0;
695         while (inPix < inEndPix) {
696           *dataPix  = DataPixel();
697           int value = inPix->getTone();
698           if (m_configuration.m_ignoreInkColors) inkId = 1;
699           if (y == 0 || y == m_src->getLy() - 1 || x == 0 ||
700               x == m_src->getLx() - 1) {
701             dataPix->m_value = 255;
702             dataPix->m_ink   = false;
703           } else {
704             dataPix->m_value = value;
705             dataPix->m_ink   = (value < threshold);
706           }
707           dataPix->m_pos.x = x++;
708           dataPix->m_pos.y = y;
709           dataPix->m_node  = 0;
710           inPix++;
711           dataPix++;
712         }
713       }
714     } else {
715       do {
716         // Inizializzo DataRasterP
717         DataRasterP dataRaster(m_src->getSize().lx + 2,
718                                m_src->getSize().ly + 2);
719         int ly              = dataRaster->getLy();
720         int lx              = dataRaster->getLx();
721         int wrap            = dataRaster->getWrap();
722         DataPixel *dataPix0 = dataRaster->pixels(0);
723         DataPixel *dataPix1 = dataRaster->pixels(0) + m_src->getLx() + 1;
724         for (y = 0; y < ly; y++, dataPix0 += wrap, dataPix1 += wrap) {
725           dataPix0->m_pos.x = 0;
726           dataPix1->m_pos.x = lx - 1;
727           dataPix0->m_pos.y = dataPix1->m_pos.y = y;
728           dataPix0->m_value = dataPix1->m_value = 0;
729           dataPix0->m_ink = dataPix1->m_ink = false;
730           dataPix0->m_node = dataPix1->m_node = 0;
731         }
732         dataPix0 = dataRaster->pixels(0);
733         dataPix1 = dataRaster->pixels(ly - 1);
734         for (x = 0; x < lx; x++, dataPix0++, dataPix1++) {
735           dataPix0->m_pos.x = dataPix1->m_pos.x = x;
736           dataPix0->m_pos.y                     = 0;
737           dataPix1->m_pos.y                     = ly - 1;
738           dataPix0->m_value = dataPix1->m_value = 0;
739           dataPix0->m_ink = dataPix1->m_ink = false;
740           dataPix0->m_node = dataPix1->m_node = 0;
741         }
742 
743         int threshold =
744             m_configuration.m_threshold;  // tolerance: 1->MAX thresh: 1-255
745         currInk = nextInk;
746         nextInk = 0;
747         m_dataRasterArray.push_back(
748             std::pair<int, DataRasterP>(currInk, dataRaster));
749 
750         // inizializza la parte centrale
751         for (y = 0; y < m_src->getLy(); y++) {
752           TPixelCM32 *inPix    = srcCM->pixels(y);
753           TPixelCM32 *inEndPix = inPix + m_src->getLx();
754           DataPixel *dataPix   = dataRaster->pixels(y + 1) + 1;
755           x                    = 0;
756           while (inPix < inEndPix) {
757             *dataPix  = DataPixel();
758             int value = inPix->getTone();
759             if (value < 255 && !m_configuration.m_ignoreInkColors) {
760               int ink = inPix->getInk();
761               if (currInk == 0) {
762                 currInk                        = ink;
763                 m_dataRasterArray.back().first = ink;
764               } else if (ink != currInk) {
765                 value = 255;
766                 if (nextInk == 0) {
767                   for (ii = 0; ii < (int)m_dataRasterArray.size() - 1; ii++)
768                     if (m_dataRasterArray[ii].first == ink) break;
769                   if (ii == (int)m_dataRasterArray.size() - 1) nextInk = ink;
770                 }
771               }
772             }
773             dataPix->m_pos.x = x++;
774             dataPix->m_pos.y = y;
775             dataPix->m_value = value;
776             dataPix->m_ink   = (value < threshold);
777             dataPix->m_node  = 0;
778             inPix++;
779             dataPix++;
780           }
781         }
782       } while (nextInk != 0);
783     }
784     if (m_configuration.m_ignoreInkColors) {
785       assert(m_dataRasterArray.size() == 1);
786       m_dataRasterArray.back().first = 1;
787     }
788   } else
789     assert(false);
790 }
791 
792 //---------------------------------------------------------
793 
outlineVectorize(const TImageP & image,const OutlineConfiguration & configuration,TPalette * palette)794 TVectorImageP VectorizerCore::outlineVectorize(
795     const TImageP &image, const OutlineConfiguration &configuration,
796     TPalette *palette) {
797   TVectorImageP out;
798 
799   OutlineVectorizer vectorizer(configuration, palette);
800 
801   TRasterImageP ri = image;
802   TToonzImageP vi  = image;
803   if (ri)
804     vectorizer.makeDataRaster(ri->getRaster());
805   else
806     vectorizer.makeDataRaster(vi->getRaster());
807   int layersCount = vectorizer.m_dataRasterArray.size();
808   if (layersCount > 1) {
809     out = new TVectorImage();
810     out->setPalette(palette);
811   }
812   int i;
813   for (i = 0; i < (int)layersCount; i++) {
814     vectorizer.m_dataRaster = vectorizer.m_dataRasterArray[i].second;
815     vectorizer.m_configuration.m_strokeStyleId =
816         vectorizer.m_dataRasterArray[i].first;
817     vectorizer.m_protoOutlines.clear();
818     vectorizer.init();
819     vectorizer.createOutlineStrokes();
820     renormalizeImage(vectorizer.m_vimage.getPointer());
821     vectorizer.m_vimage->setPalette(palette);
822     if (layersCount > 1) out->mergeImage(vectorizer.m_vimage, TAffine());
823 
824     if (i != (int)layersCount - 1) vectorizer.m_vimage = new TVectorImage();
825   }
826 
827   return (layersCount == 1) ? vectorizer.m_vimage : out;
828 }
829 
830 //=========================================================
831 
isPointInRegion(TPointD p,TRegion * r)832 static bool isPointInRegion(TPointD p, TRegion *r) {
833   int i;
834   for (i = 0; i < 5; i++) {
835     double stepX = i * 0.2;
836     int j;
837     for (j = 0; j < 5; j++) {
838       double stepY = j * 0.2;
839       if (r->contains(TPointD(p.x + stepX, p.y + stepY))) return true;
840     }
841   }
842   return false;
843 }
844 
845 //------------------------------------------------------
846 
847 // Se findInk == true :
848 //    trova il punto piu' vicino a p con ink puro e restituisce true se e'
849 //    contenuto nella regione
850 // Se findInk == false :
851 //    Trova il punto piu' vicino a p con paint puro e restituisce true se e'
852 //    contenuto nella regione
853 
854 //(Daniele) Aggiunti controlli per evitare uscite dai bounds
855 
isNearestInkOrPaintInRegion(bool findInk,const TRasterCM32P & ras,TRegion * r,const TAffine & aff,const TPoint & p)856 static bool isNearestInkOrPaintInRegion(bool findInk, const TRasterCM32P &ras,
857                                         TRegion *r, const TAffine &aff,
858                                         const TPoint &p) {
859   bool isTheLastSquare = false;
860   int mx, my, Mx, My;
861   int i;
862 
863   for (i = 1; i <= 100; i++) {
864     int j, t, s, e;
865     if (p.x - i >= 0) {
866       my = std::max(p.y - i, 0);
867       My = std::min(p.y + i, ras->getLy() - 1);
868       for (j = my; j <= My; j++) {
869         TPixelCM32 col = ras->pixels(j)[p.x - i];
870         int tone       = col.getTone();
871         if ((findInk && tone == 0) || (!findInk && tone == 255)) {
872           if (isPointInRegion(aff * TPointD(double(p.x - i), double(j)), r))
873             return true;
874           else
875             isTheLastSquare = true;
876         }
877       }
878     }
879     if (p.y + i < ras->getLy()) {
880       mx = std::max(p.x - i + 1, 0);
881       Mx = std::min(p.x + i, ras->getLx() - 1);
882       for (t = mx; t <= Mx; t++) {
883         TPixelCM32 col = ras->pixels(p.y + i)[t];
884         int tone       = col.getTone();
885         if ((findInk && tone == 0) || (!findInk && tone == 255)) {
886           if (isPointInRegion(aff * TPointD(double(t), double(p.y + i)), r))
887             return true;
888           else
889             isTheLastSquare = true;
890         }
891       }
892     }
893     if (p.x + i < ras->getLx()) {
894       my = std::max(p.y - i, 0);
895       My = std::min(p.y + i - 1, ras->getLy() - 1);
896       for (s = my; s <= My; s++) {
897         TPixelCM32 col = ras->pixels(s)[p.x + i];
898         int tone       = col.getTone();
899         if ((findInk && tone == 0) || (!findInk && tone == 255)) {
900           if (isPointInRegion(aff * TPointD(double(p.x + i), double(s)), r))
901             return true;
902           else
903             isTheLastSquare = true;
904         }
905       }
906     }
907     if (p.y - i >= 0) {
908       mx = std::max(p.x - i + 1, 0);
909       Mx = std::min(p.x + i - 1, ras->getLx() - 1);
910       for (e = mx; e <= Mx; e++) {
911         TPixelCM32 col = ras->pixels(p.y - i)[e];
912         int tone       = col.getTone();
913         if ((findInk && tone == 0) || (!findInk && tone == 255)) {
914           if (isPointInRegion(aff * TPointD(double(e), double(p.y - i)), r))
915             return true;
916           else
917             isTheLastSquare = true;
918         }
919       }
920     }
921 
922     if (isTheLastSquare) return false;
923   }
924 
925   return false;
926 }
927 
928 //======================================================
929 
isBright(const TPixelCM32 & pix,int threshold)930 inline bool isBright(const TPixelCM32 &pix, int threshold) {
931   return pix.getTone() >= threshold;
932 }
933 
isBright(const TPixelGR8 & pix,int threshold)934 inline bool isBright(const TPixelGR8 &pix, int threshold) {
935   return pix.value >= threshold;
936 }
937 
isBright(const TPixel32 & pix,int threshold)938 inline bool isBright(const TPixel32 &pix, int threshold) {
939   // Using Value in HSV color model
940   return std::max(pix.r, std::max(pix.g, pix.b)) >= threshold * (pix.m / 255.0);
941 
942   // Using Lightness in HSL color model
943   // return (max(pix.r,max(pix.g,pix.b)) + min(pix.r,min(pix.g,pix.b))) / 2.0
944   //  >= threshold * (pix.m / 255.0);
945 
946   // Using (relative) Luminance
947   // return 0.2126 * pix.r + 0.7152 * pix.g + 0.0722 * pix.b >= threshold *
948   // (pix.m / 255.0);
949 }
950 
951 //------------------------------------------------------
952 
isDark(const TPixelCM32 & pix,int threshold)953 inline bool isDark(const TPixelCM32 &pix, int threshold) {
954   return !isBright(pix, threshold);
955 }
956 
isDark(const TPixelGR8 & pix,int threshold)957 inline bool isDark(const TPixelGR8 &pix, int threshold) {
958   return !isBright(pix, threshold);
959 }
960 
isDark(const TPixelRGBM32 & pix,int threshold)961 inline bool isDark(const TPixelRGBM32 &pix, int threshold) {
962   return !isBright(pix, threshold);
963 }
964 
965 //------------------------------------------------------
966 
967 template <typename Pix, typename Selector>
getInternalPoint(const TRasterPT<Pix> & ras,const Selector & sel,const TAffine & inverse,const VectorizerConfiguration & c,const TRegion * region,TPointD & p)968 bool getInternalPoint(const TRasterPT<Pix> &ras, const Selector &sel,
969                       const TAffine &inverse, const VectorizerConfiguration &c,
970                       const TRegion *region, TPointD &p) {
971   struct Locals {
972     const TRasterPT<Pix> &m_ras;
973     const Selector &m_sel;
974     const TAffine &m_inverse;
975     double m_pixelSize;
976     const TRegion &m_region;
977 
978     static bool contains(const TRegion &region, const TPointD &p) {
979       return region.getBBox().contains(p) &&
980              (region.leftScanlineIntersections(p.x, p.y) % 2);
981     }
982 
983     bool contains(const TPointD &p) {
984       if (!contains(m_region, p)) return false;
985 
986       UINT sr, srCount = m_region.getSubregionCount();
987       for (sr = 0; sr != srCount; ++sr) {
988         if (contains(*m_region.getSubregion(sr), p)) return false;
989       }
990 
991       return true;
992     }
993 
994     // Subdivide the output scanline in even intervals, and sample each's
995     // midpoint
996     bool sampleMidpoints(TPointD &p, double x0, double x1, double y,
997                          int intervalsCount) {
998       const double iCountD = intervalsCount;
999 
1000       for (int i = 0; i != intervalsCount; ++i) {
1001         double i_x0 = tcg::numeric_ops::lerp(x0, x1, i / iCountD),
1002                i_x1 = tcg::numeric_ops::lerp(x0, x1, (i + 1) / iCountD);
1003 
1004         if (sample(p = TPointD(0.5 * (i_x0 + i_x1), y))) return true;
1005       }
1006 
1007       return false;
1008     }
1009 
1010     // Sample the output scanline's midpoint
1011     bool sample(TPointD &point) {
1012       return (contains(point) &&
1013               adjustPoint(point)  // Ensures that point is inRaster()
1014               && selected(point));
1015     }
1016 
1017     TPoint toRaster(const TPointD &p) {
1018       const TPointD &pRasD = m_inverse * p;
1019       return TPoint(pRasD.x, pRasD.y);
1020     }
1021 
1022     bool inRaster(const TPointD &point) {
1023       const TPoint &pRas = toRaster(point);
1024       return (pRas.x >= 0 && pRas.x < m_ras->getLx() && pRas.y >= 0 &&
1025               pRas.y < m_ras->getLy());
1026     }
1027 
1028     bool selected(const TPointD &point) {
1029       assert(inRaster(point));
1030 
1031       const TPoint &pRas = toRaster(point);
1032       return m_sel(m_ras->pixels(pRas.y)[pRas.x]);
1033     }
1034 
1035     bool adjustPoint(TPointD &p) {
1036       const TRectD &bbox = m_region.getBBox();
1037       const double tol   = std::max(1e-1 * m_pixelSize, 1e-4);
1038 
1039       TPointD newP = p;
1040       {
1041         // Adjust along x axis
1042         int iCount = scanlineIntersectionsBefore(newP.x, newP.y, true);
1043 
1044         double in0 = newP.x, out0 = bbox.x0, in1 = newP.x, out1 = bbox.x1;
1045 
1046         isolateBorderX(in0, out0, newP.y, iCount, tol);
1047         isolateBorderX(in1, out1, newP.y, iCount, tol);
1048 
1049         newP = TPointD(0.5 * (in0 + in1), newP.y);
1050         assert(scanlineIntersectionsBefore(newP.x, newP.y, true) == iCount);
1051       }
1052       {
1053         // Adjust along y axis
1054         int iCount = scanlineIntersectionsBefore(newP.x, newP.y, false);
1055 
1056         double in0 = newP.y, out0 = bbox.y0, in1 = newP.y, out1 = bbox.y1;
1057 
1058         isolateBorderY(newP.x, in0, out0, iCount, tol);
1059         isolateBorderY(newP.x, in1, out1, iCount, tol);
1060 
1061         newP = TPointD(newP.x, 0.5 * (in0 + in1));
1062         assert(scanlineIntersectionsBefore(newP.x, newP.y, false) == iCount);
1063       }
1064 
1065       return inRaster(newP) ? (p = newP, true) : false;
1066     }
1067 
1068     void isolateBorderX(double &xIn, double &xOut, double y, int iCount,
1069                         const double tol) {
1070       assert(scanlineIntersectionsBefore(xIn, y, true) == iCount);
1071 
1072       while (true) {
1073         // Subdivide current interval
1074         double mid = 0.5 * (xIn + xOut);
1075 
1076         if (scanlineIntersectionsBefore(mid, y, true) == iCount)
1077           xIn = mid;
1078         else
1079           xOut = mid;
1080 
1081         if (std::abs(xOut - xIn) < tol) break;
1082       }
1083     }
1084 
1085     void isolateBorderY(double x, double &yIn, double &yOut, int iCount,
1086                         const double tol) {
1087       assert(scanlineIntersectionsBefore(x, yIn, false) == iCount);
1088 
1089       while (true) {
1090         // Subdivide current interval
1091         double mid = 0.5 * (yIn + yOut);
1092 
1093         if (scanlineIntersectionsBefore(x, mid, false) == iCount)
1094           yIn = mid;
1095         else
1096           yOut = mid;
1097 
1098         if (std::abs(yOut - yIn) < tol) break;
1099       }
1100     }
1101 
1102     int scanlineIntersectionsBefore(double x, double y, bool hor) {
1103       int result = m_region.scanlineIntersectionsBefore(x, y, hor);
1104 
1105       UINT sr, srCount = m_region.getSubregionCount();
1106       for (sr = 0; sr != srCount; ++sr)
1107         result +=
1108             m_region.getSubregion(sr)->scanlineIntersectionsBefore(x, y, hor);
1109 
1110       return result;
1111     }
1112 
1113   } locals = {ras, sel, inverse, c.m_thickScale, *region};
1114 
1115   assert(region);
1116 
1117   const TRectD &regionBBox = region->getBBox();
1118   double regionMidY        = 0.5 * (regionBBox.y0 + regionBBox.y1);
1119 
1120   int ic, icEnd = tceil((regionBBox.x1 - regionBBox.x0) / c.m_thickScale) +
1121                   1;  // Say you have 4 pixels, in [0, 4]. We want to
1122                       // have at least 4 intervals where midpoints are
1123                       // taken - so end intervals count is 5.
1124   for (ic = 1; ic < icEnd; ic *= 2) {
1125     if (locals.sampleMidpoints(p, regionBBox.x0, regionBBox.x1, regionMidY, ic))
1126       return true;
1127   }
1128 
1129   return false;
1130 }
1131 
1132 //=========================================================
1133 
1134 //(Daniele)
1135 
1136 // Taking lone, unchecked points is dangerous - they could lie inside
1137 // region r and still have a wrong color (for example, if they lie
1138 //*on* a boundary stroke).
1139 // Plus, over-threshold regions should always be considered black.
1140 
1141 // In order to improve this, we search a 4way-local-brightest
1142 // neighbour of p. Observe that, however, it may still lie outside r;
1143 // would that happen, p was not significative in the first place.
1144 //---------------------------------------------------------------
1145 
takeLocalBrightest(const TRaster32P rr,TRegion * r,const VectorizerConfiguration & c,TPoint & p)1146 inline TPixel32 takeLocalBrightest(const TRaster32P rr, TRegion *r,
1147                                    const VectorizerConfiguration &c,
1148                                    TPoint &p) {
1149   TPoint pMax;
1150 
1151   while (r->contains(c.m_affine * convert(p))) {
1152     pMax = p;
1153     if (p.x > 0 && rr->pixels(p.y)[p.x - 1] > rr->pixels(pMax.y)[pMax.x])
1154       pMax = TPoint(p.x - 1, p.y);
1155     if (p.x < rr->getLx() - 1 &&
1156         rr->pixels(p.y)[p.x + 1] > rr->pixels(pMax.y)[pMax.x])
1157       pMax = TPoint(p.x + 1, p.y);
1158     if (p.y > 0 && rr->pixels(p.y - 1)[p.x] > rr->pixels(pMax.y)[pMax.x])
1159       pMax = TPoint(p.x, p.y - 1);
1160     if (p.y < rr->getLy() - 1 &&
1161         rr->pixels(p.y + 1)[p.x] > rr->pixels(pMax.y)[pMax.x])
1162       pMax = TPoint(p.x, p.y + 1);
1163 
1164     if (p == pMax) break;
1165 
1166     p = pMax;
1167   }
1168 
1169   if (!isBright(rr->pixels(p.y)[p.x], c.m_threshold))
1170     return TPixel32::Black;
1171   else
1172     return rr->pixels(p.y)[p.x];
1173 }
1174 
1175 //------------------------------------------------------
1176 
takeLocalBrightest(const TRasterGR8P rgr,TRegion * r,const VectorizerConfiguration & c,TPoint & p)1177 inline TPixel32 takeLocalBrightest(const TRasterGR8P rgr, TRegion *r,
1178                                    const VectorizerConfiguration &c,
1179                                    TPoint &p) {
1180   TPoint pMax;
1181 
1182   while (r->contains(c.m_affine * convert(p))) {
1183     pMax = p;
1184     if (p.x > 0 && rgr->pixels(pMax.y)[pMax.x] < rgr->pixels(p.y)[p.x - 1])
1185       pMax = TPoint(p.x - 1, p.y);
1186     if (p.x < rgr->getLx() - 1 &&
1187         rgr->pixels(pMax.y)[pMax.x] < rgr->pixels(p.y)[p.x + 1])
1188       pMax = TPoint(p.x + 1, p.y);
1189     if (p.y > 0 && rgr->pixels(pMax.y)[pMax.x] < rgr->pixels(p.y - 1)[p.x])
1190       pMax = TPoint(p.x, p.y - 1);
1191     if (p.y < rgr->getLy() - 1 &&
1192         rgr->pixels(pMax.y)[pMax.x] < rgr->pixels(p.y + 1)[p.x])
1193       pMax = TPoint(p.x, p.y + 1);
1194 
1195     if (p == pMax) break;
1196 
1197     p = pMax;
1198   }
1199 
1200   if (!isBright(rgr->pixels(p.y)[p.x], c.m_threshold))
1201     return TPixel32::Black;
1202   else {
1203     int val = rgr->pixels(p.y)[p.x].value;
1204     return TPixel32(val, val, val, 255);
1205   }
1206 }
1207 
1208 //---------------------------------------------------------------
1209 
takeLocalDarkest(const TRaster32P rr,TRegion * r,const VectorizerConfiguration & c,TPoint & p)1210 inline TPixel32 takeLocalDarkest(const TRaster32P rr, TRegion *r,
1211                                  const VectorizerConfiguration &c, TPoint &p) {
1212   TPoint pMax;
1213 
1214   while (r->contains(c.m_affine * convert(p)))  // 1
1215   {
1216     pMax = p;
1217     if (p.x > 0 && rr->pixels(p.y)[p.x - 1] < rr->pixels(pMax.y)[pMax.x])
1218       pMax = TPoint(p.x - 1, p.y);
1219     if (p.x < rr->getLx() - 1 &&
1220         rr->pixels(p.y)[p.x + 1] < rr->pixels(pMax.y)[pMax.x])
1221       pMax = TPoint(p.x + 1, p.y);
1222     if (p.y > 0 && rr->pixels(p.y - 1)[p.x] < rr->pixels(pMax.y)[pMax.x])
1223       pMax = TPoint(p.x, p.y - 1);
1224     if (p.y < rr->getLy() - 1 &&
1225         rr->pixels(p.y + 1)[p.x] < rr->pixels(pMax.y)[pMax.x])
1226       pMax = TPoint(p.x, p.y + 1);
1227 
1228     if (p == pMax) break;
1229 
1230     p = pMax;
1231   }
1232 
1233   return rr->pixels(p.y)[p.x];
1234 }
1235 
1236 //------------------------------------------------------
1237 
takeLocalDarkest(const TRasterGR8P rgr,TRegion * r,const VectorizerConfiguration & c,TPoint & p)1238 inline TPixel32 takeLocalDarkest(const TRasterGR8P rgr, TRegion *r,
1239                                  const VectorizerConfiguration &c, TPoint &p) {
1240   TPoint pMax;
1241 
1242   while (r->contains(c.m_affine * convert(p))) {
1243     pMax = p;
1244     if (p.x > 0 && rgr->pixels(p.y)[p.x - 1] < rgr->pixels(pMax.y)[pMax.x])
1245       pMax = TPoint(p.x - 1, p.y);
1246     if (p.x < rgr->getLx() - 1 &&
1247         rgr->pixels(p.y)[p.x + 1] < rgr->pixels(pMax.y)[pMax.x])
1248       pMax = TPoint(p.x + 1, p.y);
1249     if (p.y > 0 && rgr->pixels(p.y - 1)[p.x] < rgr->pixels(pMax.y)[pMax.x])
1250       pMax = TPoint(p.x, p.y - 1);
1251     if (p.y < rgr->getLy() - 1 &&
1252         rgr->pixels(p.y + 1)[p.x] < rgr->pixels(pMax.y)[pMax.x])
1253       pMax = TPoint(p.x, p.y + 1);
1254 
1255     if (p == pMax) break;
1256 
1257     p = pMax;
1258   }
1259 
1260   int val = rgr->pixels(p.y)[p.x].value;
1261   return TPixel32(val, val, val, 255);
1262 }
1263 
1264 //=================================================================
1265 //  Vectorizer Core
1266 //-----------------------------------------------------------------
1267 
applyFillColors(TRegion * r,const TRasterP & ras,TPalette * palette,const CenterlineConfiguration & c,int regionCount)1268 void VectorizerCore::applyFillColors(TRegion *r, const TRasterP &ras,
1269                                      TPalette *palette,
1270                                      const CenterlineConfiguration &c,
1271                                      int regionCount) {
1272   auto const alwaysTrue = [](const TPixelCM32 &) { return true; };
1273 
1274   TRasterCM32P rt = ras;
1275   TRaster32P rr   = ras;
1276   TRasterGR8P rgr = ras;
1277 
1278   assert(rt || rr || rgr);
1279 
1280   bool isBrightRegion = true;
1281   {
1282     unsigned int e, edgesCount = r->getEdgeCount();
1283     for (e = 0; e < edgesCount; ++e) {
1284       if (isInkRegionEdge(r->getEdge(e)->m_s)) {
1285         if (r->getEdge(e)->m_w0 > r->getEdge(e)->m_w1) isBrightRegion = false;
1286         break;
1287       }
1288       if (isInkRegionEdgeReversed(r->getEdge(e)->m_s)) {
1289         if (r->getEdge(e)->m_w0 < r->getEdge(e)->m_w1) isBrightRegion = false;
1290         break;
1291       }
1292     }
1293   }
1294 
1295   TAffine inverse = c.m_affine.inv();
1296   TPointD pd;
1297 
1298   typedef bool (*cm_func)(const TPixelCM32 &, int);
1299   typedef bool (*rgbm_func)(const TPixelRGBM32 &, int);
1300   typedef bool (*gr_func)(const TPixelGR8 &, int);
1301 
1302   bool tookPoint =
1303       isBrightRegion
1304           ? rt ? getInternalPoint(
1305                      rt,
1306                      std::bind(cm_func(isBright), std::placeholders::_1,
1307                                c.m_threshold),
1308                      inverse, c, r, pd) ||
1309                      // If no bright pixel could be found,
1310                      getInternalPoint(rt, alwaysTrue, inverse, c, r,
1311                                       pd)
1312                :  // then any pixel inside the region
1313                 rr ? getInternalPoint(
1314                          rr,
1315                          std::bind(rgbm_func(isBright), std::placeholders::_1,
1316                                    c.m_threshold),
1317                          inverse, c, r, pd)
1318                    :  // must suffice.
1319                     getInternalPoint(
1320                         rgr,
1321                         std::bind(gr_func(isBright), std::placeholders::_1,
1322                                   c.m_threshold),
1323                         inverse, c, r, pd)
1324           : rt ? getInternalPoint(
1325                      rt,
1326                      std::bind(cm_func(isDark), std::placeholders::_1,
1327                                c.m_threshold),
1328                      inverse, c, r, pd)
1329                : rr ? getInternalPoint(
1330                           rr,
1331                           std::bind(rgbm_func(isDark), std::placeholders::_1,
1332                                     c.m_threshold),
1333                           inverse, c, r, pd)
1334                     : getInternalPoint(
1335                           rgr,
1336                           std::bind(gr_func(isDark), std::placeholders::_1,
1337                                     c.m_threshold),
1338                           inverse, c, r, pd);
1339 
1340   if (tookPoint) {
1341     pd = inverse * pd;
1342     TPoint p(pd.x, pd.y);  // The same thing that happened inside
1343                            // getInternalPoint()
1344     if (ras->getBounds().contains(p)) {
1345       int styleId = 0;
1346 
1347       if (rt) {
1348         TPixelCM32 col = rt->pixels(p.y)[p.x];
1349         styleId        = isBrightRegion
1350                       ? col.getPaint()
1351                       : col.getInk();  // Only paint colors with centerline
1352       }                                // vectorization
1353       else {
1354         TPixel32 color;
1355 
1356         // Update color found to local brightness-extremals
1357         if (rr) {
1358           color = isBrightRegion ? takeLocalBrightest(rr, r, c, p)
1359                                  : takeLocalDarkest(rr, r, c, p);
1360         } else {
1361           color = isBrightRegion ? takeLocalBrightest(rgr, r, c, p)
1362                                  : takeLocalDarkest(rgr, r, c, p);
1363         }
1364 
1365         if (color.m != 0) {
1366           styleId           = palette->getClosestStyle(color);
1367           TPixel32 oldColor = palette->getStyle(styleId)->getMainColor();
1368           if (!(isAlmostZero(double(oldColor.r - color.r), 15.0) &&
1369                 isAlmostZero(double(oldColor.g - color.g), 15.0) &&
1370                 isAlmostZero(double(oldColor.b - color.b), 15.0))) {
1371             styleId = palette->getStyleCount();
1372             palette->getStylePage(1)->insertStyle(1, color);
1373             palette->setStyle(styleId, color);
1374           }
1375         }
1376       }
1377 
1378       ++regionCount;
1379       r->setStyle(styleId);
1380     }
1381   }
1382 
1383   for (int i = 0; i < (int)r->getSubregionCount(); ++i)
1384     applyFillColors(r->getSubregion(i), ras, palette, c, regionCount);
1385 }
1386 
1387 //-----------------------------------------------------------------
1388 /*
1389 void VectorizerCore::applyFillColors(TRegion *r, const TRasterP &ras,
1390                                      TPalette *palette,
1391                                      const OutlineConfiguration &c,
1392                                      int regionCount) {
1393   TRasterCM32P rt = ras;
1394   TRaster32P rr   = ras;
1395   TRasterGR8P rgr = ras;
1396 
1397   assert(rt || rr || rgr);
1398 
1399   TAffine inverse = c.m_affine.inv();
1400   bool doInks = !c.m_ignoreInkColors, doPaints = !c.m_leaveUnpainted;
1401 
1402   // Retrieve a point inside the specified region
1403   TPointD pd;
1404   if (r->getInternalPoint(pd)) {
1405     pd = inverse * pd;     // Convert point to raster coordinates
1406     TPoint p(pd.x, pd.y);  //
1407 
1408     // Retrieve the corresponding pixel in the raster image
1409 
1410     if (ras->getBounds().contains(p)) {
1411       int styleId = 0;
1412 
1413       if (rt) {
1414         // Toonz colormap case
1415         TPixelCM32 col =
1416             rt->pixels(p.y)[p.x];  // In the outline vectorization case, color
1417         int tone = col.getTone();  // can be either ink or paint
1418 
1419         if (tone == 0)  // Full ink case
1420           styleId = doInks ? col.getInk() : 1;
1421         else if (tone == 255 && doPaints)  // Full paint case
1422           styleId = col.getPaint();
1423         else if (tone != 255) {
1424           if (regionCount % 2 == 1) {
1425             // Whenever regionCount is odd, ink is checked first
1426 
1427             if (isNearestInkOrPaintInRegion(true, rt, r, c.m_affine, p))
1428               styleId = doInks ? col.getInk() : 1;
1429             else if (doPaints &&
1430                      isNearestInkOrPaintInRegion(false, rt, r, c.m_affine, p))
1431               styleId = col.getPaint();
1432           } else {
1433             // Whenever regionCount is even, paint is checked first
1434 
1435             if (doPaints &&
1436                 isNearestInkOrPaintInRegion(false, rt, r, c.m_affine, p))
1437               styleId = col.getPaint();
1438             else if (isNearestInkOrPaintInRegion(true, rt, r, c.m_affine, p))
1439               styleId = doInks ? col.getInk() : 1;
1440           }
1441         }
1442       } else {
1443         TPixel32 color;
1444         if (rr)
1445           color = rr->pixels(p.y)[p.x];
1446         else {
1447           int val = rgr->pixels(p.y)[p.x].value;
1448           color   = (val < 80) ? TPixel32::Black : TPixel32::White;
1449         }
1450 
1451         if ((color.m != 0) && ((!c.m_leaveUnpainted) ||
1452                                (c.m_leaveUnpainted && color == c.m_inkColor))) {
1453           styleId           = palette->getClosestStyle(color);
1454           TPixel32 oldColor = palette->getStyle(styleId)->getMainColor();
1455 
1456           if (!(isAlmostZero(double(oldColor.r - color.r), 15.0) &&
1457                 isAlmostZero(double(oldColor.g - color.g), 15.0) &&
1458                 isAlmostZero(double(oldColor.b - color.b), 15.0))) {
1459             styleId = palette->getStyleCount();
1460             palette->getStylePage(1)->insertStyle(1, color);
1461             palette->setStyle(styleId, color);
1462           }
1463         }
1464       }
1465 
1466       ++regionCount;
1467       r->setStyle(styleId);
1468     }
1469   }
1470 
1471   for (int i = 0; i < (int)r->getSubregionCount(); ++i)
1472     applyFillColors(r->getSubregion(i), ras, palette, c, regionCount);
1473 }
1474 */
1475 //-----------------------------------------------------------------
1476 
applyFillColors(TVectorImageP vi,const TImageP & img,TPalette * palette,const VectorizerConfiguration & c)1477 void VectorizerCore::applyFillColors(TVectorImageP vi, const TImageP &img,
1478                                      TPalette *palette,
1479                                      const VectorizerConfiguration &c) {
1480   const CenterlineConfiguration &centConf =
1481       static_cast<const CenterlineConfiguration &>(c);
1482   // const OutlineConfiguration &outConf =
1483   //    static_cast<const OutlineConfiguration &>(c);
1484 
1485   // If configuration is not set for color fill at all, quit.
1486   if (c.m_leaveUnpainted && !c.m_outline && !c.m_alignBoundaryStrokesDirection)
1487     return;
1488   // if (c.m_leaveUnpainted && (!c.m_outline || outConf.m_ignoreInkColors))
1489   // return;
1490 
1491   TToonzImageP ti  = img;
1492   TRasterImageP ri = img;
1493 
1494   assert(ti || ri);
1495   TRasterP ras = ti ? TRasterP(ti->getRaster()) : TRasterP(ri->getRaster());
1496 
1497   vi->findRegions();
1498 
1499   int r, regionsCount = vi->getRegionCount();
1500   // filling colors in outline mode is done in tnewoutlinevectorize.cpp
1501   // if (c.m_outline) {
1502   //  for (r = 0; r < regionsCount; ++r)
1503   //    applyFillColors(vi->getRegion(r), ras, palette, outConf, 1);
1504   //} else {
1505   for (r = 0; r < regionsCount; ++r)
1506     applyFillColors(vi->getRegion(r), ras, palette, centConf,
1507                     1);  // 1 - c.m_makeFrame;
1508 
1509   clearInkRegionFlags(vi);
1510   //}
1511 }
1512 
1513 namespace {
1514 struct StrokeData {
1515   UCHAR m_hasColor, m_hasRegion;
1516 };
1517 
1518 // found the shape boundaries and recognize their stroke direction.
1519 // based on the function getBoundaries() in levelselection.cpp
alignBoundariesDirection(TVectorImageP vi)1520 void alignBoundariesDirection(TVectorImageP vi) {
1521   enum { FORWARD = 0x1, BACKWARD = 0x2, INTERNAL = FORWARD | BACKWARD };
1522 
1523   struct locals {
1524     static void markEdges(const TRegion &region, std::vector<StrokeData> &sData,
1525                           bool parentRegionHasColor) {
1526       bool regionHasColor = (region.getStyle() != 0);
1527 
1528       // Traverse region edges, marking associated strokes accordingly
1529       UINT e, eCount = region.getEdgeCount();
1530       for (e = 0; e != eCount; ++e) {
1531         const TEdge &ed = *region.getEdge(e);
1532         assert(ed.m_s);
1533 
1534         int strokeIdx = ed.m_index;
1535         if (strokeIdx >= 0)  // Could be <0 in case the corresponding
1536         {                    // stroke is a region 'closure' (autoclose)
1537           assert(0 <= strokeIdx && strokeIdx < sData.size());
1538 
1539           StrokeData &sd = sData[strokeIdx];
1540 
1541           UCHAR side = (ed.m_w1 > ed.m_w0) ? FORWARD : BACKWARD;
1542 
1543           sd.m_hasRegion |= side;
1544           if (regionHasColor) sd.m_hasColor |= side;
1545         }
1546       }
1547 
1548       if (parentRegionHasColor) {
1549         // Mark non-region edge sides with color
1550         for (e = 0; e != eCount; ++e) {
1551           const TEdge &ed = *region.getEdge(e);
1552           assert(ed.m_s);
1553 
1554           int strokeIdx = ed.m_index;
1555           if (strokeIdx >= 0) {
1556             StrokeData &sd = sData[strokeIdx];
1557             sd.m_hasColor |= (INTERNAL & ~sd.m_hasRegion);
1558           }
1559         }
1560       }
1561 
1562       // Mark recursively on sub-regions
1563       UINT sr, srCount = region.getSubregionCount();
1564       for (sr = 0; sr != srCount; ++sr)
1565         markEdges(*region.getSubregion(sr), sData, regionHasColor);
1566     }
1567   };  // locals
1568 
1569   std::vector<StrokeData> sData(vi->getStrokeCount());
1570 
1571   // Traverse regions, mark each stroke edge with the side a COLORED region is
1572   // on
1573   UINT r, rCount = vi->getRegionCount();
1574   for (r = 0; r != rCount; ++r)
1575     locals::markEdges(*vi->getRegion(r), sData, false);
1576 
1577   UINT s, sCount = vi->getStrokeCount();
1578   for (s = 0; s != sCount; ++s) {
1579     // Strokes not appearing as region edges must be checked for region
1580     // inclusion separately
1581     if (!sData[s].m_hasRegion) {
1582       TRegion *parentRegion = vi->getRegion(vi->getStroke(s)->getPoint(0.5));
1583 
1584       if (parentRegion && parentRegion->getStyle())
1585         sData[s].m_hasColor = INTERNAL;
1586     }
1587 
1588     // flip the stroke here
1589     if (sData[s].m_hasColor == FORWARD) vi->getStroke(s)->changeDirection();
1590   }
1591 }
removeFillColors(TRegion * r)1592 void removeFillColors(TRegion *r) {
1593   UINT i, edgeCount = r->getEdgeCount();
1594   for (i = 0; i < edgeCount; ++i) {
1595     r->getEdge(i)->setStyle(0);
1596   }
1597   // Build the color for its sub-regions
1598   int j, rCount = r->getSubregionCount();
1599   for (j = 0; j < rCount; ++j) removeFillColors(r->getSubregion(j));
1600 }
1601 
removeFillColors(TVectorImageP vi)1602 void removeFillColors(TVectorImageP vi) {
1603   int i, rCount = vi->getRegionCount();
1604   for (i = 0; i < rCount; ++i) {
1605     removeFillColors(vi->getRegion(i));
1606   }
1607 }
1608 }  // namespace
1609 
1610 //=================================================================
1611 
vectorize(const TImageP & img,const VectorizerConfiguration & c,TPalette * plt)1612 TVectorImageP VectorizerCore::vectorize(const TImageP &img,
1613                                         const VectorizerConfiguration &c,
1614                                         TPalette *plt) {
1615   TVectorImageP vi;
1616 
1617   if (c.m_outline)
1618     vi = newOutlineVectorize(
1619         img, static_cast<const NewOutlineConfiguration &>(c), plt);
1620   else {
1621     TImageP img2(img);
1622     vi = centerlineVectorize(
1623         img2, static_cast<const CenterlineConfiguration &>(c), plt);
1624 
1625     if (vi) {
1626       for (int i = 0; i < (int)vi->getStrokeCount(); ++i) {
1627         TStroke *stroke = vi->getStroke(i);
1628 
1629         for (int j = 0; j < stroke->getControlPointCount(); ++j) {
1630           TThickPoint p = stroke->getControlPoint(j);
1631           p             = TThickPoint(c.m_affine * p, c.m_thickScale * p.thick);
1632 
1633           stroke->setControlPoint(j, p);
1634         }
1635       }
1636 
1637       applyFillColors(vi, img2, plt, c);
1638     }
1639   }
1640   // align boundary strokes direction
1641   if (c.m_alignBoundaryStrokesDirection) {
1642     alignBoundariesDirection(vi);
1643     vi->validateRegions(false);
1644     vi->findRegions();
1645     if (c.m_leaveUnpainted) removeFillColors(vi);
1646   }
1647 
1648   return vi;
1649 }
1650 
1651 //-----------------------------------------------------------------
1652 
emitPartialDone(void)1653 void VectorizerCore::emitPartialDone(void) {
1654   emit partialDone(m_currPartial++, m_totalPartials);
1655 }
1656 
1657 //-----------------------------------------------------------------
1658 /*
1659 void VectorizerCore::emitPartialDone(int current)
1660 {
1661   m_currPartial= current;
1662   emit partialDone(current, m_totalPartials);
1663 }
1664 */
1665