Add referenced fills - Smart Raster

This commit is contained in:
manongjohn 2021-07-29 07:57:29 -04:00
parent b9ea823143
commit fa3d9e0e4a
6 changed files with 190 additions and 62 deletions

View file

@ -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,

View file

@ -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 &params)
: 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 &params,
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 &params, TTileSaverFullColor *saver = 0);

View file

@ -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 &params,
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 &params,
// !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 &params,
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) {

View file

@ -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<int, int> m_currCell;

View file

@ -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<ToolOptionCheckbox *>(m_controls.value("Segment"));
m_onionMode =
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Onion Skin"));
m_referenced =
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Refer Visible"));
m_multiFrameMode =
dynamic_cast<ToolOptionCheckbox *>(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);

View file

@ -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 <stack>
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 &params,
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 &params,
if (params.m_emptyOnly && (tempRaster->pixels(p.y) + p.x)->getPaint() != 0)
return false;
TRaster32P refRaster;
TPixel32 clickedPosColor, color(255, 255, 255);
std::map<int, std::vector<std::pair<int, int>>> 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 &params,
std::stack<FillSeed> 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<int, int>(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 &params,
x = xa;
oldxd = (std::numeric_limits<int>::min)();
oldxc = (std::numeric_limits<int>::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<int, int>(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 &params,
}
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));