Segment Eraser for Smart Raster Levels (#518)
* Segment Eraser for Smart Raster Levels * Reduce eroding strokes * Adjust tool option visibility
This commit is contained in:
parent
5563373272
commit
52dd1a1ce7
5 changed files with 403 additions and 25 deletions
|
@ -571,7 +571,7 @@ class EraserToolOptionsBox final : public ToolOptionsBox {
|
||||||
|
|
||||||
ToolOptionCheckbox *m_pencilMode, *m_invertMode, *m_multiFrameMode;
|
ToolOptionCheckbox *m_pencilMode, *m_invertMode, *m_multiFrameMode;
|
||||||
ToolOptionCombo *m_toolType, *m_colorMode;
|
ToolOptionCombo *m_toolType, *m_colorMode;
|
||||||
QLabel *m_hardnessLabel;
|
QLabel *m_hardnessLabel, *m_colorModeLabel;
|
||||||
ToolOptionSlider *m_hardnessField;
|
ToolOptionSlider *m_hardnessField;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -76,7 +76,7 @@ DVAPI void inkFill(const TRasterCM32P &r, const TPoint &p, int ink,
|
||||||
|
|
||||||
bool DVAPI inkSegment(const TRasterCM32P &r, const TPoint &p, int ink,
|
bool DVAPI inkSegment(const TRasterCM32P &r, const TPoint &p, int ink,
|
||||||
float growFactor, bool isSelective,
|
float growFactor, bool isSelective,
|
||||||
TTileSaverCM32 *saver = 0);
|
TTileSaverCM32 *saver = 0, bool clearInk = false);
|
||||||
|
|
||||||
void DVAPI rectFillInk(const TRasterCM32P &ras, const TRect &r, int color);
|
void DVAPI rectFillInk(const TRasterCM32P &ras, const TRect &r, int color);
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "toonz/tobjecthandle.h"
|
#include "toonz/tobjecthandle.h"
|
||||||
#include "toonz/tcolumnhandle.h"
|
#include "toonz/tcolumnhandle.h"
|
||||||
#include "toonz/preferences.h"
|
#include "toonz/preferences.h"
|
||||||
|
#include "toonz/fill.h"
|
||||||
|
|
||||||
// TnzBase includes
|
// TnzBase includes
|
||||||
#include "tenv.h"
|
#include "tenv.h"
|
||||||
|
@ -62,6 +63,7 @@ using namespace ToolUtils;
|
||||||
#define RECTERASE L"Rectangular"
|
#define RECTERASE L"Rectangular"
|
||||||
#define FREEHANDERASE L"Freehand"
|
#define FREEHANDERASE L"Freehand"
|
||||||
#define POLYLINEERASE L"Polyline"
|
#define POLYLINEERASE L"Polyline"
|
||||||
|
#define SEGMENTERASE L"Segment"
|
||||||
|
|
||||||
TEnv::DoubleVar EraseSize("InknpaintEraseSize", 10);
|
TEnv::DoubleVar EraseSize("InknpaintEraseSize", 10);
|
||||||
TEnv::StringVar EraseType("InknpaintEraseType", "Normal");
|
TEnv::StringVar EraseType("InknpaintEraseType", "Normal");
|
||||||
|
@ -268,6 +270,59 @@ public:
|
||||||
int getHistoryType() override { return HistoryType::EraserTool; }
|
int getHistoryType() override { return HistoryType::EraserTool; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// RasterSegmentEraseUndo
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class RasterSegmentEraseUndo final : public TRasterUndo {
|
||||||
|
// FillParameters m_params;
|
||||||
|
bool m_saveboxOnly;
|
||||||
|
double m_autoCloseDistance;
|
||||||
|
int m_closeStyleIndex;
|
||||||
|
TPoint m_point;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RasterSegmentEraseUndo(TTileSetCM32 *tileSet, TPoint point,
|
||||||
|
TXshSimpleLevel *sl, const TFrameId &fid,
|
||||||
|
bool saveboxOnly)
|
||||||
|
: TRasterUndo(tileSet, sl, fid, false, false, 0)
|
||||||
|
, m_point(point)
|
||||||
|
, m_saveboxOnly(saveboxOnly) {}
|
||||||
|
|
||||||
|
void redo() const override {
|
||||||
|
TToonzImageP image = getImage();
|
||||||
|
if (!image) return;
|
||||||
|
bool recomputeSavebox = false;
|
||||||
|
TRasterCM32P r;
|
||||||
|
if (m_saveboxOnly) {
|
||||||
|
TRectD temp = image->getBBox();
|
||||||
|
TRect ttemp = convert(temp);
|
||||||
|
r = image->getRaster()->extract(ttemp);
|
||||||
|
} else
|
||||||
|
r = image->getRaster();
|
||||||
|
|
||||||
|
inkSegment(r, m_point, 0, 2.51, true, 0, true);
|
||||||
|
if (recomputeSavebox) ToolUtils::updateSaveBox();
|
||||||
|
|
||||||
|
TTool::Application *app = TTool::getApplication();
|
||||||
|
if (app) {
|
||||||
|
app->getCurrentXsheet()->notifyXsheetChanged();
|
||||||
|
notifyImageChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getSize() const override {
|
||||||
|
return sizeof(*this) + TRasterUndo::getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
QString getToolName() override {
|
||||||
|
return QString("Erase Tool : %1").arg("Segment");
|
||||||
|
}
|
||||||
|
int getHistoryType() override { return HistoryType::EraserTool; }
|
||||||
|
};
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
void eraseStroke(const TToonzImageP &ti, TStroke *stroke,
|
void eraseStroke(const TToonzImageP &ti, TStroke *stroke,
|
||||||
std::wstring eraseType, std::wstring colorType, bool invert,
|
std::wstring eraseType, std::wstring colorType, bool invert,
|
||||||
bool selective, bool pencil, int styleId,
|
bool selective, bool pencil, int styleId,
|
||||||
|
@ -298,6 +353,82 @@ void eraseStroke(const TToonzImageP &ti, TStroke *stroke,
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void eraseSegment(const TImageP &img, const TPointD &pos, TXshSimpleLevel *sl,
|
||||||
|
const TFrameId &fid, bool selective, int styleId) {
|
||||||
|
TTool::Application *app = TTool::getApplication();
|
||||||
|
if (!app) return;
|
||||||
|
|
||||||
|
if (TToonzImageP ti = TToonzImageP(img)) {
|
||||||
|
TPoint offs(0, 0);
|
||||||
|
TRasterCM32P ras = ti->getRaster();
|
||||||
|
|
||||||
|
if (Preferences::instance()->getFillOnlySavebox()) {
|
||||||
|
TRectD bbox = ti->getBBox();
|
||||||
|
TRect ibbox = convert(bbox);
|
||||||
|
offs = ibbox.getP00();
|
||||||
|
ras = ti->getRaster()->extract(ibbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool recomputeSavebox = false;
|
||||||
|
TPalette *plt = ti->getPalette();
|
||||||
|
|
||||||
|
if (!ras.getPointer() || ras->isEmpty()) return;
|
||||||
|
|
||||||
|
ras->lock();
|
||||||
|
|
||||||
|
TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize());
|
||||||
|
TTileSaverCM32 tileSaver(ras, tileSet);
|
||||||
|
TDimension imageSize = ti->getSize();
|
||||||
|
TPointD p(imageSize.lx % 2 ? 0.0 : 0.5, imageSize.ly % 2 ? 0.0 : 0.5);
|
||||||
|
|
||||||
|
TPointD tmp_p = pos - p;
|
||||||
|
TPoint newPoint =
|
||||||
|
TPoint((int)floor(tmp_p.x + 0.5), (int)floor(tmp_p.y + 0.5));
|
||||||
|
|
||||||
|
newPoint += ti->getRaster()->getCenter();
|
||||||
|
newPoint -= offs;
|
||||||
|
|
||||||
|
TRect rasRect(ras->getSize());
|
||||||
|
if (!rasRect.contains(newPoint)) {
|
||||||
|
ras->unlock();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
inkSegment(ras, newPoint, 0, 2.51, true, &tileSaver, true);
|
||||||
|
|
||||||
|
if (tileSaver.getTileSet()->getTileCount() != 0) {
|
||||||
|
static int count = 0;
|
||||||
|
// TSystem::outputDebug("FILL" + std::to_string(count++) + "\n");
|
||||||
|
if (offs != TPoint())
|
||||||
|
for (int i = 0; i < tileSet->getTileCount(); i++) {
|
||||||
|
TTileSet::Tile *t = tileSet->editTile(i);
|
||||||
|
t->m_rasterBounds = t->m_rasterBounds + offs;
|
||||||
|
}
|
||||||
|
TUndoManager::manager()->add(new RasterSegmentEraseUndo(
|
||||||
|
tileSet, newPoint, sl, fid,
|
||||||
|
Preferences::instance()->getFillOnlySavebox()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// instead of updateFrame :
|
||||||
|
|
||||||
|
TXshLevel *xl = app->getCurrentLevel()->getLevel();
|
||||||
|
if (!xl) return;
|
||||||
|
|
||||||
|
TXshSimpleLevel *sl = xl->getSimpleLevel();
|
||||||
|
sl->getProperties()->setDirtyFlag(true);
|
||||||
|
if (recomputeSavebox &&
|
||||||
|
Preferences::instance()->isMinimizeSaveboxAfterEditing())
|
||||||
|
ToolUtils::updateSaveBox(sl, fid);
|
||||||
|
|
||||||
|
ras->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
TTool *t = app->getCurrentTool()->getTool();
|
||||||
|
if (t) t->notifyImageChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
void drawLine(const TPointD &point, const TPointD ¢re, bool horizontal,
|
void drawLine(const TPointD &point, const TPointD ¢re, bool horizontal,
|
||||||
bool isDecimal) {
|
bool isDecimal) {
|
||||||
if (!isDecimal) {
|
if (!isDecimal) {
|
||||||
|
@ -477,6 +608,10 @@ public:
|
||||||
void doMultiEraser(const TImageP &img, double t, const TXshSimpleLevelP &sl,
|
void doMultiEraser(const TImageP &img, double t, const TXshSimpleLevelP &sl,
|
||||||
const TFrameId &fid, const TVectorImageP &firstImage,
|
const TFrameId &fid, const TVectorImageP &firstImage,
|
||||||
const TVectorImageP &lastImage);
|
const TVectorImageP &lastImage);
|
||||||
|
void doMultiSegmentEraser(const TImageP &img, double t,
|
||||||
|
const TXshSimpleLevelP &sl, const TFrameId &fid,
|
||||||
|
const TVectorImageP &firstImage,
|
||||||
|
const TVectorImageP &lastImage);
|
||||||
|
|
||||||
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
|
void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
|
||||||
void onEnter() override;
|
void onEnter() override;
|
||||||
|
@ -606,6 +741,7 @@ EraserTool::EraserTool(std::string name)
|
||||||
m_eraseType.addValue(RECTERASE);
|
m_eraseType.addValue(RECTERASE);
|
||||||
m_eraseType.addValue(FREEHANDERASE);
|
m_eraseType.addValue(FREEHANDERASE);
|
||||||
m_eraseType.addValue(POLYLINEERASE);
|
m_eraseType.addValue(POLYLINEERASE);
|
||||||
|
m_eraseType.addValue(SEGMENTERASE);
|
||||||
|
|
||||||
m_colorType.addValue(LINES);
|
m_colorType.addValue(LINES);
|
||||||
m_colorType.addValue(AREAS);
|
m_colorType.addValue(AREAS);
|
||||||
|
@ -636,6 +772,7 @@ void EraserTool::updateTranslation() {
|
||||||
m_eraseType.setItemUIName(RECTERASE, tr("Rectangular"));
|
m_eraseType.setItemUIName(RECTERASE, tr("Rectangular"));
|
||||||
m_eraseType.setItemUIName(FREEHANDERASE, tr("Freehand"));
|
m_eraseType.setItemUIName(FREEHANDERASE, tr("Freehand"));
|
||||||
m_eraseType.setItemUIName(POLYLINEERASE, tr("Polyline"));
|
m_eraseType.setItemUIName(POLYLINEERASE, tr("Polyline"));
|
||||||
|
m_eraseType.setItemUIName(SEGMENTERASE, tr("Segment"));
|
||||||
|
|
||||||
m_colorType.setQStringName(tr("Mode:"));
|
m_colorType.setQStringName(tr("Mode:"));
|
||||||
m_colorType.setItemUIName(LINES, tr("Lines"));
|
m_colorType.setItemUIName(LINES, tr("Lines"));
|
||||||
|
@ -720,7 +857,8 @@ void EraserTool::draw() {
|
||||||
lx % 2 == 0, ly % 2 == 0);
|
lx % 2 == 0, ly % 2 == 0);
|
||||||
}
|
}
|
||||||
if ((m_eraseType.getValue() == FREEHANDERASE ||
|
if ((m_eraseType.getValue() == FREEHANDERASE ||
|
||||||
m_eraseType.getValue() == POLYLINEERASE) &&
|
m_eraseType.getValue() == POLYLINEERASE ||
|
||||||
|
m_eraseType.getValue() == SEGMENTERASE) &&
|
||||||
m_multi.getValue()) {
|
m_multi.getValue()) {
|
||||||
TPixel color = TPixel32::Red;
|
TPixel color = TPixel32::Red;
|
||||||
tglColor(color);
|
tglColor(color);
|
||||||
|
@ -734,7 +872,9 @@ void EraserTool::draw() {
|
||||||
for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]);
|
for (UINT i = 0; i < m_polyline.size(); i++) tglVertex(m_polyline[i]);
|
||||||
tglVertex(m_mousePos);
|
tglVertex(m_mousePos);
|
||||||
glEnd();
|
glEnd();
|
||||||
} else if (m_eraseType.getValue() == FREEHANDERASE && !m_track.isEmpty()) {
|
} else if ((m_eraseType.getValue() == FREEHANDERASE ||
|
||||||
|
m_eraseType.getValue() == SEGMENTERASE) &&
|
||||||
|
!m_track.isEmpty()) {
|
||||||
TPixel color = ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg
|
TPixel color = ToonzCheck::instance()->getChecks() & ToonzCheck::eBlackBg
|
||||||
? TPixel32::White
|
? TPixel32::White
|
||||||
: TPixel32::Black;
|
: TPixel32::Black;
|
||||||
|
@ -966,7 +1106,8 @@ void EraserTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
|
||||||
m_workingFrameId = getFrameId();
|
m_workingFrameId = getFrameId();
|
||||||
}
|
}
|
||||||
if (m_eraseType.getValue() == FREEHANDERASE ||
|
if (m_eraseType.getValue() == FREEHANDERASE ||
|
||||||
m_eraseType.getValue() == POLYLINEERASE) {
|
m_eraseType.getValue() == POLYLINEERASE ||
|
||||||
|
m_eraseType.getValue() == SEGMENTERASE) {
|
||||||
int col = getColumnIndex();
|
int col = getColumnIndex();
|
||||||
m_enabled = col >= 0;
|
m_enabled = col >= 0;
|
||||||
|
|
||||||
|
@ -1106,7 +1247,8 @@ void EraserTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
|
||||||
}
|
}
|
||||||
invalidate(invalidateRect.enlarge(2) - rasCenter);
|
invalidate(invalidateRect.enlarge(2) - rasCenter);
|
||||||
}
|
}
|
||||||
if (m_eraseType.getValue() == FREEHANDERASE) {
|
if (m_eraseType.getValue() == FREEHANDERASE ||
|
||||||
|
m_eraseType.getValue() == SEGMENTERASE) {
|
||||||
if (!m_enabled || !m_active) return;
|
if (!m_enabled || !m_active) return;
|
||||||
m_track.add(TThickPoint(pos, m_thick), pixelSize2);
|
m_track.add(TThickPoint(pos, m_thick), pixelSize2);
|
||||||
invalidate(m_track.getModifiedRegion());
|
invalidate(m_track.getModifiedRegion());
|
||||||
|
@ -1133,7 +1275,8 @@ void EraserTool::onImageChanged() {
|
||||||
else { // cambio stato.
|
else { // cambio stato.
|
||||||
m_firstFrameSelected = true;
|
m_firstFrameSelected = true;
|
||||||
if (m_eraseType.getValue() != FREEHANDERASE &&
|
if (m_eraseType.getValue() != FREEHANDERASE &&
|
||||||
m_eraseType.getValue() != POLYLINEERASE) {
|
m_eraseType.getValue() != POLYLINEERASE &&
|
||||||
|
m_eraseType.getValue() != SEGMENTERASE) {
|
||||||
assert(!m_selectingRect.isEmpty());
|
assert(!m_selectingRect.isEmpty());
|
||||||
m_firstRect = m_selectingRect;
|
m_firstRect = m_selectingRect;
|
||||||
}
|
}
|
||||||
|
@ -1344,7 +1487,90 @@ void EraserTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e) {
|
||||||
invalidate(stroke->getBBox().enlarge(2));
|
invalidate(stroke->getBBox().enlarge(2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (m_eraseType.getValue() == SEGMENTERASE) {
|
||||||
|
bool isValid = m_enabled && m_active;
|
||||||
|
m_enabled = m_active = false;
|
||||||
|
if (!isValid) return;
|
||||||
|
if (m_track.isEmpty()) return;
|
||||||
|
double pixelSize2 = getPixelSize() * getPixelSize();
|
||||||
|
|
||||||
|
m_track.filterPoints();
|
||||||
|
double error = (30.0 / 11) * sqrt(pixelSize2);
|
||||||
|
TStroke *stroke = m_track.makeStroke(error);
|
||||||
|
|
||||||
|
stroke->setStyle(1);
|
||||||
|
m_track.clear();
|
||||||
|
|
||||||
|
bool selective = m_currentStyle.getValue();
|
||||||
|
TTool::Application *app = TTool::getApplication();
|
||||||
|
int styleId = app->getCurrentLevelStyleIndex();
|
||||||
|
if (m_multi.getValue()) // stroke multi
|
||||||
|
{
|
||||||
|
if (m_firstFrameSelected) {
|
||||||
|
TFrameId tmp = getFrameId();
|
||||||
|
if (m_firstStroke && stroke)
|
||||||
|
multiAreaEraser(m_level, m_firstFrameId, tmp, m_firstStroke,
|
||||||
|
stroke);
|
||||||
|
notifyImageChanged();
|
||||||
|
if (e.isShiftPressed()) {
|
||||||
|
TRectD invalidateRect = m_firstStroke->getBBox();
|
||||||
|
delete m_firstStroke;
|
||||||
|
m_firstStroke = 0;
|
||||||
|
invalidate(invalidateRect.enlarge(2));
|
||||||
|
m_firstStroke = stroke;
|
||||||
|
invalidateRect = m_firstStroke->getBBox();
|
||||||
|
invalidate(invalidateRect.enlarge(2));
|
||||||
|
m_firstFrameId = getFrameId();
|
||||||
|
} else {
|
||||||
|
if (m_isXsheetCell) {
|
||||||
|
app->getCurrentColumn()->setColumnIndex(m_currCell.first);
|
||||||
|
app->getCurrentFrame()->setFrame(m_currCell.second);
|
||||||
|
} else
|
||||||
|
app->getCurrentFrame()->setFid(m_veryFirstFrameId);
|
||||||
|
resetMulti();
|
||||||
|
delete stroke;
|
||||||
|
}
|
||||||
|
} else // primo frame
|
||||||
|
{
|
||||||
|
m_firstStroke = stroke;
|
||||||
|
m_isXsheetCell = app->getCurrentFrame()->isEditingScene();
|
||||||
|
m_currCell = std::pair<int, int>(getColumnIndex(), getFrame());
|
||||||
|
invalidate(m_firstStroke->getBBox().enlarge(2));
|
||||||
|
}
|
||||||
|
} else // stroke non multi
|
||||||
|
{
|
||||||
|
TUndoManager::manager()->beginBlock();
|
||||||
|
int length = (int)stroke->getLength();
|
||||||
|
TTool::Application *app = TTool::getApplication();
|
||||||
|
if (!app) return;
|
||||||
|
|
||||||
|
TXshLevel *xl = app->getCurrentLevel()->getLevel();
|
||||||
|
TXshSimpleLevel *sl = xl ? xl->getSimpleLevel() : 0;
|
||||||
|
|
||||||
|
TToonzImageP ti = TImageP(getImage(true));
|
||||||
|
if (!ti) return;
|
||||||
|
TRasterCM32P ras = ti->getRaster();
|
||||||
|
if (!ras) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
TPointD lengthAt = stroke->getPointAtLength(i);
|
||||||
|
TPointD tmpPos(lengthAt);
|
||||||
|
TPoint ipos((int)(tmpPos.x + ras->getLx() / 2),
|
||||||
|
(int)(tmpPos.y + ras->getLy() / 2));
|
||||||
|
if (!ras->getBounds().contains(ipos)) continue;
|
||||||
|
TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
|
||||||
|
if ((selective && pix.getInk() != styleId) || pix.isPurePaint())
|
||||||
|
continue;
|
||||||
|
eraseSegment(getImage(true), tmpPos, sl, getCurrentFid(), selective,
|
||||||
|
styleId);
|
||||||
|
}
|
||||||
|
|
||||||
|
TPointD mousePos = pos;
|
||||||
|
|
||||||
|
TUndoManager::manager()->endBlock();
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
ToolUtils::updateSaveBox();
|
ToolUtils::updateSaveBox();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1623,6 +1849,10 @@ void EraserTool::multiAreaEraser(const TXshSimpleLevelP &sl, TFrameId &firstFid,
|
||||||
assert(firstFid <= fid && fid <= lastFid);
|
assert(firstFid <= fid && fid <= lastFid);
|
||||||
TImageP img = sl->getFrame(fid, true);
|
TImageP img = sl->getFrame(fid, true);
|
||||||
double t = m > 1 ? (double)i / (double)(m - 1) : 0.5;
|
double t = m > 1 ? (double)i / (double)(m - 1) : 0.5;
|
||||||
|
if (m_eraseType.getValue() == SEGMENTERASE)
|
||||||
|
doMultiSegmentEraser(img, backward ? 1 - t : t, sl, fid, firstImage,
|
||||||
|
lastImage);
|
||||||
|
else
|
||||||
doMultiEraser(img, backward ? 1 - t : t, sl, fid, firstImage, lastImage);
|
doMultiEraser(img, backward ? 1 - t : t, sl, fid, firstImage, lastImage);
|
||||||
sl->getProperties()->setDirtyFlag(true);
|
sl->getProperties()->setDirtyFlag(true);
|
||||||
notifyImageChanged(fid);
|
notifyImageChanged(fid);
|
||||||
|
@ -1632,6 +1862,85 @@ void EraserTool::multiAreaEraser(const TXshSimpleLevelP &sl, TFrameId &firstFid,
|
||||||
|
|
||||||
//-------------------------------------------------------------------------------------------------
|
//-------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void EraserTool::doMultiSegmentEraser(const TImageP &img, double t,
|
||||||
|
const TXshSimpleLevelP &sl,
|
||||||
|
const TFrameId &fid,
|
||||||
|
const TVectorImageP &firstImage,
|
||||||
|
const TVectorImageP &lastImage) {
|
||||||
|
// ColorController::instance()->switchToLevelPalette();
|
||||||
|
int styleId = TTool::getApplication()->getCurrentLevelStyleIndex();
|
||||||
|
bool selective = m_currentStyle.getValue();
|
||||||
|
if (t == 0) {
|
||||||
|
TStroke *stroke = firstImage->getStroke(0);
|
||||||
|
int length = (int)stroke->getLength();
|
||||||
|
int styleId = 0;
|
||||||
|
TToonzImageP ti = img;
|
||||||
|
if (!ti) return;
|
||||||
|
TRasterCM32P ras = ti->getRaster();
|
||||||
|
if (!ras) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
TPointD lengthAt = stroke->getPointAtLength(i);
|
||||||
|
TPointD tmpPos(lengthAt);
|
||||||
|
TPoint ipos((int)(tmpPos.x + ras->getLx() / 2),
|
||||||
|
(int)(tmpPos.y + ras->getLy() / 2));
|
||||||
|
if (!ras->getBounds().contains(ipos)) continue;
|
||||||
|
TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
|
||||||
|
if ((selective && pix.getInk() != styleId) || pix.isPurePaint()) continue;
|
||||||
|
eraseSegment(img, tmpPos, sl.getPointer(), fid, selective, styleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (t == 1) {
|
||||||
|
TStroke *stroke = lastImage->getStroke(0);
|
||||||
|
int length = (int)stroke->getLength();
|
||||||
|
int styleId = 0;
|
||||||
|
TToonzImageP ti = img;
|
||||||
|
if (!ti) return;
|
||||||
|
TRasterCM32P ras = ti->getRaster();
|
||||||
|
if (!ras) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
TPointD lengthAt = stroke->getPointAtLength(i);
|
||||||
|
TPointD tmpPos(lengthAt);
|
||||||
|
TPoint ipos((int)(tmpPos.x + ras->getLx() / 2),
|
||||||
|
(int)(tmpPos.y + ras->getLy() / 2));
|
||||||
|
if (!ras->getBounds().contains(ipos)) continue;
|
||||||
|
TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
|
||||||
|
if ((selective && pix.getInk() != styleId) || pix.isPurePaint()) continue;
|
||||||
|
eraseSegment(img, tmpPos, sl.getPointer(), fid, selective, styleId);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
assert(firstImage->getStrokeCount() == 1);
|
||||||
|
assert(lastImage->getStrokeCount() == 1);
|
||||||
|
TVectorImageP vi = TInbetween(firstImage, lastImage).tween(t);
|
||||||
|
assert(vi->getStrokeCount() == 1);
|
||||||
|
TStroke *stroke = vi->getStroke(0);
|
||||||
|
int length = (int)stroke->getLength();
|
||||||
|
int styleId = 0;
|
||||||
|
TToonzImageP ti = img;
|
||||||
|
if (!ti) return;
|
||||||
|
TRasterCM32P ras = ti->getRaster();
|
||||||
|
if (!ras) return;
|
||||||
|
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
TPointD lengthAt = stroke->getPointAtLength(i);
|
||||||
|
TPointD tmpPos(lengthAt);
|
||||||
|
TPoint ipos((int)(tmpPos.x + ras->getLx() / 2),
|
||||||
|
(int)(tmpPos.y + ras->getLy() / 2));
|
||||||
|
if (!ras->getBounds().contains(ipos)) continue;
|
||||||
|
TPixelCM32 pix = ras->pixels(ipos.y)[ipos.x];
|
||||||
|
if ((selective && pix.getInk() != styleId) || pix.isPurePaint()) continue;
|
||||||
|
eraseSegment(img, tmpPos, sl.getPointer(), fid, selective, styleId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TTool::Application *app = TTool::getApplication();
|
||||||
|
if (!app) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
void EraserTool::doMultiEraser(const TImageP &img, double t,
|
void EraserTool::doMultiEraser(const TImageP &img, double t,
|
||||||
const TXshSimpleLevelP &sl, const TFrameId &fid,
|
const TXshSimpleLevelP &sl, const TFrameId &fid,
|
||||||
const TVectorImageP &firstImage,
|
const TVectorImageP &firstImage,
|
||||||
|
|
|
@ -2166,6 +2166,8 @@ EraserToolOptionsBox::EraserToolOptionsBox(QWidget *parent, TTool *tool,
|
||||||
if (m_hardnessField)
|
if (m_hardnessField)
|
||||||
m_hardnessLabel = m_labels.value(m_hardnessField->propertyName());
|
m_hardnessLabel = m_labels.value(m_hardnessField->propertyName());
|
||||||
m_colorMode = dynamic_cast<ToolOptionCombo *>(m_controls.value("Mode:"));
|
m_colorMode = dynamic_cast<ToolOptionCombo *>(m_controls.value("Mode:"));
|
||||||
|
if (m_colorMode)
|
||||||
|
m_colorModeLabel = m_labels.value(m_colorMode->propertyName());
|
||||||
m_invertMode = dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Invert"));
|
m_invertMode = dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Invert"));
|
||||||
m_multiFrameMode =
|
m_multiFrameMode =
|
||||||
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Frame Range"));
|
dynamic_cast<ToolOptionCheckbox *>(m_controls.value("Frame Range"));
|
||||||
|
@ -2194,6 +2196,14 @@ EraserToolOptionsBox::EraserToolOptionsBox(QWidget *parent, TTool *tool,
|
||||||
m_multiFrameMode->setEnabled(false);
|
m_multiFrameMode->setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_toolType && m_toolType->getProperty()->getValue() == L"Segment") {
|
||||||
|
if (m_colorMode) {
|
||||||
|
m_colorMode->setEnabled(false);
|
||||||
|
m_colorModeLabel->setEnabled(false);
|
||||||
|
}
|
||||||
|
m_invertMode->setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (m_colorMode && m_colorMode->getProperty()->getValue() == L"Areas") {
|
if (m_colorMode && m_colorMode->getProperty()->getValue() == L"Areas") {
|
||||||
assert(m_hardnessField && m_hardnessLabel && m_pencilMode);
|
assert(m_hardnessField && m_hardnessLabel && m_pencilMode);
|
||||||
m_pencilMode->setEnabled(false);
|
m_pencilMode->setEnabled(false);
|
||||||
|
@ -2224,8 +2234,13 @@ void EraserToolOptionsBox::onPencilModeToggled(bool value) {
|
||||||
void EraserToolOptionsBox::onToolTypeChanged(int index) {
|
void EraserToolOptionsBox::onToolTypeChanged(int index) {
|
||||||
const TEnumProperty::Range &range = m_toolType->getProperty()->getRange();
|
const TEnumProperty::Range &range = m_toolType->getProperty()->getRange();
|
||||||
bool value = range[index] != L"Normal";
|
bool value = range[index] != L"Normal";
|
||||||
m_invertMode->setEnabled(value);
|
|
||||||
m_multiFrameMode->setEnabled(value);
|
m_multiFrameMode->setEnabled(value);
|
||||||
|
bool isSegment = range[index] == L"Segment";
|
||||||
|
if (m_colorMode) {
|
||||||
|
m_colorMode->setDisabled(isSegment);
|
||||||
|
m_colorModeLabel->setDisabled(isSegment);
|
||||||
|
}
|
||||||
|
m_invertMode->setEnabled(!isSegment && value);
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -387,7 +387,7 @@ void FullColorAreaFiller::rectFill(const TRect &rect,
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// InkSegmenter
|
// InkSegmenter
|
||||||
|
|
||||||
const int damInk = 3;
|
const int damInk = 4094;
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -403,9 +403,12 @@ class InkSegmenter {
|
||||||
TRasterCM32P m_r;
|
TRasterCM32P m_r;
|
||||||
TTileSaverCM32 *m_saver;
|
TTileSaverCM32 *m_saver;
|
||||||
float m_growFactor;
|
float m_growFactor;
|
||||||
|
int m_oldInk;
|
||||||
|
bool m_clearInk;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
InkSegmenter(const TRasterCM32P &r, float growFactor, TTileSaverCM32 *saver)
|
InkSegmenter(const TRasterCM32P &r, float growFactor, TTileSaverCM32 *saver,
|
||||||
|
bool clearInk = false)
|
||||||
: m_r(r)
|
: m_r(r)
|
||||||
, m_lx(r->getLx())
|
, m_lx(r->getLx())
|
||||||
, m_ly(r->getLy())
|
, m_ly(r->getLy())
|
||||||
|
@ -413,6 +416,8 @@ public:
|
||||||
, m_buf((TPixelCM32 *)r->getRawData())
|
, m_buf((TPixelCM32 *)r->getRawData())
|
||||||
, m_bBox(r->getBounds())
|
, m_bBox(r->getBounds())
|
||||||
, m_saver(saver)
|
, m_saver(saver)
|
||||||
|
, m_clearInk(clearInk)
|
||||||
|
, m_oldInk(-1)
|
||||||
, m_growFactor(growFactor) {
|
, m_growFactor(growFactor) {
|
||||||
m_displaceVector[0] = -m_wrap - 1;
|
m_displaceVector[0] = -m_wrap - 1;
|
||||||
m_displaceVector[1] = -m_wrap;
|
m_displaceVector[1] = -m_wrap;
|
||||||
|
@ -445,7 +450,7 @@ public:
|
||||||
pix = m_buf + p.y * m_wrap + p.x;
|
pix = m_buf + p.y * m_wrap + p.x;
|
||||||
|
|
||||||
/*-- 同じインクの場合はreturn --*/
|
/*-- 同じインクの場合はreturn --*/
|
||||||
if (pix->getInk() == ink) return false;
|
if (pix->getInk() == ink && !m_clearInk) return false;
|
||||||
|
|
||||||
if (!ConnectionTable[neighboursCode(pix, p)]) {
|
if (!ConnectionTable[neighboursCode(pix, p)]) {
|
||||||
master = slave = pix;
|
master = slave = pix;
|
||||||
|
@ -469,9 +474,13 @@ public:
|
||||||
inkSegmentFill(p, ink, isSelective, m_saver);
|
inkSegmentFill(p, ink, isSelective, m_saver);
|
||||||
|
|
||||||
// UINT i;
|
// UINT i;
|
||||||
|
if (m_clearInk) {
|
||||||
|
drawSegment(d1p1, d1p2, m_oldInk, m_saver, true);
|
||||||
|
drawSegment(d2p1, d2p2, m_oldInk, m_saver, true);
|
||||||
|
} else {
|
||||||
drawSegment(d1p1, d1p2, ink, m_saver);
|
drawSegment(d1p1, d1p2, ink, m_saver);
|
||||||
drawSegment(d2p1, d2p2, ink, m_saver);
|
drawSegment(d2p1, d2p2, ink, m_saver);
|
||||||
|
}
|
||||||
|
|
||||||
/* for (i=0; i<oldInks.size(); i++)
|
/* for (i=0; i<oldInks.size(); i++)
|
||||||
(oldInks[i].first)->setInk(ink);*/
|
(oldInks[i].first)->setInk(ink);*/
|
||||||
|
@ -482,7 +491,8 @@ public:
|
||||||
private:
|
private:
|
||||||
void drawSegment(
|
void drawSegment(
|
||||||
const TPoint &p0, const TPoint &p1, int ink,
|
const TPoint &p0, const TPoint &p1, int ink,
|
||||||
/*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver);
|
/*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver,
|
||||||
|
bool clearInk = false);
|
||||||
|
|
||||||
int findTwinPoints(TPixelCM32 *pix, const TPoint &p, TPixelCM32 *&master,
|
int findTwinPoints(TPixelCM32 *pix, const TPoint &p, TPixelCM32 *&master,
|
||||||
TPoint &mp, TPixelCM32 *&slave, TPoint &sp);
|
TPoint &mp, TPixelCM32 *&slave, TPoint &sp);
|
||||||
|
@ -565,14 +575,23 @@ private:
|
||||||
if (saver) saver->save(TPoint(x1 + x, y1 + y)); \
|
if (saver) saver->save(TPoint(x1 + x, y1 + y)); \
|
||||||
/*if (buf->getInk()!=damInk)*/ \
|
/*if (buf->getInk()!=damInk)*/ \
|
||||||
/* oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk()));*/ \
|
/* oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk()));*/ \
|
||||||
|
if (!clearInk) buf->setInk(ink); \
|
||||||
|
pixels.push_back(buf); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CLEAR_INK \
|
||||||
|
{ \
|
||||||
|
if (saver) saver->save(TPoint(x1 + x, y1 + y)); \
|
||||||
buf->setInk(ink); \
|
buf->setInk(ink); \
|
||||||
|
buf->setTone(255); \
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
void InkSegmenter::drawSegment(
|
void InkSegmenter::drawSegment(
|
||||||
const TPoint &p0, const TPoint &p1, int ink,
|
const TPoint &p0, const TPoint &p1, int ink,
|
||||||
/*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver) {
|
/*vector<pair<TPixelCM32*, int> >& oldInks,*/ TTileSaverCM32 *saver,
|
||||||
|
bool clearInk) {
|
||||||
int x, y, dx, dy, d, incr_1, incr_2;
|
int x, y, dx, dy, d, incr_1, incr_2;
|
||||||
|
|
||||||
int x1 = p0.x;
|
int x1 = p0.x;
|
||||||
|
@ -586,8 +605,8 @@ void InkSegmenter::drawSegment(
|
||||||
}
|
}
|
||||||
|
|
||||||
TPixelCM32 *buf = m_r->pixels() + y1 * m_wrap + x1;
|
TPixelCM32 *buf = m_r->pixels() + y1 * m_wrap + x1;
|
||||||
/*if (buf->getInk()!=damInk)
|
if (buf->getInk() != damInk) m_oldInk = buf->getInk();
|
||||||
oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk()));
|
/* oldInks.push_back(pair<TPixelCM32*, int>(buf, buf->getInk()));
|
||||||
if ((m_r->pixels() + y2*m_wrap + x2)->getInk()!=damInk)
|
if ((m_r->pixels() + y2*m_wrap + x2)->getInk()!=damInk)
|
||||||
oldInks.push_back(pair<TPixelCM32*, int>(m_r->pixels() + y2*m_wrap +
|
oldInks.push_back(pair<TPixelCM32*, int>(m_r->pixels() + y2*m_wrap +
|
||||||
x2, (m_r->pixels() + y2*m_wrap + x2)->getInk()));*/
|
x2, (m_r->pixels() + y2*m_wrap + x2)->getInk()));*/
|
||||||
|
@ -600,6 +619,14 @@ void InkSegmenter::drawSegment(
|
||||||
buf->setInk(ink);
|
buf->setInk(ink);
|
||||||
(m_r->pixels() + y2 * m_wrap + x2)->setInk(ink);
|
(m_r->pixels() + y2 * m_wrap + x2)->setInk(ink);
|
||||||
|
|
||||||
|
std::vector<TPixelCM32*> pixels;
|
||||||
|
if (clearInk) {
|
||||||
|
buf->setTone(255);
|
||||||
|
(m_r->pixels() + y2 * m_wrap + x2)->setTone(255);
|
||||||
|
pixels.push_back(buf);
|
||||||
|
pixels.push_back(m_r->pixels() + y2 * m_wrap + x2);
|
||||||
|
}
|
||||||
|
|
||||||
dx = x2 - x1;
|
dx = x2 - x1;
|
||||||
dy = y2 - y1;
|
dy = y2 - y1;
|
||||||
|
|
||||||
|
@ -618,6 +645,31 @@ void InkSegmenter::drawSegment(
|
||||||
DRAW_SEGMENT(y, x, dy, dx, (buf -= m_wrap), (buf -= (m_wrap - 1)),
|
DRAW_SEGMENT(y, x, dy, dx, (buf -= m_wrap), (buf -= (m_wrap - 1)),
|
||||||
SET_INK)
|
SET_INK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (clearInk) {
|
||||||
|
bool lonelyPixels = true;
|
||||||
|
// make sure we don't put back the original color of isolated pixels.
|
||||||
|
for (auto pix : pixels) {
|
||||||
|
if ((ePix(pix)->getInk() != damInk && !ePix(pix)->isPurePaint()) || (wPix(pix)->getInk() != damInk && !wPix(pix)->isPurePaint()) ||
|
||||||
|
(sPix(pix)->getInk() != damInk && !sPix(pix)->isPurePaint()) || (nPix(pix)->getInk() != damInk && !nPix(pix)->isPurePaint()) ||
|
||||||
|
(nePix(pix)->getInk() != damInk && !nePix(pix)->isPurePaint()) || (sePix(pix)->getInk() != damInk && !sePix(pix)->isPurePaint()) ||
|
||||||
|
(swPix(pix)->getInk() != damInk && !swPix(pix)->isPurePaint()) || (nwPix(pix)->getInk() != damInk && !nwPix(pix)->isPurePaint())) {
|
||||||
|
lonelyPixels = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lonelyPixels) {
|
||||||
|
for (auto pix : pixels) {
|
||||||
|
pix->setInk(ink);
|
||||||
|
pix->setTone(255);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (auto pix : pixels) {
|
||||||
|
pix->setInk(ink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -631,7 +683,7 @@ void InkSegmenter::inkSegmentFill(const TPoint &p, int ink, bool isSelective,
|
||||||
TPixelCM32 *pix = pixels + p.y * m_wrap + x;
|
TPixelCM32 *pix = pixels + p.y * m_wrap + x;
|
||||||
int oldInk;
|
int oldInk;
|
||||||
|
|
||||||
if (pix->isPurePaint() || pix->getInk() == ink) return;
|
if (pix->isPurePaint() || (pix->getInk() == ink && !m_clearInk)) return;
|
||||||
|
|
||||||
if (isSelective) oldInk = pix->getInk();
|
if (isSelective) oldInk = pix->getInk();
|
||||||
|
|
||||||
|
@ -645,13 +697,14 @@ void InkSegmenter::inkSegmentFill(const TPoint &p, int ink, bool isSelective,
|
||||||
x = seed.x;
|
x = seed.x;
|
||||||
y = seed.y;
|
y = seed.y;
|
||||||
TPixelCM32 *pix = pixels + (y * m_wrap + x);
|
TPixelCM32 *pix = pixels + (y * m_wrap + x);
|
||||||
if (pix->isPurePaint() || pix->getInk() == ink || pix->getInk() == damInk ||
|
if (pix->isPurePaint() || (pix->getInk() == ink && !m_clearInk) ||
|
||||||
(isSelective && pix->getInk() != oldInk))
|
pix->getInk() == damInk || (isSelective && pix->getInk() != oldInk))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (saver) saver->save(seed);
|
if (saver) saver->save(seed);
|
||||||
|
|
||||||
pix->setInk(ink);
|
pix->setInk(ink);
|
||||||
|
if (m_clearInk) pix->setTone(255);
|
||||||
|
|
||||||
if (x > 0) seeds.push(TPoint(x - 1, y));
|
if (x > 0) seeds.push(TPoint(x - 1, y));
|
||||||
if (y > 0) seeds.push(TPoint(x, y - 1));
|
if (y > 0) seeds.push(TPoint(x, y - 1));
|
||||||
|
@ -1112,9 +1165,10 @@ int InkSegmenter::nextPointIsGoodRev(TPoint mp, TPoint sp, TPixelCM32 *slave,
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
bool inkSegment(const TRasterCM32P &r, const TPoint &p, int ink,
|
bool inkSegment(const TRasterCM32P &r, const TPoint &p, int ink,
|
||||||
float growFactor, bool isSelective, TTileSaverCM32 *saver) {
|
float growFactor, bool isSelective, TTileSaverCM32 *saver,
|
||||||
|
bool clearInk) {
|
||||||
r->lock();
|
r->lock();
|
||||||
InkSegmenter is(r, growFactor, saver);
|
InkSegmenter is(r, growFactor, saver, clearInk);
|
||||||
bool ret = is.compute(p, ink, isSelective);
|
bool ret = is.compute(p, ink, isSelective);
|
||||||
r->unlock();
|
r->unlock();
|
||||||
return ret;
|
return ret;
|
||||||
|
|
Loading…
Reference in a new issue