diff --git a/toonz/sources/include/tools/tooloptions.h b/toonz/sources/include/tools/tooloptions.h index c2be6cf1..c7c8eb38 100644 --- a/toonz/sources/include/tools/tooloptions.h +++ b/toonz/sources/include/tools/tooloptions.h @@ -504,6 +504,7 @@ class FillToolOptionsBox final : public ToolOptionsBox { ToolOptionSlider *m_rasterGapSlider; StyleIndexFieldAndChip *m_styleIndex; QLabel *m_gapSliderLabel, *m_styleIndexLabel, *m_rasterGapLabel; + ToolOptionCheckbox *m_referenced; public: FillToolOptionsBox(QWidget *parent, TTool *tool, TPaletteHandle *pltHandle, diff --git a/toonz/sources/include/toonz/fill.h b/toonz/sources/include/toonz/fill.h index 1fb878d4..81a6e26e 100644 --- a/toonz/sources/include/toonz/fill.h +++ b/toonz/sources/include/toonz/fill.h @@ -3,6 +3,8 @@ #ifndef T_FILL_INCLUDED #define T_FILL_INCLUDED +#include "txsheet.h" + class TPalette; #undef DVAPI @@ -28,6 +30,7 @@ public: TPoint m_p; TPalette *m_palette; bool m_prevailing; + bool m_referenced; FillParameters() : m_styleId(0) @@ -39,7 +42,8 @@ public: , m_p() , m_shiftFill(false) , m_palette(0) - , m_prevailing(true) {} + , m_prevailing(true) + , m_referenced(false) {} FillParameters(const FillParameters ¶ms) : m_styleId(params.m_styleId) , m_fillType(params.m_fillType) @@ -50,7 +54,8 @@ public: , m_p(params.m_p) , m_shiftFill(params.m_shiftFill) , m_palette(params.m_palette) - , m_prevailing(params.m_prevailing) {} + , m_prevailing(params.m_prevailing) + , m_referenced(params.m_referenced) {} }; //============================================================================= @@ -65,7 +70,8 @@ class TTileSaverFullColor; DVAPI bool fill(const TRasterCM32P &r, const FillParameters ¶ms, TTileSaverCM32 *saver = 0, bool fillGaps = false, bool closeGaps = false, int closeStyleIndex = -1, - double autoCloseDistance = -1.0); + double autoCloseDistance = -1.0, TXsheet *xsheet = 0, + int frameIndex = -1); DVAPI void fill(const TRaster32P &ras, const TRaster32P &ref, const FillParameters ¶ms, TTileSaverFullColor *saver = 0); diff --git a/toonz/sources/tnztools/filltool.cpp b/toonz/sources/tnztools/filltool.cpp index 3f3d0dcf..3f9800ac 100644 --- a/toonz/sources/tnztools/filltool.cpp +++ b/toonz/sources/tnztools/filltool.cpp @@ -69,6 +69,7 @@ TEnv::IntVar FillSelective("InknpaintFillSelective", 0); TEnv::IntVar FillOnion("InknpaintFillOnion", 0); TEnv::IntVar FillSegment("InknpaintFillSegment", 0); TEnv::IntVar FillRange("InknpaintFillRange", 0); +TEnv::IntVar FillReferenced("InknpaintFillReferenced", 0); TEnv::StringVar RasterGapSetting("RasterGapSetting", "Ignore Gaps"); extern TEnv::DoubleVar AutocloseDistance; @@ -452,6 +453,8 @@ class RasterFillUndo final : public TRasterUndo { bool m_saveboxOnly, m_closeGaps, m_fillGaps; double m_autoCloseDistance; int m_closeStyleIndex; + TXsheet *m_xsheet; + int m_frameIndex; public: /*RasterFillUndo(TTileSetCM32 *tileSet, TPoint fillPoint, @@ -466,14 +469,17 @@ public: RasterFillUndo(TTileSetCM32 *tileSet, const FillParameters ¶ms, TXshSimpleLevel *sl, const TFrameId &fid, bool saveboxOnly, bool fillGaps, bool closeGaps, int closeStyleIndex, - double autoCloseDistance) + double autoCloseDistance, TXsheet *xsheet = 0, + int frameIndex = -1) : TRasterUndo(tileSet, sl, fid, false, false, 0) , m_params(params) , m_closeGaps(closeGaps) , m_fillGaps(fillGaps) , m_autoCloseDistance(autoCloseDistance) , m_closeStyleIndex(closeStyleIndex) - , m_saveboxOnly(saveboxOnly) {} + , m_saveboxOnly(saveboxOnly) + , m_xsheet(xsheet) + , m_frameIndex(frameIndex) {} void redo() const override { TToonzImageP image = getImage(); @@ -489,12 +495,14 @@ public: if (m_params.m_fillType == ALL || m_params.m_fillType == AREAS) { if (m_params.m_shiftFill) { FillParameters aux(m_params); - aux.m_styleId = (m_params.m_styleId == 0) ? 1 : 0; - recomputeSavebox = fill(r, aux, 0, m_fillGaps, m_closeGaps, - m_closeStyleIndex, m_autoCloseDistance); + aux.m_styleId = (m_params.m_styleId == 0) ? 1 : 0; + recomputeSavebox = + fill(r, aux, 0, m_fillGaps, m_closeGaps, m_closeStyleIndex, + m_autoCloseDistance, m_xsheet, m_frameIndex); } - recomputeSavebox = fill(r, m_params, 0, m_fillGaps, m_closeGaps, - m_closeStyleIndex, m_autoCloseDistance); + recomputeSavebox = + fill(r, m_params, 0, m_fillGaps, m_closeGaps, m_closeStyleIndex, + m_autoCloseDistance, m_xsheet, m_frameIndex); } if (m_params.m_fillType == ALL || m_params.m_fillType == LINES) { if (m_params.m_segment) @@ -505,6 +513,7 @@ public: if (recomputeSavebox) ToolUtils::updateSaveBox(); + TTool::Application *app = TTool::getApplication(); if (app) { app->getCurrentXsheet()->notifyXsheetChanged(); @@ -1138,15 +1147,23 @@ void doFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, // !autoPaintLines will temporary disable autopaint line feature if (plt && hasAutoInks(plt) && autopaintLines) params.m_palette = plt; + int frameIndex = app->getCurrentFrame()->getFrameIndex(); + + TXsheetHandle *xsh = app->getCurrentXsheet(); + TXsheet *xsheet = + params.m_referenced && !app->getCurrentFrame()->isEditingLevel() && xsh + ? xsh->getXsheet() + : 0; + if (params.m_fillType == ALL || params.m_fillType == AREAS) { if (isShiftFill) { FillParameters aux(params); aux.m_styleId = (params.m_styleId == 0) ? 1 : 0; - recomputeSavebox = - fill(ras, aux, &tileSaver, fillGaps, closeGaps, closeStyleIndex); + recomputeSavebox = fill(ras, aux, &tileSaver, fillGaps, closeGaps, + closeStyleIndex, -1.0, xsheet, frameIndex); } - recomputeSavebox = - fill(ras, params, &tileSaver, fillGaps, closeGaps, closeStyleIndex); + recomputeSavebox = fill(ras, params, &tileSaver, fillGaps, closeGaps, + closeStyleIndex, -1.0, xsheet, frameIndex); } if (params.m_fillType == ALL || params.m_fillType == LINES) { if (params.m_segment) @@ -1166,7 +1183,7 @@ void doFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, TUndoManager::manager()->add(new RasterFillUndo( tileSet, params, sl, fid, Preferences::instance()->getFillOnlySavebox(), fillGaps, closeGaps, - closeStyleIndex, AutocloseDistance)); + closeStyleIndex, AutocloseDistance, xsheet, frameIndex)); } // instead of updateFrame : @@ -1962,7 +1979,8 @@ FillTool::FillTool(int targetType) , m_rasterGapDistance("Distance:", 1, 100, 10) , m_closeRasterGaps("Gaps:") , m_firstTime(true) - , m_autopaintLines("Autopaint Lines", true) { + , m_autopaintLines("Autopaint Lines", true) + , m_referenced("Refer Visible", false) { m_rectFill = new AreaFillTool(this); m_normalLineFillTool = new NormalLineFillTool(this); @@ -1994,6 +2012,7 @@ FillTool::FillTool(int targetType) m_closeRasterGaps.addValue(CLOSEANDFILLGAPS); m_prop.bind(m_onion); + if (targetType == TTool::ToonzImage) m_prop.bind(m_referenced); m_prop.bind(m_frameRange); if (targetType == TTool::VectorImage) { m_prop.bind(m_maxGapDistance); @@ -2007,6 +2026,7 @@ FillTool::FillTool(int targetType) m_fillType.setId("Type"); m_colorType.setId("Mode"); m_autopaintLines.setId("AutopaintLines"); + m_referenced.setId("Refer Visible"); } //----------------------------------------------------------------------------- @@ -2051,6 +2071,7 @@ void FillTool::updateTranslation() { m_colorType.setItemUIName(ALL, tr("Lines & Areas")); m_onion.setQStringName(tr("Onion Skin")); + m_referenced.setQStringName(tr("Refer Visible")); m_fillDepth.setQStringName(tr("Fill Depth")); m_segment.setQStringName(tr("Segment")); m_maxGapDistance.setQStringName(tr("Maximum Gap")); @@ -2075,6 +2096,7 @@ FillParameters FillTool::getFillParameters() const { params.m_segment = m_segment.getValue(); params.m_minFillDepth = (int)m_fillDepth.getValue().first; params.m_maxFillDepth = (int)m_fillDepth.getValue().second; + params.m_referenced = m_referenced.getValue(); return params; } @@ -2371,6 +2393,8 @@ bool FillTool::onPropertyChanged(std::string propertyName) { } } } + } else if (propertyName == m_referenced.getName()) { + FillReferenced = (int)(m_referenced.getValue()); } /*--- fillType, frameRange, selective, colorTypeが変わったとき ---*/ @@ -2575,6 +2599,7 @@ void FillTool::onActivate() { m_onion.setValue(FillOnion ? 1 : 0); m_segment.setValue(FillSegment ? 1 : 0); m_frameRange.setValue(FillRange ? 1 : 0); + m_referenced.setValue(FillReferenced ? 1 : 0); m_firstTime = false; if (m_fillType.getValue() != NORMALFILL) { diff --git a/toonz/sources/tnztools/filltool.h b/toonz/sources/tnztools/filltool.h index 0bbb66fd..a38362b8 100644 --- a/toonz/sources/tnztools/filltool.h +++ b/toonz/sources/tnztools/filltool.h @@ -101,6 +101,7 @@ class FillTool final : public QObject, public TTool { TStyleIndexProperty m_closeStyleIndex; AreaFillTool *m_rectFill; NormalLineFillTool *m_normalLineFillTool; + TBoolProperty m_referenced; TPropertyGroup m_prop; std::pair m_currCell; diff --git a/toonz/sources/tnztools/tooloptions.cpp b/toonz/sources/tnztools/tooloptions.cpp index aa8409cf..d6775c8e 100644 --- a/toonz/sources/tnztools/tooloptions.cpp +++ b/toonz/sources/tnztools/tooloptions.cpp @@ -1558,7 +1558,8 @@ FillToolOptionsBox::FillToolOptionsBox(QWidget *parent, TTool *tool, , m_fillDepthField(0) , m_gapSliderLabel(0) , m_styleIndexLabel(0) - , m_segmentMode(0) { + , m_segmentMode(0) + , m_referenced(0) { TPropertyGroup *props = tool->getProperties(0); assert(props->getPropertyCount() > 0); @@ -1579,6 +1580,8 @@ FillToolOptionsBox::FillToolOptionsBox(QWidget *parent, TTool *tool, dynamic_cast(m_controls.value("Segment")); m_onionMode = dynamic_cast(m_controls.value("Onion Skin")); + m_referenced = + dynamic_cast(m_controls.value("Refer Visible")); m_multiFrameMode = dynamic_cast(m_controls.value("Frame Range")); m_autopaintMode = @@ -1622,6 +1625,7 @@ FillToolOptionsBox::FillToolOptionsBox(QWidget *parent, TTool *tool, m_multiFrameMode->isChecked()) m_onionMode->setEnabled(false); if (m_autopaintMode) m_autopaintMode->setEnabled(false); + m_referenced->setEnabled(false); } if (m_toolType->getProperty()->getValue() != L"Normal") { if (m_segmentMode) m_segmentMode->setEnabled(false); @@ -1658,6 +1662,7 @@ void FillToolOptionsBox::onColorModeChanged(int index) { } enabled = range[index] != L"Lines" && !m_multiFrameMode->isChecked(); m_onionMode->setEnabled(enabled); + m_referenced->setEnabled(enabled); checkGapSettingsVisibility(); } @@ -1715,6 +1720,7 @@ void FillToolOptionsBox::checkGapSettingsVisibility() { void FillToolOptionsBox::onToolTypeChanged(int index) { const TEnumProperty::Range &range = m_toolType->getProperty()->getRange(); bool enabled = range[index] == L"Normal"; + m_referenced->setEnabled(enabled); if (m_segmentMode) m_segmentMode->setEnabled( enabled ? m_colorMode->getProperty()->getValue() != L"Areas" : false); diff --git a/toonz/sources/toonzlib/fill.cpp b/toonz/sources/toonzlib/fill.cpp index bc257a35..080bf35d 100644 --- a/toonz/sources/toonzlib/fill.cpp +++ b/toonz/sources/toonzlib/fill.cpp @@ -8,6 +8,14 @@ #include "toonz/autoclose.h" #include "tenv.h" #include "tropcm.h" + +#include "toonz/txsheet.h" +#include "toonz/txshcell.h" +#include "toonz/txshsimplelevel.h" + +#include "toonz/toonzscene.h" +#include "toonz/tcamera.h" + #include extern TEnv::DoubleVar AutocloseDistance; @@ -46,9 +54,9 @@ inline TPoint nearestInkNotDiagonal(const TRasterCM32P &r, const TPoint &p) { // region in the Rectangular, Freehand and Polyline fill procedures // in order to make the paint to protlude behind the line. -void fillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, - int paint, TPalette *palette, TTileSaverCM32 *saver, - bool prevailing = true) { +// Calculates the endpoints for the line of pixels in which to fill +bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, + int paint, TPalette *palette, bool prevailing = true) { int tone, oldtone; TPixelCM32 *pix, *pix0, *limit, *tmp_limit; @@ -154,26 +162,7 @@ void fillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, xa = p.x + pix - pix0 + 1; - if (saver) saver->save(TRect(xa, p.y, xb, p.y)); - - if (xb >= xa) { - pix = line + xa; - int n; - for (n = 0; n < xb - xa + 1; n++, pix++) { - if (palette && pix->isPurePaint()) { - TPoint pInk = nearestInkNotDiagonal(r, TPoint(xa + n, p.y)); - if (pInk != TPoint(-1, -1)) { - TPixelCM32 *pixInk = - (TPixelCM32 *)r->getRawData() + (pInk.y * r->getWrap() + pInk.x); - if (pixInk->getInk() != paint && - palette->getStyle(pixInk->getInk())->getFlags() != 0) - inkFill(r, pInk, paint, 0, saver); - } - } - - pix->setPaint(paint); - } - } + return (xb >= xa); } //----------------------------------------------------------------------------- @@ -302,6 +291,16 @@ void fullColorFindSegment(const TRaster32P &r, const TPoint &p, int &xa, //----------------------------------------------------------------------------- +bool calcRefFillRow(TRaster32P &refRas, const TPoint &p, int &xa, int &xb, + const TPixel32 &color, const TPixel32 &clickedPosColor, + const int fillDepth = 254) { + fullColorFindSegment(refRas, p, xa, xb, color, clickedPosColor, fillDepth); + + return (xb >= xa); +} + +//----------------------------------------------------------------------------- + class FillSeed { public: int m_xa, m_xb; @@ -320,6 +319,34 @@ inline int threshTone(const TPixelCM32 &pix, int fillDepth) { : pix.getTone(); } +void fillRow(const TRasterCM32P &r, const TPoint &p, int xa, int xb, int paint, + TPalette *palette, TTileSaverCM32 *saver) { + /* vai a destra */ + TPixelCM32 *line = r->pixels(p.y); + TPixelCM32 *pix = line + p.x; + + if (saver) saver->save(TRect(xa, p.y, xb, p.y)); + + if (xb >= xa) { + pix = line + xa; + int n; + for (n = 0; n < xb - xa + 1; n++, pix++) { + if (palette && pix->isPurePaint()) { + TPoint pInk = nearestInkNotDiagonal(r, TPoint(xa + n, p.y)); + if (pInk != TPoint(-1, -1)) { + TPixelCM32 *pixInk = + (TPixelCM32 *)r->getRawData() + (pInk.y * r->getWrap() + pInk.x); + if (pixInk->getInk() != paint && + palette->getStyle(pixInk->getInk())->getFlags() != 0) + inkFill(r, pInk, paint, 0, saver); + } + } + + pix->setPaint(paint); + } + } +} + //----------------------------------------------------------------------------- inline int threshMatte(int matte, int fillDepth) { @@ -374,11 +401,17 @@ bool floodCheck(const TPixel32 &clickColor, const TPixel32 *targetPix, /*-- The return value is whether the saveBox has been updated --*/ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, TTileSaverCM32 *saver, bool fillGaps, bool closeGaps, - int closeStyleIndex, double autoCloseDistance) { + int closeStyleIndex, double autoCloseDistance, TXsheet *xsheet, + int frameIndex) { + auto fullColorThreshMatte = [](int matte, int fillDepth) -> int { + return (matte <= fillDepth) ? matte : 255; + }; + TPixelCM32 *pix, *limit, *pix0, *oldpix; + TPixel32 *refpix, *oldrefpix; int oldy, xa, xb, xc, xd, dy; int oldxc, oldxd; - int tone, oldtone; + int tone, oldtone, matte, oldmatte; TPoint p = params.m_p; int x = p.x, y = p.y; int paint = params.m_styleId; @@ -412,18 +445,40 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, if (params.m_emptyOnly && (tempRaster->pixels(p.y) + p.x)->getPaint() != 0) return false; + TRaster32P refRaster; + TPixel32 clickedPosColor, color(255, 255, 255); + + std::map>> segments; + + if (xsheet) { + ToonzScene *scene = xsheet->getScene(); + TCamera *camera = scene->getCurrentCamera(); + TDimension bbox = camera->getRes(); + refRaster.create(bbox); + refRaster->clear(); + scene->renderFrame(refRaster, frameIndex); + refpix = refRaster->pixels(p.y) + p.x; + clickedPosColor = *refpix; + + if (params.m_emptyOnly && clickedPosColor != TPixel32(0, 0, 0, 0)) + return false; + } + assert(fillDepth >= 0 && fillDepth < 16); - switch (TPixelCM32::getMaxTone()) { - case 15: - fillDepth = (15 - fillDepth); - break; - case 255: + if (xsheet) fillDepth = ((15 - fillDepth) << 4) | (15 - fillDepth); - break; - default: - assert(false); - } + else + switch (TPixelCM32::getMaxTone()) { + case 15: + fillDepth = (15 - fillDepth); + break; + case 255: + fillDepth = ((15 - fillDepth) << 4) | (15 - fillDepth); + break; + default: + assert(false); + } /*-- Look at the colors in the four corners and update the saveBox if even one * changes --*/ TPixelCM32 borderIndex[4]; @@ -443,8 +498,12 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, std::stack seeds; - fillRow(tempRaster, p, xa, xb, paint, params.m_palette, saver, - params.m_prevailing); + bool fillIt = !xsheet ? calcFillRow(tempRaster, p, xa, xb, paint, + params.m_palette, params.m_prevailing) + : calcRefFillRow(refRaster, p, xa, xb, color, + clickedPosColor, fillDepth); + if (fillIt) fillRow(tempRaster, p, xa, xb, paint, params.m_palette, saver); + if (xsheet) segments[y].push_back(std::pair(xa, xb)); seeds.push(FillSeed(xa, xb, y, 1)); seeds.push(FillSeed(xa, xb, y, -1)); // Set the ink on gaps that were used to 4095 @@ -477,16 +536,38 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, x = xa; oldxd = (std::numeric_limits::min)(); oldxc = (std::numeric_limits::max)(); + + if (xsheet) { + refpix = refRaster->pixels(y) + xa; + oldrefpix = refRaster->pixels(oldy) + xa; + } while (pix <= limit) { - oldtone = threshTone(*oldpix, fillDepth); - tone = threshTone(*pix, fillDepth); - // the last condition is added in order to prevent fill area from - // protruding behind the colored line - if (pix->getPaint() != paint && tone <= oldtone && tone != 0 && - (pix->getPaint() != pix->getInk() || - pix->getPaint() == paintAtClickedPos)) { - fillRow(tempRaster, TPoint(x, y), xc, xd, paint, params.m_palette, - saver, params.m_prevailing); + bool canPaint = false; + if (!xsheet) { + oldtone = threshTone(*oldpix, fillDepth); + tone = threshTone(*pix, fillDepth); + // the last condition is added in order to prevent fill area from + // protruding behind the colored line + canPaint = pix->getPaint() != paint && tone <= oldtone && tone != 0 && + (pix->getPaint() != pix->getInk() || + pix->getPaint() == paintAtClickedPos); + } else { + bool test = false; + if (segments.find(y) != segments.end()) + test = isPixelInSegment(segments[y], x); + canPaint = *refpix != color && !test && + floodCheck(clickedPosColor, refpix, oldrefpix, fillDepth); + } + if (canPaint) { + bool fillIt = !xsheet + ? calcFillRow(tempRaster, TPoint(x, y), xc, xd, paint, + params.m_palette, params.m_prevailing) + : calcRefFillRow(refRaster, TPoint(x, y), xc, xd, + color, clickedPosColor, fillDepth); + if (fillIt) + fillRow(tempRaster, TPoint(x, y), xc, xd, paint, params.m_palette, + saver); + if (xsheet) insertSegment(segments[y], std::pair(xc, xd)); // Set the ink on gaps that were used to 4095 { TPixelCM32 *tempPix = tempRaster->pixels(0); @@ -511,10 +592,18 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, } pix += xd - x + 1; oldpix += xd - x + 1; + if (xsheet) { + refpix += xd - x + 1; + oldrefpix += xd - x + 1; + } x += xd - x + 1; } else { pix++; oldpix++, x++; + if (xsheet) { + refpix++; + oldrefpix++; + } } } if (oldxd > 0) seeds.push(FillSeed(oldxc, oldxd, y, dy));