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:
Jeremy Bullock 2020-11-20 09:01:21 -07:00 committed by GitHub
parent 5563373272
commit 52dd1a1ce7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 403 additions and 25 deletions

View file

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

View file

@ -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);

View file

@ -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 &centre, bool horizontal, void drawLine(const TPointD &point, const TPointD &centre, 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,

View file

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

View file

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