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 ¶ms,
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 ¤tFid, 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 ¤tFid)
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 ¤tFid) {
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 ¤tFid) {
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 ¶ms,
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 ¶ms, 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 ¶ms) {
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