1 
2 #include "filltool.h"
3 
4 #include "toonz/tframehandle.h"
5 #include "toonz/tcolumnhandle.h"
6 #include "toonz/tpalettehandle.h"
7 #include "toonz/preferences.h"
8 #include "toonz/txsheethandle.h"
9 #include "toonz/tobjecthandle.h"
10 #include "toonz/tscenehandle.h"
11 #include "tools/toolhandle.h"
12 #include "tools/toolutils.h"
13 #include "toonz/tonionskinmaskhandle.h"
14 #include "timagecache.h"
15 #include "tundo.h"
16 #include "tpalette.h"
17 #include "tcolorstyles.h"
18 #include "tvectorimage.h"
19 #include "tools/cursors.h"
20 #include "ttoonzimage.h"
21 #include "tproperty.h"
22 #include "tenv.h"
23 #include "tools/stylepicker.h"
24 
25 #include "toonz/stage2.h"
26 #include "tstroke.h"
27 #include "drawutil.h"
28 #include "tsystem.h"
29 #include "tinbetween.h"
30 #include "tregion.h"
31 #include "tgl.h"
32 #include "trop.h"
33 
34 #include "toonz/onionskinmask.h"
35 #include "toonz/ttileset.h"
36 #include "toonz/ttilesaver.h"
37 #include "toonz/toonzimageutils.h"
38 #include "toonz/levelproperties.h"
39 
40 #include "toonz/txshcell.h"
41 #include "toonzqt/imageutils.h"
42 #include "autofill.h"
43 
44 #include "historytypes.h"
45 
46 #include <stack>
47 
48 // For Qt translation support
49 #include <QCoreApplication>
50 
51 using namespace ToolUtils;
52 
53 //#define LINES L"Lines"
54 //#define AREAS L"Areas"
55 //#define ALL L"Lines & Areas"
56 
57 #define NORMALFILL L"Normal"
58 #define RECTFILL L"Rectangular"
59 #define FREEHANDFILL L"Freehand"
60 #define POLYLINEFILL L"Polyline"
61 
62 TEnv::IntVar MinFillDepth("InknpaintMinFillDepth", 0);
63 TEnv::IntVar MaxFillDepth("InknpaintMaxFillDepth", 10);
64 TEnv::StringVar FillType("InknpaintFillType", "Normal");
65 TEnv::StringVar FillColorType("InknpaintFillColorType", "Areas");
66 TEnv::IntVar FillSelective("InknpaintFillSelective", 0);
67 TEnv::IntVar FillOnion("InknpaintFillOnion", 0);
68 TEnv::IntVar FillSegment("InknpaintFillSegment", 0);
69 TEnv::IntVar FillRange("InknpaintFillRange", 0);
70 
71 //-----------------------------------------------------------------------------
72 namespace {
73 
vectorFill(const TVectorImageP & img,const std::wstring & type,const TPointD & point,int style,bool emptyOnly=false)74 inline int vectorFill(const TVectorImageP &img, const std::wstring &type,
75                       const TPointD &point, int style, bool emptyOnly = false) {
76   if (type == ALL || type == LINES) {
77     int oldStyleId = img->fillStrokes(point, style);
78     if (oldStyleId != -1) return oldStyleId;
79   }
80 
81   if (type == ALL || type == AREAS) return img->fill(point, style, emptyOnly);
82 
83   return -1;
84 }
85 
86 //=============================================================================
87 // VectorFillUndo
88 //-----------------------------------------------------------------------------
89 
90 class VectorFillUndo final : public TToolUndo {
91   int m_oldColorStyle;
92   int m_newColorStyle;
93   TPointD m_point;
94   std::wstring m_type;
95   int m_row;
96   int m_column;
97 
98 public:
VectorFillUndo(int newColorStyle,int oldColorStyle,std::wstring fillType,TPointD clickPoint,TXshSimpleLevel * sl,const TFrameId & fid)99   VectorFillUndo(int newColorStyle, int oldColorStyle, std::wstring fillType,
100                  TPointD clickPoint, TXshSimpleLevel *sl, const TFrameId &fid)
101       : TToolUndo(sl, fid)
102       , m_newColorStyle(newColorStyle)
103       , m_oldColorStyle(oldColorStyle)
104       , m_point(clickPoint)
105       , m_type(fillType) {
106     TTool::Application *app = TTool::getApplication();
107     if (app) {
108       m_row    = app->getCurrentFrame()->getFrame();
109       m_column = app->getCurrentColumn()->getColumnIndex();
110     }
111   }
112 
undo() const113   void undo() const override {
114     TTool::Application *app = TTool::getApplication();
115     if (!app) return;
116 
117     app->getCurrentLevel()->setLevel(m_level.getPointer());
118     TVectorImageP img = m_level->getFrame(m_frameId, true);
119     if (app->getCurrentFrame()->isEditingScene()) {
120       app->getCurrentFrame()->setFrame(m_row);
121       app->getCurrentColumn()->setColumnIndex(m_column);
122     } else
123       app->getCurrentFrame()->setFid(m_frameId);
124     assert(img);
125     if (!img) return;
126     QMutexLocker lock(img->getMutex());
127 
128     vectorFill(img, m_type, m_point, m_oldColorStyle);
129 
130     app->getCurrentXsheet()->notifyXsheetChanged();
131     notifyImageChanged();
132   }
133 
redo() const134   void redo() const override {
135     TTool::Application *app = TTool::getApplication();
136     if (!app) return;
137 
138     app->getCurrentLevel()->setLevel(m_level.getPointer());
139     TVectorImageP img = m_level->getFrame(m_frameId, true);
140     if (app->getCurrentFrame()->isEditingScene()) {
141       app->getCurrentFrame()->setFrame(m_row);
142       app->getCurrentColumn()->setColumnIndex(m_column);
143     } else
144       app->getCurrentFrame()->setFid(m_frameId);
145     assert(img);
146     if (!img) return;
147     QMutexLocker lock(img->getMutex());
148 
149     vectorFill(img, m_type, m_point, m_newColorStyle);
150 
151     app->getCurrentXsheet()->notifyXsheetChanged();
152     notifyImageChanged();
153   }
154 
onAdd()155   void onAdd() override {}
156 
getSize() const157   int getSize() const override { return sizeof(*this); }
158 
getToolName()159   QString getToolName() override {
160     return QString("Fill Tool : %1").arg(QString::fromStdWString(m_type));
161   }
getHistoryType()162   int getHistoryType() override { return HistoryType::FillTool; }
163 };
164 
165 //=============================================================================
166 // VectorRectFillUndo
167 //-----------------------------------------------------------------------------
168 
169 class VectorRectFillUndo final : public TToolUndo {
170   std::vector<TFilledRegionInf> *m_regionFillInformation;
171   std::vector<std::pair<int, int>> *m_strokeFillInformation;
172   TRectD m_selectionArea;
173   int m_styleId;
174   bool m_unpaintedOnly;
175   TStroke *m_stroke;
176 
177 public:
~VectorRectFillUndo()178   ~VectorRectFillUndo() {
179     if (m_regionFillInformation) delete m_regionFillInformation;
180     if (m_strokeFillInformation) delete m_strokeFillInformation;
181     if (m_stroke) delete m_stroke;
182   }
183 
VectorRectFillUndo(std::vector<TFilledRegionInf> * regionFillInformation,std::vector<std::pair<int,int>> * strokeFillInformation,TRectD selectionArea,TStroke * stroke,int styleId,bool unpaintedOnly,TXshSimpleLevel * sl,const TFrameId & fid)184   VectorRectFillUndo(std::vector<TFilledRegionInf> *regionFillInformation,
185                      std::vector<std::pair<int, int>> *strokeFillInformation,
186                      TRectD selectionArea, TStroke *stroke, int styleId,
187                      bool unpaintedOnly, TXshSimpleLevel *sl,
188                      const TFrameId &fid)
189       : TToolUndo(sl, fid)
190       , m_regionFillInformation(regionFillInformation)
191       , m_strokeFillInformation(strokeFillInformation)
192       , m_selectionArea(selectionArea)
193       , m_styleId(styleId)
194       , m_unpaintedOnly(unpaintedOnly)
195       , m_stroke(0) {
196     if (stroke) m_stroke = new TStroke(*stroke);
197   }
198 
undo() const199   void undo() const override {
200     TTool::Application *app = TTool::getApplication();
201     if (!app) return;
202 
203     TVectorImageP img = m_level->getFrame(m_frameId, true);
204     assert(!!img);
205     if (!img) return;
206     if (m_regionFillInformation) {
207       for (UINT i = 0; i < m_regionFillInformation->size(); i++) {
208         TRegion *reg = img->getRegion((*m_regionFillInformation)[i].m_regionId);
209         if (reg) reg->setStyle((*m_regionFillInformation)[i].m_styleId);
210       }
211     }
212     if (m_strokeFillInformation) {
213       for (UINT i = 0; i < m_strokeFillInformation->size(); i++) {
214         TStroke *s = img->getStroke((*m_strokeFillInformation)[i].first);
215         s->setStyle((*m_strokeFillInformation)[i].second);
216       }
217     }
218 
219     app->getCurrentXsheet()->notifyXsheetChanged();
220     notifyImageChanged();
221   }
222 
redo() const223   void redo() const override {
224     TTool::Application *app = TTool::getApplication();
225     if (!app) return;
226 
227     TVectorImageP img = m_level->getFrame(m_frameId, true);
228     assert(img);
229     if (!img) return;
230 
231     img->selectFill(m_selectionArea, m_stroke, m_styleId, m_unpaintedOnly,
232                     m_regionFillInformation != 0, m_strokeFillInformation != 0);
233     app->getCurrentXsheet()->notifyXsheetChanged();
234     notifyImageChanged();
235   }
236 
onAdd()237   void onAdd() override {}
238 
getSize() const239   int getSize() const override {
240     int size1 = m_regionFillInformation ? m_regionFillInformation->capacity() *
241                                               sizeof(m_regionFillInformation)
242                                         : 0;
243     int size2 = m_strokeFillInformation ? m_strokeFillInformation->capacity() *
244                                               sizeof(m_strokeFillInformation)
245                                         : 0;
246     return sizeof(*this) + size1 + size2 + 500;
247   }
248 
getToolName()249   QString getToolName() override { return QString("Fill Tool : "); }
getHistoryType()250   int getHistoryType() override { return HistoryType::FillTool; }
251 };
252 
253 //=============================================================================
254 // VectorGapSizeChangeUndo
255 //-----------------------------------------------------------------------------
256 
257 class VectorGapSizeChangeUndo final : public TToolUndo {
258   double m_oldGapSize;
259   double m_newGapSize;
260   int m_row;
261   int m_column;
262   TVectorImageP m_vi;
263   std::vector<TFilledRegionInf> m_oldFillInformation;
264 
265 public:
VectorGapSizeChangeUndo(double oldGapSize,double newGapSize,TXshSimpleLevel * sl,const TFrameId & fid,TVectorImageP vi,std::vector<TFilledRegionInf> oldFillInformation)266   VectorGapSizeChangeUndo(double oldGapSize, double newGapSize,
267                           TXshSimpleLevel *sl, const TFrameId &fid,
268                           TVectorImageP vi,
269                           std::vector<TFilledRegionInf> oldFillInformation)
270       : TToolUndo(sl, fid)
271       , m_oldGapSize(oldGapSize)
272       , m_newGapSize(newGapSize)
273       , m_oldFillInformation(oldFillInformation)
274       , m_vi(vi) {
275     TTool::Application *app = TTool::getApplication();
276     if (app) {
277       m_row    = app->getCurrentFrame()->getFrame();
278       m_column = app->getCurrentColumn()->getColumnIndex();
279     }
280   }
281 
undo() const282   void undo() const override {
283     TTool::Application *app = TTool::getApplication();
284     if (!app || !m_level) return;
285     app->getCurrentLevel()->setLevel(m_level.getPointer());
286     if (app->getCurrentFrame()->isEditingScene()) {
287       app->getCurrentFrame()->setFrame(m_row);
288       app->getCurrentColumn()->setColumnIndex(m_column);
289     } else
290       app->getCurrentFrame()->setFid(m_frameId);
291 
292     m_vi->setAutocloseTolerance(m_oldGapSize);
293     int count = m_vi->getStrokeCount();
294     std::vector<int> v(count);
295     int i;
296     for (i = 0; i < (int)count; i++) v[i] = i;
297     m_vi->notifyChangedStrokes(v, std::vector<TStroke *>(), false);
298     if (m_vi->isComputedRegionAlmostOnce()) m_vi->findRegions();
299     if (m_oldFillInformation.size()) {
300       for (UINT j = 0; j < m_oldFillInformation.size(); j++) {
301         TRegion *reg = m_vi->getRegion(m_oldFillInformation[j].m_regionId);
302         if (reg) reg->setStyle(m_oldFillInformation[j].m_styleId);
303       }
304     }
305     app->getCurrentXsheet()->notifyXsheetChanged();
306     app->getCurrentTool()->notifyToolChanged();
307     notifyImageChanged();
308   }
309 
redo() const310   void redo() const override {
311     TTool::Application *app = TTool::getApplication();
312     if (!app || !m_level) return;
313 
314     app->getCurrentLevel()->setLevel(m_level.getPointer());
315     TVectorImageP img = m_level->getFrame(m_frameId, true);
316     if (app->getCurrentFrame()->isEditingScene()) {
317       app->getCurrentFrame()->setFrame(m_row);
318       app->getCurrentColumn()->setColumnIndex(m_column);
319     } else
320       app->getCurrentFrame()->setFid(m_frameId);
321 
322     m_vi->setAutocloseTolerance(m_newGapSize);
323     int count = m_vi->getStrokeCount();
324     std::vector<int> v(count);
325     int i;
326     for (i = 0; i < (int)count; i++) v[i] = i;
327     m_vi->notifyChangedStrokes(v, std::vector<TStroke *>(), false);
328     app->getCurrentXsheet()->notifyXsheetChanged();
329     app->getCurrentTool()->notifyToolChanged();
330     notifyImageChanged();
331   }
332 
onAdd()333   void onAdd() override {}
334 
getSize() const335   int getSize() const override { return sizeof(*this); }
336 
getToolName()337   QString getToolName() override {
338     return QString("Fill Tool: Set Gap Size ") + QString::number(m_newGapSize);
339   }
getHistoryType()340   int getHistoryType() override { return HistoryType::FillTool; }
341 };
342 
343 //=============================================================================
344 // RasterFillUndo
345 //-----------------------------------------------------------------------------
346 
347 class RasterFillUndo final : public TRasterUndo {
348   FillParameters m_params;
349   bool m_saveboxOnly;
350 
351 public:
352   /*RasterFillUndo(TTileSetCM32 *tileSet, TPoint fillPoint,
353                                                            int paintId, int
354      fillDepth,
355                                                            std::wstring
356      fillType, bool isSegment,
357                                                            bool selective, bool
358      isShiftFill,
359                                                            TXshSimpleLevel* sl,
360      const TFrameId& fid)*/
RasterFillUndo(TTileSetCM32 * tileSet,const FillParameters & params,TXshSimpleLevel * sl,const TFrameId & fid,bool saveboxOnly)361   RasterFillUndo(TTileSetCM32 *tileSet, const FillParameters &params,
362                  TXshSimpleLevel *sl, const TFrameId &fid, bool saveboxOnly)
363       : TRasterUndo(tileSet, sl, fid, false, false, 0)
364       , m_params(params)
365       , m_saveboxOnly(saveboxOnly) {}
366 
redo() const367   void redo() const override {
368     TToonzImageP image = getImage();
369     if (!image) return;
370     bool recomputeSavebox = false;
371     TRasterCM32P r;
372     if (m_saveboxOnly) {
373       TRectD temp = image->getBBox();
374       TRect ttemp = convert(temp);
375       r           = image->getRaster()->extract(ttemp);
376     } else
377       r = image->getRaster();
378     if (m_params.m_fillType == ALL || m_params.m_fillType == AREAS) {
379       if (m_params.m_shiftFill) {
380         FillParameters aux(m_params);
381         aux.m_styleId    = (m_params.m_styleId == 0) ? 1 : 0;
382         recomputeSavebox = fill(r, aux);
383       }
384       recomputeSavebox = fill(r, m_params);
385     }
386     if (m_params.m_fillType == ALL || m_params.m_fillType == LINES) {
387       if (m_params.m_segment)
388         inkSegment(r, m_params.m_p, m_params.m_styleId, 2.51, true);
389       else
390         inkFill(r, m_params.m_p, m_params.m_styleId, 2);
391     }
392 
393     if (recomputeSavebox) ToolUtils::updateSaveBox();
394 
395     TTool::Application *app = TTool::getApplication();
396     if (app) {
397       app->getCurrentXsheet()->notifyXsheetChanged();
398       notifyImageChanged();
399     }
400   }
401 
getSize() const402   int getSize() const override {
403     return sizeof(*this) + TRasterUndo::getSize();
404   }
405 
getToolName()406   QString getToolName() override {
407     return QString("Fill Tool : %1")
408         .arg(QString::fromStdWString(m_params.m_fillType));
409   }
getHistoryType()410   int getHistoryType() override { return HistoryType::FillTool; }
411 };
412 
413 //=============================================================================
414 // RasterRectFillUndo
415 //-----------------------------------------------------------------------------
416 
417 class RasterRectFillUndo final : public TRasterUndo {
418   TRect m_fillArea;
419   int m_paintId;
420   std::wstring m_colorType;
421   TStroke *m_s;
422   bool m_onlyUnfilled;
423   TPalette *m_palette;
424 
425 public:
~RasterRectFillUndo()426   ~RasterRectFillUndo() {
427     if (m_s) delete m_s;
428   }
429 
RasterRectFillUndo(TTileSetCM32 * tileSet,TStroke * s,TRect fillArea,int paintId,TXshSimpleLevel * level,std::wstring colorType,bool onlyUnfilled,const TFrameId & fid,TPalette * palette)430   RasterRectFillUndo(TTileSetCM32 *tileSet, TStroke *s, TRect fillArea,
431                      int paintId, TXshSimpleLevel *level,
432                      std::wstring colorType, bool onlyUnfilled,
433                      const TFrameId &fid, TPalette *palette)
434       : TRasterUndo(tileSet, level, fid, false, false, 0)
435       , m_fillArea(fillArea)
436       , m_paintId(paintId)
437       , m_colorType(colorType)
438       , m_onlyUnfilled(onlyUnfilled)
439       , m_palette(palette) {
440     m_s = s ? new TStroke(*s) : 0;
441   }
442 
redo() const443   void redo() const override {
444     TToonzImageP image = getImage();
445     if (!image) return;
446     TRasterCM32P ras = image->getRaster();
447     AreaFiller filler(ras);
448     if (!m_s)
449       filler.rectFill(m_fillArea, m_paintId, m_onlyUnfilled,
450                       m_colorType != LINES, m_colorType != AREAS);
451     else
452       filler.strokeFill(m_s, m_paintId, m_onlyUnfilled, m_colorType != LINES,
453                         m_colorType != AREAS);
454 
455     if (m_palette) {
456       TRect rect   = m_fillArea;
457       TRect bounds = ras->getBounds();
458       if (bounds.overlaps(rect)) {
459         rect *= bounds;
460         const TTileSetCM32::Tile *tile =
461             m_tiles->getTile(m_tiles->getTileCount() - 1);
462         TRasterCM32P rbefore;
463         tile->getRaster(rbefore);
464         fillautoInks(ras, rect, rbefore, m_palette);
465       }
466     }
467     TTool::Application *app = TTool::getApplication();
468     if (app) {
469       app->getCurrentXsheet()->notifyXsheetChanged();
470       notifyImageChanged();
471     }
472   }
473 
getSize() const474   int getSize() const override {
475     int size =
476         m_s ? m_s->getControlPointCount() * sizeof(TThickPoint) + 100 : 0;
477     return sizeof(*this) + TRasterUndo::getSize() + size;
478   }
479 
getToolName()480   QString getToolName() override {
481     return QString("Fill Tool : %1").arg(QString::fromStdWString(m_colorType));
482   }
getHistoryType()483   int getHistoryType() override { return HistoryType::FillTool; }
484 };
485 
486 //=============================================================================
487 // RasterRectAutoFillUndo
488 //-----------------------------------------------------------------------------
489 
490 class RasterRectAutoFillUndo final : public TRasterUndo {
491   TRect m_rectToFill;
492   TFrameId m_fidToLearn;
493   bool m_onlyUnfilled;
494 
495 public:
~RasterRectAutoFillUndo()496   ~RasterRectAutoFillUndo() {}
497 
RasterRectAutoFillUndo(TTileSetCM32 * tileSet,const TRect & rectToFill,TXshSimpleLevel * level,bool onlyUnfilled,const TFrameId & currentFid,const TFrameId & fidToLearn)498   RasterRectAutoFillUndo(TTileSetCM32 *tileSet, const TRect &rectToFill,
499                          TXshSimpleLevel *level, bool onlyUnfilled,
500                          const TFrameId &currentFid, const TFrameId &fidToLearn)
501       : TRasterUndo(tileSet, level, currentFid, false, false, 0)
502       , m_rectToFill(rectToFill)
503       , m_onlyUnfilled(onlyUnfilled)
504       , m_fidToLearn(fidToLearn) {}
505 
redo() const506   void redo() const override {
507     TToonzImageP image        = getImage();
508     TToonzImageP imageToLearn = m_level->getFrame(m_fidToLearn, false);
509     if (!image || !imageToLearn) return;
510     rect_autofill_learn(imageToLearn, m_rectToFill.x0, m_rectToFill.y0,
511                         m_rectToFill.x1, m_rectToFill.y1);
512 
513     TTileSetCM32 tileSet(image->getRaster()->getSize());
514     bool recomputeBBox = rect_autofill_apply(
515         image, m_rectToFill.x0, m_rectToFill.y0, m_rectToFill.x1,
516         m_rectToFill.y1, m_onlyUnfilled, &tileSet);
517     if (recomputeBBox) ToolUtils::updateSaveBox();
518     TTool::Application *app = TTool::getApplication();
519     if (app) {
520       app->getCurrentXsheet()->notifyXsheetChanged();
521       notifyImageChanged();
522     }
523   }
524 
getSize() const525   int getSize() const override {
526     return sizeof(*this) + TRasterUndo::getSize();
527   }
528 };
529 
530 //=============================================================================
531 // RasterStrokeAutoFillUndo
532 //-----------------------------------------------------------------------------
533 
534 class RasterStrokeAutoFillUndo final : public TRasterUndo {
535   TTileSetCM32 *m_tileSet;
536 
537 public:
~RasterStrokeAutoFillUndo()538   ~RasterStrokeAutoFillUndo() {
539     if (m_tileSet) delete m_tileSet;
540   }
541 
RasterStrokeAutoFillUndo(TTileSetCM32 * tileSet,TXshSimpleLevel * level,const TFrameId & currentFid)542   RasterStrokeAutoFillUndo(TTileSetCM32 *tileSet, TXshSimpleLevel *level,
543                            const TFrameId &currentFid)
544       : TRasterUndo(tileSet, level, currentFid, false, false, 0)
545       , m_tileSet(0) {}
546 
setTileSet(TTileSetCM32 * tileSet)547   void setTileSet(TTileSetCM32 *tileSet) { m_tileSet = tileSet; }
548 
redo() const549   void redo() const override {
550     TToonzImageP image = getImage();
551     if (!image) return;
552 
553     ToonzImageUtils::paste(image, m_tileSet);
554     ToolUtils::updateSaveBox(m_level, m_frameId);
555 
556     TTool::Application *app = TTool::getApplication();
557     if (app) {
558       app->getCurrentXsheet()->notifyXsheetChanged();
559       notifyImageChanged();
560     }
561   }
562 
getSize() const563   int getSize() const override {
564     return sizeof(*this) + TRasterUndo::getSize() + m_tileSet->getMemorySize();
565   }
566 };
567 
568 //=============================================================================
569 // RasterRectAutoFillUndo
570 //-----------------------------------------------------------------------------
571 
572 class VectorAutoFillUndo final : public TToolUndo {
573   std::vector<TFilledRegionInf> *m_regionFillInformation;
574   TRectD m_selectionArea;
575   TStroke *m_selectingStroke;
576   bool m_unpaintedOnly;
577   TFrameId m_onionFid;
578   int m_row;
579   int m_column;
580 
581 public:
~VectorAutoFillUndo()582   ~VectorAutoFillUndo() {
583     if (m_regionFillInformation) delete m_regionFillInformation;
584     if (m_selectingStroke) delete m_selectingStroke;
585   }
586 
VectorAutoFillUndo(std::vector<TFilledRegionInf> * regionFillInformation,TRectD selectionArea,TStroke * selectingStroke,bool unpaintedOnly,TXshSimpleLevel * sl,const TFrameId & fid,const TFrameId & onionFid)587   VectorAutoFillUndo(std::vector<TFilledRegionInf> *regionFillInformation,
588                      TRectD selectionArea, TStroke *selectingStroke,
589                      bool unpaintedOnly, TXshSimpleLevel *sl,
590                      const TFrameId &fid, const TFrameId &onionFid)
591       : TToolUndo(sl, fid)
592       , m_regionFillInformation(regionFillInformation)
593       , m_selectionArea(selectionArea)
594       , m_unpaintedOnly(unpaintedOnly)
595       , m_onionFid(onionFid) {
596     m_selectingStroke = selectingStroke ? new TStroke(*selectingStroke) : 0;
597   }
598 
undo() const599   void undo() const override {
600     TTool::Application *app = TTool::getApplication();
601     if (!app) return;
602     TVectorImageP img = m_level->getFrame(m_frameId, true);
603     ;
604     assert(!!img);
605     if (!img) return;
606     if (m_regionFillInformation) {
607       for (UINT i = 0; i < m_regionFillInformation->size(); i++) {
608         TRegion *reg = img->getRegion((*m_regionFillInformation)[i].m_regionId);
609         if (reg) reg->setStyle((*m_regionFillInformation)[i].m_styleId);
610       }
611     }
612 
613     app->getCurrentXsheet()->notifyXsheetChanged();
614     notifyImageChanged();
615   }
616 
redo() const617   void redo() const override {
618     TTool::Application *app = TTool::getApplication();
619     if (!app) return;
620 
621     TVectorImageP img = m_level->getFrame(m_frameId, true);
622     assert(img);
623     if (!img) return;
624 
625     TVectorImageP onionImg = m_level->getFrame(m_onionFid, false);
626     if (!onionImg) return;
627 
628     if (m_selectingStroke) {
629       stroke_autofill_learn(onionImg, m_selectingStroke);
630       stroke_autofill_apply(img, m_selectingStroke, m_unpaintedOnly);
631     } else {
632       rect_autofill_learn(onionImg, m_selectionArea);
633       rect_autofill_apply(img, m_selectionArea, m_unpaintedOnly);
634     }
635 
636     app->getCurrentXsheet()->notifyXsheetChanged();
637     notifyImageChanged();
638   }
639 
getSize() const640   int getSize() const override {
641     int size = m_selectingStroke ? m_selectingStroke->getControlPointCount() *
642                                            sizeof(TThickPoint) +
643                                        100
644                                  : 0;
645     return sizeof(*this) +
646            m_regionFillInformation->capacity() *
647                sizeof(m_regionFillInformation) +
648            500 + size;
649   }
650 };
651 
652 //-----------------------------------------------------------------------------
653 
doRectAutofill(const TImageP & img,const TRectD selectingRect,bool onlyUnfilled,const OnionSkinMask & osMask,TXshSimpleLevel * sl,const TFrameId & currentFid)654 void doRectAutofill(const TImageP &img, const TRectD selectingRect,
655                     bool onlyUnfilled, const OnionSkinMask &osMask,
656                     TXshSimpleLevel *sl, const TFrameId &currentFid) {
657   TToonzImageP ti(img);
658   TVectorImageP vi(img);
659   if (!img || !sl) return;
660 
661   std::vector<int> rows;
662   osMask.getAll(sl->guessIndex(currentFid), rows);
663   if (rows.empty()) return;
664 
665   TFrameId onionFid;
666   int i;
667   for (i = 0; i < (int)rows.size(); i++) {
668     const TFrameId &app = sl->index2fid(rows[i]);
669     if (app > currentFid) break;
670     onionFid = app;
671   }
672   if (onionFid.isEmptyFrame()) onionFid = sl->index2fid(rows[0]);
673   if (onionFid.isEmptyFrame() || onionFid == currentFid || !sl->isFid(onionFid))
674     return;
675   if (ti) {
676     TRect rect = ToonzImageUtils::convertWorldToRaster(selectingRect, ti);
677 
678     TToonzImageP onionImg(sl->getFrame(onionFid, false));
679     if (!onionImg) return;
680     TRect workRect = rect * ti->getRaster()->getBounds();
681     if (workRect.isEmpty()) return;
682 
683     rect_autofill_learn(onionImg, workRect.x0, workRect.y0, workRect.x1,
684                         workRect.y1);
685     TTileSetCM32 *tileSet = new TTileSetCM32(ti->getRaster()->getSize());
686     bool recomputeBBox =
687         rect_autofill_apply(ti, workRect.x0, workRect.y0, workRect.x1,
688                             workRect.y1, onlyUnfilled, tileSet);
689     if (recomputeBBox) ToolUtils::updateSaveBox();
690     if (tileSet->getTileCount() > 0)
691       TUndoManager::manager()->add(new RasterRectAutoFillUndo(
692           tileSet, workRect, sl, onlyUnfilled, currentFid, onionFid));
693   } else if (vi) {
694     TVectorImageP onionImg(sl->getFrame(onionFid, false));
695     if (!onionImg) return;
696     std::vector<TFilledRegionInf> *regionFillInformation =
697         new std::vector<TFilledRegionInf>;
698     ImageUtils::getFillingInformationInArea(vi, *regionFillInformation,
699                                             selectingRect);
700     onionImg->findRegions();
701     vi->findRegions();
702     rect_autofill_learn(onionImg, selectingRect);
703     bool hasFilled = rect_autofill_apply(vi, selectingRect, onlyUnfilled);
704     if (hasFilled)
705       TUndoManager::manager()->add(
706           new VectorAutoFillUndo(regionFillInformation, selectingRect, 0,
707                                  onlyUnfilled, sl, currentFid, onionFid));
708   }
709 }
710 
711 //-----------------------------------------------------------------------------
712 
doStrokeAutofill(const TImageP & img,TStroke * selectingStroke,bool onlyUnfilled,const OnionSkinMask & osMask,TXshSimpleLevel * sl,const TFrameId & currentFid)713 void doStrokeAutofill(const TImageP &img, TStroke *selectingStroke,
714                       bool onlyUnfilled, const OnionSkinMask &osMask,
715                       TXshSimpleLevel *sl, const TFrameId &currentFid) {
716   TToonzImageP ti(img);
717   TVectorImageP vi(img);
718   if (!img || !sl) return;
719 
720   std::vector<int> rows;
721   osMask.getAll(sl->guessIndex(currentFid), rows);
722   if (rows.empty()) return;
723 
724   TFrameId onionFid;
725   int i;
726   for (i = 0; i < (int)rows.size(); i++) {
727     const TFrameId &app = sl->index2fid(rows[i]);
728     if (app > currentFid) break;
729     onionFid = app;
730   }
731   if (onionFid.isEmptyFrame()) onionFid = sl->index2fid(rows[0]);
732   if (onionFid.isEmptyFrame() || onionFid == currentFid || !sl->isFid(onionFid))
733     return;
734   if (ti) {
735     TToonzImageP onionImg(sl->getFrame(onionFid, false));
736     if (!onionImg) return;
737 
738     TRasterCM32P ras = onionImg->getRaster();
739     TPointD center   = ras->getCenterD();
740 
741     TPoint pos;
742     TRaster32P image =
743         convertStrokeToImage(selectingStroke, ras->getBounds(), pos);
744     TRect bbox = (image->getBounds() + pos).enlarge(2);
745     pos        = bbox.getP00();
746 
747     TRasterCM32P onionAppRas = ras->extract(bbox)->clone();
748     TRasterCM32P tiAppRas    = ti->getRaster()->extract(bbox)->clone();
749 
750     TRect workRect = onionAppRas->getBounds().enlarge(-1);
751     TToonzImageP onionApp(onionAppRas, workRect);
752     TToonzImageP tiApp(tiAppRas, workRect);
753 
754     ToonzImageUtils::eraseImage(onionApp, image, TPoint(2, 2), true, true, true,
755                                 false, 1);
756     ToonzImageUtils::eraseImage(tiApp, image, TPoint(2, 2), true, true, true,
757                                 false, 1);
758 
759     rect_autofill_learn(onionApp, workRect.x0, workRect.y0, workRect.x1,
760                         workRect.y1);
761     TTileSetCM32 *tileSet = new TTileSetCM32(ti->getRaster()->getSize());
762     bool recomputeBBox =
763         rect_autofill_apply(tiApp, workRect.x0, workRect.y0, workRect.x1,
764                             workRect.y1, onlyUnfilled, tileSet);
765 
766     delete tileSet;
767     tileSet = new TTileSetCM32(ti->getRaster()->getSize());
768     tileSet->add(ti->getRaster(), bbox);
769     RasterStrokeAutoFillUndo *undo =
770         new RasterStrokeAutoFillUndo(tileSet, sl, currentFid);
771     TRop::over(ti->getRaster(), tiAppRas, pos);
772     TTileSetCM32 *newTileSet = new TTileSetCM32(ti->getRaster()->getSize());
773     newTileSet->add(ti->getRaster(), bbox);
774     undo->setTileSet(newTileSet);
775     TUndoManager::manager()->add(undo);
776 
777   } else if (vi) {
778     TVectorImageP onionImg(sl->getFrame(onionFid, false));
779     if (!onionImg) return;
780     std::vector<TFilledRegionInf> *regionFillInformation =
781         new std::vector<TFilledRegionInf>;
782     ImageUtils::getFillingInformationInArea(vi, *regionFillInformation,
783                                             selectingStroke->getBBox());
784     onionImg->findRegions();
785     vi->findRegions();
786     stroke_autofill_learn(onionImg, selectingStroke);
787     bool hasFilled = stroke_autofill_apply(vi, selectingStroke, onlyUnfilled);
788     if (hasFilled)
789       TUndoManager::manager()->add(new VectorAutoFillUndo(
790           regionFillInformation, TRectD(), selectingStroke, onlyUnfilled, sl,
791           currentFid, onionFid));
792   }
793 }
794 
795 //=============================================================================
796 // fillRectWithUndo
797 //-----------------------------------------------------------------------------
798 
hasAutoInks(const TPalette * plt)799 bool inline hasAutoInks(const TPalette *plt) {
800   for (int i = 0; i < plt->getStyleCount(); i++)
801     if (plt->getStyle(i)->getFlags() != 0) return true;
802   return false;
803 }
804 
805 //-----------------------------------------------------------------------------
806 
fillAreaWithUndo(const TImageP & img,const TRectD & area,TStroke * stroke,bool onlyUnfilled,std::wstring colorType,TXshSimpleLevel * sl,const TFrameId & fid,int cs,bool autopaintLines)807 void fillAreaWithUndo(const TImageP &img, const TRectD &area, TStroke *stroke,
808                       bool onlyUnfilled, std::wstring colorType,
809                       TXshSimpleLevel *sl, const TFrameId &fid, int cs,
810                       bool autopaintLines) {
811   TRectD selArea = stroke ? stroke->getBBox() : area;
812 
813   if (TToonzImageP ti = img) {
814     // allargo di 1 la savebox, perche cosi' il rectfill di tutta l'immagine fa
815     // una sola fillata
816     TRect enlargedSavebox =
817         ti->getSavebox().enlarge(1) * TRect(TPoint(0, 0), ti->getSize());
818     TRect rasterFillArea =
819         ToonzImageUtils::convertWorldToRaster(selArea, ti) * enlargedSavebox;
820     if (rasterFillArea.isEmpty()) return;
821 
822     TRasterCM32P ras = ti->getRaster();
823     /*-- tileSetでFill範囲のRectをUndoに格納しておく --*/
824     TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize());
825     tileSet->add(ras, rasterFillArea);
826     AreaFiller filler(ti->getRaster());
827     if (!stroke) {
828       bool ret = filler.rectFill(rasterFillArea, cs, onlyUnfilled,
829                                  colorType != LINES, colorType != AREAS);
830       if (!ret) {
831         delete tileSet;
832         return;
833       }
834     } else
835       filler.strokeFill(stroke, cs, onlyUnfilled, colorType != LINES,
836                         colorType != AREAS);
837 
838     TPalette *plt = ti->getPalette();
839 
840     // !autopaintLines will temporary disable autopaint line feature
841     if ((plt && !hasAutoInks(plt)) || !autopaintLines) plt = 0;
842 
843     if (plt) {
844       TRect rect   = rasterFillArea;
845       TRect bounds = ras->getBounds();
846       if (bounds.overlaps(rect)) {
847         rect *= bounds;
848         const TTileSetCM32::Tile *tile =
849             tileSet->getTile(tileSet->getTileCount() - 1);
850         TRasterCM32P rbefore;
851         tile->getRaster(rbefore);
852         fillautoInks(ras, rect, rbefore, plt);
853       }
854     }
855     ToolUtils::updateSaveBox(sl, fid);
856 
857     TUndoManager::manager()->add(
858         new RasterRectFillUndo(tileSet, stroke, rasterFillArea, cs, sl,
859                                colorType, onlyUnfilled, fid, plt));
860   } else if (TVectorImageP vi = img) {
861     TPalette *palette = vi->getPalette();
862     assert(palette);
863     const TColorStyle *style = palette->getStyle(cs);
864     // if( !style->isRegionStyle() )
865     // return;
866 
867     vi->findRegions();
868 
869     std::vector<TFilledRegionInf> *regionFillInformation    = 0;
870     std::vector<std::pair<int, int>> *strokeFillInformation = 0;
871     if (colorType != LINES) {
872       regionFillInformation = new std::vector<TFilledRegionInf>;
873       ImageUtils::getFillingInformationInArea(vi, *regionFillInformation,
874                                               selArea);
875     }
876     if (colorType != AREAS) {
877       strokeFillInformation = new std::vector<std::pair<int, int>>;
878       ImageUtils::getStrokeStyleInformationInArea(vi, *strokeFillInformation,
879                                                   selArea);
880     }
881 
882     VectorRectFillUndo *fUndo =
883         new VectorRectFillUndo(regionFillInformation, strokeFillInformation,
884                                selArea, stroke, cs, onlyUnfilled, sl, fid);
885 
886     QMutexLocker lock(vi->getMutex());
887     if (vi->selectFill(area, stroke, cs, onlyUnfilled, colorType != LINES,
888                        colorType != AREAS))
889       TUndoManager::manager()->add(fUndo);
890     else
891       delete fUndo;
892   }
893 }
894 
895 //=============================================================================
896 // doFill
897 //-----------------------------------------------------------------------------
898 
doFill(const TImageP & img,const TPointD & pos,FillParameters & params,bool isShiftFill,TXshSimpleLevel * sl,const TFrameId & fid,bool autopaintLines)899 void doFill(const TImageP &img, const TPointD &pos, FillParameters &params,
900             bool isShiftFill, TXshSimpleLevel *sl, const TFrameId &fid,
901             bool autopaintLines) {
902   TTool::Application *app = TTool::getApplication();
903   if (!app) return;
904 
905   if (TToonzImageP ti = TToonzImageP(img)) {
906     TPoint offs(0, 0);
907     TRasterCM32P ras = ti->getRaster();
908 
909     if (Preferences::instance()->getFillOnlySavebox()) {
910       TRectD bbox = ti->getBBox();
911       TRect ibbox = convert(bbox);
912       offs        = ibbox.getP00();
913       ras         = ti->getRaster()->extract(ibbox);
914     }
915 
916     bool recomputeSavebox = false;
917     TPalette *plt         = ti->getPalette();
918 
919     if (!ras.getPointer() || ras->isEmpty()) return;
920 
921     ras->lock();
922 
923     TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize());
924     TTileSaverCM32 tileSaver(ras, tileSet);
925     TDimension imageSize = ti->getSize();
926     TPointD p(imageSize.lx % 2 ? 0.0 : 0.5, imageSize.ly % 2 ? 0.0 : 0.5);
927 
928     /*-- params.m_p = convert(pos-p)では、マイナス座標でずれが生じる --*/
929     TPointD tmp_p = pos - p;
930     params.m_p = TPoint((int)floor(tmp_p.x + 0.5), (int)floor(tmp_p.y + 0.5));
931 
932     params.m_p += ti->getRaster()->getCenter();
933     params.m_p -= offs;
934     params.m_shiftFill = isShiftFill;
935 
936     TRect rasRect(ras->getSize());
937     if (!rasRect.contains(params.m_p)) {
938       ras->unlock();
939       return;
940     }
941 
942     // !autoPaintLines will temporary disable autopaint line feature
943     if (plt && hasAutoInks(plt) && autopaintLines) params.m_palette = plt;
944 
945     if (params.m_fillType == ALL || params.m_fillType == AREAS) {
946       if (isShiftFill) {
947         FillParameters aux(params);
948         aux.m_styleId    = (params.m_styleId == 0) ? 1 : 0;
949         recomputeSavebox = fill(ras, aux, &tileSaver);
950       }
951       recomputeSavebox = fill(ras, params, &tileSaver);
952     }
953     if (params.m_fillType == ALL || params.m_fillType == LINES) {
954       if (params.m_segment)
955         inkSegment(ras, params.m_p, params.m_styleId, 2.51, true, &tileSaver);
956       else if (!params.m_segment)
957         inkFill(ras, params.m_p, params.m_styleId, 2, &tileSaver);
958     }
959 
960     if (tileSaver.getTileSet()->getTileCount() != 0) {
961       static int count = 0;
962       TSystem::outputDebug("FILL" + std::to_string(count++) + "\n");
963       if (offs != TPoint())
964         for (int i = 0; i < tileSet->getTileCount(); i++) {
965           TTileSet::Tile *t = tileSet->editTile(i);
966           t->m_rasterBounds = t->m_rasterBounds + offs;
967         }
968       TUndoManager::manager()->add(
969           new RasterFillUndo(tileSet, params, sl, fid,
970                              Preferences::instance()->getFillOnlySavebox()));
971     }
972 
973     // al posto di updateFrame:
974 
975     TXshLevel *xl = app->getCurrentLevel()->getLevel();
976     if (!xl) return;
977 
978     TXshSimpleLevel *sl = xl->getSimpleLevel();
979     sl->getProperties()->setDirtyFlag(true);
980     if (recomputeSavebox &&
981         Preferences::instance()->isMinimizeSaveboxAfterEditing())
982       ToolUtils::updateSaveBox(sl, fid);
983 
984     ras->unlock();
985   } else if (TVectorImageP vi = TImageP(img)) {
986     int oldStyleId;
987     QMutexLocker lock(vi->getMutex());
988     /*if(params.m_fillType==ALL || params.m_fillType==AREAS)
989 vi->computeRegion(pos, params.m_styleId);*/
990 
991     if ((oldStyleId = vectorFill(vi, params.m_fillType, pos, params.m_styleId,
992                                  params.m_emptyOnly)) != -1)
993       TUndoManager::manager()->add(new VectorFillUndo(
994           params.m_styleId, oldStyleId, params.m_fillType, pos, sl, fid));
995   }
996 
997   TTool *t = app->getCurrentTool()->getTool();
998   if (t) t->notifyImageChanged();
999 }
1000 
1001 //=============================================================================
1002 // SequencePainter
1003 // da spostare in toolutils?
1004 //-----------------------------------------------------------------------------
1005 
1006 class SequencePainter {
1007 public:
1008   virtual void process(TImageP img /*, TImageLocation &imgloc*/, double t,
1009                        TXshSimpleLevel *sl, const TFrameId &fid) = 0;
1010   void processSequence(TXshSimpleLevel *sl, TFrameId firstFid,
1011                        TFrameId lastFid);
~SequencePainter()1012   virtual ~SequencePainter() {}
1013 };
1014 
1015 //-----------------------------------------------------------------------------
1016 
processSequence(TXshSimpleLevel * sl,TFrameId firstFid,TFrameId lastFid)1017 void SequencePainter::processSequence(TXshSimpleLevel *sl, TFrameId firstFid,
1018                                       TFrameId lastFid) {
1019   if (!sl) return;
1020 
1021   bool backward = false;
1022   if (firstFid > lastFid) {
1023     std::swap(firstFid, lastFid);
1024     backward = true;
1025   }
1026   assert(firstFid <= lastFid);
1027   std::vector<TFrameId> allFids;
1028   sl->getFids(allFids);
1029 
1030   std::vector<TFrameId>::iterator i0 = allFids.begin();
1031   while (i0 != allFids.end() && *i0 < firstFid) i0++;
1032   if (i0 == allFids.end()) return;
1033   std::vector<TFrameId>::iterator i1 = i0;
1034   while (i1 != allFids.end() && *i1 <= lastFid) i1++;
1035   assert(i0 < i1);
1036   std::vector<TFrameId> fids(i0, i1);
1037   int m = fids.size();
1038   assert(m > 0);
1039 
1040   TUndoManager::manager()->beginBlock();
1041   for (int i = 0; i < m; ++i) {
1042     TFrameId fid = fids[i];
1043     assert(firstFid <= fid && fid <= lastFid);
1044     TImageP img = sl->getFrame(fid, true);
1045     double t    = m > 1 ? (double)i / (double)(m - 1) : 0.5;
1046     process(img, backward ? 1 - t : t, sl, fid);
1047     // Setto il fid come corrente per notificare il cambiamento dell'immagine
1048     TTool::Application *app = TTool::getApplication();
1049     if (app) {
1050       if (app->getCurrentFrame()->isEditingScene())
1051         app->getCurrentFrame()->setFrame(fid.getNumber());
1052       else
1053         app->getCurrentFrame()->setFid(fid);
1054       TTool *tool = app->getCurrentTool()->getTool();
1055       if (tool) tool->notifyImageChanged(fid);
1056     }
1057   }
1058   TUndoManager::manager()->endBlock();
1059 }
1060 
1061 //=============================================================================
1062 // MultiAreaFiller : SequencePainter
1063 //-----------------------------------------------------------------------------
1064 
1065 class MultiAreaFiller final : public SequencePainter {
1066   TRectD m_firstRect, m_lastRect;
1067   bool m_unfilledOnly;
1068   std::wstring m_colorType;
1069   TVectorImageP m_firstImage, m_lastImage;
1070   int m_styleIndex;
1071   bool m_autopaintLines;
1072 
1073 public:
MultiAreaFiller(const TRectD & firstRect,const TRectD & lastRect,bool unfilledOnly,std::wstring colorType,int styleIndex,bool autopaintLines)1074   MultiAreaFiller(const TRectD &firstRect, const TRectD &lastRect,
1075                   bool unfilledOnly, std::wstring colorType, int styleIndex,
1076                   bool autopaintLines)
1077       : m_firstRect(firstRect)
1078       , m_lastRect(lastRect)
1079       , m_unfilledOnly(unfilledOnly)
1080       , m_colorType(colorType)
1081       , m_firstImage()
1082       , m_lastImage()
1083       , m_styleIndex(styleIndex)
1084       , m_autopaintLines(autopaintLines) {}
1085 
~MultiAreaFiller()1086   ~MultiAreaFiller() {
1087     if (m_firstImage) {
1088       m_firstImage->removeStroke(0);
1089       m_lastImage->removeStroke(0);
1090     }
1091   }
1092 
MultiAreaFiller(TStroke * & firstStroke,TStroke * & lastStroke,bool unfilledOnly,std::wstring colorType,int styleIndex,bool autopaintLines)1093   MultiAreaFiller(TStroke *&firstStroke, TStroke *&lastStroke,
1094                   bool unfilledOnly, std::wstring colorType, int styleIndex,
1095                   bool autopaintLines)
1096       : m_firstRect()
1097       , m_lastRect()
1098       , m_unfilledOnly(unfilledOnly)
1099       , m_colorType(colorType)
1100       , m_styleIndex(styleIndex)
1101       , m_autopaintLines(autopaintLines) {
1102     m_firstImage = new TVectorImage();
1103     m_lastImage  = new TVectorImage();
1104     m_firstImage->addStroke(firstStroke);
1105     m_lastImage->addStroke(lastStroke);
1106   }
1107 
process(TImageP img,double t,TXshSimpleLevel * sl,const TFrameId & fid)1108   void process(TImageP img, double t, TXshSimpleLevel *sl,
1109                const TFrameId &fid) override {
1110     if (!m_firstImage) {
1111       TPointD p0 = m_firstRect.getP00() * (1 - t) + m_lastRect.getP00() * t;
1112       TPointD p1 = m_firstRect.getP11() * (1 - t) + m_lastRect.getP11() * t;
1113       TRectD rect(p0.x, p0.y, p1.x, p1.y);
1114       fillAreaWithUndo(img, rect, 0, m_unfilledOnly, m_colorType, sl, fid,
1115                        m_styleIndex, m_autopaintLines);
1116     } else {
1117       if (t == 0)
1118         fillAreaWithUndo(img, TRectD(), m_firstImage->getStroke(0),
1119                          m_unfilledOnly, m_colorType, sl, fid, m_styleIndex,
1120                          m_autopaintLines);
1121       else if (t == 1)
1122         fillAreaWithUndo(img, TRectD(), m_lastImage->getStroke(0),
1123                          m_unfilledOnly, m_colorType, sl, fid, m_styleIndex,
1124                          m_autopaintLines);
1125       else
1126       // if(t>1)
1127       {
1128         assert(t > 0 && t < 1);
1129         assert(m_firstImage->getStrokeCount() == 1);
1130         assert(m_lastImage->getStrokeCount() == 1);
1131 
1132         TVectorImageP vi = TInbetween(m_firstImage, m_lastImage).tween(t);
1133 
1134         assert(vi->getStrokeCount() == 1);
1135 
1136         fillAreaWithUndo(img, TRectD(), vi->getStroke(0) /*, imgloc*/,
1137                          m_unfilledOnly, m_colorType, sl, fid, m_styleIndex,
1138                          m_autopaintLines);
1139       }
1140     }
1141   }
1142 };
1143 
1144 //=============================================================================
1145 // MultiFiller : SequencePainter
1146 //-----------------------------------------------------------------------------
1147 
1148 class MultiFiller final : public SequencePainter {
1149   TPointD m_firstPoint, m_lastPoint;
1150   FillParameters m_params;
1151   bool m_autopaintLines;
1152 
1153 public:
MultiFiller(const TPointD & firstPoint,const TPointD & lastPoint,const FillParameters & params,bool autopaintLines)1154   MultiFiller(const TPointD &firstPoint, const TPointD &lastPoint,
1155               const FillParameters &params, bool autopaintLines)
1156       : m_firstPoint(firstPoint)
1157       , m_lastPoint(lastPoint)
1158       , m_params(params)
1159       , m_autopaintLines(autopaintLines) {}
process(TImageP img,double t,TXshSimpleLevel * sl,const TFrameId & fid)1160   void process(TImageP img, double t, TXshSimpleLevel *sl,
1161                const TFrameId &fid) override {
1162     TPointD p = m_firstPoint * (1 - t) + m_lastPoint * t;
1163     doFill(img, p, m_params, false, sl, fid, m_autopaintLines);
1164   }
1165 };
1166 
1167 //=============================================================================
1168 /*
1169                                         if(e.isShiftPressed())
1170             {
1171                                                 m_firstPoint = pos;
1172                                                 m_firstFrameId =
1173 TApplication::instance()->getCurrentFrameId();
1174                                                 }
1175                                         else
1176                                           {
1177                                                 m_firstClick = false;
1178                                                 TApplication::instance()->setCurrentFrame(m_veryFirstFrameId);
1179                                                 }
1180                                         TNotifier::instance()->notify(TLevelChange());
1181           TNotifier::instance()->notify(TStageChange());
1182           }
1183 }
1184 */
1185 
1186 //=============================================================================
1187 // AreaFillTool
1188 //-----------------------------------------------------------------------------
1189 
drawPolyline(const std::vector<TPointD> & points)1190 void drawPolyline(const std::vector<TPointD> &points) {
1191   if (points.empty()) return;
1192 
1193   tglDrawCircle(points[0], 2);
1194 
1195   for (UINT i = 0; i < points.size() - 1; i++)
1196     tglDrawSegment(points[i], points[i + 1]);
1197 }
1198 
1199 //-----------------------------------------------------------------------------
1200 
AreaFillTool(TTool * parent)1201 AreaFillTool::AreaFillTool(TTool *parent)
1202     : m_frameRange(false)
1203     , m_onlyUnfilled(false)
1204     , m_selecting(false)
1205     , m_selectingRect(TRectD())
1206     , m_firstRect(TRectD())
1207     , m_firstFrameSelected(false)
1208     , m_level(0)
1209     , m_parent(parent)
1210     , m_colorType(AREAS)
1211     , m_currCell(-1, -1)
1212     , m_type(RECT)
1213     , m_isPath(false)
1214     , m_enabled(false)
1215     , m_active(false)
1216     , m_firstStroke(0)
1217     , m_thick(0.5)
1218     , m_mousePosition()
1219     , m_onion(false)
1220     , m_isLeftButtonPressed(false)
1221     , m_autopaintLines(true) {}
1222 
draw()1223 void AreaFillTool::draw() {
1224   m_thick = m_parent->getPixelSize() / 2.0;
1225 
1226   TPixel color = TPixel32::Red;
1227   if (m_type == RECT) {
1228     if (m_frameRange && m_firstFrameSelected)
1229       drawRect(m_firstRect, color, 0x3F33, true);
1230     if (m_selecting || (m_frameRange && !m_firstFrameSelected))
1231       drawRect(m_selectingRect, color, 0xFFFF, true);
1232   } else if ((m_type == FREEHAND || m_type == POLYLINE) && m_frameRange) {
1233     tglColor(color);
1234     if (m_firstStroke) drawStrokeCenterline(*m_firstStroke, 1);
1235   }
1236 
1237   if (m_type == POLYLINE && !m_polyline.empty()) {
1238     glPushMatrix();
1239     tglColor(TPixel::Red);
1240     tglDrawCircle(m_polyline[0], 2);
1241     glBegin(GL_LINE_STRIP);
1242     for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]);
1243     tglVertex(m_mousePosition);
1244     glEnd();
1245     glPopMatrix();
1246   } else if (m_type == FREEHAND && !m_track.isEmpty()) {
1247     tglColor(TPixel::Red);
1248     glPushMatrix();
1249     m_track.drawAllFragments();
1250     glPopMatrix();
1251   }
1252 }
1253 
resetMulti()1254 void AreaFillTool::resetMulti() {
1255   m_firstFrameSelected = false;
1256   m_firstRect.empty();
1257   m_selectingRect.empty();
1258   TTool::Application *app = TTool::getApplication();
1259   TXshLevel *xl           = app->getCurrentLevel()->getLevel();
1260   m_level                 = xl ? xl->getSimpleLevel() : 0;
1261   m_firstFrameId = m_veryFirstFrameId = m_parent->getCurrentFid();
1262   if (m_firstStroke) {
1263     delete m_firstStroke;
1264     m_firstStroke = 0;
1265   }
1266 }
1267 
leftButtonDown(const TPointD & pos,const TMouseEvent &,TImage * img)1268 void AreaFillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &,
1269                                   TImage *img) {
1270   TVectorImageP vi = TImageP(img);
1271   TToonzImageP ti  = TToonzImageP(img);
1272 
1273   if (!vi && !ti) {
1274     m_selecting = false;
1275     return;
1276   }
1277 
1278   m_selecting = true;
1279   if (m_type == RECT) {
1280     m_selectingRect.x0 = pos.x;
1281     m_selectingRect.y0 = pos.y;
1282     m_selectingRect.x1 = pos.x + 1;
1283     m_selectingRect.y1 = pos.y + 1;
1284   } else if (m_type == FREEHAND || m_type == POLYLINE) {
1285     int col  = TTool::getApplication()->getCurrentColumn()->getColumnIndex();
1286     m_isPath = TTool::getApplication()
1287                    ->getCurrentObject()
1288                    ->isSpline();  // getApplication()->isEditingSpline();
1289     m_enabled = col >= 0 || m_isPath;
1290 
1291     if (!m_enabled) return;
1292     m_active = true;
1293 
1294     m_track.clear();
1295     m_firstPos        = pos;
1296     double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize();
1297     m_track.add(TThickPoint(pos, m_thick), pixelSize2);
1298     if (m_type == POLYLINE) {
1299       if (m_polyline.empty() || m_polyline.back() != pos)
1300         m_polyline.push_back(pos);
1301       m_mousePosition = pos;
1302     } else
1303       m_track.add(TThickPoint(pos, m_thick), pixelSize2);
1304 
1305     if (m_type == POLYLINE) {
1306       if (m_polyline.empty() || m_polyline.back() != pos)
1307         m_polyline.push_back(pos);
1308     }
1309   }
1310   m_isLeftButtonPressed = true;
1311 }
1312 
1313 /*-- PolyLineFillを閉じる時に呼ばれる --*/
leftButtonDoubleClick(const TPointD & pos,const TMouseEvent & e)1314 void AreaFillTool::leftButtonDoubleClick(const TPointD &pos,
1315                                          const TMouseEvent &e) {
1316   TStroke *stroke;
1317 
1318   TTool::Application *app = TTool::getApplication();
1319   if (!app) return;
1320 
1321   if (m_polyline.size() <= 1) {
1322     resetMulti();
1323     m_isLeftButtonPressed = false;
1324     return;
1325   }
1326 
1327   if (m_polyline.back() != pos) m_polyline.push_back(pos);
1328   if (m_polyline.back() != m_polyline.front())
1329     m_polyline.push_back(m_polyline.front());
1330   std::vector<TThickPoint> strokePoints;
1331   for (UINT i = 0; i < m_polyline.size() - 1; i++) {
1332     strokePoints.push_back(TThickPoint(m_polyline[i], 1));
1333     strokePoints.push_back(
1334         TThickPoint(0.5 * (m_polyline[i] + m_polyline[i + 1]), 1));
1335   }
1336   strokePoints.push_back(TThickPoint(m_polyline.back(), 1));
1337   m_polyline.clear();
1338   stroke = new TStroke(strokePoints);
1339   assert(stroke->getPoint(0) == stroke->getPoint(1));
1340 
1341   //    if (m_type==POLYLINE)
1342   //      m_polyline.push_back(pos);
1343   // drawPolyline(m_polyline);
1344   int styleIndex = app->getCurrentLevelStyleIndex();
1345   if (m_frameRange)  // stroke multi
1346   {
1347     if (m_firstFrameSelected) {
1348       MultiAreaFiller filler(m_firstStroke, stroke, m_onlyUnfilled, m_colorType,
1349                              styleIndex, m_autopaintLines);
1350       filler.processSequence(m_level.getPointer(), m_firstFrameId,
1351                              m_parent->getCurrentFid());
1352       m_parent->invalidate(m_selectingRect.enlarge(2));
1353       if (e.isShiftPressed()) {
1354         m_firstStroke  = stroke;
1355         m_firstFrameId = m_parent->getCurrentFid();
1356       } else {
1357         if (app->getCurrentFrame()->isEditingScene()) {
1358           app->getCurrentColumn()->setColumnIndex(m_currCell.first);
1359           app->getCurrentFrame()->setFrame(m_currCell.second);
1360         } else
1361           app->getCurrentFrame()->setFid(m_veryFirstFrameId);
1362         resetMulti();
1363       }
1364     } else  // primo frame
1365     {
1366       m_firstStroke = stroke;
1367       // if (app->getCurrentFrame()->isEditingScene())
1368       m_currCell =
1369           std::pair<int, int>(app->getCurrentColumn()->getColumnIndex(),
1370                               app->getCurrentFrame()->getFrame());
1371     }
1372   } else {
1373     if (m_onion) {
1374       OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask();
1375       doStrokeAutofill(m_parent->getImage(true), stroke, m_onlyUnfilled, osMask,
1376                        m_level.getPointer(), m_parent->getCurrentFid());
1377     } else
1378       fillAreaWithUndo(m_parent->getImage(true), TRectD(), stroke,
1379                        m_onlyUnfilled, m_colorType, m_level.getPointer(),
1380                        m_parent->getCurrentFid(), styleIndex, m_autopaintLines);
1381     TTool *t = app->getCurrentTool()->getTool();
1382     if (t) t->notifyImageChanged();
1383   }
1384 }
1385 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)1386 void AreaFillTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
1387   if (!m_isLeftButtonPressed) return;
1388   if (m_type == RECT) {
1389     m_selectingRect.x1 = pos.x;
1390     m_selectingRect.y1 = pos.y;
1391     m_parent->invalidate();
1392   } else if (m_type == FREEHAND) {
1393     if (!m_enabled || !m_active) return;
1394 
1395     double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize();
1396     m_track.add(TThickPoint(pos, m_thick), pixelSize2);
1397     m_parent->invalidate();
1398   }
1399 }
1400 
mouseMove(const TPointD & pos,const TMouseEvent & e)1401 void AreaFillTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
1402   if (m_type != POLYLINE || m_polyline.empty()) return;
1403   if (!m_enabled || !m_active) return;
1404   m_mousePosition = pos;
1405   m_parent->invalidate();
1406 }
1407 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)1408 void AreaFillTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) {
1409   if (!m_isLeftButtonPressed) return;
1410   m_isLeftButtonPressed = false;
1411 
1412   TTool::Application *app = TTool::getApplication();
1413   if (!app) return;
1414 
1415   TXshLevel *xl = app->getCurrentLevel()->getLevel();
1416   m_level       = xl ? xl->getSimpleLevel() : 0;
1417 
1418   int styleIndex = app->getCurrentLevelStyleIndex();
1419   m_selecting    = false;
1420   if (m_type == RECT) {
1421     if (m_selectingRect.x0 > m_selectingRect.x1)
1422       std::swap(m_selectingRect.x0, m_selectingRect.x1);
1423     if (m_selectingRect.y0 > m_selectingRect.y1)
1424       std::swap(m_selectingRect.y0, m_selectingRect.y1);
1425 
1426     if (m_frameRange) {
1427       if (m_firstFrameSelected) {
1428         MultiAreaFiller filler(m_firstRect, m_selectingRect, m_onlyUnfilled,
1429                                m_colorType, styleIndex, m_autopaintLines);
1430         filler.processSequence(m_level.getPointer(), m_firstFrameId,
1431                                m_parent->getCurrentFid());
1432         m_parent->invalidate(m_selectingRect.enlarge(2));
1433         if (e.isShiftPressed()) {
1434           m_firstRect    = m_selectingRect;
1435           m_firstFrameId = m_parent->getCurrentFid();
1436         } else {
1437           if (app->getCurrentFrame()->isEditingScene()) {
1438             app->getCurrentColumn()->setColumnIndex(m_currCell.first);
1439             app->getCurrentFrame()->setFrame(m_currCell.second);
1440           } else
1441             app->getCurrentFrame()->setFid(m_veryFirstFrameId);
1442           resetMulti();
1443         }
1444       } else {
1445         // if (app->getCurrentFrame()->isEditingScene())
1446         m_currCell =
1447             std::pair<int, int>(app->getCurrentColumn()->getColumnIndex(),
1448                                 app->getCurrentFrame()->getFrame());
1449       }
1450     } else {
1451       if (m_onion) {
1452         OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask();
1453         doRectAutofill(m_parent->getImage(true), m_selectingRect,
1454                        m_onlyUnfilled, osMask, m_level.getPointer(),
1455                        m_parent->getCurrentFid());
1456       } else
1457         fillAreaWithUndo(m_parent->getImage(true), m_selectingRect, 0,
1458                          m_onlyUnfilled, m_colorType, m_level.getPointer(),
1459                          m_parent->getCurrentFid(), styleIndex,
1460                          m_autopaintLines);
1461       m_parent->invalidate();
1462       m_selectingRect.empty();
1463       TTool *t = app->getCurrentTool()->getTool();
1464       if (t) t->notifyImageChanged();
1465     }
1466   } else if (m_type == FREEHAND) {
1467 #if defined(MACOSX)
1468 // m_parent->m_viewer->enableRedraw(true);
1469 #endif
1470 
1471     bool isValid = m_enabled && m_active;
1472     m_enabled = m_active = false;
1473     if (!isValid || m_track.isEmpty()) return;
1474     double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize();
1475     m_track.add(TThickPoint(m_firstPos, m_thick), pixelSize2);
1476     m_track.filterPoints();
1477     double error    = (m_isPath ? 20.0 : 30.0 / 11) * sqrt(pixelSize2);
1478     TStroke *stroke = m_track.makeStroke(error);
1479 
1480     stroke->setStyle(1);
1481     m_track.clear();
1482 
1483     if (m_frameRange)  // stroke multi
1484     {
1485       if (m_firstFrameSelected) {
1486         MultiAreaFiller filler(m_firstStroke, stroke, m_onlyUnfilled,
1487                                m_colorType, styleIndex, m_autopaintLines);
1488         filler.processSequence(m_level.getPointer(), m_firstFrameId,
1489                                m_parent->getCurrentFid());
1490         m_parent->invalidate(m_selectingRect.enlarge(2));
1491         if (e.isShiftPressed()) {
1492           m_firstStroke  = stroke;
1493           m_firstFrameId = m_parent->getCurrentFid();
1494         } else {
1495           if (app->getCurrentFrame()->isEditingScene()) {
1496             app->getCurrentColumn()->setColumnIndex(m_currCell.first);
1497             app->getCurrentFrame()->setFrame(m_currCell.second);
1498           } else
1499             app->getCurrentFrame()->setFid(m_veryFirstFrameId);
1500           resetMulti();
1501         }
1502       } else  // primo frame
1503       {
1504         m_firstStroke = stroke;
1505         // if (app->getCurrentFrame()->isEditingScene())
1506         m_currCell =
1507             std::pair<int, int>(app->getCurrentColumn()->getColumnIndex(),
1508                                 app->getCurrentFrame()->getFrame());
1509       }
1510 
1511     } else  // stroke non multi
1512     {
1513       if (!m_parent->getImage(true)) return;
1514       if (m_onion) {
1515         OnionSkinMask osMask = app->getCurrentOnionSkin()->getOnionSkinMask();
1516         doStrokeAutofill(m_parent->getImage(true), stroke, m_onlyUnfilled,
1517                          osMask, m_level.getPointer(),
1518                          m_parent->getCurrentFid());
1519       } else
1520         fillAreaWithUndo(
1521             m_parent->getImage(true), TRectD(), stroke /*, imageLocation*/,
1522             m_onlyUnfilled, m_colorType, m_level.getPointer(),
1523             m_parent->getCurrentFid(), styleIndex, m_autopaintLines);
1524       delete stroke;
1525       TTool *t = app->getCurrentTool()->getTool();
1526       if (t) t->notifyImageChanged();
1527       m_parent->invalidate();
1528     }
1529   }
1530 }
1531 
onImageChanged()1532 void AreaFillTool::onImageChanged() {
1533   if (!m_frameRange) return;
1534   TTool::Application *app = TTool::getApplication();
1535   if (!app) return;
1536   TXshLevel *xshl = app->getCurrentLevel()->getLevel();
1537 
1538   if (!xshl || m_level.getPointer() != xshl ||
1539       (m_selectingRect.isEmpty() && !m_firstStroke))
1540     resetMulti();
1541   else if (m_firstFrameId == m_parent->getCurrentFid())
1542     m_firstFrameSelected = false;  // nel caso sono passato allo stato 1 e
1543                                    // torno all'immagine iniziale, torno allo
1544                                    // stato iniziale
1545   else {                           // cambio stato.
1546     m_firstFrameSelected = true;
1547     if (m_type != FREEHAND && m_type != POLYLINE) {
1548       assert(!m_selectingRect.isEmpty());
1549       m_firstRect = m_selectingRect;
1550     }
1551   }
1552 }
1553 
1554 /*--Normal以外のTypeが選択された場合に呼ばれる--*/
onPropertyChanged(bool multi,bool onlyUnfilled,bool onion,Type type,std::wstring colorType,bool autopaintLines)1555 bool AreaFillTool::onPropertyChanged(bool multi, bool onlyUnfilled, bool onion,
1556                                      Type type, std::wstring colorType,
1557                                      bool autopaintLines) {
1558   m_frameRange     = multi;
1559   m_onlyUnfilled   = onlyUnfilled;
1560   m_colorType      = colorType;
1561   m_type           = type;
1562   m_onion          = onion;
1563   m_autopaintLines = autopaintLines;
1564 
1565   if (m_frameRange) resetMulti();
1566 
1567   /*--動作中にプロパティが変わったら、現在の動作を無効にする--*/
1568   if (m_isLeftButtonPressed) m_isLeftButtonPressed = false;
1569 
1570   if (m_type == POLYLINE && !m_polyline.empty()) m_polyline.clear();
1571 
1572   return true;
1573 }
1574 
onActivate()1575 void AreaFillTool::onActivate() {
1576   // getApplication()->editImage();
1577 
1578   if (m_frameRange) resetMulti();
1579 
1580   if (TVectorImageP vi = TImageP(m_parent->getImage(false))) vi->findRegions();
1581 }
1582 
onEnter()1583 void AreaFillTool::onEnter() {
1584   // getApplication()->editImage();
1585 }
1586 
descending(int i,int j)1587 bool descending(int i, int j) { return (i > j); }
1588 
1589 }  // namespace
1590 
1591 //=============================================================================
1592 /*! NormalLineFillTool
1593         マウスドラッグで直線を延ばし、その直線がまたいだ点をFillLineするツール。
1594         Raster - Normal - Line Fillツール(FrameRangeなし)のとき使用可能にする
1595 */
1596 class NormalLineFillTool {
1597   TTool *m_parent;
1598   TPointD m_startPosition, m_mousePosition;
1599   bool m_isEditing;
1600 
1601 public:
NormalLineFillTool(TTool * parent)1602   NormalLineFillTool(TTool *parent)
1603       : m_parent(parent), m_isEditing(false), m_mousePosition() {}
1604 
1605   /*-- FillLineツールに戻ってきたときに前の位置情報をリセットする --*/
init()1606   void init() {
1607     m_startPosition = TPointD();
1608     m_mousePosition = TPointD();
1609     m_isEditing     = false;
1610   }
1611 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)1612   void leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
1613     m_startPosition = pos; /*-始点-*/
1614     m_mousePosition = pos; /*-終点-*/
1615 
1616     m_isEditing = true;
1617   }
1618 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)1619   void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
1620     if (!m_isEditing) return;
1621 
1622     m_mousePosition = pos;
1623     m_parent->invalidate();
1624   }
1625 
leftButtonUp(const TPointD & pos,const TMouseEvent & e,TImage * img,FillParameters & params)1626   void leftButtonUp(const TPointD &pos, const TMouseEvent &e, TImage *img,
1627                     FillParameters &params) {
1628     if (!m_isEditing) return;
1629 
1630     m_mousePosition = pos;
1631 
1632     TTool::Application *app = TTool::getApplication();
1633     if (!app) return;
1634 
1635     TXshLevel *xl       = app->getCurrentLevel()->getLevel();
1636     TXshSimpleLevel *sl = xl ? xl->getSimpleLevel() : 0;
1637 
1638     TToonzImageP ti = TImageP(m_parent->getImage(true));
1639     if (!ti) return;
1640     TRasterCM32P ras = ti->getRaster();
1641     if (!ras) return;
1642     int styleId = params.m_styleId;
1643 
1644     /*--- 線分上にある全ての点でdoFillを行う ---*/
1645     double dx = m_mousePosition.x - m_startPosition.x;
1646     double dy = m_mousePosition.y - m_startPosition.y;
1647     if (std::abs(dx) > std::abs(dy)) /*-- 横長の線分の場合 --*/
1648     {
1649       double k = dy / dx; /*-- 直線の傾き --*/
1650       /*--- roundでは負値のときにうまく繋がらない ---*/
1651       int start      = std::min((int)floor(m_startPosition.x + 0.5),
1652                            (int)floor(m_mousePosition.x + 0.5));
1653       int end        = std::max((int)floor(m_startPosition.x + 0.5),
1654                          (int)floor(m_mousePosition.x + 0.5));
1655       double start_x = (m_startPosition.x < m_mousePosition.x)
1656                            ? m_startPosition.x
1657                            : m_mousePosition.x;
1658       double start_y = (m_startPosition.x < m_mousePosition.x)
1659                            ? m_startPosition.y
1660                            : m_mousePosition.y;
1661       for (int x = start; x <= end; x++) {
1662         double ddx = (double)(x - start);
1663         TPointD tmpPos(start_x + ddx, ddx * k + start_y);
1664         TPoint ipos((int)(tmpPos.x + ras->getLx() / 2),
1665                     (int)(tmpPos.y + ras->getLy() / 2));
1666         if (!ras->getBounds().contains(ipos)) continue;
1667         TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
1668         if (pix.getInk() == styleId || pix.isPurePaint()) continue;
1669         doFill(img, tmpPos, params, e.isShiftPressed(), sl,
1670                m_parent->getCurrentFid(), true);
1671       }
1672     } else /*-- 縦長の線分の場合 --*/
1673     {
1674       double k = dx / dy; /*-- 直線の傾き --*/
1675       /*--- roundでは負値のときにうまく繋がらない ---*/
1676       int start      = std::min((int)floor(m_startPosition.y + 0.5),
1677                            (int)floor(m_mousePosition.y + 0.5));
1678       int end        = std::max((int)floor(m_startPosition.y + 0.5),
1679                          (int)floor(m_mousePosition.y + 0.5));
1680       double start_x = (m_startPosition.y < m_mousePosition.y)
1681                            ? m_startPosition.x
1682                            : m_mousePosition.x;
1683       double start_y = (m_startPosition.y < m_mousePosition.y)
1684                            ? m_startPosition.y
1685                            : m_mousePosition.y;
1686       for (int y = start; y <= end; y++) {
1687         double ddy = (double)(y - start);
1688         TPointD tmpPos(ddy * k + start_x, ddy + start_y);
1689         TPoint ipos((int)(tmpPos.x + ras->getLx() / 2),
1690                     (int)(tmpPos.y + ras->getLy() / 2));
1691         if (!ras->getBounds().contains(ipos)) continue;
1692         TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
1693         if (pix.getInk() == styleId || pix.isPurePaint()) continue;
1694         doFill(img, tmpPos, params, e.isShiftPressed(), sl,
1695                m_parent->getCurrentFid(), true);
1696       }
1697     }
1698     m_isEditing = false;
1699     m_parent->invalidate();
1700   }
1701 
draw()1702   void draw() {
1703     if (m_isEditing) {
1704       tglColor(TPixel32::Red);
1705       glBegin(GL_LINE_STRIP);
1706       tglVertex(m_startPosition);
1707       tglVertex(m_mousePosition);
1708       glEnd();
1709     }
1710   }
1711 };
1712 //=============================================================================
1713 
1714 //=============================================================================
1715 // Fill Tool
1716 //-----------------------------------------------------------------------------
1717 
1718 //-----------------------------------------------------------------------------
1719 
FillTool(int targetType)1720 FillTool::FillTool(int targetType)
1721     : TTool("T_Fill")
1722     , m_frameRange("Frame Range", false)  // W_ToolOptions_FrameRange
1723     , m_fillType("Type:")
1724     , m_selective("Selective", false)
1725     , m_colorType("Mode:")
1726     , m_onion("Onion Skin", false)
1727     , m_fillDepth("Fill Depth", 0, 15, 0, 15)
1728     , m_segment("Segment", false)
1729     , m_onionStyleId(0)
1730     , m_currCell(-1, -1)
1731     , m_maxGapDistance("Maximum Gap", 0.01, 10.0, 1.15)
1732     , m_firstTime(true)
1733     , m_autopaintLines("Autopaint Lines", true) {
1734   m_rectFill           = new AreaFillTool(this);
1735   m_normalLineFillTool = new NormalLineFillTool(this);
1736 
1737   bind(targetType);
1738   m_prop.bind(m_fillType);
1739   m_fillType.addValue(NORMALFILL);
1740   m_fillType.addValue(RECTFILL);
1741   m_fillType.addValue(FREEHANDFILL);
1742   m_fillType.addValue(POLYLINEFILL);
1743 
1744   m_prop.bind(m_colorType);
1745   m_colorType.addValue(LINES);
1746   m_colorType.addValue(AREAS);
1747   m_colorType.addValue(ALL);
1748 
1749   m_prop.bind(m_selective);
1750   if (targetType == TTool::ToonzImage) {
1751     m_prop.bind(m_fillDepth);
1752     m_prop.bind(m_segment);
1753   }
1754   m_prop.bind(m_onion);
1755   m_prop.bind(m_frameRange);
1756   if (targetType == TTool::VectorImage) {
1757     m_prop.bind(m_maxGapDistance);
1758     m_maxGapDistance.setId("MaxGapDistance");
1759   }
1760   if (targetType == TTool::ToonzImage) m_prop.bind(m_autopaintLines);
1761   m_selective.setId("Selective");
1762   m_onion.setId("OnionSkin");
1763   m_frameRange.setId("FrameRange");
1764   m_segment.setId("SegmentInk");
1765   m_fillType.setId("Type");
1766   m_colorType.setId("Mode");
1767   m_autopaintLines.setId("AutopaintLines");
1768 }
1769 //-----------------------------------------------------------------------------
1770 
getCursorId() const1771 int FillTool::getCursorId() const {
1772   int ret;
1773   if (m_colorType.getValue() == LINES)
1774     ret = ToolCursor::FillCursorL;
1775   else {
1776     ret = ToolCursor::FillCursor;
1777     if (m_colorType.getValue() == AREAS) ret = ret | ToolCursor::Ex_Area;
1778     if (!m_autopaintLines.getValue())
1779       ret = ret | ToolCursor::Ex_Fill_NoAutopaint;
1780   }
1781   if (m_fillType.getValue() == FREEHANDFILL)
1782     ret = ret | ToolCursor::Ex_FreeHand;
1783   else if (m_fillType.getValue() == POLYLINEFILL)
1784     ret = ret | ToolCursor::Ex_PolyLine;
1785   else if (m_fillType.getValue() == RECTFILL)
1786     ret = ret | ToolCursor::Ex_Rectangle;
1787 
1788   if (ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg)
1789     ret = ret | ToolCursor::Ex_Negate;
1790   return ret;
1791 }
1792 
1793 //-----------------------------------------------------------------------------
1794 
updateTranslation()1795 void FillTool::updateTranslation() {
1796   m_frameRange.setQStringName(tr("Frame Range"));
1797 
1798   m_fillType.setQStringName(tr("Type:"));
1799   m_fillType.setItemUIName(NORMALFILL, tr("Normal"));
1800   m_fillType.setItemUIName(RECTFILL, tr("Rectangular"));
1801   m_fillType.setItemUIName(FREEHANDFILL, tr("Freehand"));
1802   m_fillType.setItemUIName(POLYLINEFILL, tr("Polyline"));
1803 
1804   m_selective.setQStringName(tr("Selective"));
1805 
1806   m_colorType.setQStringName(tr("Mode:"));
1807   m_colorType.setItemUIName(LINES, tr("Lines"));
1808   m_colorType.setItemUIName(AREAS, tr("Areas"));
1809   m_colorType.setItemUIName(ALL, tr("Lines & Areas"));
1810 
1811   m_onion.setQStringName(tr("Onion Skin"));
1812   m_fillDepth.setQStringName(tr("Fill Depth"));
1813   m_segment.setQStringName(tr("Segment"));
1814   m_maxGapDistance.setQStringName(tr("Maximum Gap"));
1815   m_autopaintLines.setQStringName(tr("Autopaint Lines"));
1816 }
1817 
1818 //-----------------------------------------------------------------------------
1819 
getFillParameters() const1820 FillParameters FillTool::getFillParameters() const {
1821   FillParameters params;
1822   int styleId      = TTool::getApplication()->getCurrentLevelStyleIndex();
1823   params.m_styleId = styleId;
1824   /*---紛らわしいことに、colorTypeをfillTypeに名前を変えて保存している。間違いではない。---*/
1825   params.m_fillType     = m_colorType.getValue();
1826   params.m_emptyOnly    = m_selective.getValue();
1827   params.m_segment      = m_segment.getValue();
1828   params.m_minFillDepth = (int)m_fillDepth.getValue().first;
1829   params.m_maxFillDepth = (int)m_fillDepth.getValue().second;
1830   return params;
1831 }
1832 
1833 //-----------------------------------------------------------------------------
1834 
leftButtonDown(const TPointD & pos,const TMouseEvent & e)1835 void FillTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
1836   TTool::Application *app = TTool::getApplication();
1837   if (!app) return;
1838 
1839   m_clickPoint = pos;
1840   if (m_fillType.getValue() != NORMALFILL) {
1841     m_rectFill->leftButtonDown(pos, e, getImage(true));
1842     return;
1843   }
1844   /*--以下、NormalFillの場合--*/
1845   FillParameters params = getFillParameters();
1846 
1847   if (m_onion.getValue()) {
1848     m_onionStyleId = pickOnionColor(pos);
1849     if (m_onionStyleId > 0) app->setCurrentLevelStyleIndex(m_onionStyleId);
1850   } else if (m_frameRange.getValue()) {
1851     if (!m_firstClick) {
1852       // PRIMO CLICK
1853       // if (app->getCurrentFrame()->isEditingScene())
1854       m_currCell = std::pair<int, int>(getColumnIndex(), getFrame());
1855 
1856       m_firstClick   = true;
1857       m_firstPoint   = pos;
1858       m_firstFrameId = m_veryFirstFrameId = getCurrentFid();
1859       // gmt. NON BISOGNA DISEGNARE DENTRO LE CALLBACKS!!!!
1860       // drawCross(m_firstPoint, 6);
1861       invalidate();
1862     } else {
1863       // When using tablet on windows, the mouse press event may be called AFTER
1864       // tablet release. It causes unwanted another "first click" just after
1865       // frame-range-filling. Calling processEvents() here to make sure to
1866       // consume the mouse press event in advance.
1867       qApp->processEvents();
1868       // SECONDO CLICK
1869       TFrameId fid = getCurrentFid();
1870       MultiFiller filler(m_firstPoint, pos, params,
1871                          m_autopaintLines.getValue());
1872       filler.processSequence(m_level.getPointer(), m_firstFrameId, fid);
1873       if (e.isShiftPressed()) {
1874         m_firstPoint   = pos;
1875         m_firstFrameId = getCurrentFid();
1876       } else {
1877         m_firstClick = false;
1878         if (app->getCurrentFrame()->isEditingScene()) {
1879           app->getCurrentColumn()->setColumnIndex(m_currCell.first);
1880           app->getCurrentFrame()->setFrame(m_currCell.second);
1881         } else
1882           app->getCurrentFrame()->setFid(m_veryFirstFrameId);
1883       }
1884       TTool *t = app->getCurrentTool()->getTool();
1885       if (t) t->notifyImageChanged();
1886     }
1887 
1888   } else {
1889     if (params.m_fillType == LINES && m_targetType == TTool::ToonzImage)
1890       m_normalLineFillTool->leftButtonDown(pos, e);
1891     else {
1892       TXshLevel *xl = app->getCurrentLevel()->getLevel();
1893       m_level       = xl ? xl->getSimpleLevel() : 0;
1894       doFill(getImage(true), pos, params, e.isShiftPressed(),
1895              m_level.getPointer(), getCurrentFid(),
1896              m_autopaintLines.getValue());
1897       invalidate();
1898     }
1899   }
1900 }
1901 
1902 //-----------------------------------------------------------------------------
1903 
leftButtonDoubleClick(const TPointD & pos,const TMouseEvent & e)1904 void FillTool::leftButtonDoubleClick(const TPointD &pos, const TMouseEvent &e) {
1905   if (m_fillType.getValue() != NORMALFILL) {
1906     m_rectFill->leftButtonDoubleClick(pos, e);
1907     return;
1908   }
1909 }
1910 
1911 //-----------------------------------------------------------------------------
1912 
leftButtonDrag(const TPointD & pos,const TMouseEvent & e)1913 void FillTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
1914   if ((m_fillType.getValue() != NORMALFILL && !m_onion.getValue()) ||
1915       (m_colorType.getValue() == AREAS && m_onion.getValue()))
1916     m_rectFill->leftButtonDrag(pos, e);
1917   else if (!m_onion.getValue() && !m_frameRange.getValue()) {
1918     FillParameters params = getFillParameters();
1919     if (params.m_fillType == LINES && m_targetType == TTool::ToonzImage) {
1920       m_normalLineFillTool->leftButtonDrag(pos, e);
1921       return;
1922     }
1923     if (m_clickPoint == pos) return;
1924     TImageP img = getImage(true);
1925     int styleId = params.m_styleId;
1926     if (TVectorImageP vi = img) {
1927       TRegion *r = vi->getRegion(pos);
1928       if (r && r->getStyle() == styleId) return;
1929     } else if (TToonzImageP ti = img) {
1930       TRasterCM32P ras = ti->getRaster();
1931       if (!ras) return;
1932       TPointD center = ras->getCenterD();
1933       TPoint ipos    = convert(pos + center);
1934       if (!ras->getBounds().contains(ipos)) return;
1935       TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
1936       if (pix.getPaint() == styleId) {
1937         invalidate();
1938         return;
1939       }
1940       TSystem::outputDebug("ok. pix=" + std::to_string(pix.getTone()) + "," +
1941                            std::to_string(pix.getPaint()) + "\n");
1942     } else
1943       return;
1944     doFill(img, pos, params, e.isShiftPressed(), m_level.getPointer(),
1945            getCurrentFid(), m_autopaintLines.getValue());
1946     invalidate();
1947   }
1948 }
1949 
1950 //-----------------------------------------------------------------------------
1951 
leftButtonUp(const TPointD & pos,const TMouseEvent & e)1952 void FillTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) {
1953   if (m_onion.getValue()) {
1954     if (m_fillType.getValue() != NORMALFILL && m_colorType.getValue() == AREAS)
1955       m_rectFill->leftButtonUp(pos, e);
1956     else if (m_onionStyleId > 0) {
1957       FillParameters tmp = getFillParameters();
1958       doFill(getImage(true), pos, tmp, e.isShiftPressed(), m_level.getPointer(),
1959              getCurrentFid(), m_autopaintLines.getValue());
1960       invalidate();
1961     }
1962   } else if (m_fillType.getValue() != NORMALFILL) {
1963     m_rectFill->leftButtonUp(pos, e);
1964     return;
1965   }
1966 
1967   if (!m_frameRange.getValue()) {
1968     TFrameId fid = getCurrentFid();
1969     // notifyImageChanged();
1970     if (getFillParameters().m_fillType == LINES &&
1971         m_targetType == TTool::ToonzImage) {
1972       FillParameters params = getFillParameters();
1973       m_normalLineFillTool->leftButtonUp(pos, e, getImage(true), params);
1974       return;
1975     }
1976   }
1977 }
1978 
1979 //-----------------------------------------------------------------------------
1980 
resetMulti()1981 void FillTool::resetMulti() {
1982   m_firstClick   = false;
1983   m_firstFrameId = -1;
1984   m_firstPoint   = TPointD();
1985   TXshLevel *xl  = TTool::getApplication()->getCurrentLevel()->getLevel();
1986   m_level        = xl ? xl->getSimpleLevel() : 0;
1987 }
1988 
1989 //-----------------------------------------------------------------------------
1990 
onPropertyChanged(std::string propertyName)1991 bool FillTool::onPropertyChanged(std::string propertyName) {
1992   /*--- m_rectFill->onPropertyChangedを呼ぶかどうかのフラグ
1993                   fillType, frameRange, selective,
1994      colorTypeが変わったときに呼ぶ---*/
1995   bool rectPropChangedflag = false;
1996 
1997   // Areas, Lines etc.
1998   if (propertyName == m_colorType.getName()) {
1999     FillColorType       = ::to_string(m_colorType.getValue());
2000     rectPropChangedflag = true;
2001 
2002     /*--- ColorModelのCursor更新のためにSIGNALを出す ---*/
2003     TTool::getApplication()->getCurrentTool()->notifyToolChanged();
2004     /*--- FillLineツールに戻ってきたときに前回の位置情報をリセットする ---*/
2005     if (FillColorType.getValue() == "Lines") m_normalLineFillTool->init();
2006   }
2007   // Rect, Polyline etc.
2008   else if (propertyName == m_fillType.getName()) {
2009     if (m_fillType.getValue() != NORMALFILL) {
2010       FillOnion   = (int)(m_onion.getValue());
2011       FillSegment = (int)(m_segment.getValue());
2012     }
2013     FillType            = ::to_string(m_fillType.getValue());
2014     rectPropChangedflag = true;
2015   }
2016   // Onion Skin
2017   else if (propertyName == m_onion.getName()) {
2018     if (m_onion.getValue()) FillType = ::to_string(m_fillType.getValue());
2019     FillOnion = (int)(m_onion.getValue());
2020   }
2021   // Frame Range
2022   else if (propertyName == m_frameRange.getName()) {
2023     FillRange = (int)(m_frameRange.getValue());
2024     resetMulti();
2025     rectPropChangedflag = true;
2026   }
2027   // Selective
2028   else if (propertyName == m_selective.getName()) {
2029     rectPropChangedflag = true;
2030   }
2031   // Fill Depth
2032   else if (propertyName == m_fillDepth.getName()) {
2033     MinFillDepth = (int)m_fillDepth.getValue().first;
2034     MaxFillDepth = (int)m_fillDepth.getValue().second;
2035   }
2036   // Segment
2037   else if (propertyName == m_segment.getName()) {
2038     if (m_segment.getValue()) FillType = ::to_string(m_fillType.getValue());
2039     FillSegment = (int)(m_segment.getValue());
2040   }
2041 
2042   // Autopaint
2043   else if (propertyName == m_autopaintLines.getName()) {
2044     rectPropChangedflag = true;
2045   }
2046 
2047   else if (!m_frameSwitched &&
2048            (propertyName == m_maxGapDistance.getName() ||
2049             propertyName == m_maxGapDistance.getName() + "withUndo")) {
2050     TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
2051     m_level       = xl ? xl->getSimpleLevel() : 0;
2052     if (TVectorImageP vi = getImage(true)) {
2053       if (m_changedGapOriginalValue == -1.0) {
2054         ImageUtils::getFillingInformationInArea(vi, m_oldFillInformation,
2055                                                 vi->getBBox());
2056         m_changedGapOriginalValue = vi->getAutocloseTolerance();
2057       }
2058       TFrameId fid = getCurrentFid();
2059       vi->setAutocloseTolerance(m_maxGapDistance.getValue());
2060       int count = vi->getStrokeCount();
2061       std::vector<int> v(count);
2062       int i;
2063       for (i = 0; i < (int)count; i++) v[i] = i;
2064       vi->notifyChangedStrokes(v, std::vector<TStroke *>(), false);
2065 
2066       if (m_level) {
2067         m_level->setDirtyFlag(true);
2068         TTool::getApplication()->getCurrentLevel()->notifyLevelChange();
2069         if (propertyName == m_maxGapDistance.getName() + "withUndo" &&
2070             m_changedGapOriginalValue != -1.0) {
2071           TUndoManager::manager()->add(new VectorGapSizeChangeUndo(
2072               m_changedGapOriginalValue, m_maxGapDistance.getValue(),
2073               m_level.getPointer(), fid, vi, m_oldFillInformation));
2074           m_changedGapOriginalValue = -1.0;
2075           m_oldFillInformation.clear();
2076           TTool::Application *app = TTool::getApplication();
2077           app->getCurrentXsheet()->notifyXsheetChanged();
2078           notifyImageChanged();
2079         }
2080       }
2081     }
2082   }
2083 
2084   /*--- fillType, frameRange, selective, colorTypeが変わったとき ---*/
2085   if (rectPropChangedflag && m_fillType.getValue() != NORMALFILL) {
2086     AreaFillTool::Type type;
2087     if (m_fillType.getValue() == RECTFILL)
2088       type = AreaFillTool::RECT;
2089     else if (m_fillType.getValue() == FREEHANDFILL)
2090       type = AreaFillTool::FREEHAND;
2091     else if (m_fillType.getValue() == POLYLINEFILL)
2092       type = AreaFillTool::POLYLINE;
2093     else
2094       assert(false);
2095 
2096     m_rectFill->onPropertyChanged(
2097         m_frameRange.getValue(), m_selective.getValue(), m_onion.getValue(),
2098         type, m_colorType.getValue(), m_autopaintLines.getValue());
2099   }
2100 
2101   return true;
2102 }
2103 
2104 //-----------------------------------------------------------------------------
2105 
mouseMove(const TPointD & pos,const TMouseEvent & e)2106 void FillTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
2107   if (m_fillType.getValue() != NORMALFILL) m_rectFill->mouseMove(pos, e);
2108 }
2109 
2110 //-----------------------------------------------------------------------------
2111 
onImageChanged()2112 void FillTool::onImageChanged() {
2113   if (m_fillType.getValue() != NORMALFILL) {
2114     m_rectFill->onImageChanged();
2115     return;
2116   }
2117   if (TVectorImageP vi = getImage(true)) {
2118     m_frameSwitched = true;
2119     if (m_maxGapDistance.getValue() != vi->getAutocloseTolerance()) {
2120       m_maxGapDistance.setValue(vi->getAutocloseTolerance());
2121       getApplication()->getCurrentTool()->notifyToolChanged();
2122     }
2123     m_frameSwitched = false;
2124   }
2125   if (!m_level) resetMulti();
2126 }
2127 
2128 //-----------------------------------------------------------------------------
2129 
onFrameSwitched()2130 void FillTool::onFrameSwitched() {
2131   m_frameSwitched = true;
2132   if (TVectorImageP vi = getImage(true)) {
2133     if (m_maxGapDistance.getValue() != vi->getAutocloseTolerance()) {
2134       m_maxGapDistance.setValue(vi->getAutocloseTolerance());
2135       getApplication()->getCurrentTool()->notifyToolChanged();
2136     }
2137   }
2138   m_frameSwitched           = false;
2139   m_changedGapOriginalValue = -1.0;
2140 }
2141 
2142 //-----------------------------------------------------------------------------
2143 
draw()2144 void FillTool::draw() {
2145   if (Preferences::instance()->getFillOnlySavebox()) {
2146     TToonzImageP ti = (TToonzImageP)getImage(false);
2147     if (ti) {
2148       TRectD bbox =
2149           ToonzImageUtils::convertRasterToWorld(convert(ti->getBBox()), ti);
2150       drawRect(bbox.enlarge(0.5) * ti->getSubsampling(), TPixel32::Black,
2151                0x5555, true);
2152     }
2153   }
2154   if (m_fillType.getValue() != NORMALFILL) {
2155     m_rectFill->draw();
2156     return;
2157   }
2158 
2159   if (m_frameRange.getValue() && m_firstClick) {
2160     tglColor(TPixel::Red);
2161     drawCross(m_firstPoint, 6);
2162   } else if (!m_frameRange.getValue() &&
2163              getFillParameters().m_fillType == LINES &&
2164              m_targetType == TTool::ToonzImage)
2165     m_normalLineFillTool->draw();
2166 }
2167 
2168 //-----------------------------------------------------------------------------
2169 
pick(const TImageP & image,const TPointD & pos,const int frame)2170 int FillTool::pick(const TImageP &image, const TPointD &pos, const int frame) {
2171   TToonzImageP ti  = image;
2172   TVectorImageP vi = image;
2173   if (!ti && !vi) return 0;
2174 
2175   StylePicker picker(image);
2176   double scale2 = 1.0;
2177   if (vi) {
2178     TAffine aff = getViewer()->getViewMatrix() * getCurrentColumnMatrix(frame);
2179     scale2      = aff.det();
2180   }
2181   TPointD pickPos = pos;
2182   // in case that the column is animated in scene-editing mode
2183   if (frame > 0) {
2184     TPointD dpiScale = getViewer()->getDpiScale();
2185     pickPos.x *= dpiScale.x;
2186     pickPos.y *= dpiScale.y;
2187     TPointD worldPos = getCurrentColumnMatrix() * pickPos;
2188     pickPos          = getCurrentColumnMatrix(frame).inv() * worldPos;
2189     pickPos.x /= dpiScale.x;
2190     pickPos.y /= dpiScale.y;
2191   }
2192   // thin stroke can be picked with 10 pixel range
2193   return picker.pickStyleId(pickPos, 10.0, scale2);
2194 }
2195 
2196 //-----------------------------------------------------------------------------
2197 
pickOnionColor(const TPointD & pos)2198 int FillTool::pickOnionColor(const TPointD &pos) {
2199   TTool::Application *app = TTool::getApplication();
2200   if (!app) return 0;
2201   bool filmStripEditing = !app->getCurrentObject()->isSpline();
2202   OnionSkinMask osMask  = app->getCurrentOnionSkin()->getOnionSkinMask();
2203 
2204   TFrameId fid = getCurrentFid();
2205 
2206   TXshSimpleLevel *sl = m_level.getPointer();
2207   if (!sl) return 0;
2208 
2209   std::vector<int> rows;
2210 
2211   // level editing case
2212   if (app->getCurrentFrame()->isEditingLevel()) {
2213     osMask.getAll(sl->guessIndex(fid), rows);
2214     int i, j;
2215     for (i = 0; i < (int)rows.size(); i++)
2216       if (sl->index2fid(rows[i]) > fid) break;
2217 
2218     int onionStyleId = 0;
2219     for (j = i - 1; j >= 0; j--) {
2220       TFrameId onionFid = sl->index2fid(rows[j]);
2221       if (onionFid != fid &&
2222           ((onionStyleId =
2223                 pick(m_level->getFrame(onionFid, ImageManager::none, 1), pos)) >
2224            0))  // subsampling must be 1, otherwise onionfill does  not work
2225         break;
2226     }
2227     if (onionStyleId == 0)
2228       for (j = i; j < (int)rows.size(); j++) {
2229         TFrameId onionFid = sl->index2fid(rows[j]);
2230         if (onionFid != fid &&
2231             ((onionStyleId = pick(
2232                   m_level->getFrame(onionFid, ImageManager::none, 1), pos)) >
2233              0))  // subsampling must be 1, otherwise onionfill does  not work
2234           break;
2235       }
2236     return onionStyleId;
2237   } else {  // scene editing case
2238     TXsheet *xsh = app->getCurrentXsheet()->getXsheet();
2239     int colId    = app->getCurrentColumn()->getColumnIndex();
2240     int row      = app->getCurrentFrame()->getFrame();
2241     osMask.getAll(row, rows);
2242     std::vector<int>::iterator it = rows.begin();
2243     while (it != rows.end() && *it < row) it++;
2244     std::sort(rows.begin(), it, descending);
2245     int onionStyleId = 0;
2246     for (int i = 0; i < (int)rows.size(); i++) {
2247       if (rows[i] == row) continue;
2248       TXshCell cell = xsh->getCell(rows[i], colId);
2249       TXshLevel *xl = cell.m_level.getPointer();
2250       if (!xl || xl->getSimpleLevel() != sl) continue;
2251       TFrameId onionFid = cell.getFrameId();
2252       onionStyleId = pick(m_level->getFrame(onionFid, ImageManager::none, 1),
2253                           pos, rows[i]);
2254       if (onionStyleId > 0) break;
2255     }
2256     return onionStyleId;
2257   }
2258 }
2259 
2260 //-----------------------------------------------------------------------------
2261 
onEnter()2262 void FillTool::onEnter() {
2263   // resetMulti();
2264 
2265   // getApplication()->editImage();
2266 }
2267 
2268 //-----------------------------------------------------------------------------
2269 
onActivate()2270 void FillTool::onActivate() {
2271   // OnionSkinMask osMask = getApplication()->getOnionSkinMask(false);
2272   /*
2273   for (int i=0; i<osMask.getMosCount(); i++)
2274   boh = osMask.getMos(i);
2275   for (i=0; i<osMask.getFosCount(); i++)
2276   boh = osMask.getFos(i);
2277   */
2278   if (m_firstTime) {
2279     m_fillDepth.setValue(
2280         TDoublePairProperty::Value(MinFillDepth, MaxFillDepth));
2281     m_fillType.setValue(::to_wstring(FillType.getValue()));
2282     m_colorType.setValue(::to_wstring(FillColorType.getValue()));
2283     //		m_onlyEmpty.setValue(FillSelective ? 1 :0);
2284     m_onion.setValue(FillOnion ? 1 : 0);
2285     m_segment.setValue(FillSegment ? 1 : 0);
2286     m_frameRange.setValue(FillRange ? 1 : 0);
2287     m_firstTime = false;
2288 
2289     if (m_fillType.getValue() != NORMALFILL) {
2290       AreaFillTool::Type type;
2291       if (m_fillType.getValue() == RECTFILL)
2292         type = AreaFillTool::RECT;
2293       else if (m_fillType.getValue() == FREEHANDFILL)
2294         type = AreaFillTool::FREEHAND;
2295       else if (m_fillType.getValue() == POLYLINEFILL)
2296         type = AreaFillTool::POLYLINE;
2297       else
2298         assert(false);
2299 
2300       m_rectFill->onPropertyChanged(
2301           m_frameRange.getValue(), m_selective.getValue(), m_onion.getValue(),
2302           type, m_colorType.getValue(), m_autopaintLines.getValue());
2303     }
2304   }
2305 
2306   if (m_fillType.getValue() != NORMALFILL) {
2307     m_rectFill->onActivate();
2308     return;
2309   }
2310 
2311   if (FillColorType.getValue() == "Lines") m_normalLineFillTool->init();
2312 
2313   resetMulti();
2314 
2315   //  getApplication()->editImage();
2316   TVectorImageP vi = TImageP(getImage(false));
2317   if (!vi) return;
2318   vi->findRegions();
2319   if (m_targetType == TTool::VectorImage) {
2320     if (m_level) {
2321       TImageP img = getImage(true);
2322       if (TVectorImageP vi = img) {
2323         double tolerance = vi->getAutocloseTolerance();
2324         if (tolerance < 9.9) tolerance += 0.000001;
2325         m_maxGapDistance.setValue(tolerance);
2326       }
2327     }
2328   }
2329   bool ret = true;
2330   ret      = ret && connect(TTool::m_application->getCurrentFrame(),
2331                        SIGNAL(frameSwitched()), this, SLOT(onFrameSwitched()));
2332   ret      = ret && connect(TTool::m_application->getCurrentScene(),
2333                        SIGNAL(sceneSwitched()), this, SLOT(onFrameSwitched()));
2334   ret      = ret &&
2335         connect(TTool::m_application->getCurrentColumn(),
2336                 SIGNAL(columnIndexSwitched()), this, SLOT(onFrameSwitched()));
2337   assert(ret);
2338 }
2339 
2340 //-----------------------------------------------------------------------------
2341 
onDeactivate()2342 void FillTool::onDeactivate() {
2343   disconnect(TTool::m_application->getCurrentFrame(), SIGNAL(frameSwitched()),
2344              this, SLOT(onFrameSwitched()));
2345   disconnect(TTool::m_application->getCurrentScene(), SIGNAL(sceneSwitched()),
2346              this, SLOT(onFrameSwitched()));
2347   disconnect(TTool::m_application->getCurrentColumn(),
2348              SIGNAL(columnIndexSwitched()), this, SLOT(onFrameSwitched()));
2349 }
2350 
2351 //-----------------------------------------------------------------------------
2352 
2353 FillTool FillVectorTool(TTool::VectorImage);
2354 FillTool FiilRasterTool(TTool::ToonzImage);
2355