diff --git a/toonz/sources/include/toonz/fill.h b/toonz/sources/include/toonz/fill.h index 075bebb8..5a4eece2 100644 --- a/toonz/sources/include/toonz/fill.h +++ b/toonz/sources/include/toonz/fill.h @@ -27,6 +27,7 @@ public: bool m_shiftFill; TPoint m_p; TPalette *m_palette; + bool m_prevailing; FillParameters() : m_styleId(0) @@ -37,7 +38,8 @@ public: , m_maxFillDepth(0) , m_p() , m_shiftFill(false) - , m_palette(0) {} + , m_palette(0) + , m_prevailing(true) {} FillParameters(const FillParameters ¶ms) : m_styleId(params.m_styleId) , m_fillType(params.m_fillType) @@ -47,7 +49,8 @@ public: , m_maxFillDepth(params.m_maxFillDepth) , m_p(params.m_p) , m_shiftFill(params.m_shiftFill) - , m_palette(params.m_palette) {} + , m_palette(params.m_palette) + , m_prevailing(params.m_prevailing) {} }; //============================================================================= diff --git a/toonz/sources/toonzlib/fill.cpp b/toonz/sources/toonzlib/fill.cpp index 9f0a25ed..902a853b 100644 --- a/toonz/sources/toonzlib/fill.cpp +++ b/toonz/sources/toonzlib/fill.cpp @@ -32,9 +32,14 @@ inline TPoint nearestInkNotDiagonal(const TRasterCM32P &r, const TPoint &p) { // la riga ridisegnata va da *xa a *xb compresi // x1 <= *xa <= *xb <= x2 // N.B. se non viene disegnato neanche un pixel *xa>*xb +// +// "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. void fillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, - int paint, TPalette *palette, TTileSaverCM32 *saver) { + int paint, TPalette *palette, TTileSaverCM32 *saver, + bool prevailing = true) { int tone, oldtone; TPixelCM32 *pix, *pix0, *limit, *tmp_limit; @@ -49,7 +54,34 @@ void fillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, for (; pix <= limit; pix++) { if (pix->getPaint() == paint) break; tone = pix->getTone(); - if (tone > oldtone || 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()) + break; + while (pix != pix0) { + // iterate back in order to leave the pixel with the lowest tone + // unpainted + pix--; + // make the one-pixel-width semi-transparent line to be painted + if (prevailing && pix->getInk() != pix->getPaint()) break; + if (pix->getTone() > oldtone) { + // check if the current pixel is NOT with the lowest tone among the + // vertical neighbors as well + if (p.y > 0 && p.y < r->getLy() - 1) { + TPixelCM32 *upPix = pix - r->getWrap(); + TPixelCM32 *downPix = pix + r->getWrap(); + if (upPix->getTone() > pix->getTone() && + downPix->getTone() > pix->getTone()) + continue; + } + break; + } + } + pix++; + break; + } oldtone = tone; } if (tone == 0) { @@ -72,7 +104,34 @@ void fillRow(const TRasterCM32P &r, const TPoint &p, int &xa, int &xb, for (pix--; pix >= limit; pix--) { if (pix->getPaint() == paint) break; tone = pix->getTone(); - if (tone > oldtone || 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()) + break; + while (pix != pix0) { + // iterate forward in order to leave the pixel with the lowest tone + // unpainted + pix++; + // make the one-pixel-width semi-transparent line to be painted + if (prevailing && pix->getInk() != pix->getPaint()) break; + if (pix->getTone() > oldtone) { + // check if the current pixel is NOT with the lowest tone among the + // vertical neighbors as well + if (p.y > 0 && p.y < r->getLy() - 1) { + TPixelCM32 *upPix = pix - r->getWrap(); + TPixelCM32 *downPix = pix + r->getWrap(); + if (upPix->getTone() > pix->getTone() && + downPix->getTone() > pix->getTone()) + continue; + } + break; + } + } + pix--; + break; + } oldtone = tone; } if (tone == 0) { @@ -234,7 +293,8 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, /*- 画面外のクリックの場合はreturn -*/ if (!bbbox.contains(p)) return false; /*- 既に同じ色が塗られている場合はreturn -*/ - if ((r->pixels(p.y) + p.x)->getPaint() == paint) return false; + int paintAtClickedPos = (r->pixels(p.y) + p.x)->getPaint(); + if (paintAtClickedPos == paint) return false; /*- 「透明部分だけを塗る」オプションが有効で、既に色が付いている場合はreturn * -*/ if (params.m_emptyOnly && (r->pixels(p.y) + p.x)->getPaint() != 0) @@ -252,7 +312,6 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, default: assert(false); } - /*-- 四隅の色を見て、一つでも変わったらsaveBoxを更新する --*/ TPixelCM32 borderIndex[4]; TPixelCM32 *borderPix[4]; @@ -271,7 +330,7 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, std::stack seeds; - fillRow(r, p, xa, xb, paint, params.m_palette, saver); + fillRow(r, p, xa, xb, paint, params.m_palette, saver, params.m_prevailing); seeds.push(FillSeed(xa, xb, y, 1)); seeds.push(FillSeed(xa, xb, y, -1)); @@ -294,8 +353,13 @@ bool fill(const TRasterCM32P &r, const FillParameters ¶ms, while (pix <= limit) { oldtone = threshTone(*oldpix, fillDepth); tone = threshTone(*pix, fillDepth); - if (pix->getPaint() != paint && tone <= oldtone && tone != 0) { - fillRow(r, TPoint(x, y), xc, xd, paint, params.m_palette, saver); + // the last condition is added in order to prevent fill area from + // protruding behind the colored line + if (pix->getPaint() != paint && tone <= oldtone && tone != 0 && + (pix->getPaint() != pix->getInk() || + pix->getPaint() == paintAtClickedPos)) { + fillRow(r, TPoint(x, y), xc, xd, paint, params.m_palette, saver, + params.m_prevailing); 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) diff --git a/toonz/sources/toonzlib/fillutil.cpp b/toonz/sources/toonzlib/fillutil.cpp index 9912df76..2d5e9687 100644 --- a/toonz/sources/toonzlib/fillutil.cpp +++ b/toonz/sources/toonzlib/fillutil.cpp @@ -69,6 +69,8 @@ void fillArea(const TRasterCM32P &ras, TRegion *r, int colorId, void restoreColors(const TRasterCM32P &r, const std::vector> &seeds) { FillParameters params; + // in order to make the paint to protlude 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; @@ -232,6 +234,8 @@ void AreaFiller::rectFill(const TRect &rect, int color, bool onlyUnfilled, // fillate. count1 = 0; FillParameters params; + // in order to make the paint to protlude behind the line + params.m_prevailing = false; if (r.x0 > 0) for (y = r.y0; y <= r.y1; y++) { params.m_p = TPoint(r.x0, y);