From a8d743be18da8a081f82f4b0e14221858c014010 Mon Sep 17 00:00:00 2001 From: manongjohn <19245851+manongjohn@users.noreply.github.com> Date: Tue, 28 Feb 2023 16:44:58 -0500 Subject: [PATCH] Auto fill/close - Raster levels --- toonz/sources/include/tools/tooloptions.h | 11 +++ toonz/sources/include/toonz/fill.h | 4 +- toonz/sources/tnztools/fullcolorfilltool.cpp | 90 +++++++++++++++--- toonz/sources/tnztools/fullcolorfilltool.h | 10 +- toonz/sources/tnztools/tooloptions.cpp | 58 ++++++++++++ toonz/sources/toonzlib/fill.cpp | 96 +++++++++++++++++--- 6 files changed, 241 insertions(+), 28 deletions(-) diff --git a/toonz/sources/include/tools/tooloptions.h b/toonz/sources/include/tools/tooloptions.h index b9f75c47..c8e6efe3 100644 --- a/toonz/sources/include/tools/tooloptions.h +++ b/toonz/sources/include/tools/tooloptions.h @@ -502,10 +502,21 @@ protected slots: class FullColorFillToolOptionsBox final : public ToolOptionsBox { Q_OBJECT + ToolOptionCombo *m_rasterGapSettings; + ToolOptionSlider *m_rasterGapSlider; + StyleIndexFieldAndChip *m_styleIndex; + + QLabel *m_gapSliderLabel, *m_styleIndexLabel, *m_rasterGapLabel; + public: FullColorFillToolOptionsBox(QWidget *parent, TTool *tool, TPaletteHandle *pltHandle, ToolHandle *toolHandle); + + void checkGapSettingsVisibility(); + +protected slots: + void onGapSettingChanged(int); }; //============================================================================= diff --git a/toonz/sources/include/toonz/fill.h b/toonz/sources/include/toonz/fill.h index 0f386ab9..7ed8f17b 100644 --- a/toonz/sources/include/toonz/fill.h +++ b/toonz/sources/include/toonz/fill.h @@ -99,7 +99,9 @@ void DVAPI fillautoInks(TRasterCM32P &r, TRect &rect, void DVAPI fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, TTileSaverFullColor *saver = 0, TXsheet *xsheet = 0, - int frameIndex = -1); + int frameIndex = -1, bool fillGaps = false, + bool closeGaps = false, int closeStyleIndex = -1, + double autoCloseDistance = -1.0); //============================================================================= //! The class AreaFiller allows to fill a raster area, delimited by rect or diff --git a/toonz/sources/tnztools/fullcolorfilltool.cpp b/toonz/sources/tnztools/fullcolorfilltool.cpp index 86a4b8e3..ae3b00c9 100644 --- a/toonz/sources/tnztools/fullcolorfilltool.cpp +++ b/toonz/sources/tnztools/fullcolorfilltool.cpp @@ -10,6 +10,8 @@ #include "toonz/preferences.h" #include "toonz/txsheethandle.h" #include "toonz/tframehandle.h" +#include "toonz/tonionskinmaskhandle.h" +#include "toonz/tpalettehandle.h" #include "tools/toolhandle.h" #include "tools/toolutils.h" @@ -25,6 +27,10 @@ TEnv::IntVar FullColorMinFillDepth("InknpaintFullColorMinFillDepth", 4); TEnv::IntVar FullColorMaxFillDepth("InknpaintFullColorMaxFillDepth", 12); TEnv::IntVar FullColorFillReferenced("InknpaintFullColorFillReferenced", 0); +TEnv::StringVar FullColorRasterGapSetting("InknpaintFullColorRasterGapSetting", + "Ignore Gaps"); +extern TEnv::DoubleVar AutocloseDistance; + namespace { //============================================================================= @@ -84,7 +90,8 @@ public: void doFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, bool isShiftFill, TXshSimpleLevel *sl, const TFrameId &fid, - TXsheet *xsheet, int frameIndex) { + TXsheet *xsheet, int frameIndex, bool fillGaps = false, + bool closeGaps = false, int closeStyleIndex = -1) { TTool::Application *app = TTool::getApplication(); if (!app || !sl) return; @@ -115,7 +122,8 @@ void doFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, return; } - fullColorFill(ras, params, &tileSaver, xsheet, frameIndex); + fullColorFill(ras, params, &tileSaver, xsheet, frameIndex, fillGaps, + closeGaps, closeStyleIndex, AutocloseDistance); if (tileSaver.getTileSet()->getTileCount() != 0) { static int count = 0; @@ -149,15 +157,35 @@ void doFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, FullColorFillTool::FullColorFillTool() : TTool("T_Fill") , m_fillDepth("Fill Depth", 0, 15, 4, 12) - , m_referenced("Refer Visible", false) { + , m_referenced("Refer Visible", false) + , m_closeStyleIndex("Style Index:", L"current") // W_ToolOptions_InkIndex + , m_rasterGapDistance("Distance:", 1, 100, 10) + , m_closeRasterGaps("Gaps:") { bind(TTool::RasterImage); m_prop.bind(m_fillDepth); + m_prop.bind(m_closeRasterGaps); + m_prop.bind(m_rasterGapDistance); + m_prop.bind(m_closeStyleIndex); m_prop.bind(m_referenced); + + m_closeRasterGaps.setId("CloseGaps"); + m_rasterGapDistance.setId("RasterGapDistance"); + m_closeStyleIndex.setId("RasterGapInkIndex"); + + m_closeRasterGaps.addValue(IGNOREGAPS); + m_closeRasterGaps.addValue(FILLGAPS); + m_closeRasterGaps.addValue(CLOSEANDFILLGAPS); } void FullColorFillTool::updateTranslation() { m_fillDepth.setQStringName(tr("Fill Depth")); m_referenced.setQStringName(tr("Refer Visible")); + m_rasterGapDistance.setQStringName(tr("Distance:")); + m_closeStyleIndex.setQStringName(tr("Style Index:")); + m_closeRasterGaps.setQStringName(tr("Gaps:")); + m_closeRasterGaps.setItemUIName(IGNOREGAPS, tr("Ignore Gaps")); + m_closeRasterGaps.setItemUIName(FILLGAPS, tr("Fill Gaps")); + m_closeRasterGaps.setItemUIName(CLOSEANDFILLGAPS, tr("Close and Fill")); } FillParameters FullColorFillTool::getFillParameters() const { @@ -180,6 +208,11 @@ void FullColorFillTool::leftButtonDown(const TPointD &pos, m_level = xl ? xl->getSimpleLevel() : 0; FillParameters params = getFillParameters(); + int closeStyleIndex = m_closeStyleIndex.getStyleIndex(); + if (closeStyleIndex == -1) { + closeStyleIndex = app->getCurrentPalette()->getStyleIndex(); + } + int frameIndex = app->getCurrentFrame()->getFrameIndex(); TXsheetHandle *xsh = app->getCurrentXsheet(); @@ -188,8 +221,10 @@ void FullColorFillTool::leftButtonDown(const TPointD &pos, ? xsh->getXsheet() : 0; - applyFill(getImage(true), pos, params, e.isShiftPressed(), m_level.getPointer(), - getCurrentFid(), xsheet, frameIndex); + applyFill(getImage(true), pos, params, e.isShiftPressed(), + m_level.getPointer(), getCurrentFid(), xsheet, frameIndex, + m_closeRasterGaps.getIndex() > 0, m_closeRasterGaps.getIndex() > 1, + closeStyleIndex); invalidate(); } @@ -224,8 +259,15 @@ void FullColorFillTool::leftButtonDrag(const TPointD &pos, ? xsh->getXsheet() : 0; + int closeStyleIndex = m_closeStyleIndex.getStyleIndex(); + if (closeStyleIndex == -1) { + closeStyleIndex = app->getCurrentPalette()->getStyleIndex(); + } + applyFill(img, pos, params, e.isShiftPressed(), m_level.getPointer(), - getCurrentFid(), xsheet, frameIndex); + getCurrentFid(), xsheet, frameIndex, + m_closeRasterGaps.getIndex() > 0, m_closeRasterGaps.getIndex() > 1, + closeStyleIndex); invalidate(); } @@ -235,7 +277,20 @@ bool FullColorFillTool::onPropertyChanged(std::string propertyName) { if (propertyName == m_fillDepth.getName()) { FullColorMinFillDepth = (int)m_fillDepth.getValue().first; FullColorMaxFillDepth = (int)m_fillDepth.getValue().second; + } else if (propertyName == m_rasterGapDistance.getName()) { + AutocloseDistance = m_rasterGapDistance.getValue(); + TTool::Application *app = TTool::getApplication(); + // This is a hack to get the viewer to update with the distance. + app->getCurrentOnionSkin()->notifyOnionSkinMaskChanged(); } + + else if (propertyName == m_closeStyleIndex.getName()) { + } + + else if (propertyName == m_closeRasterGaps.getName()) { + FullColorRasterGapSetting = ::to_string(m_closeRasterGaps.getValue()); + } + return true; } @@ -246,6 +301,10 @@ void FullColorFillTool::onActivate() { FullColorMaxFillDepth)); firstTime = false; } + + m_closeRasterGaps.setValue( + ::to_wstring(FullColorRasterGapSetting.getValue())); + m_rasterGapDistance.setValue(AutocloseDistance); } int FullColorFillTool::getCursorId() const { @@ -258,7 +317,8 @@ int FullColorFillTool::getCursorId() const { void FullColorFillTool::applyFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, bool isShiftFill, TXshSimpleLevel *sl, const TFrameId &fid, - TXsheet *xsheet, int frameIndex) { + TXsheet *xsheet, int frameIndex, bool fillGap, + bool closeGap, int closeStyleIndex) { TRasterImageP ri = TRasterImageP(img); SymmetryTool *symmetryTool = dynamic_cast( @@ -267,21 +327,21 @@ void FullColorFillTool::applyFill(const TImageP &img, const TPointD &pos, TUndoManager::manager()->beginBlock(); } - doFill(img, pos, params, isShiftFill, m_level.getPointer(), getCurrentFid(), - xsheet, frameIndex); + doFill(img, pos, params, isShiftFill, sl, fid, xsheet, frameIndex, fillGap, + closeGap, closeStyleIndex); if (ri && symmetryTool && symmetryTool->isGuideEnabled()) { - TPointD dpiScale = getViewer()->getDpiScale(); - TRasterP ras = ri->getRaster(); - TPointD rasCenter = ras ? ras->getCenterD() : TPointD(0, 0); - TPointD fillPt = pos + rasCenter; + TPointD dpiScale = getViewer()->getDpiScale(); + TRasterP ras = ri->getRaster(); + TPointD rasCenter = ras ? ras->getCenterD() : TPointD(0, 0); + TPointD fillPt = pos + rasCenter; std::vector symmPts = symmetryTool->getSymmetryPoints(fillPt, rasCenter, dpiScale); for (int i = 0; i < symmPts.size(); i++) { if (symmPts[i] == fillPt) continue; - doFill(img, symmPts[i] - rasCenter, params, isShiftFill, - m_level.getPointer(), getCurrentFid(), xsheet, frameIndex); + doFill(img, symmPts[i] - rasCenter, params, isShiftFill, sl, fid, xsheet, + frameIndex, fillGap, closeGap, closeStyleIndex); } TUndoManager::manager()->endBlock(); diff --git a/toonz/sources/tnztools/fullcolorfilltool.h b/toonz/sources/tnztools/fullcolorfilltool.h index d61c05a8..4935208d 100644 --- a/toonz/sources/tnztools/fullcolorfilltool.h +++ b/toonz/sources/tnztools/fullcolorfilltool.h @@ -14,6 +14,10 @@ #include #include +#define IGNOREGAPS L"Ignore Gaps" +#define FILLGAPS L"Fill Gaps" +#define CLOSEANDFILLGAPS L"Close and Fill" + class FullColorFillTool final : public QObject, public TTool { Q_DECLARE_TR_FUNCTIONS(FullColorFillTool) @@ -22,6 +26,9 @@ class FullColorFillTool final : public QObject, public TTool { TPropertyGroup m_prop; TPointD m_clickPoint; TBoolProperty m_referenced; + TDoubleProperty m_rasterGapDistance; + TEnumProperty m_closeRasterGaps; + TStyleIndexProperty m_closeStyleIndex; public: FullColorFillTool(); @@ -45,7 +52,8 @@ public: private: void applyFill(const TImageP &img, const TPointD &pos, FillParameters ¶ms, bool isShiftFill, TXshSimpleLevel *sl, const TFrameId &fid, - TXsheet *xsheet, int frameInde); + TXsheet *xsheet, int frameIndex, bool fillGap, bool closeGap, + int closeStyleIndex); }; #endif // FULLCOLORFILLTOOL_H \ No newline at end of file diff --git a/toonz/sources/tnztools/tooloptions.cpp b/toonz/sources/tnztools/tooloptions.cpp index 6c49ddb7..69d21b39 100644 --- a/toonz/sources/tnztools/tooloptions.cpp +++ b/toonz/sources/tnztools/tooloptions.cpp @@ -1749,9 +1749,67 @@ FullColorFillToolOptionsBox::FullColorFillToolOptionsBox( ToolOptionControlBuilder builder(this, tool, pltHandle, toolHandle); if (tool && tool->getProperties(0)) tool->getProperties(0)->accept(builder); + m_rasterGapSettings = + dynamic_cast(m_controls.value("Gaps:")); + m_rasterGapSlider = + dynamic_cast(m_controls.value("Distance:")); + m_styleIndex = + dynamic_cast(m_controls.value("Style Index:")); + m_rasterGapLabel = m_labels.value(m_rasterGapSettings->propertyName()); + m_styleIndexLabel = m_labels.value(m_styleIndex->propertyName()); + m_gapSliderLabel = m_labels.value(m_rasterGapSlider->propertyName()); + + bool ret = connect(m_rasterGapSettings, SIGNAL(currentIndexChanged(int)), + this, SLOT(onGapSettingChanged(int))); + + checkGapSettingsVisibility(); + m_layout->addStretch(0); } +//----------------------------------------------------------------------------- + +void FullColorFillToolOptionsBox::checkGapSettingsVisibility() { + if (m_rasterGapSettings->getProperty()->getValue() == L"Ignore Gaps") { + m_styleIndex->hide(); + m_styleIndexLabel->hide(); + m_rasterGapSlider->hide(); + m_gapSliderLabel->hide(); + } else if (m_rasterGapSettings->getProperty()->getValue() == L"Fill Gaps") { + m_styleIndex->hide(); + m_styleIndexLabel->hide(); + m_rasterGapSlider->show(); + m_gapSliderLabel->show(); + } else if (m_rasterGapSettings->getProperty()->getValue() == + L"Close and Fill") { + m_styleIndex->show(); + m_styleIndexLabel->show(); + m_rasterGapSlider->show(); + m_gapSliderLabel->show(); + } +} + +//----------------------------------------------------------------------------- + +void FullColorFillToolOptionsBox::onGapSettingChanged(int index) { + if (index == 0) { + m_styleIndex->hide(); + m_styleIndexLabel->hide(); + m_rasterGapSlider->hide(); + m_gapSliderLabel->hide(); + } else if (index == 1) { + m_styleIndex->hide(); + m_styleIndexLabel->hide(); + m_rasterGapSlider->show(); + m_gapSliderLabel->hide(); + } else if (index == 2) { + m_styleIndex->show(); + m_styleIndexLabel->show(); + m_rasterGapSlider->show(); + m_gapSliderLabel->show(); + } +} + //============================================================================= // // FillToolOptionsBox diff --git a/toonz/sources/toonzlib/fill.cpp b/toonz/sources/toonzlib/fill.cpp index 54d7ead2..e7677f41 100644 --- a/toonz/sources/toonzlib/fill.cpp +++ b/toonz/sources/toonzlib/fill.cpp @@ -920,10 +920,12 @@ void inkFill(const TRasterCM32P &r, const TPoint &pin, int ink, int searchRay, //----------------------------------------------------------------------------- void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, - TTileSaverFullColor *saver, TXsheet *xsheet, - int frameIndex) { + TTileSaverFullColor *saver, TXsheet *xsheet, int frameIndex, + bool fillGaps, bool closeGaps, int closeStyleIndex, + double autoCloseDistance) { int oldy, xa, xb, xc, xd, dy, oldxd, oldxc; TPixel32 *pix, *limit, *pix0, *oldpix, *refpix, *oldrefpix; + TPixelCM32 *refCMpix; int x = params.m_p.x, y = params.m_p.y; TRect bbbox = ras->getBounds(); @@ -938,6 +940,10 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, TRaster32P refRas(bbbox.getSize()); + TPixel32 gapColor = plt->getStyle(closeStyleIndex)->getMainColor(); + int styleIndex = 4094; + int fakeStyleIndex = 4095; + if (xsheet) { ToonzScene *scene = xsheet->getScene(); TCamera *camera = scene->getCurrentCamera(); @@ -952,6 +958,34 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, refRas->copy(tmpRaster, offset); clickedPosColor = *(refRas->pixels(y) + x); + } else if (fillGaps) { + refRas->lock(); + TPoint offset((refRas->getLx() - ras->getLx()) / 2, + (refRas->getLy() - ras->getLy()) / 2); + refRas->fill(color); + refRas->copy(ras, offset); + } + + TRasterCM32P refCMRaster; + + if (fillGaps) { + TRasterCM32P cr = convertRaster2CM(refRas); + refCMRaster = cr->clone(); + fillGaps = TAutocloser(refCMRaster, autoCloseDistance, AutocloseAngle, + styleIndex, AutocloseOpacity) + .exec(); + if (fillGaps) { + // Transfer the gap segments to the refRaster + refCMpix = refCMRaster->pixels(0); + TPixel32 *refPix = refRas->pixels(0); + for (int refCMY = 0; refCMY < refCMRaster->getLy(); refCMY++) { + for (int refCMX = 0; refCMX < refCMRaster->getLx(); + refCMX++, refCMpix++, refPix++) { + if (refCMpix->getInk() != styleIndex) continue; + *refPix = color; + } + } + } } int fillDepth = @@ -966,7 +1000,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, std::stack seeds; std::map>> segments; - if (!xsheet) + if (!xsheet && !fillGaps) fullColorFindSegment(ras, params.m_p, xa, xb, color, clickedPosColor, fillDepth); else @@ -977,6 +1011,20 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, seeds.push(FillSeed(xa, xb, y, 1)); seeds.push(FillSeed(xa, xb, y, -1)); + if (fillGaps && closeGaps) { + // Set the ink on gaps that were used to 4095 + TPixelCM32 *tempPix = refCMRaster->pixels(0); + tempPix += (y * refCMRaster->getLx()) + xa - 1; + int i = xa; + while (i <= xb) { + if (tempPix->getInk() == styleIndex) { + tempPix->setInk(fakeStyleIndex); + } + tempPix++; + i++; + } + } + while (!seeds.empty()) { FillSeed fs = seeds.top(); seeds.pop(); @@ -995,7 +1043,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, // left end of the fill seed pixels oldpix = ras->pixels(oldy) + xa; - if (xsheet) { + if (xsheet || fillGaps) { refpix = refRas->pixels(y) + xa; oldrefpix = refRas->pixels(oldy) + xa; } @@ -1011,7 +1059,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, if (segments.find(y) != segments.end()) test = isPixelInSegment(segments[y], x); bool canPaint = false; - if (!xsheet) + if (!xsheet && !fillGaps) canPaint = *pix != color && !test && floodCheck(clickedPosColor, pix, oldpix, fillDepth); else @@ -1019,7 +1067,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, floodCheck(clickedPosColor, refpix, oldrefpix, fillDepth); if (canPaint) { // compute horizontal range to be filled - if (!xsheet) + if (!xsheet && !fillGaps) fullColorFindSegment(ras, TPoint(x, y), xc, xd, color, clickedPosColor, fillDepth); else @@ -1027,6 +1075,20 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, clickedPosColor, fillDepth); // insert segment to be filled insertSegment(segments[y], std::pair(xc, xd)); + if (fillGaps && closeGaps) { + // Set the ink on gaps that were used to 4095 + TPixelCM32 *tempPix = refCMRaster->pixels(0); + tempPix += (y * refCMRaster->getLx()) + xa - 1; + int i = xa; + while (i <= xb) { + if (tempPix->getInk() == styleIndex) { + tempPix->setInk(fakeStyleIndex); + } + tempPix++; + i++; + } + } + // create new fillSeed to invert direction, if needed if (xc < xa) seeds.push(FillSeed(xc, xa - 1, y, -dy)); if (xd > xb) seeds.push(FillSeed(xb + 1, xd, y, -dy)); @@ -1040,7 +1102,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, // jump to the next pixel to the right end of the range pix += xd - x + 1; oldpix += xd - x + 1; - if (xsheet) { + if (xsheet || fillGaps) { refpix += xd - x + 1; oldrefpix += xd - x + 1; } @@ -1048,7 +1110,7 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, } else { pix++; oldpix++, x++; - if (xsheet) { + if (xsheet || fillGaps) { refpix++; oldrefpix++; } @@ -1058,15 +1120,14 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, if (oldxd > 0) seeds.push(FillSeed(oldxc, oldxd, y, dy)); } - if (xsheet) refRas->unlock(); + if (xsheet || fillGaps) refRas->unlock(); // pixels are actually filled here TPixel32 premultiColor = premultiply(color); std::map>>::iterator it; for (it = segments.begin(); it != segments.end(); it++) { - TPixel32 *line = ras->pixels(it->first); - TPixel32 *refLine = 0; + TPixel32 *line = ras->pixels(it->first); std::vector> segmentVector = it->second; for (int i = 0; i < (int)segmentVector.size(); i++) { std::pair segment = segmentVector[i]; @@ -1088,4 +1149,17 @@ void fullColorFill(const TRaster32P &ras, const FillParameters ¶ms, } } } + + if (fillGaps && closeGaps) { + TPixelCM32 *tempPix = refCMRaster->pixels(); + TPixel32 *keepPix = ras->pixels(); + for (int tempY = 0; tempY < refCMRaster->getLy(); tempY++) { + for (int tempX = 0; tempX < refCMRaster->getLx(); + tempX++, tempPix++, keepPix++) { + if (tempPix->getInk() == fakeStyleIndex) { + *keepPix = gapColor; + } + } + } + } }