Auto fill/close - Raster levels

This commit is contained in:
manongjohn 2023-02-28 16:44:58 -05:00
parent d7e641b113
commit a8d743be18
6 changed files with 241 additions and 28 deletions

View file

@ -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);
};
//=============================================================================

View file

@ -99,7 +99,9 @@ void DVAPI fillautoInks(TRasterCM32P &r, TRect &rect,
void DVAPI fullColorFill(const TRaster32P &ras, const FillParameters &params,
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

View file

@ -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 &params,
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 &params,
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 &params,
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 &params, 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<SymmetryTool *>(
@ -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<TPointD> 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();

View file

@ -14,6 +14,10 @@
#include <QCoreApplication>
#include <QObject>
#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 &params,
bool isShiftFill, TXshSimpleLevel *sl, const TFrameId &fid,
TXsheet *xsheet, int frameInde);
TXsheet *xsheet, int frameIndex, bool fillGap, bool closeGap,
int closeStyleIndex);
};
#endif // FULLCOLORFILLTOOL_H

View file

@ -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<ToolOptionCombo *>(m_controls.value("Gaps:"));
m_rasterGapSlider =
dynamic_cast<ToolOptionSlider *>(m_controls.value("Distance:"));
m_styleIndex =
dynamic_cast<StyleIndexFieldAndChip *>(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

View file

@ -920,10 +920,12 @@ void inkFill(const TRasterCM32P &r, const TPoint &pin, int ink, int searchRay,
//-----------------------------------------------------------------------------
void fullColorFill(const TRaster32P &ras, const FillParameters &params,
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 &params,
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 &params,
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 &params,
std::stack<FillSeed> seeds;
std::map<int, std::vector<std::pair<int, int>>> 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 &params,
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 &params,
// 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 &params,
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 &params,
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 &params,
clickedPosColor, fillDepth);
// insert segment to be filled
insertSegment(segments[y], std::pair<int, int>(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 &params,
// 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 &params,
} else {
pix++;
oldpix++, x++;
if (xsheet) {
if (xsheet || fillGaps) {
refpix++;
oldrefpix++;
}
@ -1058,15 +1120,14 @@ void fullColorFill(const TRaster32P &ras, const FillParameters &params,
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<int, std::vector<std::pair<int, int>>>::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<std::pair<int, int>> segmentVector = it->second;
for (int i = 0; i < (int)segmentVector.size(); i++) {
std::pair<int, int> segment = segmentVector[i];
@ -1088,4 +1149,17 @@ void fullColorFill(const TRaster32P &ras, const FillParameters &params,
}
}
}
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;
}
}
}
}
}