Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// tcg includes
Toshihiro Shimizu 890ddd
#include "tcg/tcg_misc.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "trop.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
/*! \file terodilate.cpp
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
This file contains an implementation of a greyscale (ie per-channel)
Shinya Kitaoka 120a6e
erode/dilate
Toshihiro Shimizu 890ddd
morphological operator, following the van Herk/Gil-Werman O(row*cols) algorithm.
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
An extension with circular structuring element is attempted - unfortunately I
Shinya Kitaoka 120a6e
could
Toshihiro Shimizu 890ddd
not retrieve a copy of Miyataka's paper about that, which seemingly claimed
Shinya Kitaoka 120a6e
O(rows * cols) too. The implemented algorithm is a sub-optimal
Shinya Kitaoka 120a6e
O(rows*cols*radius).
Toshihiro Shimizu 890ddd
*/
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
//    Auxiliary  functions
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename pix=""></typename>
Shinya Kitaoka 120a6e
void copyMatte(const TRasterPT<pix> &src,</pix>
Shinya Kitaoka 120a6e
               const TRasterPT<typename pix::channel=""> &matte) {</typename>
Shinya Kitaoka 120a6e
  typedef typename Pix::Channel Chan;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int y, lx = src->getLx(), ly = src->getLy();
Shinya Kitaoka 120a6e
  for (y = 0; y != ly; ++y) {
Shinya Kitaoka 120a6e
    Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx;
Shinya Kitaoka 120a6e
    Chan *m, *mBegin = matte->pixels(y);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (s = sBegin, m = mBegin; s != sEnd; ++s, ++m) *m = s->m;
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename pix=""></typename>
Shinya Kitaoka 120a6e
void copyChannels_erode(const TRasterPT<pix> &src,</pix>
Shinya Kitaoka 120a6e
                        const TRasterPT<typename pix::channel=""> &matte,</typename>
Shinya Kitaoka 120a6e
                        const TRasterPT<pix> &dst) {</pix>
Shinya Kitaoka 120a6e
  typedef typename Pix::Channel Chan;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  // Just assemble src and matte, remembering to depremultiply src pixels before
Shinya Kitaoka 120a6e
  // applying the new matte
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  double fac;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  int y, lx = src->getLx(), ly = src->getLy();
Shinya Kitaoka 120a6e
  for (y = 0; y != ly; ++y) {
Shinya Kitaoka 120a6e
    const Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx;
Shinya Kitaoka 120a6e
    Pix *d, *dBegin = dst->pixels(y);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    Chan *m, *mBegin = matte->pixels(y);
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
    for (s = sBegin, d = dBegin, m = mBegin; s != sEnd; ++s, ++d, ++m) {
Shinya Kitaoka 120a6e
      fac  = double(*m) / double(s->m);
Shinya Kitaoka 120a6e
      d->r = fac * s->r, d->g = fac * s->g, d->b = fac * s->b, d->m = *m;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename pix=""></typename>
Shinya Kitaoka 120a6e
void copyChannels_dilate(const TRasterPT<pix> &src,</pix>
Shinya Kitaoka 120a6e
                         const TRasterPT<typename pix::channel=""> &matte,</typename>
Shinya Kitaoka 120a6e
                         const TRasterPT<pix> &dst) {</pix>
Shinya Kitaoka 120a6e
  typedef typename Pix::Channel Chan;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Trickier - since src is presumably premultiplied, increasing its pixels'
Shinya Kitaoka 120a6e
  // alpha by direct
Shinya Kitaoka 120a6e
  // substitution would expose the excessive RGB discretization of pixels with a
Shinya Kitaoka 120a6e
  // low matte value.
Shinya Kitaoka 120a6e
  // So, let's just put the pixels on a black background. It should do fine.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double max = Pix::maxChannelValue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int y, lx = src->getLx(), ly = src->getLy();
Shinya Kitaoka 120a6e
  for (y = 0; y != ly; ++y) {
Shinya Kitaoka 120a6e
    const Pix *s, *sBegin = src->pixels(y), *sEnd = sBegin + lx;
Shinya Kitaoka 120a6e
    Pix *d, *dBegin = dst->pixels(y);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    const Chan *m, *mBegin = matte->pixels(y);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (s = sBegin, d = dBegin, m = mBegin; s != sEnd; ++s, ++d, ++m) {
Shinya Kitaoka 120a6e
      *d   = *s;
Shinya Kitaoka 120a6e
      d->m = s->m + (1.0 - s->m / max) * *m;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
//    EroDilate  algorithms
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename chan=""></typename>
Toshihiro Shimizu 890ddd
struct MaxFunc {
Shinya Kitaoka 120a6e
  inline Chan operator()(const Chan &a, const Chan &b) {
Shinya Kitaoka 120a6e
    return std::max(a, b);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename chan=""></typename>
Toshihiro Shimizu 890ddd
struct MinFunc {
Shinya Kitaoka 120a6e
  inline Chan operator()(const Chan &a, const Chan &b) {
Shinya Kitaoka 120a6e
    return std::min(a, b);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// NOTE: src and dst must be NOT OVERLAPPING (eg src != dst)
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename chan,="" func="" typename=""></typename>
Toshihiro Shimizu 890ddd
void erodilate_row(int len, const Chan *src, int sIncr, Chan *dst, int dIncr,
Shinya Kitaoka 120a6e
                   int rad, double radR, Func func) {
Shinya Kitaoka 120a6e
  assert(rad >= 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Segment the row of specified length into wCount windows of max wSize
Shinya Kitaoka 120a6e
  // elements
Shinya Kitaoka 120a6e
  int w, wSize = 2 * rad + 1, wCount = len / wSize + 1;
Shinya Kitaoka 120a6e
  int swIncr = wSize * sIncr, srIncr = rad * sIncr;
Shinya Kitaoka 120a6e
  int dwIncr = wSize * dIncr, drIncr = rad * dIncr;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  const Chan *s, *sEnd = src + len * sIncr;
Shinya Kitaoka 120a6e
  Chan *d, *dEnd       = dst + len * dIncr;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double one_radR = (1.0 - radR);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  for (w = 0; w != wCount; ++w) {
Shinya Kitaoka 120a6e
    Chan *dwBegin = dst + w * dwIncr, *dwEnd = std::min(dwBegin + dwIncr, dEnd);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Compute prefixes
Shinya Kitaoka 120a6e
    const Chan *swBegin = src + std::max(w * swIncr - srIncr - sIncr, 0),
Shinya Kitaoka 120a6e
               *swEnd =
Shinya Kitaoka 120a6e
                   src + std::min(w * swIncr + srIncr + sIncr, len * sIncr);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    s = swEnd - sIncr, d = dst + ((s - src) / sIncr) * dIncr +
Shinya Kitaoka 120a6e
                           drIncr;  // d already decremented by dIncr
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    Chan val = *s, oldVal;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (s -= sIncr; (d >= dEnd) && (s >= swBegin);
Shinya Kitaoka 120a6e
         s -= sIncr, d -= dIncr)  // s decremented here
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      assert(s >= src);
Shinya Kitaoka 120a6e
      assert(s < sEnd);
Shinya Kitaoka 120a6e
      assert((s - src) % sIncr == 0);
Shinya Kitaoka 120a6e
      assert(d >= dst);
Shinya Kitaoka 120a6e
      assert((d - dst) % dIncr == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      val = func(oldVal = val, *s);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (; s >= swBegin; s -= sIncr, d -= dIncr) {
Shinya Kitaoka 120a6e
      assert(s >= src);
Shinya Kitaoka 120a6e
      assert(s < sEnd);
Shinya Kitaoka 120a6e
      assert((s - src) % sIncr == 0);
Shinya Kitaoka 120a6e
      assert(d >= dst);
Shinya Kitaoka 120a6e
      assert(d < dEnd);
Shinya Kitaoka 120a6e
      assert((d - dst) % dIncr == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      val = func(oldVal = val, *s);
Shinya Kitaoka 120a6e
      *d = (oldVal == val) ? val : one_radR * oldVal + radR * val;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (d = std::min(d, dEnd - dIncr); d >= dwBegin; d -= dIncr) {
Shinya Kitaoka 120a6e
      assert(d >= dst);
Shinya Kitaoka 120a6e
      assert(d < dEnd);
Shinya Kitaoka 120a6e
      assert((d - dst) % dIncr == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      val = func(oldVal = val, 0);
Shinya Kitaoka 120a6e
      *d = (oldVal == val) ? val : one_radR * oldVal + radR * val;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Compute suffixes
Shinya Kitaoka 120a6e
    swBegin = src + w * swIncr + srIncr,
Shinya Kitaoka 120a6e
    swEnd   = std::min(swBegin + swIncr + sIncr, sEnd);
Shinya Kitaoka 120a6e
    if (swBegin >= swEnd) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    s = swBegin, d = dwBegin;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    val = *s;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (s += sIncr; (s < swEnd); s += sIncr, d += dIncr) {
Shinya Kitaoka 120a6e
      assert(s >= src);
Shinya Kitaoka 120a6e
      assert(s < sEnd);
Shinya Kitaoka 120a6e
      assert((s - src) % sIncr == 0);
Shinya Kitaoka 120a6e
      assert(d >= dst);
Shinya Kitaoka 120a6e
      assert(d < dEnd);
Shinya Kitaoka 120a6e
      assert((d - dst) % dIncr == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      val = func(oldVal = val, *s);
Shinya Kitaoka 120a6e
      *d = func(*d, (oldVal == val) ? val : one_radR * oldVal + radR * val);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (; d < dwEnd; d += dIncr) {
Shinya Kitaoka 120a6e
      assert(d >= dst);
Shinya Kitaoka 120a6e
      assert(d < dEnd);
Shinya Kitaoka 120a6e
      assert((d - dst) % dIncr == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      val = func(oldVal = val, 0);
Shinya Kitaoka 120a6e
      *d = func(*d, (oldVal == val) ? val : one_radR * oldVal + radR * val);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename chan="" pix,="" typename=""></typename>
Shinya Kitaoka 120a6e
void erodilate_chan(const TRasterPT<pix> &src, const TRasterPT<chan> &dst,</chan></pix>
Shinya Kitaoka 120a6e
                    double radius, bool dilate) {
Shinya Kitaoka 120a6e
  assert(radius > 0.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int radI    = tfloor(radius);
Shinya Kitaoka 120a6e
  double radR = radius - radI;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Using a temporary raster to keep intermediate results. This allows us to
Shinya Kitaoka 120a6e
  // perform a cache-friendly iteration in the separable/square kernel case
Shinya Kitaoka 120a6e
  int x, y, lx = src->getLx(), ly = src->getLy();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Peform rows erodilation
Shinya Kitaoka 120a6e
  TRasterPT<chan> temp(ly, lx);  // Notice transposition plz</chan>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    if (dilate)
Shinya Kitaoka 120a6e
      for (y = 0; y != ly; ++y)
Shinya Kitaoka 120a6e
        ::erodilate_row(lx, &src->pixels(y)->m, 4, temp->pixels(0) + y, ly,
Shinya Kitaoka 120a6e
                        radI, radR, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      for (y = 0; y != ly; ++y)
Shinya Kitaoka 120a6e
        ::erodilate_row(lx, &src->pixels(y)->m, 4, temp->pixels(0) + y, ly,
Shinya Kitaoka 120a6e
                        radI, radR, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Perform columns erodilation
Shinya Kitaoka 120a6e
  {
Shinya Kitaoka 120a6e
    if (dilate)
Shinya Kitaoka 120a6e
      for (x = 0; x != lx; ++x)
Shinya Kitaoka 120a6e
        ::erodilate_row(ly, temp->pixels(x), 1, dst->pixels(0) + x,
Shinya Kitaoka 120a6e
                        dst->getWrap(), radI, radR, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      for (x = 0; x != lx; ++x)
Shinya Kitaoka 120a6e
        ::erodilate_row(ly, temp->pixels(x), 1, dst->pixels(0) + x,
Shinya Kitaoka 120a6e
                        dst->getWrap(), radI, radR, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename pix=""></typename>
Shinya Kitaoka 120a6e
void rect_erodilate(const TRasterPT<pix> &src, const TRasterPT<pix> &dst,</pix></pix>
Shinya Kitaoka 120a6e
                    double radius) {
Shinya Kitaoka 120a6e
  typedef typename Pix::Channel Chan;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (radius == 0.0) {
Shinya Kitaoka 120a6e
    // No-op case
Shinya Kitaoka 120a6e
    TRop::copy(dst, src);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool dilate = (radius >= 0.0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Perform columns erodilation
Shinya Kitaoka 120a6e
  TRasterPT<chan> temp(src->getLx(), src->getLy());</chan>
Shinya Kitaoka 120a6e
  ::erodilate_chan(src, temp, fabs(radius), dilate);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Remember that we have just calculated the matte values. We still have to
Shinya Kitaoka 120a6e
  // apply them to the old RGB
Shinya Kitaoka 120a6e
  // values, which requires depremultiplying from source matte and
Shinya Kitaoka 120a6e
  // premultiplying with the new one.
Shinya Kitaoka 120a6e
  if (dilate)
Shinya Kitaoka 120a6e
    ::copyChannels_dilate(src, temp, dst);
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    ::copyChannels_erode(src, temp, dst);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
//    EroDilate  round algorithm
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename chan,="" func="" typename=""></typename>
Shinya Kitaoka 120a6e
void erodilate_quarters(int lx, int ly, Chan *src, int sIncrX, int sIncrY,
Shinya Kitaoka 120a6e
                        Chan *dst, int dIncrX, int dIncrY, double radius,
Shinya Kitaoka 120a6e
                        double shift, Func func) {
Shinya Kitaoka 120a6e
  double sqRadius     = sq(radius);
Shinya Kitaoka 120a6e
  double squareHeight = radius * M_SQRT1_2;
Shinya Kitaoka 120a6e
  int squareHeightI   = tfloor(squareHeight);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // For every arc point
Shinya Kitaoka 120a6e
  int arcY;
Shinya Kitaoka 120a6e
  for (arcY = -squareHeightI; arcY <= squareHeightI; ++arcY) {
Shinya Kitaoka 120a6e
    // Calculate x and weights
Shinya Kitaoka 120a6e
    double sqArcY = sq(arcY);
Shinya Kitaoka 120a6e
    assert(sqRadius >= sqArcY);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    double x = shift + sqrt(sqRadius - sqArcY) - squareHeight;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int arcX = tfloor(x);
Shinya Kitaoka 120a6e
    double w = x - arcX, one_w = 1.0 - w;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Build dst area influenced by the arc point. Func with 0 outside that.
Shinya Kitaoka 120a6e
    TRect bounds(0, 0, lx, ly);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TRect dRect(bounds * (bounds + TPoint(-arcX, -arcY)));
Shinya Kitaoka 120a6e
    TRect sRect(bounds * (bounds + TPoint(arcX, arcY)));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int sy, dy;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Func with 0 before dRect.y0
Shinya Kitaoka 120a6e
    for (dy = 0; dy < dRect.y0; ++dy) {
Shinya Kitaoka 120a6e
      Chan *d, *dBegin = dst + dy * dIncrY, *dEnd = dBegin + lx * dIncrX;
Shinya Kitaoka 120a6e
      for (d = dBegin; d != dEnd; d += dIncrX) {
Shinya Kitaoka 120a6e
        // assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
Shinya Kitaoka 120a6e
        *d = func(*d, 0);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Func with 0 after dRect.y1
Shinya Kitaoka 120a6e
    for (dy = dRect.y1; dy < ly; ++dy) {
Shinya Kitaoka 120a6e
      Chan *d, *dBegin = dst + dy * dIncrY, *dEnd = dBegin + lx * dIncrX;
Shinya Kitaoka 120a6e
      for (d = dBegin; d != dEnd; d += dIncrX) {
Shinya Kitaoka 120a6e
        // assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
Shinya Kitaoka 120a6e
        *d = func(*d, 0);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // For every dst pixel in the area, Func with the corresponding pixel in src
Shinya Kitaoka 120a6e
    for (dy = dRect.y0, sy = sRect.y0; dy != dRect.y1; ++dy, ++sy) {
Shinya Kitaoka 120a6e
      Chan *d, *dLine = dst + dy * dIncrY, *dBegin = dLine + dRect.x0 * dIncrX;
Shinya Kitaoka 120a6e
      Chan *s, *sLine = src + sy * sIncrY, *sBegin = sLine + sRect.x0 * sIncrX,
Shinya Kitaoka 120a6e
               *sEnd = sLine + sRect.x1 * sIncrX;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      Chan *sLast = sEnd - sIncrX;  // sLast would lerp with sEnd
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      for (d = dBegin, s = sBegin; s != sLast;
Shinya Kitaoka 120a6e
           d += dIncrX, s += sIncrX)  // hence we stop before it
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        // assert(s >= src); assert(s < sEnd); assert((s-src) % sIncrX == 0);
Shinya Kitaoka 120a6e
        // assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        *d = func(*d, *s * one_w + *(s + sIncrX) * w);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // assert(s >= src); assert(s < sEnd); assert((s-src) % sIncrX == 0);
Shinya Kitaoka 120a6e
      // assert(d >= dst); assert(d < dEnd); assert((d-dst) % dIncrX == 0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      *d = func(*d, *s * one_w);  // lerp sLast with 0
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//--------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
template <typename pix=""></typename>
Shinya Kitaoka 120a6e
void circular_erodilate(const TRasterPT<pix> &src, const TRasterPT<pix> &dst,</pix></pix>
Shinya Kitaoka 120a6e
                        double radius) {
Shinya Kitaoka 120a6e
  typedef typename Pix::Channel Chan;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (radius == 0.0) {
Shinya Kitaoka 120a6e
    // No-op case
Shinya Kitaoka 120a6e
    TRop::copy(dst, src);
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Ok, the idea is: consider the maximal embedded square in our circular
Shinya Kitaoka 120a6e
  // structuring element.
Shinya Kitaoka 120a6e
  // Erodilating by it consists in the consecutive erodilation by rows and
Shinya Kitaoka 120a6e
  // columns with the same
Shinya Kitaoka 120a6e
  // 'square' radius. Now, it's easy to see that the square could be 'bent' so
Shinya Kitaoka 120a6e
  // that one of its
Shinya Kitaoka 120a6e
  // edges matches that of a 1/4 of the circle's edge, while remaining inside
Shinya Kitaoka 120a6e
  // the circle.
Shinya Kitaoka 120a6e
  // Erodilating by the bent square can be achieved by erodilating first by rows
Shinya Kitaoka 120a6e
  // or column for
Shinya Kitaoka 120a6e
  // the square edge radius, followed by perpendicular erodilationg with a
Shinya Kitaoka 120a6e
  // fourth of our
Shinya Kitaoka 120a6e
  // circumference. Sum the 4 erodilations needed to complete the circumference
Shinya Kitaoka 120a6e
  // - and it's done.
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // NOTE: Unfortunately, the above decomposition has lots of intersections
Shinya Kitaoka 120a6e
  // among the pieces - yet
Shinya Kitaoka 120a6e
  // it's simple enough and removes an O(radius) from the naive algorithm. Could
Shinya Kitaoka 120a6e
  // be done better?
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // First, build the various erodilation data
Shinya Kitaoka 120a6e
  bool dilate = (radius >= 0.0);
Shinya Kitaoka 120a6e
  radius      = fabs(radius);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double inner_square_diameter = radius * M_SQRT2;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double shift =
Shinya Kitaoka 120a6e
      0.25 *
Shinya Kitaoka 120a6e
      inner_square_diameter;  // Shift of the bent square SE needed to avoid
Shinya Kitaoka 120a6e
                              // touching the circumference on the other side
Shinya Kitaoka 120a6e
  double row_filter_radius = 0.5 * (inner_square_diameter - shift);
Shinya Kitaoka 120a6e
  double cseShift = 0.5 * shift;  // circumference structuring element shift
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int lx = src->getLx(), ly = src->getLy();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TRasterPT<chan> temp1(lx, ly), temp2(lx, ly);</chan>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int radI    = tfloor(row_filter_radius);
Shinya Kitaoka 120a6e
  double radR = row_filter_radius - radI;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (dilate) {
Shinya Kitaoka 120a6e
    temp2->fill(0);  // Initialize with a Func-neutral value
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (row_filter_radius > 0.0)
Shinya Kitaoka 120a6e
      for (int y = 0; y != ly; ++y)
Shinya Kitaoka 120a6e
        ::erodilate_row(lx, &src->pixels(y)->m, 4, temp1->pixels(y), 1, radI,
Shinya Kitaoka 120a6e
                        radR, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      ::copyMatte(src, temp1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::erodilate_quarters(lx, ly, temp1->pixels(0), 1, lx, temp2->pixels(0), 1,
Shinya Kitaoka 120a6e
                         lx, radius, cseShift, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    ::erodilate_quarters(lx, ly, temp1->pixels(0) + lx - 1, -1, lx,
Shinya Kitaoka 120a6e
                         temp2->pixels(0) + lx - 1, -1, lx, radius, cseShift,
Shinya Kitaoka 120a6e
                         MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (row_filter_radius > 0.0)
Shinya Kitaoka 120a6e
      for (int x = 0; x != lx; ++x)
Shinya Kitaoka 120a6e
        ::erodilate_row(ly, &src->pixels(0)[x].m, 4 * src->getWrap(),
Shinya Kitaoka 120a6e
                        temp1->pixels(0) + x, lx, radI, radR, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      ::copyMatte(src, temp1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::erodilate_quarters(ly, lx, temp1->pixels(0), lx, 1, temp2->pixels(0), lx,
Shinya Kitaoka 120a6e
                         1, radius, cseShift, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    ::erodilate_quarters(ly, lx, temp1->pixels(0) + lx * ly - 1, -lx, -1,
Shinya Kitaoka 120a6e
                         temp2->pixels(0) + lx * ly - 1, -lx, -1, radius,
Shinya Kitaoka 120a6e
                         cseShift, MaxFunc<chan>());</chan>
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    temp2->fill((std::numeric_limits<chan>::max)());  // Initialize with a</chan>
Shinya Kitaoka 120a6e
                                                      // Func-neutral value
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (row_filter_radius > 0.0)
Shinya Kitaoka 120a6e
      for (int y = 0; y != ly; ++y)
Shinya Kitaoka 120a6e
        ::erodilate_row(lx, &src->pixels(y)->m, 4, temp1->pixels(y), 1, radI,
Shinya Kitaoka 120a6e
                        radR, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      ::copyMatte(src, temp1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::erodilate_quarters(lx, ly, temp1->pixels(0), 1, lx, temp2->pixels(0), 1,
Shinya Kitaoka 120a6e
                         lx, radius, cseShift, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    ::erodilate_quarters(lx, ly, temp1->pixels(0) + lx - 1, -1, lx,
Shinya Kitaoka 120a6e
                         temp2->pixels(0) + lx - 1, -1, lx, radius, cseShift,
Shinya Kitaoka 120a6e
                         MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (row_filter_radius > 0.0)
Shinya Kitaoka 120a6e
      for (int x = 0; x != lx; ++x)
Shinya Kitaoka 120a6e
        ::erodilate_row(ly, &src->pixels(0)[x].m, 4 * src->getWrap(),
Shinya Kitaoka 120a6e
                        temp1->pixels(0) + x, lx, radI, radR, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      ::copyMatte(src, temp1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    ::erodilate_quarters(ly, lx, temp1->pixels(0), lx, 1, temp2->pixels(0), lx,
Shinya Kitaoka 120a6e
                         1, radius, cseShift, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
    ::erodilate_quarters(ly, lx, temp1->pixels(0) + lx * ly - 1, -lx, -1,
Shinya Kitaoka 120a6e
                         temp2->pixels(0) + lx * ly - 1, -lx, -1, radius,
Shinya Kitaoka 120a6e
                         cseShift, MinFunc<chan>());</chan>
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Remember that we have just calculated the matte values. We still have to
Shinya Kitaoka 120a6e
  // apply them to the old RGB
Shinya Kitaoka 120a6e
  // values, which requires depremultiplying from source matte and
Shinya Kitaoka 120a6e
  // premultiplying with the new one.
Shinya Kitaoka 120a6e
  if (dilate)
Shinya Kitaoka 120a6e
    ::copyChannels_dilate(src, temp2, dst);
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    ::copyChannels_erode(src, temp2, dst);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
//    EroDilate  main functions
Toshihiro Shimizu 890ddd
//********************************************************
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void TRop::erodilate(const TRasterP &src, const TRasterP &dst, double radius,
Shinya Kitaoka 120a6e
                     ErodilateMaskType type) {
Shinya Kitaoka 120a6e
  assert(src->getSize() == dst->getSize());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  src->lock(), dst->lock();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if ((TRaster32P)src && (TRaster32P)dst) switch (type) {
Shinya Kitaoka 120a6e
    case ED_rectangular:
Shinya Kitaoka 120a6e
      ::rect_erodilate<tpixel32>(src, dst, radius);</tpixel32>
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    case ED_circular:
Shinya Kitaoka 120a6e
      ::circular_erodilate<tpixel32>(src, dst, radius);</tpixel32>
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    default:
Shinya Kitaoka 120a6e
      assert(!"Unknown mask type");
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  else if ((TRaster64P)src && (TRaster64P)dst)
Shinya Kitaoka 120a6e
    switch (type) {
Shinya Kitaoka 120a6e
    case ED_rectangular:
Shinya Kitaoka 120a6e
      ::rect_erodilate<tpixel64>(src, dst, radius);</tpixel64>
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    case ED_circular:
Shinya Kitaoka 120a6e
      ::circular_erodilate<tpixel64>(src, dst, radius);</tpixel64>
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    default:
Shinya Kitaoka 120a6e
      assert(!"Unknown mask type");
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  else
Shinya Kitaoka 120a6e
    assert(!"Unsupported raster type!");
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  src->unlock(), dst->unlock();
Toshihiro Shimizu 890ddd
}