| #include <memory> |
| |
| #include "tropcm.h" |
| |
| |
| #include "traster.h" |
| |
| |
| #include <limits> |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace { |
| |
| |
| |
| |
| |
| |
| |
| |
| unsigned int takeoverDist(unsigned int a, unsigned int b, unsigned int d) { |
| |
| |
| |
| |
| return (b < a) ? d : std::max((d + (b - a) / d + 1) / 2, |
| d); |
| } |
| |
| |
| |
| template <typename Pix, typename IsInsideFunc> |
| void initializeDT(const TRasterPT<Pix> &ras, |
| const TRasterPT<unsigned int> &dtRas, IsInsideFunc isInside) { |
| assert(ras->getLx() == dtRas->getLx() && ras->getLy() == dtRas->getLy()); |
| |
| static const unsigned int uiMax = |
| (std::numeric_limits<unsigned int>::max)() - 2; |
| |
| int lx = ras->getLx(), ly = ras->getLy(); |
| for (int y = 0; y != ly; ++y) { |
| Pix *pix = ras->pixels(y), *rowEnd = pix + lx; |
| unsigned int *dt = dtRas->pixels(y); |
| |
| for (; pix != rowEnd; ++pix, ++dt) { |
| assert(*dt == 0u); |
| |
| if (!isInside(*pix)) *dt = uiMax; |
| } |
| } |
| } |
| |
| |
| |
| template <typename Pix, typename OutFunc> |
| void expand(int lineLength, int linesCount, Pix *buf, int incrPix, int incrLine, |
| unsigned int *dtBuf, int dtIncrPix, int dtIncrLine, |
| OutFunc outFunc) { |
| struct locals { |
| static void copyLine(unsigned int *dst, unsigned int *src, |
| unsigned int *srcEnd, int srcStride) { |
| for (; src != srcEnd; src += srcStride, ++dst) *dst = *src; |
| } |
| |
| static void buildRange(unsigned int *dtRef, unsigned int *dtLineEnd, |
| unsigned int *&dtEnd, unsigned int *&dtNewRef) { |
| unsigned int d = 1, |
| dNew = 0, |
| dMax = (std::numeric_limits<unsigned int>::max)(); |
| |
| |
| unsigned int *dt = dtRef + 1; |
| |
| for (; d <= dMax && dt != dtLineEnd; |
| ++d, ++dt) |
| { |
| unsigned int newDMax = ::takeoverDist(*dtRef, *dt, d); |
| if (newDMax <= dMax) { |
| dNew = d; |
| dMax = newDMax; |
| } |
| } |
| |
| dtEnd = |
| dtRef + std::min(d, dMax); |
| dtNewRef = dtRef + dNew; |
| } |
| }; |
| |
| |
| |
| |
| std::unique_ptr<unsigned[]> dtOriginalLine(new unsigned[lineLength]); |
| |
| unsigned int *odtLineStart = dtOriginalLine.get(), |
| *odtLineEnd = odtLineStart + lineLength; |
| |
| |
| for (int l = 0; l != linesCount; ++l) { |
| unsigned int *dtLineStart = |
| dtBuf + |
| dtIncrLine * |
| l, |
| *dtLineEnd = |
| dtLineStart + |
| dtIncrPix * lineLength, |
| *dt = dtLineStart, |
| *odtRef = odtLineStart; |
| |
| Pix *lineStart = buf + incrLine * l, *pix = lineStart; |
| |
| |
| locals::copyLine(dtOriginalLine.get(), dtLineStart, dtLineEnd, dtIncrPix); |
| |
| |
| while (dt != dtLineEnd) { |
| |
| |
| |
| |
| unsigned int *dtEnd, *odtNewRef; |
| locals::buildRange(odtRef, odtLineEnd, dtEnd, odtNewRef); |
| |
| assert(odtLineStart <= odtNewRef && odtNewRef <= odtLineEnd); |
| assert(odtLineStart <= dtEnd && dtEnd <= odtLineEnd); |
| |
| dtEnd = |
| dtLineStart + |
| dtIncrPix * |
| (dtEnd - odtLineStart); |
| |
| |
| Pix *ref = lineStart + incrPix * (odtRef - odtLineStart); |
| |
| unsigned int d = (pix - ref) / incrPix; |
| for (; dt != dtEnd; ++d, dt += dtIncrPix, pix += incrPix) |
| outFunc(*pix, *ref, *dt = *odtRef + sq(d)); |
| |
| odtRef = odtNewRef; |
| } |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <typename Pix, typename IsInsideFunc, typename OutFunc> |
| void distanceTransform(const TRasterPT<Pix> &ras, IsInsideFunc isInside, |
| OutFunc outFunc) { |
| int lx = ras->getLx(), ly = ras->getLy(); |
| |
| |
| |
| |
| TRasterPT<unsigned int> dtRas( |
| lx, ly); |
| |
| ::initializeDT(ras, dtRas, |
| isInside); |
| |
| |
| expand(lx, ly, ras->pixels(0), 1, ras->getWrap(), dtRas->pixels(0), 1, |
| dtRas->getWrap(), outFunc); |
| expand(lx, ly, ras->pixels(0) + lx - 1, -1, ras->getWrap(), |
| dtRas->pixels(0) + lx - 1, -1, dtRas->getWrap(), outFunc); |
| |
| expand(ly, lx, ras->pixels(0), ras->getWrap(), 1, dtRas->pixels(0), |
| dtRas->getWrap(), 1, outFunc); |
| expand(ly, lx, ras->pixels(ly - 1), -ras->getWrap(), 1, dtRas->pixels(ly - 1), |
| -dtRas->getWrap(), 1, outFunc); |
| } |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace { |
| |
| struct SomePaint { |
| inline bool operator()(const TPixelCM32 &pix) const { |
| return (pix.getTone() != 0) || (pix.getPaint() != 0); |
| } |
| }; |
| |
| struct CopyPaint { |
| inline void operator()(TPixelCM32 &out, const TPixelCM32 &in, |
| unsigned int) const { |
| out.setPaint(in.getPaint()); |
| } |
| }; |
| } |
| |
| |
| |
| |
| |
| void TRop::expandPaint(const TRasterCM32P &rasCM) { |
| distanceTransform(rasCM, SomePaint(), CopyPaint()); |
| } |
| |
| |
| |
| |
| |
| #if defined UNIT_TEST && !defined NDEBUG |
| |
| namespace { |
| |
| void assertEqualBufs(const TRasterT<unsigned int> &a, |
| const TRasterT<unsigned int> &b) { |
| for (int y = 0; y != a.getLy(); ++y) { |
| for (int x = 0; x != a.getLx(); ++x) |
| assert(a.pixels(y)[x] == b.pixels(y)[x]); |
| } |
| } |
| |
| struct Selector { |
| inline bool operator()(unsigned int val) const { return val; } |
| }; |
| |
| struct OutputDT { |
| inline void operator()(unsigned int &out, const unsigned int &in, |
| unsigned int d2) const { |
| out = d2; |
| } |
| }; |
| |
| struct DTTest { |
| DTTest() { |
| unsigned int imgBuf[] = { |
| 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, |
| 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| }; |
| |
| unsigned int dtBuf[] = { |
| 4, 1, 0, 1, 4, 5, 2, 1, 0, 1, 1, 2, 1, 0, 0, |
| 0, 0, 1, 1, 0, 1, 1, 1, 2, 2, 1, 2, 4, 4, 5, |
| }; |
| |
| TRasterPT<unsigned int> imgRas(6, 5, 6, imgBuf, false), |
| dtRas(6, 5, 6, dtBuf, false); |
| |
| distanceTransform(imgRas, Selector(), OutputDT()); |
| assertEqualBufs(*imgRas, *dtRas); |
| } |
| } dtTest; |
| |
| } |
| |
| #endif |