Merge pull request #1215 from TomDoingArt/Issue_1151_ResidualGapOnFillGaps

Issue 1151 residual gap on fill gaps
This commit is contained in:
manongjohn 2023-10-11 20:54:07 -04:00 committed by GitHub
commit dbf0fc8da2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 491 additions and 174 deletions

View file

@ -3717,6 +3717,12 @@ TThickCubic *TCubicStroke::generateCubic3D(const T3DPointD pointsArrayBegin[],
TCubicStroke, cioe' uno stroke cubico. Da questo trova lo stroke
quadratico.
*/
//! Returns a stroke from a vector of TThickPoints
/*!
Transforms an array of TThickPoints into an array of T3DPointDs
which is used for a TCubicStroke, i.e. a cubic stroke.
From this he finds the stroke quadratic.
*/
TStroke *TStroke::interpolate(const vector<TThickPoint> &points, double error,
bool findCorners) {
vector<T3DPointD> pointsArray3D;

View file

@ -73,6 +73,8 @@ class TTileSaverFullColor;
//=============================================================================
DVAPI void outputPixels(const std::string _str, const TRasterCM32P& r);
// returns true if the savebox is changed typically, if you fill the bg)
DVAPI bool fill(const TRasterCM32P &r, const FillParameters &params,
TTileSaverCM32 *saver = 0, bool fillGaps = false,
@ -103,6 +105,12 @@ void DVAPI fullColorFill(const TRaster32P &ras, const FillParameters &params,
bool closeGaps = false, int closeStyleIndex = -1,
double autoCloseDistance = -1.0);
void DVAPI finishGapLines(TRasterCM32P &rin, TRect &rect,
const TRasterCM32P &rbefore,
const TRasterCM32P &combined, TPalette *plt,
int clickedColorStyle, int fillIndex,
int closeColorStyle, bool closeGaps);
//=============================================================================
//! The class AreaFiller allows to fill a raster area, delimited by rect or
//! spline.

View file

@ -22,6 +22,9 @@ class TStroke;
Genera degli stroke sulla base di un vettore di punti,
questo vettore e' fatto di punti che vengono inseriti.
Consente di visualizzare dei frammenti fatti dai punti che acquisisce.
Generate strokes based on a vector of points,
this vector is made of points that are inserted.
Allows you to view fragments made from the points it acquires.
*/
class DVAPI StrokeGenerator {
//! Vettore di TThickPoint

View file

@ -65,6 +65,10 @@ using namespace ToolUtils;
#define POLYLINEFILL L"Polyline"
#define FREEPICKFILL L"Freepick"
#define GAP_CLOSE_USED 4095
#define GAP_CLOSE_TEMP 4094
#define IGNORECOLORSTYLE 4093
TEnv::IntVar MinFillDepth("InknpaintMinFillDepth", 1);
TEnv::IntVar MaxFillDepth("InknpaintMaxFillDepth", 10);
TEnv::StringVar FillType("InknpaintFillType", "Normal");
@ -153,6 +157,8 @@ bool applyAutoclose(const TToonzImageP &ti, int distance, int angle,
ras = raux;
if (!ras) return false;
//outputPixels("ras in applyAutoClose", ras);
TAutocloser ac(ras, distance, angle, newInkIndex, opacity);
std::vector<TAutocloser::Segment> segments;
@ -465,14 +471,9 @@ class RasterFillUndo final : public TRasterUndo {
public:
/*RasterFillUndo(TTileSetCM32 *tileSet, TPoint fillPoint,
int paintId, int
fillDepth,
std::wstring
fillType, bool isSegment,
bool selective, bool
isShiftFill,
TXshSimpleLevel* sl,
const TFrameId& fid)*/
int paintId, int fillDepth, std::wstring fillType,
bool isSegment, bool selective, bool isShiftFill,
TXshSimpleLevel* sl, const TFrameId& fid)*/
RasterFillUndo(TTileSetCM32 *tileSet, const FillParameters &params,
TXshSimpleLevel *sl, const TFrameId &fid, bool saveboxOnly,
bool fillGaps, bool closeGaps, int closeStyleIndex,
@ -585,13 +586,13 @@ public:
TRasterCM32P ras = image->getRaster();
TRasterCM32P tempRaster;
int styleIndex = 4094;
int styleIndex = GAP_CLOSE_TEMP;
if (m_fillGaps) {
TToonzImageP tempTi = image->clone();
tempRaster = tempTi->getRaster();
TRectD doubleFillArea = convert(m_fillArea);
applyAutoclose(tempTi, AutocloseDistance, AutocloseAngle,
AutocloseOpacity, 4094, doubleFillArea, m_s);
AutocloseOpacity, GAP_CLOSE_TEMP, doubleFillArea, m_s);
} else {
tempRaster = ras;
}
@ -605,23 +606,31 @@ public:
m_colorType != AREAS, m_fillArea);
if (m_fillGaps) {
TRect rect = m_fillArea;
finishGapLines(
tempRaster,
rect,
ras,
tempRaster,
m_palette,
0,
m_paintId,
m_closeStyleIndex,
m_closeGaps);
TPixelCM32 *tempPix = tempRaster->pixels();
TPixelCM32 *keepPix = ras->pixels();
for (int tempY = 0; tempY < tempRaster->getLy(); tempY++) {
for (int tempX = 0; tempX < tempRaster->getLx();
tempX++, tempPix++, keepPix++) {
keepPix->setPaint(tempPix->getPaint());
if (tempPix->getInk() != styleIndex) {
if (m_colorType == AREAS && m_closeGaps &&
tempPix->getInk() == 4095) {
keepPix->setInk(m_closeStyleIndex);
keepPix->setTone(tempPix->getTone());
} else if (m_colorType != AREAS && tempPix->getInk() == 4095) {
keepPix->setInk(m_paintId);
keepPix->setTone(tempPix->getTone());
} else if (tempPix->getInk() != 4095) {
if (tempPix->getInk() >= IGNORECOLORSTYLE || tempPix->getPaint() >= IGNORECOLORSTYLE) {
continue;
} else {
keepPix->setInk(tempPix->getInk());
}
keepPix->setPaint(tempPix->getPaint());
keepPix->setTone(tempPix->getTone());
}
}
}
@ -989,33 +998,37 @@ void fillAreaWithUndo(const TImageP &img, const TRectD &area, TStroke *stroke,
TRectD selArea = stroke ? stroke->getBBox() : area;
if (TToonzImageP ti = img) {
// allargo di 1 la savebox, perche cosi' il rectfill di tutta l'immagine fa
// una sola fillata
// Widen the savebox by 1, because this is how the rectfill of the whole image does
// a single fill
TRect enlargedSavebox =
fillOnlySavebox
? ti->getSavebox().enlarge(1) * TRect(TPoint(0, 0), ti->getSize())
: TRect(TPoint(0, 0), ti->getSize());
TRect rasterFillArea =
ToonzImageUtils::convertWorldToRaster(selArea, ti) * enlargedSavebox;
if (rasterFillArea.isEmpty()) return;
TRasterCM32P ras = ti->getRaster();
/*-- tileSetでFill範囲のRectをUndoに格納しておく --*/
/* Store the Rect of the Fill range in Undo with tileSet */
TTileSetCM32 *tileSet = new TTileSetCM32(ras->getSize());
tileSet->add(ras, rasterFillArea);
TRasterCM32P tempRaster;
int styleIndex = 4094;
if (fillGaps) {
int styleIndex = GAP_CLOSE_TEMP;
TToonzImageP tempTi = ti->clone();
tempRaster = tempTi->getRaster();
if (fillGaps) {
applyAutoclose(tempTi, AutocloseDistance, AutocloseAngle,
AutocloseOpacity, 4094, convert(rasterFillArea), stroke);
} else {
tempRaster = ras;
AutocloseOpacity, GAP_CLOSE_TEMP, convert(rasterFillArea), stroke);
}
AreaFiller filler(tempRaster);
if (!stroke) {
bool ret = filler.rectFill(rasterFillArea, cs, onlyUnfilled,
colorType != LINES, colorType != AREAS);
@ -1023,29 +1036,29 @@ void fillAreaWithUndo(const TImageP &img, const TRectD &area, TStroke *stroke,
delete tileSet;
return;
}
} else
} else {
filler.strokeFill(stroke, cs, onlyUnfilled, colorType != LINES,
colorType != AREAS, rasterFillArea);
}
if (fillGaps) {
TPixelCM32 *tempPix = tempRaster->pixels();
TPixelCM32 *keepPix = ras->pixels();
finishGapLines(tempRaster, rasterFillArea, ras, ras, ti->getPalette(), 0,
cs, closeStyleIndex, closeGaps);
for (int tempY = 0; tempY < tempRaster->getLy(); tempY++) {
for (int tempX = 0; tempX < tempRaster->getLx();
tempX++, tempPix++, keepPix++) {
keepPix->setPaint(tempPix->getPaint());
if (tempPix->getInk() != styleIndex) {
if (colorType == AREAS && closeGaps && tempPix->getInk() == 4095) {
keepPix->setInk(closeStyleIndex);
keepPix->setTone(tempPix->getTone());
} else if (colorType != AREAS && tempPix->getInk() == 4095) {
keepPix->setInk(cs);
keepPix->setTone(tempPix->getTone());
} else if (tempPix->getInk() != 4095) {
if (tempPix->getInk() < IGNORECOLORSTYLE) {
keepPix->setInk(tempPix->getInk());
}
if (tempPix->getPaint() < IGNORECOLORSTYLE) {
keepPix->setPaint(tempPix->getPaint());
}
}
keepPix->setTone(tempPix->getTone());
}
}
@ -1185,7 +1198,7 @@ void doFill(const TImageP &img, const TPointD &pos, FillParameters &params,
if (tileSaver.getTileSet()->getTileCount() != 0) {
static int count = 0;
TSystem::outputDebug("FILL" + std::to_string(count++) + "\n");
TSystem::outputDebug("FILL" + std::to_string(count++));
if (offs != TPoint())
for (int i = 0; i < tileSet->getTileCount(); i++) {
TTileSet::Tile *t = tileSet->editTile(i);
@ -2000,7 +2013,14 @@ void AreaFillTool::leftButtonUp(const TPointD &pos, const TMouseEvent &e,
m_enabled = m_active = false;
if (!isValid || m_track.isEmpty()) return;
double pixelSize2 = m_parent->getPixelSize() * m_parent->getPixelSize();
if (m_type == FREEPICK){
//strip off the first n points to allow the stroke to begin away from the initial click point where the color style will be selected.
TPointD firstPoint = m_track.getFirstPoint();
}
// add the first point as also the last point, to form a complete loop.
m_track.add(TThickPoint(m_firstPos, m_thick), pixelSize2);
m_track.filterPoints();
double error = (m_isPath ? 20.0 : 30.0 / 11) * sqrt(pixelSize2);
TStroke *stroke = m_track.makeStroke(error);
@ -2587,8 +2607,8 @@ void FillTool::leftButtonDrag(const TPointD &pos, const TMouseEvent &e) {
invalidate();
return;
}
TSystem::outputDebug("ok. pix=" + std::to_string(pix.getTone()) + "," +
std::to_string(pix.getPaint()) + "\n");
//TSystem::outputDebug("ok. pix=" + std::to_string(pix.getTone()) + "," +
// std::to_string(pix.getPaint()));
} else
return;
int closeStyleIndex = m_closeStyleIndex.getStyleIndex();

View file

@ -1799,7 +1799,7 @@ void FullColorFillToolOptionsBox::onGapSettingChanged(int index) {
m_styleIndex->hide();
m_styleIndexLabel->hide();
m_rasterGapSlider->show();
m_gapSliderLabel->hide();
m_gapSliderLabel->show();
} else if (index == 2) {
m_styleIndex->show();
m_styleIndexLabel->show();

View file

@ -20,11 +20,17 @@
#include <QDebug>
#include "tsystem.h"
extern TEnv::DoubleVar AutocloseDistance;
extern TEnv::DoubleVar AutocloseAngle;
extern TEnv::IntVar AutocloseInk;
extern TEnv::IntVar AutocloseOpacity;
#define IGNORECOLORSTYLE 4093
#define GAP_CLOSE_TEMP 4094
#define GAP_CLOSE_USED 4095
//-----------------------------------------------------------------------------
namespace { // Utility Function
//-----------------------------------------------------------------------------
@ -54,7 +60,7 @@ inline TPoint nearestInkNotDiagonal(const TRasterCM32P &r, const TPoint &p) {
//
// "prevailing" is set to false on revert-filling the border of
// region in the Rectangular, Freehand and Polyline fill procedures
// in order to make the paint to protlude behind the line.
// in order to make the paint extend behind the line.
// Calculates the endpoints for the line of pixels in which to fill
bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
@ -72,10 +78,16 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
oldtone = pix->getTone();
tone = oldtone;
for (; pix <= limit; pix++) {
if (pix->getPaint() == paint) break;
if (emptyOnly && pix->getPaint() != 0) break;
if (pix->getPaint() == paint) {
//break;
}
if (emptyOnly && pix->getPaint() != 0) {
//break;
}
tone = pix->getTone();
if (tone == 0) break;
if (tone == 0) {
break;
}
// prevent fill area from protruding behind the colored line
if (tone > oldtone) {
// not-yet-colored line case
@ -94,8 +106,7 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
TPixelCM32 *upPix = pix - r->getWrap();
TPixelCM32 *downPix = pix + r->getWrap();
if (upPix->getTone() > pix->getTone() &&
downPix->getTone() > pix->getTone())
continue;
downPix->getTone() > pix->getTone()) continue;
}
break;
}
@ -109,12 +120,14 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
tmp_limit = pix + 10; // edge stop fill == 10 per default
if (limit > tmp_limit) limit = tmp_limit;
for (; pix <= limit; pix++) {
if (pix->getPaint() == paint) break;
if (pix->getTone() != 0) break;
//if (pix->getPaint() == paint) break; // commented out for issue 1151
if (pix->getTone() != 0) {
break;
}
}
}
xb = p.x + pix - pix0 - 1;
xb = p.x + pix - pix0 - 1; //go backward one pixel from the current pixel which triggered the boundary condition.
/* go left */
@ -123,15 +136,22 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
oldtone = pix->getTone();
tone = oldtone;
for (pix--; pix >= limit; pix--) {
if (pix->getPaint() == paint) break;
if (emptyOnly && pix->getPaint() != 0) break;
if (pix->getPaint() == paint) {
break;
}
if (emptyOnly && pix->getPaint() != 0) {
break;
}
tone = pix->getTone();
if (tone == 0) break;
if (tone == 0) {
break;
}
// prevent fill area from protruding behind the colored line
if (tone > oldtone) {
// not-yet-colored line case
if (prevailing && !pix->isPurePaint() && pix->getInk() != pix->getPaint())
if (prevailing && !pix->isPurePaint() && pix->getInk() != pix->getPaint()){
break;
}
while (pix != pix0) {
// iterate forward in order to leave the pixel with the lowest tone
// unpainted
@ -145,9 +165,10 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
TPixelCM32 *upPix = pix - r->getWrap();
TPixelCM32 *downPix = pix + r->getWrap();
if (upPix->getTone() > pix->getTone() &&
downPix->getTone() > pix->getTone())
downPix->getTone() > pix->getTone()) {
continue;
}
}
break;
}
}
@ -160,12 +181,14 @@ bool calcFillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb,
tmp_limit = pix - 10;
if (limit < tmp_limit) limit = tmp_limit;
for (; pix >= limit; pix--) {
if (pix->getPaint() == paint) break;
if (pix->getTone() != 0) break;
//if (pix->getPaint() == paint) break; // commented out for issue 1151
if (pix->getTone() != 0) {
break;
}
}
}
xa = p.x + pix - pix0 + 1;
xa = p.x + pix - pix0 + 1; //go backward one pixel from the current pixel which triggered the boundary condition.
return (xb >= xa);
}
@ -430,6 +453,44 @@ TRasterCM32P convertRaster2CM(const TRasterP &inputRaster) {
return rout;
}
//-----------------------------------------------------------------------------
void outputPixels(const std::string _str, const TRasterCM32P &r) {
r->lock();
TPixelCM32* tempPix = r->pixels(0);
std::cout << "\noutputPixels for:";
std::cout << _str;
std::cout << ", bbox is y : ";
std::cout << r->getLy();
std::cout << ",x:";
std::cout << r->getLx();
std::cout << "----";
tempPix = tempPix + ((r->getLy()-1) * r->getLx());
for (int tempY = r->getLy()-1; tempY >= 0; tempY--) {
if (tempY < r->getLy() - 1) {
tempPix = tempPix - 2 * r->getLx();
}
std::cout << "\ny:";
std::cout << tempY;
for (int tempX = 0; tempX < r->getLx();
tempX++, tempPix++) {
std::cout << "|x:";
std::cout << tempX;
std::cout << ":";
std::cout << tempPix->getInk();
std::cout << ".";
std::cout << tempPix->getPaint();
std::cout << ".";
std::cout << tempPix->getTone();
}
}
r->unlock();
std::cout << "\n";
return;
}
//-----------------------------------------------------------------------------
/*-- The return value is whether the saveBox has been updated or not. --*/
bool fill(const TRasterCM32P &r, const FillParameters &params,
TTileSaverCM32 *saver, bool fillGaps, bool closeGaps,
@ -449,9 +510,9 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
int paint = params.m_styleId;
int fillDepth =
params.m_shiftFill ? params.m_maxFillDepth : params.m_minFillDepth;
TRasterCM32P tempRaster;
int styleIndex = 4094;
int fakeStyleIndex = 4095;
TRasterCM32P tempRaster, cr, refCMRaster;
int styleIndex = GAP_CLOSE_TEMP;
int fakeStyleIndex = GAP_CLOSE_USED;
if (autoCloseDistance < 0.0) autoCloseDistance = AutocloseDistance;
bool gapsClosed = false, refGapsClosed = false;
@ -473,7 +534,10 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
if (!bbbox.contains(p)) return false;
/*- If the same color has already been painted, return -*/
int paintAtClickedPos = (tempRaster->pixels(p.y) + p.x)->getPaint();
if (paintAtClickedPos == paint) return false;
if (paintAtClickedPos == paint) {
return false;
}
/*- If the "paint only transparent areas" option is enabled and the area is
* already colored, return
* -*/
@ -511,8 +575,8 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
}
if (fillGaps) {
TRasterCM32P cr = convertRaster2CM(refRaster);
TRasterCM32P refCMRaster = cr->clone();
cr = convertRaster2CM(refRaster);
refCMRaster = cr->clone();
refGapsClosed = TAutocloser(refCMRaster, autoCloseDistance,
AutocloseAngle, styleIndex, AutocloseOpacity)
@ -529,7 +593,7 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
refCMX++, refCMPix++, refPix++, tempPix++) {
if (refCMPix->getInk() != styleIndex) continue;
*refPix = color;
if (closeGaps) {
if (fillGaps) {
tempPix->setInk(refCMPix->getInk());
tempPix->setTone(refCMPix->getTone());
}
@ -539,7 +603,9 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
}
}
if (fillGaps && !gapsClosed && !refGapsClosed) fillGaps = false;
if (fillGaps && !gapsClosed && !refGapsClosed) {
fillGaps = false;
}
assert(fillDepth >= 0 && fillDepth < 16);
@ -580,23 +646,11 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
params.m_prevailing, params.m_emptyOnly)
: 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
{
TPixelCM32 *tempPix = tempRaster->pixels(0);
tempPix += (y * tempRaster->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();
@ -647,19 +701,7 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
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);
tempPix += (y * tempRaster->getLx()) + xa - 1;
int i = xa;
while (i <= xb) {
if (tempPix->getInk() == styleIndex) {
tempPix->setInk(fakeStyleIndex);
}
tempPix++;
i++;
}
}
if (xc < xa) seeds.push(FillSeed(xc, xa - 1, y, -dy));
if (xd > xb) seeds.push(FillSeed(xb + 1, xd, y, -dy));
if (oldxd >= xc - 1)
@ -703,26 +745,27 @@ bool fill(const TRasterCM32P &r, const FillParameters &params,
}
if (fillGaps) {
TPixelCM32 *tempPix = tempRaster->pixels();
TPixelCM32 *keepPix = r->pixels();
finishGapLines(tempRaster, bbbox, r, refCMRaster, params.m_palette, paintAtClickedPos, paint, closeStyleIndex, closeGaps);
TPixelCM32 *tempPix, *tempPixRestart;
tempPixRestart = tempPix = tempRaster->pixels();
TPixelCM32 *keepPix, *keepPixRestart;
keepPixRestart = keepPix = r->pixels();
int fillNeighbors = 0;
int paintableNeighbors = 0;
for (int tempY = 0; tempY < tempRaster->getLy(); tempY++) {
for (int tempX = 0; tempX < tempRaster->getLx();
tempX++, tempPix++, keepPix++) {
if (tempPix->getInk() != styleIndex &&
tempPix->getInk() != fakeStyleIndex)
keepPix->setPaint(tempPix->getPaint());
// This next line takes care of autopaint lines
if (tempPix->getInk() != styleIndex) {
if (closeGaps && tempPix->getInk() == fakeStyleIndex) {
keepPix->setInk(closeStyleIndex);
keepPix->setTone(tempPix->getTone());
} else if (tempPix->getInk() != fakeStyleIndex) {
if (tempPix->getInk() >= IGNORECOLORSTYLE || tempPix->getPaint() >= IGNORECOLORSTYLE) {
continue;
}
keepPix->setInk(tempPix->getInk());
keepPix->setPaint(tempPix->getPaint());
keepPix->setTone(tempPix->getTone());
}
}
}
}
}
return saveBoxChanged;
}
@ -949,8 +992,8 @@ void fullColorFill(const TRaster32P &ras, const FillParameters &params,
TRaster32P refRas(bbbox.getSize());
TPixel32 gapColor = plt->getStyle(closeStyleIndex)->getMainColor();
int styleIndex = 4094;
int fakeStyleIndex = 4095;
int styleIndex = GAP_CLOSE_TEMP;
int fakeStyleIndex = GAP_CLOSE_USED;
if (xsheet) {
ToonzScene *scene = xsheet->getScene();
@ -975,9 +1018,10 @@ void fullColorFill(const TRaster32P &ras, const FillParameters &params,
}
TRasterCM32P refCMRaster;
TRasterCM32P cr;
if (fillGaps) {
TRasterCM32P cr = convertRaster2CM(refRas);
cr = convertRaster2CM(refRas);
refCMRaster = cr->clone();
fillGaps = TAutocloser(refCMRaster, autoCloseDistance, AutocloseAngle,
styleIndex, AutocloseOpacity)
@ -1019,20 +1063,6 @@ 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();
@ -1083,19 +1113,6 @@ 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));
@ -1158,14 +1175,59 @@ void fullColorFill(const TRaster32P &ras, const FillParameters &params,
}
}
if (fillGaps && closeGaps) {
// final check for close gap pixels
if (fillGaps) {
TPixelCM32 *tempPix = refCMRaster->pixels();
TPixel32 *keepPix = ras->pixels();
int fillNeighbors;
for (int tempY = 0; tempY < refCMRaster->getLy(); tempY++) {
for (int tempX = 0; tempX < refCMRaster->getLx();
tempX++, tempPix++, keepPix++) {
if (tempPix->getInk() == fakeStyleIndex) {
// if (tempPix->getInk() == fakeStyleIndex) {
if (tempPix->getInk() == styleIndex) {
// how many fill pixel neighbors for the current pixel?
fillNeighbors = 0;
if ((tempX > 0) && *(keepPix - 1) == color) fillNeighbors++; // west
if (tempX < ras->getLx() - 1 && *(keepPix + 1) == color)
fillNeighbors++; // east
if (tempY < ras->getLy() - 1 && *(keepPix + ras->getWrap()) == color)
fillNeighbors++; // north
if (tempY > 0 && *(keepPix - ras->getWrap()) == color)
fillNeighbors++; // south
if (fillNeighbors < 1) {
// no neighboring fill pixels, this is an unused gap close pixel
continue;
} else if (fillNeighbors > 3) {
// too many neighboring fill pixels to be a gap close pixel
// , convert it to a fill pixel
*keepPix = color;
} else {
// does it have at least one unpainted pixel neighbor?
if (((tempX > 0) && (tempPix - 1)->getInk() == 0 &&
(*(keepPix - 1) == clickedPosColor)) // west
|| (tempX < refCMRaster->getLx() - 1 &&
(tempPix + 1)->getInk() == 0 &&
*(keepPix + 1) == clickedPosColor) // east
|| (tempY < refCMRaster->getLy() - 1 &&
(tempPix + refCMRaster->getWrap())->getInk() == 0 &&
*(keepPix + ras->getWrap()) == clickedPosColor) // north
|| (tempY > 0 &&
(tempPix - refCMRaster->getWrap())->getInk() == 0 &&
*(keepPix - ras->getWrap()) == clickedPosColor) // south
) {
// yes, persist it as a gap close pixel
if (closeGaps) {
*keepPix = gapColor;
} else {
*keepPix = color;
}
} else {
// it is not acting as a border pixel so convert it to a fill
// pixel
*keepPix = color;
}
}
}
}
}

View file

@ -12,6 +12,11 @@
#include "tpixelutils.h"
#include <stack>
#include <tsystem.h>
#define IGNORECOLORSTYLE 4093
#define GAP_CLOSE_TEMP 4094
#define GAP_CLOSE_USED 4095
using namespace SkeletonLut;
@ -79,8 +84,7 @@ void fillArea(const TRasterCM32P &ras, TRegion *r, int colorId,
for (int k = from; k < to; k++, pix++) {
if (fillPaints && (!onlyUnfilled || pix->getPaint() == 0))
pix->setPaint(colorId);
if (fillInks && pix->getInk() != 4094) pix->setInk(colorId);
if (pix->getInk() == 4094) pix->setInk(4095);
if (fillInks && pix->getInk() != GAP_CLOSE_TEMP) pix->setInk(colorId);
}
}
}
@ -92,11 +96,12 @@ void fillArea(const TRasterCM32P &ras, TRegion *r, int colorId,
void restoreColors(const TRasterCM32P &r,
const std::vector<std::pair<TPoint, int>> &seeds) {
FillParameters params;
// in order to make the paint to protlude behind the line
// in order to make the paint to protrude behind the line
params.m_prevailing = false;
for (UINT i = 0; i < seeds.size(); i++) {
params.m_p = seeds[i].first;
params.m_styleId = seeds[i].second;
// params.m_styleId = seeds[i].second;
params.m_styleId = IGNORECOLORSTYLE;
fill(r, params);
}
}
@ -137,6 +142,171 @@ bool areRectPixelsTransparent(TPixel32 *pixels, TRect rect, int wrap) {
} // namespace
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void finishGapLine(const TRasterCM32P &r, const TRasterCM32P &combined,
const TPoint &pin, int clickedColorStyle, int fillColorStyle,
int closeColorStyle, int searchRay, TRect *insideRect,
bool closeGaps) {
r->lock();
TRasterCM32P myCombined;
if (!combined.getPointer() || combined->isEmpty()) {
myCombined = r;
} else {
myCombined = combined;
}
TPixelCM32 *pixels = (TPixelCM32 *)r->getRawData();
TPixelCM32 *combinedPixels = (TPixelCM32 *)myCombined->getRawData();
;
TPoint p = pin;
int filledNeighbor = 0;
int unfilledNeighbor = 0;
int inkStyle = 0;
int paintStyle = 0;
int toneValue = 0;
TPixelCM32 *pix = pixels + (p.y * r->getWrap() + p.x);
TPixelCM32 *pixc = combinedPixels + (p.y * myCombined->getWrap() + p.x);
std::stack<TPoint> gapLinePixels;
std::stack<TPoint> seeds;
seeds.push(p);
while (!seeds.empty()) {
p = seeds.top();
seeds.pop();
if (!r->getBounds().contains(p)) {
continue;
}
if (insideRect && !insideRect->contains(p)) {
continue;
}
TPixelCM32 *pix = pixels + (p.y * r->getWrap() + p.x);
TPixelCM32 *pixc = combinedPixels + (p.y * myCombined->getWrap() + p.x);
// handle gap close pixel
if (pix->getInk() == GAP_CLOSE_USED) continue;
if (pix->getInk() == GAP_CLOSE_TEMP) {
pix->setInk(GAP_CLOSE_USED);
// push to the gapLinePixels collection for later processing as a line
gapLinePixels.push(p);
// push neighboring pixels into the seeds queue
seeds.push(TPoint(p.x - 1, p.y - 1)); // sw
seeds.push(TPoint(p.x - 1, p.y)); // west
seeds.push(TPoint(p.x - 1, p.y + 1)); // nw
seeds.push(TPoint(p.x, p.y - 1)); // south
seeds.push(TPoint(p.x, p.y + 1)); // north
seeds.push(TPoint(p.x + 1, p.y - 1)); // se
seeds.push(TPoint(p.x + 1, p.y)); // east
seeds.push(TPoint(p.x + 1, p.y + 1)); // ne
continue;
}
// a neighboring filled pixel
if (pix->getTone() > 0 && pix->getPaint() == fillColorStyle) {
filledNeighbor++;
continue;
}
// a neighboring fillable but unfilled pixel
if ((pix->getTone() == 255 &&
((pix->getPaint() == clickedColorStyle) || (pix->getPaint() == 0) ||
(pix->getPaint() == IGNORECOLORSTYLE))) &&
(pixc->getTone() == 255 && ((pixc->getPaint() == clickedColorStyle) ||
(pixc->getPaint() == 0)))) {
unfilledNeighbor++;
continue;
}
}
// determine the final disposition of the gap line
if (filledNeighbor > 0) {
if (unfilledNeighbor > 0) {
if (closeGaps) {
inkStyle = closeColorStyle;
paintStyle = fillColorStyle;
toneValue = 0;
} else {
inkStyle = 0;
paintStyle = fillColorStyle;
toneValue = 255;
}
} else {
inkStyle = 0;
paintStyle = fillColorStyle;
toneValue = 255;
}
} else {
inkStyle = IGNORECOLORSTYLE;
paintStyle = 0;
toneValue = 0;
}
// process the stored gap close line pixels
while (!gapLinePixels.empty()) {
p = gapLinePixels.top();
gapLinePixels.pop();
TPixelCM32 *pix = pixels + (p.y * r->getWrap() + p.x);
pix->setInk(inkStyle);
pix->setPaint(paintStyle);
pix->setTone(toneValue);
}
r->unlock();
}
//-----------------------------------------------------------------------------
// This function finishes candidate gap lines that were created
// during a fill process.
// combined is the levels combined as is done for the "use visible" tool option
void finishGapLines(TRasterCM32P &rin, TRect &rect, const TRasterCM32P &rbefore,
const TRasterCM32P &combined, TPalette *plt,
int clickedColorStyle, int fillIndex, int closeColorStyle,
bool closeGaps) {
assert(plt);
TRasterCM32P r = rin->extract(rect);
assert(r->getSize() == rbefore->getSize());
assert(r->getSize() == combined->getSize());
int i, j;
for (i = 0; i < r->getLy(); i++) {
TPixelCM32 *pix = r->pixels(i);
TPixelCM32 *pixb = rbefore->pixels(i);
for (j = 0; j < r->getLx(); j++, pix++, pixb++) {
int paint = pix->getPaint();
int tone = pix->getTone();
int ink = pix->getInk();
if (ink == GAP_CLOSE_TEMP &&
(
// north
(i < rin->getLy() - 1 &&
(pix + rin->getWrap())->getPaint() == fillIndex &&
(pix + rin->getWrap())->getPaint() !=
(pixb + rbefore->getWrap())->getPaint())
// south
|| (i > 0 && (pix - rin->getWrap())->getPaint() == fillIndex &&
(pix - rin->getWrap())->getPaint() !=
(pixb - rbefore->getWrap())->getPaint())
// east
|| (j < rin->getLx() - 1 && (pix + 1)->getPaint() == fillIndex &&
(pix + 1)->getPaint() != (pixb + 1)->getPaint())
// west
|| (j > 0 && (pix - 1)->getPaint() == fillIndex &&
(pix - 1)->getPaint() != (pixb - 1)->getPaint()))) {
finishGapLine(rin, combined, TPoint(j, i) + rect.getP00(),
clickedColorStyle, fillIndex, closeColorStyle, 0, &rect,
closeGaps);
}
}
}
}
//=============================================================================
// AreaFiller
@ -154,10 +324,9 @@ AreaFiller::AreaFiller(const TRasterCM32P &ras)
AreaFiller::~AreaFiller() { m_ras->unlock(); }
//-----------------------------------------------------------------------------
// questa funzione viene chiamata dopo il fill rect delle aree, e colora gli
// inchiostri di tipo "autoink"
// che confinano con le aree appena fillate con il rect. rbefore e' il rect del
// raster prima del rectfill.
// This function is called after rect fill of the areas, and colors the
// "autoink" type inks bordering the areas just filled with the rect.
// rbefore is the rect of the raster before rectfill.
void fillautoInks(TRasterCM32P &rin, TRect &rect, const TRasterCM32P &rbefore,
TPalette *plt, int fillIndex) {
assert(plt);
@ -172,9 +341,36 @@ void fillautoInks(TRasterCM32P &rin, TRect &rect, const TRasterCM32P &rbefore,
int paint = pix->getPaint();
int tone = pix->getTone();
int ink = pix->getInk();
if (paint != pixb->getPaint() && paint == fillIndex && ink != paint &&
plt->getStyle(ink)->getFlags() != 0)
inkFill(rin, TPoint(j, i) + rect.getP00(), paint, 0, NULL, &rect);
/* new
* Pseudocode:
* Start the inkFill procedure at the current pixel if:
* The ink colorstyle has autopaint enabled
* The ink colorstyle is not already the same as the fill colorstyle
* The paint colorstyle of a neighboring pixel:
* is the same as the fill colorstyle
* has changed from its prior version
*/
if (plt->getStyle(ink)->getFlags() != 0 && ink != fillIndex &&
(
// north
(i < r->getLy() - 1 &&
(pix + r->getWrap())->getPaint() == fillIndex &&
(pix + r->getWrap())->getPaint() !=
(pixb + rbefore->getWrap())->getPaint())
// south
|| (i > 0 && (pix - r->getWrap())->getPaint() == fillIndex &&
(pix - r->getWrap())->getPaint() !=
(pixb - rbefore->getWrap())->getPaint())
// east
|| (j < r->getLx() - 1 && (pix + 1)->getPaint() == fillIndex &&
(pix + 1)->getPaint() != (pixb + 1)->getPaint())
// west
|| (j > 0 && (pix - 1)->getPaint() == fillIndex &&
(pix - 1)->getPaint() != (pixb - 1)->getPaint()))) {
inkFill(rin, TPoint(j, i) + rect.getP00(), fillIndex, 0, NULL, &rect);
}
}
}
}
@ -184,19 +380,21 @@ void fillautoInks(TRasterCM32P &rin, TRect &rect, const TRasterCM32P &rbefore,
bool AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled,
bool fillPaints, bool fillInks) {
// Synopsis:
// This gets the color of the pixes at the edge of the rect
// This gets the color of the pixels at the edge of the rect
// Then fills in EVERYTHING with 'color'
// Then uses the fill command to fill in the edges with their original color
// This makes sure only the enclosed areas not on the edge get filled.
/*- In case of FillInk only -*/
if (!fillPaints) {
assert(fillInks);
assert(m_ras->getBounds().contains(rect));
for (int y = rect.y0; y <= rect.y1; y++) {
TPixelCM32 *pix = m_ras->pixels(y) + rect.x0;
for (int x = rect.x0; x <= rect.x1; x++, pix++) {
if (pix->getInk() == 4094)
pix->setInk(4095);
if (pix->getInk() == GAP_CLOSE_TEMP)
//pix->setInk(TEMP_GAP_CLOSE_WAS_USED);
continue;
else
pix->setInk(color);
}
@ -249,18 +447,16 @@ bool AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled,
for (x = r.x0; x <= r.x1; x++, pix++) {
if (pix->getPaint() == 0) // BackgroundStyle
pix->setPaint(color);
if (fillInks && (pix->getInk() != 4094 && pix->getInk() != 4095))
if (fillInks && (pix->getInk() != GAP_CLOSE_TEMP && pix->getInk() != GAP_CLOSE_USED))
pix->setInk(color);
if (pix->getInk() == 4094) pix->setInk(4095);
}
}
else
for (y = r.y0; y <= r.y1; y++, pix += m_wrap - dx - 1) {
for (x = r.x0; x <= r.x1; x++, pix++) {
pix->setPaint(color);
if (fillInks && (pix->getInk() != 4094 && pix->getInk() != 4095))
if (fillInks && (pix->getInk() != GAP_CLOSE_TEMP && pix->getInk() != GAP_CLOSE_USED))
pix->setInk(color);
if (pix->getInk() == 4094) pix->setInk(4095);
}
}
@ -323,14 +519,26 @@ void AreaFiller::strokeFill(TStroke *stroke, int colorId, bool onlyUnfilled,
TVectorImage app;
app.addStroke(stroke);
app.findRegions();
//std::cout << "\nAreaFiller::strokeFill().fillArea()";
for (UINT i = 0; i < app.getRegionCount(); i++)
fillArea(m_ras, app.getRegion(i), colorId, onlyUnfilled, fillPaints,
fillInks);
//std::cout << "\nAreaFiller::strokeFill(), after fillArea()";
//outputPixels("tempRaster", m_ras); // issue 1151
app.removeStroke(0);
stroke->transform(TTranslation(convert(-m_ras->getCenter())));
restoreColors(m_ras, seeds);
//std::cout << "\nAreaFiller::strokeFill(), after restoreColors()";
//outputPixels("tempRaster", m_ras); // issue 1151
m_ras->unlock();
}
@ -412,7 +620,7 @@ void FullColorAreaFiller::rectFill(const TRect &rect,
//=============================================================================
// InkSegmenter
const int damInk = 4094;
const int damInk = 4094; //same value as the tempoary gap close lines?
//-----------------------------------------------------------------------------

View file

@ -57,12 +57,15 @@ void StrokeGenerator::filterPoints() {
// filtra m_points iniziali: generalmente elevate variazioni di thickness
// si hanno tra m_points[0] (al massimo m_points[1]) e i successivi)
// filter initial m_points: usually large thickness variations
// you have between m_points[0] (at most m_points[1]) and the following ones)
int size1 = m_points.size();
int kMin = 0;
int kMax = std::min(
4,
size1 -
2); // confronta 5 m_points iniziali con i successivi corrispondenti
// compare initial 5 m_points with subsequent matched ones
int k = kMax;
for (k = kMax; k >= kMin; --k) {
TThickPoint currPoint = m_points[k];
@ -72,10 +75,12 @@ void StrokeGenerator::filterPoints() {
if (deltaThick > 0.6 * dist) // deltaThick <= dist (condizione
// approssimata di non-autocontenimento per
// TTQ)
// deltaThick <= dist (approximate non-self-contained condition for TTQ)
{
vector<TThickPoint>::iterator it1 = m_points.begin();
vector<TThickPoint>::iterator it2 = it1 + k + 1;
m_points.erase(it1, it2); // cancella da m_points[0] a m_points[k]
// delete from m_points[0] to m_points[k]
assert((int)m_points.size() == size1 - k - 1);
break;
}
@ -83,11 +88,15 @@ void StrokeGenerator::filterPoints() {
// filtra m_points finali: generalmente elevate variazioni di thickness
// si hanno tra m_points[size - 1] (al massimo m_points[size - 2]) e i
// predecessori)
// filter final m_points: usually large thickness variations
// have between m_points[size - 1] (at most m_points[size - 2]) and i
// predecessors)
int size2 = m_points.size();
kMax = size2 - 1;
kMin = std::max(
kMax - 4,
1); // confronta 5 m_points finali con i predecessori corrispondenti
// compare final 5 m_points with matching ancestors
k = kMin;
for (k = kMin; k <= kMax; ++k) {
TThickPoint currPoint = m_points[k];
@ -100,6 +109,7 @@ void StrokeGenerator::filterPoints() {
{
int kTmp = k;
while (k <= kMax) // cancella da m_points[k] a m_points[size2 - 1]
// delete from m_points[k] to m_points[size2 - 1]
{
m_points.pop_back();
++k;