1 
2 
3 #include "trastercm.h"
4 #include "toonz/fill.h"
5 #include "tregion.h"
6 #include "tstroke.h"
7 #include "tvectorimage.h"
8 #include "toonz/ttileset.h"
9 #include "toonz/ttilesaver.h"
10 #include "toonz/toonzimageutils.h"
11 #include "skeletonlut.h"
12 #include "tpixelutils.h"
13 
14 #include <stack>
15 
16 using namespace SkeletonLut;
17 
18 //-----------------------------------------------------------------------------
19 namespace {  // Utility Function
20 //-----------------------------------------------------------------------------
21 
computeSeeds(const TRasterCM32P & r,TStroke * stroke,std::vector<std::pair<TPoint,int>> & seeds)22 void computeSeeds(const TRasterCM32P &r, TStroke *stroke,
23                   std::vector<std::pair<TPoint, int>> &seeds) {
24   int length = (int)stroke->getLength();
25   TRect bbox = r->getBounds();
26 
27   TPoint oldP;
28 
29   for (int i = 0; i < length; i++) {
30     TPoint p = convert(stroke->getPointAtLength(i));
31     if (p == oldP || !bbox.contains(p)) continue;
32     seeds.push_back(
33         std::pair<TPoint, int>(p, (r->pixels(p.y) + p.x)->getPaint()));
34     oldP = p;
35   }
36 }
37 
38 //-----------------------------------------------------------------------------
39 
fillArea(const TRasterCM32P & ras,TRegion * r,int colorId,bool onlyUnfilled,bool fillPaints,bool fillInks)40 void fillArea(const TRasterCM32P &ras, TRegion *r, int colorId,
41               bool onlyUnfilled, bool fillPaints, bool fillInks) {
42   TRect bbox = convert(r->getBBox());
43   bbox *= ras->getBounds();
44   ras->lock();
45 
46   for (int i = bbox.y0; i <= bbox.y1; i++) {
47     TPixelCM32 *line = ras->pixels(i);
48     std::vector<double> intersections;
49     r->computeScanlineIntersections(i, intersections);
50     assert(!(intersections.size() & 0x1));
51 
52     for (UINT j = 0; j < intersections.size(); j += 2) {
53       if (intersections[j] == intersections[j + 1]) continue;
54       int from        = std::max(tfloor(intersections[j]), bbox.x0);
55       int to          = std::min(tceil(intersections[j + 1]), bbox.x1);
56       TPixelCM32 *pix = line + from;
57       for (int k = from; k < to; k++, pix++) {
58         if (fillPaints && (!onlyUnfilled || pix->getPaint() == 0))
59           pix->setPaint(colorId);
60         if (fillInks) pix->setInk(colorId);
61       }
62     }
63   }
64   ras->unlock();
65 }
66 
67 //-----------------------------------------------------------------------------
68 
restoreColors(const TRasterCM32P & r,const std::vector<std::pair<TPoint,int>> & seeds)69 void restoreColors(const TRasterCM32P &r,
70                    const std::vector<std::pair<TPoint, int>> &seeds) {
71   FillParameters params;
72   // in order to make the paint to protlude behind the line
73   params.m_prevailing = false;
74   for (UINT i = 0; i < seeds.size(); i++) {
75     params.m_p       = seeds[i].first;
76     params.m_styleId = seeds[i].second;
77     fill(r, params);
78   }
79 }
80 
81 //-----------------------------------------------------------------------------
82 
83 /*!
84   Return true if all \b pixels in \b rect are pure paint; otherwise return
85   false.
86 */
areRectPixelsPurePaint(TPixelCM32 * pixels,TRect rect,int wrap)87 bool areRectPixelsPurePaint(TPixelCM32 *pixels, TRect rect, int wrap) {
88   int dx          = rect.x1 - rect.x0;
89   TPixelCM32 *pix = pixels + rect.y0 * wrap + rect.x0;
90   int x, y;
91   for (y = rect.y0; y <= rect.y1; y++, pix += wrap - dx - 1)
92     for (x = rect.x0; x <= rect.x1; x++, pix++)
93       if (!pix->isPurePaint()) return false;
94   return true;
95 }
96 
97 //-----------------------------------------------------------------------------
98 
99 /*!
100   Return true if all \b pixels in \b rect are transparent; otherwise return
101   false.
102 */
areRectPixelsTransparent(TPixel32 * pixels,TRect rect,int wrap)103 bool areRectPixelsTransparent(TPixel32 *pixels, TRect rect, int wrap) {
104   int dx        = rect.x1 - rect.x0;
105   TPixel32 *pix = pixels + rect.y0 * wrap + rect.x0;
106   int x, y;
107   for (y = rect.y0; y <= rect.y1; y++, pix += wrap - dx - 1)
108     for (x = rect.x0; x <= rect.x1; x++, pix++)
109       if (pix->m <= 0) return false;
110   return true;
111 }
112 
113 //-----------------------------------------------------------------------------
114 }  // namespace
115 //-----------------------------------------------------------------------------
116 
117 //=============================================================================
118 // AreaFiller
119 
AreaFiller(const TRasterCM32P & ras)120 AreaFiller::AreaFiller(const TRasterCM32P &ras)
121     : m_ras(ras)
122     , m_bounds(ras->getBounds())
123     , m_pixels(ras->pixels())
124     , m_wrap(ras->getWrap())
125     , m_color(0) {
126   m_ras->lock();
127 }
128 
129 //-----------------------------------------------------------------------------
130 
~AreaFiller()131 AreaFiller::~AreaFiller() { m_ras->unlock(); }
132 
133 //-----------------------------------------------------------------------------
134 // questa funzione viene chiamata dopo il fill rect delle aree, e colora gli
135 // inchiostri di tipo "autoink"
136 // che confinano con le aree appena fillate con il rect. rbefore e' il rect del
137 // raster prima del rectfill.
fillautoInks(TRasterCM32P & rin,TRect & rect,const TRasterCM32P & rbefore,TPalette * plt)138 void fillautoInks(TRasterCM32P &rin, TRect &rect, const TRasterCM32P &rbefore,
139                   TPalette *plt) {
140   assert(plt);
141   TRasterCM32P r = rin->extract(rect);
142   assert(r->getSize() == rbefore->getSize());
143   int i, j;
144 
145   for (i = 0; i < r->getLy(); i++) {
146     TPixelCM32 *pix  = r->pixels(i);
147     TPixelCM32 *pixb = rbefore->pixels(i);
148     for (j = 0; j < r->getLx(); j++, pix++, pixb++) {
149       int paint = pix->getPaint();
150       int tone  = pix->getTone();
151       int ink   = pix->getInk();
152       if (paint != pixb->getPaint() && tone > 0 && tone < 255 && ink != paint &&
153           plt->getStyle(ink)->getFlags() != 0)
154         inkFill(rin, TPoint(j, i) + rect.getP00(), paint, 0, NULL, &rect);
155     }
156   }
157 }
158 
159 //-----------------------------------------------------------------------------
160 
rectFill(const TRect & rect,int color,bool onlyUnfilled,bool fillPaints,bool fillInks)161 bool AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled,
162                           bool fillPaints, bool fillInks) {
163   // Viene trattato il caso fillInks
164   /*- FillInkのみの場合 -*/
165   if (!fillPaints) {
166     assert(fillInks);
167     assert(m_ras->getBounds().contains(rect));
168     for (int y = rect.y0; y <= rect.y1; y++) {
169       TPixelCM32 *pix = m_ras->pixels(y) + rect.x0;
170       for (int x = rect.x0; x <= rect.x1; x++, pix++) pix->setInk(color);
171     }
172     return true;
173   }
174 
175   TRect r = m_bounds * rect;
176 
177   int dx = r.x1 - r.x0;
178   int dy = (r.y1 - r.y0) * m_wrap;
179   if (dx < 2 || dy < 2)  // rect degenere(area contenuta nulla), skippo.
180     return false;
181 
182   std::vector<int> frameSeed(2 * (r.getLx() + r.getLy() - 2));
183 
184   int x, y, count1, count2;
185   /*- ptrをRect範囲のスタート地点に移動 -*/
186   Pixel *ptr = m_pixels + r.y0 * m_wrap + r.x0;
187   count1     = 0;
188   count2     = r.y1 - r.y0 + 1;
189 
190   // Se il rettangolo non contiene il bordo del raster e se tutti i pixels
191   // contenuti nel rettangolo sono pure paint non deve fare nulla!
192   if (!rect.contains(m_bounds) && areRectPixelsPurePaint(m_pixels, r, m_wrap))
193     return false;
194 
195   // Viene riempito frameSeed con tutti i paint delle varie aree del rettangolo
196   // di contorno.
197   // Viene verificato se i pixels del rettangolo sono tutti pure paint.
198   /*- 輪郭のPaintのIDをframeseed内に格納 -*/
199   for (y = r.y0; y <= r.y1; y++, ptr += m_wrap, count1++, count2++) {
200     if (r.x0 > 0) frameSeed[count1] = ptr->getPaint();
201     if (r.x1 < m_ras->getLx() - 1) frameSeed[count2] = (ptr + dx)->getPaint();
202   }
203   ptr    = m_pixels + r.y0 * m_wrap + r.x0 + 1;
204   count1 = count2;
205   count2 = count1 + r.x1 - r.x0 - 1;
206   for (x = r.x0 + 1; x < r.x1; x++, ptr++, count1++, count2++) {
207     if (r.y0 > 0) frameSeed[count1] = ptr->getPaint();
208     if (r.y1 < m_ras->getLy() - 1) frameSeed[count2] = (ptr + dy)->getPaint();
209   }
210   assert(count2 == 2 * (r.getLx() + r.getLy() - 2));
211 
212   // Viene fillato l'interno e il bordo del rettangolo rect con color
213   Pixel *pix = m_pixels + r.y0 * m_wrap + r.x0;
214   if (onlyUnfilled)
215     for (y = r.y0; y <= r.y1; y++, pix += m_wrap - dx - 1) {
216       for (x = r.x0; x <= r.x1; x++, pix++) {
217         if (pix->getPaint() == 0)  // BackgroundStyle
218           pix->setPaint(color);
219         if (fillInks) pix->setInk(color);
220       }
221     }
222   else
223     for (y = r.y0; y <= r.y1; y++, pix += m_wrap - dx - 1) {
224       for (x = r.x0; x <= r.x1; x++, pix++) {
225         pix->setPaint(color);
226         if (fillInks) pix->setInk(color);
227       }
228     }
229 
230   // Vengono fillati i pixel del rettangolo con i paint (mantenuti in frameSeed)
231   // che
232   // c'erano prima di fillare l'intero rettangolo, in questo modo si riportano
233   // al colore originale le aree che non sono chiuse e non dovevano essere
234   // fillate.
235   count1 = 0;
236   FillParameters params;
237   // in order to make the paint to protlude behind the line
238   params.m_prevailing = false;
239   if (r.x0 > 0)
240     for (y = r.y0; y <= r.y1; y++) {
241       params.m_p       = TPoint(r.x0, y);
242       params.m_styleId = frameSeed[count1++];
243       fill(m_ras, params);
244     }
245   else
246     count1 += r.y1 - r.y0 + 1;
247 
248   if (r.x1 < m_ras->getLx() - 1)
249     for (y = r.y0; y <= r.y1; y++) {
250       params.m_p       = TPoint(r.x1, y);
251       params.m_styleId = frameSeed[count1++];
252       fill(m_ras, params);
253     }
254   else
255     count1 += r.y1 - r.y0 + 1;
256 
257   if (r.y0 > 0)
258     for (x = r.x0 + 1; x < r.x1; x++) {
259       params.m_p       = TPoint(x, r.y0);
260       params.m_styleId = frameSeed[count1++];
261       fill(m_ras, params);
262     }
263   else
264     count1 += r.x1 - r.x0 - 1;
265 
266   if (r.y1 < m_ras->getLy() - 1)
267     for (x = r.x0 + 1; x < r.x1; x++) {
268       params.m_p       = TPoint(x, r.y1);
269       params.m_styleId = frameSeed[count1++];
270       fill(m_ras, params);
271     }
272   return true;
273 }
274 
275 //-----------------------------------------------------------------------------
276 
strokeFill(TStroke * stroke,int colorId,bool onlyUnfilled,bool fillPaints,bool fillInks)277 void AreaFiller::strokeFill(TStroke *stroke, int colorId, bool onlyUnfilled,
278                             bool fillPaints, bool fillInks) {
279   stroke->transform(TTranslation(convert(m_ras->getCenter())));
280   m_ras->lock();
281 
282   std::vector<std::pair<TPoint, int>> seeds;
283   computeSeeds(m_ras, stroke, seeds);
284 
285   TVectorImage app;
286   app.addStroke(stroke);
287   app.findRegions();
288   for (UINT i = 0; i < app.getRegionCount(); i++)
289     fillArea(m_ras, app.getRegion(i), colorId, onlyUnfilled, fillPaints,
290              fillInks);
291   app.removeStroke(0);
292 
293   stroke->transform(TTranslation(convert(-m_ras->getCenter())));
294   restoreColors(m_ras, seeds);
295   m_ras->unlock();
296 }
297 
298 //=============================================================================
299 // FullColorAreaFiller
300 
FullColorAreaFiller(const TRaster32P & ras)301 FullColorAreaFiller::FullColorAreaFiller(const TRaster32P &ras)
302     : m_ras(ras)
303     , m_bounds(ras->getBounds())
304     , m_pixels(ras->pixels())
305     , m_wrap(ras->getWrap())
306     , m_color(0) {
307   m_ras->lock();
308 }
309 
310 //-----------------------------------------------------------------------------
311 
~FullColorAreaFiller()312 FullColorAreaFiller::~FullColorAreaFiller() { m_ras->unlock(); }
313 
314 //-----------------------------------------------------------------------------
315 
rectFill(const TRect & rect,const FillParameters & params,bool onlyUnfilled)316 void FullColorAreaFiller::rectFill(const TRect &rect,
317                                    const FillParameters &params,
318                                    bool onlyUnfilled) {
319   TRect bbox = m_ras->getBounds();
320   TRect r    = rect * bbox;
321   if (r.isEmpty()) return;
322 
323   TRaster32P workRas = m_ras->extract(r);
324   TRaster32P copy    = workRas->clone();
325 
326   TPixel32 color = params.m_palette->getStyle(params.m_styleId)->getMainColor();
327 
328   // Fillo tutto il quadaratino con color
329   int x, y;
330   for (y = 0; y < workRas->getLy(); y++) {
331     TPixel32 *line = workRas->pixels(y);
332     for (x = 0; x < workRas->getLx(); x++)
333       *(line + x) = overPix(color, workRas->pixels(y)[x]);
334   }
335 
336   FillParameters paramsApp = params;
337   TPixel32 refColor;
338   for (y = 0; y < workRas->getLy(); y++) {
339     paramsApp.m_p = TPoint(0, y);
340     if (y == 0 ||
341         refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) {
342       fill(workRas, copy, paramsApp);
343       refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x];
344     }
345   }
346   for (y = 0; y < workRas->getLy(); y++) {
347     paramsApp.m_p = TPoint(workRas->getLx() - 1, y);
348     if (y == 0 ||
349         refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) {
350       fill(workRas, copy, paramsApp);
351       refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x];
352     }
353   }
354 
355   for (x = 0; x < workRas->getLx(); x++) {
356     paramsApp.m_p = TPoint(x, 0);
357     if (x == 0 ||
358         refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) {
359       fill(workRas, copy, paramsApp);
360       refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x];
361     }
362   }
363   for (x = 0; x < workRas->getLx(); x++) {
364     paramsApp.m_p = TPoint(x, workRas->getLy() - 1);
365     if (x == 0 ||
366         refColor != workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x]) {
367       fill(workRas, copy, paramsApp);
368       refColor = workRas->pixels(paramsApp.m_p.y)[paramsApp.m_p.x];
369     }
370   }
371 }
372 
373 //=============================================================================
374 // InkSegmenter
375 
376 const int damInk = 3;
377 
378 //-----------------------------------------------------------------------------
379 
380 #define GROW_FACTOR 2.51
381 
382 //-----------------------------------------------------------------------------
383 
384 class InkSegmenter {
385   int m_lx, m_ly, m_wrap;
386   int m_displaceVector[8];
387   TPixelCM32 *m_buf;
388   TRect m_bBox;
389   TRasterCM32P m_r;
390   TTileSaverCM32 *m_saver;
391   float m_growFactor;
392 
393 public:
InkSegmenter(const TRasterCM32P & r,float growFactor,TTileSaverCM32 * saver)394   InkSegmenter(const TRasterCM32P &r, float growFactor, TTileSaverCM32 *saver)
395       : m_r(r)
396       , m_lx(r->getLx())
397       , m_ly(r->getLy())
398       , m_wrap(r->getWrap())
399       , m_buf((TPixelCM32 *)r->getRawData())
400       , m_bBox(r->getBounds())
401       , m_saver(saver)
402       , m_growFactor(growFactor) {
403     m_displaceVector[0] = -m_wrap - 1;
404     m_displaceVector[1] = -m_wrap;
405     m_displaceVector[2] = -m_wrap + 1;
406     m_displaceVector[3] = -1;
407     m_displaceVector[4] = +1;
408     m_displaceVector[5] = m_wrap - 1;
409     m_displaceVector[6] = m_wrap;
410     m_displaceVector[7] = m_wrap + 1;
411   }
412 
413   //-----------------------------------------------------------------------------
414 
compute(const TPoint & pin,int ink,bool isSelective)415   bool compute(const TPoint &pin, int ink, bool isSelective) {
416     TPixelCM32 *pix;
417     int distance;
418     TPixelCM32 *master;
419     TPoint mp, sp;
420     TPixelCM32 *slave;
421     TPixelCM32 *d11, *d12, *d21, *d22;
422     TPoint d1p1, d1p2, d2p1, d2p2;
423     TPoint p = pin;
424 
425     if (!m_bBox.contains(p)) return false;
426 
427     if ((m_buf + p.y * m_wrap + p.x)->isPurePaint() &&
428         ((p = nearestInk(p, 2)) == TPoint(-1, -1)))
429       return false;
430 
431     pix = m_buf + p.y * m_wrap + p.x;
432 
433     /*-- 同じインクの場合はreturn --*/
434     if (pix->getInk() == ink) return false;
435 
436     if (!ConnectionTable[neighboursCode(pix, p)]) {
437       master = slave = pix;
438       mp = sp  = p;
439       distance = 0;
440     } else
441       distance = findTwinPoints(pix, p, master, mp, slave, sp);
442 
443     if (distance == -1) return false;
444 
445     if (!findDam(master, mp, slave, sp, distance, d11, d1p1, d12, d1p2))
446       d11 = d12 = d21 = d22 = 0;
447     else
448       findDamRev(master, mp, slave, sp, distance, d21, d2p1, d22, d2p2);
449 
450     // vector<pair<TPixelCM32*, int> > oldInks;
451 
452     drawSegment(d1p1, d1p2, damInk, m_saver);
453     drawSegment(d2p1, d2p2, damInk, m_saver);
454 
455     inkSegmentFill(p, ink, isSelective, m_saver);
456 
457     // UINT i;
458 
459     drawSegment(d1p1, d1p2, ink, m_saver);
460     drawSegment(d2p1, d2p2, ink, m_saver);
461 
462     /*	for (i=0; i<oldInks.size(); i++)
463     (oldInks[i].first)->setInk(ink);*/
464 
465     return true;
466   }
467 
468 private:
469   void drawSegment(
470       const TPoint &p0, const TPoint &p1, int ink,
471       /*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver);
472 
473   int findTwinPoints(TPixelCM32 *pix, const TPoint &p, TPixelCM32 *&master,
474                      TPoint &mp, TPixelCM32 *&slave, TPoint &sp);
475   int searchForNearestSlave(TPixelCM32 *pix1, TPixelCM32 *pix2,
476                             const TPoint &p1, TPoint &p2, TPixelCM32 *&slave,
477                             TPoint &sp);
478   int rearrangePoints(TPixelCM32 *&master, TPoint &mp, TPixelCM32 *&slave,
479                       int s_prewalker, TPoint &sp, int walk);
480   int rearrangePointsRev(TPixelCM32 *&master, TPoint &mp, TPixelCM32 *&slave,
481                          int s_prewalker, TPoint &sp, int walk);
482   int dragSlave(TPoint mp, TPixelCM32 *&slave, int &s_prewalker, TPoint &sp);
483   int dragSlaveRev(TPoint mp, TPixelCM32 *&slave, int &s_prewalker, TPoint &sp,
484                    TPixelCM32 *first_slave);
485   bool findDam(TPixelCM32 *master, TPoint mp, TPixelCM32 *slave, TPoint sp,
486                int distance, TPixelCM32 *&d11, TPoint &d1p1, TPixelCM32 *&d12,
487                TPoint &d1p2);
488   void findDamRev(TPixelCM32 *master, TPoint mp, TPixelCM32 *slave, TPoint sp,
489                   int distance, TPixelCM32 *&d11, TPoint &d1p1,
490                   TPixelCM32 *&d12, TPoint &d1p2);
491   int nextPointIsGoodRev(TPoint mp, TPoint sp, TPixelCM32 *slave,
492                          int s_prewalker, int distance);
493   int nextPointIsGood(TPoint mp, TPoint sp, TPixelCM32 *slave, int s_prewalker,
494                       int distance);
495   void inkSegmentFill(const TPoint &p, int ink, bool isSelective,
496                       TTileSaverCM32 *saver);
497   TPoint nearestInk(const TPoint &p, int ray);
498   inline int stepReversed(TPixelCM32 *walker, int prewalker, int &distance,
499                           const TPoint &p1, TPoint &p2);
500   inline int stepForward(TPixelCM32 *walker, int prewalker, int &distance,
501                          const TPoint &p1, TPoint &p2);
502 
ePix(TPixelCM32 * br)503   TPixelCM32 *ePix(TPixelCM32 *br) { return (br + 1); }
wPix(TPixelCM32 * br)504   TPixelCM32 *wPix(TPixelCM32 *br) { return (br - 1); }
nPix(TPixelCM32 * br)505   TPixelCM32 *nPix(TPixelCM32 *br) { return (br + m_wrap); }
sPix(TPixelCM32 * br)506   TPixelCM32 *sPix(TPixelCM32 *br) { return (br - m_wrap); }
swPix(TPixelCM32 * br)507   TPixelCM32 *swPix(TPixelCM32 *br) { return (br - m_wrap - 1); }
nwPix(TPixelCM32 * br)508   TPixelCM32 *nwPix(TPixelCM32 *br) { return (br + m_wrap - 1); }
nePix(TPixelCM32 * br)509   TPixelCM32 *nePix(TPixelCM32 *br) { return (br + m_wrap + 1); }
sePix(TPixelCM32 * br)510   TPixelCM32 *sePix(TPixelCM32 *br) { return (br - m_wrap + 1); }
511 
neighboursCode(TPixelCM32 * seed,const TPoint & p)512   UCHAR neighboursCode(TPixelCM32 *seed, const TPoint &p) {
513     // assert(p == TPoint((seed-m_buf)%m_wrap, (seed-m_buf)/m_wrap));
514     bool w = (p.x > 0), e = (p.x < m_lx - 1), s = (p.y > 0),
515          n = (p.y < m_ly - 1);
516 
517     return (((s && w) ? ((!swPix(seed)->isPurePaint())) : 0) |
518             ((s) ? ((!sPix(seed)->isPurePaint()) << 1) : 0) |
519             ((s && e) ? ((!sePix(seed)->isPurePaint()) << 2) : 0) |
520             ((w) ? ((!wPix(seed)->isPurePaint()) << 3) : 0) |
521             ((e) ? ((!ePix(seed)->isPurePaint()) << 4) : 0) |
522             ((n && w) ? ((!nwPix(seed)->isPurePaint()) << 5) : 0) |
523             ((n) ? ((!nPix(seed)->isPurePaint()) << 6) : 0) |
524             ((n && e) ? ((!nePix(seed)->isPurePaint()) << 7) : 0));
525   }
526 };
527 
528 //-----------------------------------------------------------------------------
529 
530 #define DRAW_SEGMENT(a, b, da, db, istr1, istr2, block)                        \
531   {                                                                            \
532     d      = 2 * db - da;                                                      \
533     incr_1 = 2 * db;                                                           \
534     incr_2 = 2 * (db - da);                                                    \
535     while (a < da) {                                                           \
536       if (d <= 0) {                                                            \
537         d += incr_1;                                                           \
538         a++;                                                                   \
539         istr1;                                                                 \
540       } else {                                                                 \
541         d += incr_2;                                                           \
542         a++;                                                                   \
543         b++;                                                                   \
544         istr2;                                                                 \
545       }                                                                        \
546       block;                                                                   \
547     }                                                                          \
548   }
549 #define SET_INK                                                                \
550   {                                                                            \
551     if (saver) saver->save(TPoint(x1 + x, y1 + y));                            \
552     /*if (buf->getInk()!=damInk)*/                                             \
553     /*  oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk()));*/       \
554     buf->setInk(ink);                                                          \
555   }
556 
557 //-----------------------------------------------------------------------------
558 
drawSegment(const TPoint & p0,const TPoint & p1,int ink,TTileSaverCM32 * saver)559 void InkSegmenter::drawSegment(
560     const TPoint &p0, const TPoint &p1, int ink,
561     /*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver) {
562   int x, y, dx, dy, d, incr_1, incr_2;
563 
564   int x1 = p0.x;
565   int y1 = p0.y;
566   int x2 = p1.x;
567   int y2 = p1.y;
568 
569   if (x1 > x2) {
570     std::swap(x1, x2);
571     std::swap(y1, y2);
572   }
573 
574   TPixelCM32 *buf = m_r->pixels() + y1 * m_wrap + x1;
575   /*if (buf->getInk()!=damInk)
576           oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk()));
577   if ((m_r->pixels() + y2*m_wrap + x2)->getInk()!=damInk)
578           oldInks.push_back(pair<TPixelCM32*, int>(m_r->pixels() + y2*m_wrap +
579   x2, (m_r->pixels() + y2*m_wrap + x2)->getInk()));*/
580 
581   if (saver) {
582     saver->save(p0);
583     saver->save(p1);
584   }
585 
586   buf->setInk(ink);
587   (m_r->pixels() + y2 * m_wrap + x2)->setInk(ink);
588 
589   dx = x2 - x1;
590   dy = y2 - y1;
591 
592   x = y = 0;
593 
594   if (dy >= 0) {
595     if (dy <= dx)
596       DRAW_SEGMENT(x, y, dx, dy, (buf++), (buf += m_wrap + 1), SET_INK)
597     else
598       DRAW_SEGMENT(y, x, dy, dx, (buf += m_wrap), (buf += m_wrap + 1), SET_INK)
599   } else {
600     dy = -dy;
601     if (dy <= dx)
602       DRAW_SEGMENT(x, y, dx, dy, (buf++), (buf -= (m_wrap - 1)), SET_INK)
603     else
604       DRAW_SEGMENT(y, x, dy, dx, (buf -= m_wrap), (buf -= (m_wrap - 1)),
605                    SET_INK)
606   }
607 }
608 
609 //-----------------------------------------------------------------------------
610 
inkSegmentFill(const TPoint & p,int ink,bool isSelective,TTileSaverCM32 * saver)611 void InkSegmenter::inkSegmentFill(const TPoint &p, int ink, bool isSelective,
612                                   TTileSaverCM32 *saver) {
613   int x = p.x, y = p.y;
614   int lx             = m_r->getLx();
615   int ly             = m_r->getLy();
616   TPixelCM32 *pixels = (TPixelCM32 *)m_r->getRawData();
617   TPixelCM32 *pix    = pixels + p.y * m_wrap + x;
618   int oldInk;
619 
620   if (pix->isPurePaint() || pix->getInk() == ink) return;
621 
622   if (isSelective) oldInk = pix->getInk();
623 
624   std::stack<TPoint> seeds;
625   seeds.push(p);
626 
627   while (!seeds.empty()) {
628     TPoint seed = seeds.top();
629     seeds.pop();
630     // if(!m_r->getBounds().contains(seed)) continue;
631     x               = seed.x;
632     y               = seed.y;
633     TPixelCM32 *pix = pixels + (y * m_wrap + x);
634     if (pix->isPurePaint() || pix->getInk() == ink || pix->getInk() == damInk ||
635         (isSelective && pix->getInk() != oldInk))
636       continue;
637 
638     if (saver) saver->save(seed);
639 
640     pix->setInk(ink);
641 
642     if (x > 0) seeds.push(TPoint(x - 1, y));
643     if (y > 0) seeds.push(TPoint(x, y - 1));
644     if (y < ly - 1) seeds.push(TPoint(x, y + 1));
645     if (x < lx - 1) seeds.push(TPoint(x + 1, y));
646 
647     if (x == lx - 1 || x == 0 || y == ly - 1 || y == 0) continue;
648 
649     if (ePix(pix)->getInk() == damInk || wPix(pix)->getInk() == damInk ||
650         sPix(pix)->getInk() == damInk || nPix(pix)->getInk() == damInk ||
651         nePix(pix)->getInk() == damInk || sePix(pix)->getInk() == damInk ||
652         swPix(pix)->getInk() == damInk || nwPix(pix)->getInk() == damInk)
653       continue;
654 
655     seeds.push(TPoint(x - 1, y - 1));
656     seeds.push(TPoint(x - 1, y + 1));
657     seeds.push(TPoint(x + 1, y - 1));
658     seeds.push(TPoint(x + 1, y + 1));
659   }
660 }
661 
662 //-----------------------------------------------------------------------------
663 
nearestInk(const TPoint & p,int ray)664 TPoint InkSegmenter::nearestInk(const TPoint &p, int ray) {
665   int i, j;
666 
667   for (j = std::max(p.y - ray, 0); j <= std::min(p.y + ray, m_ly - 1); j++)
668     for (i = std::max(p.x - ray, 0); i <= std::min(p.x + ray, m_lx - 1); i++)
669       if (!(m_buf + j * m_wrap + i)->isPurePaint()) return TPoint(i, j);
670 
671   return TPoint(-1, -1);
672 }
673 
674 //-----------------------------------------------------------------------------
675 
findTwinPoints(TPixelCM32 * pix,const TPoint & p,TPixelCM32 * & master,TPoint & mp,TPixelCM32 * & slave,TPoint & sp)676 int InkSegmenter::findTwinPoints(TPixelCM32 *pix, const TPoint &p,
677                                  TPixelCM32 *&master, TPoint &mp,
678                                  TPixelCM32 *&slave, TPoint &sp) {
679   TPixelCM32 *row_p1, *col_p1, *row_p2, *col_p2;
680   int distance;
681   int row_x1, row_x2, col_y1, col_y2;
682 
683   row_p1 = pix - 1;
684   row_x1 = p.x - 1;
685 
686   while (row_x1 + 1 < m_lx && !(row_p1 + 1)->isPurePaint()) {
687     row_p1++;
688     row_x1++;
689   }
690 
691   row_p2 = pix + 1;
692   row_x2 = p.x + 1;
693 
694   while (row_x2 - 1 > 0 && !(row_p2 - 1)->isPurePaint()) {
695     row_p2--;
696     row_x2--;
697   }
698 
699   master = row_p1;
700   mp.x   = row_x1;
701   mp.y   = p.y;
702 
703   col_p1 = pix - m_wrap;
704   col_y1 = p.y - 1;
705 
706   while (col_y1 + 1 < m_ly && !(col_p1 + m_wrap)->isPurePaint()) {
707     col_p1 += m_wrap;
708     col_y1++;
709   }
710 
711   col_p2 = pix + m_wrap;
712   col_y2 = p.y + 1;
713 
714   while (col_y2 - 1 > 0 && !(col_p2 - 1)->isPurePaint()) {
715     col_p2 -= m_wrap;
716     col_y2--;
717   }
718 
719   if (row_x1 - row_x2 <= col_y1 - col_y2) {
720     master = row_p1;
721     mp     = TPoint(row_x1, p.y);
722 
723     TPoint auxp(row_x2, p.y);
724     if ((distance =
725              searchForNearestSlave(row_p1, row_p2, mp, auxp, slave, sp)) != 0)
726       return distance;
727 
728     master = col_p1;
729     mp     = TPoint(p.x, col_y1);
730 
731     auxp = TPoint(p.x, col_y2);
732     if ((distance = searchForNearestSlave(col_p1, col_p2, mp, auxp, slave,
733                                           sp)) == 0 /*&& !is_connecting(p1)*/)
734       return -1;
735   } else {
736     master = col_p1;
737     mp     = TPoint(p.x, col_y1);
738 
739     TPoint auxp(p.x, col_y2);
740     if ((distance =
741              searchForNearestSlave(col_p1, col_p2, mp, auxp, slave, sp)) != 0)
742       return distance;
743 
744     master = row_p1;
745     mp     = TPoint(row_x1, p.y);
746     auxp   = TPoint(row_x2, p.y);
747     if ((distance = searchForNearestSlave(row_p1, row_p2, mp, auxp, slave,
748                                           sp)) == 0 /*&& !is_connecting(p1)*/)
749       return -1;
750   }
751 
752   return distance;
753 }
754 
755 //-----------------------------------------------------------------------------
756 
newP(int next,TPoint & p)757 inline void newP(int next, TPoint &p) {
758   switch (next) {
759   case 0:
760   case 3:
761   case 5:
762     p.x -= 1;
763     break;
764   case 2:
765   case 4:
766   case 7:
767     p.x += 1;
768     break;
769   }
770 
771   switch (next) {
772   case 0:
773   case 1:
774   case 2:
775     p.y -= 1;
776     break;
777   case 5:
778   case 6:
779   case 7:
780     p.y += 1;
781     break;
782   }
783 }
784 
785 //-----------------------------------------------------------------------------
786 
stepReversed(TPixelCM32 * walker,int prewalker,int & distance,const TPoint & p1,TPoint & p2)787 inline int InkSegmenter::stepReversed(TPixelCM32 *walker, int prewalker,
788                                       int &distance, const TPoint &p1,
789                                       TPoint &p2) {
790   int next = NextPointTableRev[(neighboursCode(walker, p2) << 3) | prewalker];
791   newP(next, p2);
792   distance = norm2(p1 - p2);
793   return next;
794 }
795 
796 /*------------------------------------------------------------------------*/
797 
stepForward(TPixelCM32 * walker,int prewalker,int & distance,const TPoint & p1,TPoint & p2)798 inline int InkSegmenter::stepForward(TPixelCM32 *walker, int prewalker,
799                                      int &distance, const TPoint &p1,
800                                      TPoint &p2) {
801   int next = NextPointTable[(neighboursCode(walker, p2) << 3) | prewalker];
802   newP(next, p2);
803   distance = norm2(p1 - p2);
804   return next;
805 }
806 
807 //-----------------------------------------------------------------------------
808 
searchForNearestSlave(TPixelCM32 * pix1,TPixelCM32 * pix2,const TPoint & p1,TPoint & p2,TPixelCM32 * & slave,TPoint & sp)809 int InkSegmenter::searchForNearestSlave(TPixelCM32 *pix1, TPixelCM32 *pix2,
810                                         const TPoint &p1, TPoint &p2,
811                                         TPixelCM32 *&slave, TPoint &sp) {
812   int curr_distance, new_distance;
813   UCHAR prewalker, next;
814   TPixelCM32 *walker;
815   TPoint currp2;
816 
817   currp2 = p2;
818 
819   curr_distance = norm2(p1 - p2);
820   walker        = pix2;
821   slave         = pix2;
822   sp            = p2;
823 
824   prewalker = FirstPreseedTable[neighboursCode(walker, p2)];
825 
826   next = stepForward(walker, prewalker, new_distance, p1, p2);
827 
828   if (curr_distance != 0 && new_distance < curr_distance) {
829     while (p2.x > 0 && p2.x < m_lx - 1 && p2.y > 0 && p2.y < m_ly - 1 &&
830            new_distance < curr_distance && new_distance != 0) {
831       curr_distance = new_distance;
832       sp.x          = p2.x;
833       sp.y          = p2.y;
834       walker        = walker + m_displaceVector[next];
835       slave         = walker;
836       prewalker     = (~next) & 0x7;
837       next          = stepForward(walker, prewalker, new_distance, p1, p2);
838     }
839     if (new_distance != 0) return curr_distance;
840   }
841 
842   curr_distance = norm2(p1 - p2);
843   walker        = pix2;
844   p2            = currp2;
845 
846   UCHAR code = neighboursCode(walker, p2);
847   next       = FirstPreseedTable[code];
848   next       = NextPointTable[(code << 3) | next];
849   prewalker  = next;
850 
851   next = stepReversed(walker, prewalker, new_distance, p1, p2);
852 
853   if (p2.x > 0 && p2.x < m_lx - 1 && p2.y > 0 && p2.y < m_ly - 1 &&
854       curr_distance != 0 && new_distance < curr_distance) {
855     while (new_distance < curr_distance && new_distance > 0) {
856       curr_distance = new_distance;
857       sp            = p2;
858 
859       walker = walker + m_displaceVector[next];
860       slave  = walker;
861 
862       prewalker = (~next) & 0x7;
863       next      = stepReversed(walker, prewalker, new_distance, p1, p2);
864     }
865     if (new_distance != 0) return curr_distance;
866   } else if (new_distance != 0)
867     return curr_distance;
868   return 0;
869 }
870 
871 //-----------------------------------------------------------------------------
872 
rearrangePoints(TPixelCM32 * & master,TPoint & mp,TPixelCM32 * & slave,int s_prewalker,TPoint & sp,int walk)873 int InkSegmenter::rearrangePoints(TPixelCM32 *&master, TPoint &mp,
874                                   TPixelCM32 *&slave, int s_prewalker,
875                                   TPoint &sp, int walk) {
876   int s_next;
877 
878   while (walk-- && sp.x > 0 && sp.x < m_lx - 1 && sp.y > 0 && sp.y < m_ly - 1) {
879     s_next =
880         NextPointTableRev[((neighboursCode(slave, sp)) << 3) | s_prewalker];
881 
882     newP(s_next, sp);
883 
884     slave       = slave + m_displaceVector[s_next];
885     s_prewalker = (~s_next) & 0x7;
886   }
887   return 1;
888 }
889 
890 //-----------------------------------------------------------------------------
891 
rearrangePointsRev(TPixelCM32 * & master,TPoint & mp,TPixelCM32 * & slave,int s_prewalker,TPoint & sp,int walk)892 int InkSegmenter::rearrangePointsRev(TPixelCM32 *&master, TPoint &mp,
893                                      TPixelCM32 *&slave, int s_prewalker,
894                                      TPoint &sp, int walk) {
895   int s_next;
896 
897   while (walk-- && sp.x > 0 && sp.x < m_lx - 1 && sp.y > 0 && sp.y < m_ly - 1) {
898     s_next = NextPointTable[((neighboursCode(slave, sp)) << 3) | s_prewalker];
899     // s_next = NEXT_POINT_24(*slave, s_prewalker);
900 
901     newP(s_next, sp);
902 
903     slave       = slave + m_displaceVector[s_next];
904     s_prewalker = (~s_next) & 0x7;
905   }
906   return 1;
907 }
908 
909 //-----------------------------------------------------------------------------
910 
dragSlave(TPoint mp,TPixelCM32 * & slave,int & s_prewalker,TPoint & sp)911 int InkSegmenter::dragSlave(TPoint mp, TPixelCM32 *&slave, int &s_prewalker,
912                             TPoint &sp) {
913   int distance, s_next, new_distance;
914   int ret = 0;
915 
916   distance = norm2(mp - sp);
917 
918   s_next = stepForward(slave, s_prewalker, new_distance, mp, sp);
919 
920   while (sp.x > 0 && sp.x < m_lx - 1 && sp.y > 0 && sp.y < m_ly - 1 &&
921          (new_distance < distance ||
922           nextPointIsGood(mp, sp, slave + m_displaceVector[s_next],
923                           (~s_next) & 0x7, distance))) {
924     if (!ret) ret = 1;
925     distance    = new_distance;
926     slave       = slave + m_displaceVector[s_next];
927     s_prewalker = (~s_next) & 0x7;
928     s_next      = stepForward(slave, s_prewalker, new_distance, mp, sp);
929   }
930 
931   newP(((~s_next) & 0x7), sp);
932   return ret;
933 }
934 
935 //-----------------------------------------------------------------------------
936 
dragSlaveRev(TPoint mp,TPixelCM32 * & slave,int & s_prewalker,TPoint & sp,TPixelCM32 * first_slave)937 int InkSegmenter::dragSlaveRev(TPoint mp, TPixelCM32 *&slave, int &s_prewalker,
938                                TPoint &sp, TPixelCM32 *first_slave) {
939   int distance, new_distance, s_next;
940   int ret = 0;
941 
942   distance = norm2(mp - sp);
943 
944   s_next = stepReversed(slave, s_prewalker, new_distance, mp, sp);
945 
946   while (sp.x > 0 && sp.x < m_lx - 1 && sp.y > 0 && sp.y < m_ly - 1 &&
947          (new_distance < distance ||
948           nextPointIsGoodRev(mp, sp, slave + m_displaceVector[s_next],
949                              (~s_next) & 0x7, distance))) {
950     if (!ret) ret = 1;
951     distance = new_distance;
952     slave    = slave + m_displaceVector[s_next];
953     if (slave == first_slave) return -1;
954 
955     s_prewalker = (~s_next) & 0x7;
956     s_next      = stepReversed(slave, s_prewalker, new_distance, mp, sp);
957   }
958 
959   newP(((~s_next) & 0x7), sp);
960   return ret;
961 }
962 
963 //-----------------------------------------------------------------------------
964 
findDam(TPixelCM32 * master,TPoint mp,TPixelCM32 * slave,TPoint sp,int distance,TPixelCM32 * & d11,TPoint & d1p1,TPixelCM32 * & d12,TPoint & d1p2)965 bool InkSegmenter::findDam(TPixelCM32 *master, TPoint mp, TPixelCM32 *slave,
966                            TPoint sp, int distance, TPixelCM32 *&d11,
967                            TPoint &d1p1, TPixelCM32 *&d12, TPoint &d1p2)
968 
969 {
970   int ref_distance, m_prewalker, s_prewalker, m_next, next, ret;
971   unsigned int walkalone = 0;
972   TPixelCM32 *first_slave, *first_master;
973 
974   first_slave  = slave;
975   first_master = master;
976 
977   ref_distance = tround(m_growFactor * ((float)distance + 1));
978 
979   m_prewalker = FirstPreseedTable[neighboursCode(master, mp)];
980 
981   if (!ConnectionTable[neighboursCode(master, mp)])
982     s_prewalker = FirstPreseedTableRev[neighboursCode(slave, sp)];
983   else {
984     UCHAR code  = neighboursCode(slave, sp);
985     next        = FirstPreseedTable[code];
986     next        = NextPointTable[(code << 3) | next];
987     s_prewalker = next;
988   }
989 
990   while (mp.x > 0 && mp.x < m_lx - 1 && mp.y > 0 && mp.y < m_ly - 1 &&
991          distance < ref_distance &&
992          !(((m_next = NextPointTable[((neighboursCode(master, mp)) << 3) |
993                                      m_prewalker]) == s_prewalker) &&
994            master == slave)) {
995     newP(m_next, mp);
996     master      = master + m_displaceVector[m_next];
997     m_prewalker = (~m_next) & 0x7;
998 
999     ret = dragSlaveRev(mp, slave, s_prewalker, sp, first_slave);
1000 
1001     if (ret == -1) return false;
1002 
1003     if (ret == 0)
1004       walkalone++;
1005     else
1006       walkalone = 0;
1007 
1008     if (master == first_master) break;
1009     distance = norm2(mp - sp);
1010   }
1011 
1012   if (walkalone > 0)
1013     rearrangePoints(master, mp, slave, s_prewalker, sp, walkalone);
1014 
1015   d11  = master;
1016   d1p1 = mp;
1017   d12  = slave;
1018   d1p2 = sp;
1019   return 1;
1020 }
1021 
1022 //-----------------------------------------------------------------------------
1023 
findDamRev(TPixelCM32 * master,TPoint mp,TPixelCM32 * slave,TPoint sp,int distance,TPixelCM32 * & d11,TPoint & d1p1,TPixelCM32 * & d12,TPoint & d1p2)1024 void InkSegmenter::findDamRev(TPixelCM32 *master, TPoint mp, TPixelCM32 *slave,
1025                               TPoint sp, int distance, TPixelCM32 *&d11,
1026                               TPoint &d1p1, TPixelCM32 *&d12, TPoint &d1p2)
1027 
1028 {
1029   int ref_distance, m_prewalker, s_prewalker, m_next, next;
1030   unsigned int walkalone = 0;
1031   TPixelCM32 *first_master;
1032 
1033   first_master = master;
1034 
1035   ref_distance = tround(GROW_FACTOR * ((float)distance + 1));
1036 
1037   m_prewalker = FirstPreseedTableRev[neighboursCode(master, mp)];
1038 
1039   if (!ConnectionTable[neighboursCode(master, mp)]) {
1040     UCHAR code  = neighboursCode(slave, sp);
1041     next        = FirstPreseedTableRev[code];
1042     next        = NextPointTableRev[(code << 3) | next];
1043     s_prewalker = next;
1044   } else
1045     s_prewalker = FirstPreseedTable[neighboursCode(slave, sp)];
1046 
1047   while (mp.x > 0 && mp.x < m_lx - 1 && mp.y > 0 && mp.y < m_ly - 1 &&
1048          distance < ref_distance &&
1049          !(((m_next = NextPointTableRev[((neighboursCode(master, mp)) << 3) |
1050                                         m_prewalker]) == s_prewalker) &&
1051            master == slave)) {
1052     newP(m_next, mp);
1053     master      = master + m_displaceVector[m_next];
1054     m_prewalker = (~m_next) & 0x7;
1055     if (!dragSlave(mp, slave, s_prewalker, sp))
1056       walkalone++;
1057     else
1058       walkalone = 0;
1059     if (master == first_master) break;
1060     distance = norm2(mp - sp);
1061   }
1062 
1063   if (walkalone > 0)
1064     rearrangePointsRev(master, mp, slave, s_prewalker, sp, walkalone);
1065 
1066   d11  = master;
1067   d1p1 = mp;
1068   d12  = slave;
1069   d1p2 = sp;
1070 }
1071 
1072 //-----------------------------------------------------------------------------
1073 
nextPointIsGood(TPoint mp,TPoint sp,TPixelCM32 * slave,int s_prewalker,int distance)1074 int InkSegmenter::nextPointIsGood(TPoint mp, TPoint sp, TPixelCM32 *slave,
1075                                   int s_prewalker, int distance) {
1076   int s_next;
1077 
1078   s_next = NextPointTable[((neighboursCode(slave, sp)) << 3) | s_prewalker];
1079 
1080   newP(s_next, sp);
1081 
1082   return (norm2(mp - sp) <= distance);
1083 }
1084 
1085 //-----------------------------------------------------------------------------
1086 
nextPointIsGoodRev(TPoint mp,TPoint sp,TPixelCM32 * slave,int s_prewalker,int distance)1087 int InkSegmenter::nextPointIsGoodRev(TPoint mp, TPoint sp, TPixelCM32 *slave,
1088                                      int s_prewalker, int distance) {
1089   int s_next;
1090 
1091   s_next = NextPointTableRev[((neighboursCode(slave, sp)) << 3) | s_prewalker];
1092 
1093   newP(s_next, sp);
1094 
1095   return (norm2(mp - sp) <= distance);
1096 }
1097 
1098 //-----------------------------------------------------------------------------
1099 
inkSegment(const TRasterCM32P & r,const TPoint & p,int ink,float growFactor,bool isSelective,TTileSaverCM32 * saver)1100 bool inkSegment(const TRasterCM32P &r, const TPoint &p, int ink,
1101                 float growFactor, bool isSelective, TTileSaverCM32 *saver) {
1102   r->lock();
1103   InkSegmenter is(r, growFactor, saver);
1104   bool ret = is.compute(p, ink, isSelective);
1105   r->unlock();
1106   return ret;
1107 }
1108