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 ¶ms,
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