shun-iwasawa 1b1839
shun-iwasawa 1b1839
#include "iwa_bokeh_util.h"
shun-iwasawa 1b1839
shun-iwasawa 1b1839
#include "trop.h"
shun-iwasawa 481b59
#include "tparamcontainer.h"
shun-iwasawa 481b59
shun-iwasawa 1b1839
#include <array></array>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
#include <qset></qset>
shun-iwasawa 1b1839
#include <qmap></qmap>
shun-iwasawa 1b1839
#include <qreadwritelock></qreadwritelock>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
namespace {
shun-iwasawa 1b1839
QReadWriteLock lock;
shun-iwasawa 1b1839
QMutex mutex;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
// modify fft coordinate to normal
shun-iwasawa 1b1839
inline int getCoord(int index, int lx, int ly) {
shun-iwasawa 1b1839
  int i = index % lx;
shun-iwasawa 1b1839
  int j = index / lx;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int cx = i - lx / 2;
shun-iwasawa 1b1839
  int cy = j - ly / 2;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (cx < 0) cx += lx;
shun-iwasawa 1b1839
  if (cy < 0) cy += ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  return cy * lx + cx;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
// RGB value <--> Exposure
shun-iwasawa 1b1839
inline double valueToExposure(double value, double filmGamma) {
shun-iwasawa 1b1839
  double logVal = (value - 0.5) / filmGamma;
shun-iwasawa 1b1839
  return std::pow(10.0, logVal);
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
inline double exposureToValue(double exposure, double filmGamma) {
shun-iwasawa 1b1839
  return std::log10(exposure) * filmGamma + 0.5;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
inline double clamp01(double val) {
shun-iwasawa 1b1839
  return (val < 0.0) ? 0.0 : ((val > 1.0) ? 1.0 : val);
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template <typename t=""></typename>
shun-iwasawa 1b1839
TRasterGR8P allocateRasterAndLock(T** buf, TDimensionI dim) {
shun-iwasawa 1b1839
  TRasterGR8P ras(dim.lx * sizeof(T), dim.ly);
shun-iwasawa 1b1839
  ras->lock();
shun-iwasawa 1b1839
  *buf = (T*)ras->getRawData();
shun-iwasawa 1b1839
  return ras;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
// release all registered raster memories and free all fft plans
shun-iwasawa 1b1839
void releaseAllRastersAndPlans(QList<trastergr8p>& rasterList,</trastergr8p>
shun-iwasawa 1b1839
                               QList<kiss_fftnd_cfg>& planList) {</kiss_fftnd_cfg>
shun-iwasawa 1b1839
  for (int r = 0; r < rasterList.size(); r++) rasterList.at(r)->unlock();
shun-iwasawa 1b1839
  for (int p = 0; p < planList.size(); p++) kiss_fft_free(planList.at(p));
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
}  // namespace
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// thread for computing bokeh of each channel
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
shun-iwasawa 1b1839
BokehUtils::MyThread::MyThread(Channel channel, TRasterP layerTileRas,
shun-iwasawa 1b1839
                               double4* result, double* alpha_bokeh,
shun-iwasawa 1b1839
                               kiss_fft_cpx* kissfft_comp_iris,
shun-iwasawa 481b59
                               double layerGamma, double masterGamma)
shun-iwasawa 1b1839
    : m_channel(channel)
shun-iwasawa 1b1839
    , m_layerTileRas(layerTileRas)
shun-iwasawa 1b1839
    , m_result(result)
shun-iwasawa 1b1839
    , m_alpha_bokeh(alpha_bokeh)
shun-iwasawa 1b1839
    , m_kissfft_comp_iris(kissfft_comp_iris)
shun-iwasawa 481b59
    , m_layerGamma(layerGamma)
shun-iwasawa 481b59
    , m_masterGamma(masterGamma)
shun-iwasawa 1b1839
    , m_finished(false)
shun-iwasawa 1b1839
    , m_kissfft_comp_in(0)
shun-iwasawa 1b1839
    , m_kissfft_comp_out(0)
shun-iwasawa 481b59
    , m_isTerminated(false) {
shun-iwasawa 481b59
  if (m_masterGamma == 0.0) m_masterGamma = m_layerGamma;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
bool BokehUtils::MyThread::init() {
shun-iwasawa 1b1839
  // obtain the input image size
shun-iwasawa 1b1839
  int lx, ly;
shun-iwasawa 1b1839
  lx = m_layerTileRas->getSize().lx;
shun-iwasawa 1b1839
  ly = m_layerTileRas->getSize().ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // memory allocation for input
shun-iwasawa 1b1839
  m_kissfft_comp_in_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly);
shun-iwasawa 1b1839
  m_kissfft_comp_in_ras->lock();
shun-iwasawa 1b1839
  m_kissfft_comp_in = (kiss_fft_cpx*)m_kissfft_comp_in_ras->getRawData();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // allocation check
shun-iwasawa 1b1839
  if (m_kissfft_comp_in == 0) return false;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (m_isTerminated) {
shun-iwasawa 1b1839
    m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
    return false;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // memory allocation for output
shun-iwasawa 1b1839
  m_kissfft_comp_out_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly);
shun-iwasawa 1b1839
  m_kissfft_comp_out_ras->lock();
shun-iwasawa 1b1839
  m_kissfft_comp_out = (kiss_fft_cpx*)m_kissfft_comp_out_ras->getRawData();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // allocation check
shun-iwasawa 1b1839
  if (m_kissfft_comp_out == 0) {
shun-iwasawa 1b1839
    m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_in = 0;
shun-iwasawa 1b1839
    return false;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (m_isTerminated) {
shun-iwasawa 1b1839
    m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_in = 0;
shun-iwasawa 1b1839
    m_kissfft_comp_out_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_out = 0;
shun-iwasawa 1b1839
    return false;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // create the forward FFT plan
shun-iwasawa 1b1839
  int dims[2]        = {ly, lx};
shun-iwasawa 1b1839
  int ndims          = 2;
shun-iwasawa 1b1839
  m_kissfft_plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
shun-iwasawa 1b1839
  // allocation and cancel check
shun-iwasawa 1b1839
  if (m_kissfft_plan_fwd == NULL || m_isTerminated) {
shun-iwasawa 1b1839
    m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_in = 0;
shun-iwasawa 1b1839
    m_kissfft_comp_out_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_out = 0;
shun-iwasawa 1b1839
    return false;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // create the backward FFT plan
shun-iwasawa 1b1839
  m_kissfft_plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0);
shun-iwasawa 1b1839
  // allocation and cancel check
shun-iwasawa 1b1839
  if (m_kissfft_plan_bkwd == NULL || m_isTerminated) {
shun-iwasawa 1b1839
    m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_in = 0;
shun-iwasawa 1b1839
    m_kissfft_comp_out_ras->unlock();
shun-iwasawa 1b1839
    m_kissfft_comp_out = 0;
shun-iwasawa 1b1839
    kiss_fft_free(m_kissfft_plan_fwd);
shun-iwasawa 1b1839
    m_kissfft_plan_fwd = NULL;
shun-iwasawa 1b1839
    return false;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // return true if all the initializations are done
shun-iwasawa 1b1839
  return true;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
// convert layer RGB to exposure
shun-iwasawa 1b1839
// multiply alpha
shun-iwasawa 1b1839
// set to kiss_fft_cpx
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa 1b1839
void BokehUtils::MyThread::setLayerRaster(const RASTER srcRas,
shun-iwasawa 1b1839
                                          kiss_fft_cpx* dstMem,
shun-iwasawa 1b1839
                                          TDimensionI dim) {
shun-iwasawa 1b1839
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa 1b1839
    PIXEL* pix = srcRas->pixels(j);
shun-iwasawa 1b1839
    for (int i = 0; i < dim.lx; i++, pix++) {
shun-iwasawa 1b1839
      if (pix->m != 0) {
shun-iwasawa 8e3b15
        double val = (m_channel == Red)     ? (double)pix->r
shun-iwasawa 8e3b15
                     : (m_channel == Green) ? (double)pix->g
shun-iwasawa 8e3b15
                                            : (double)pix->b;
shun-iwasawa 1b1839
        // multiply the exposure by alpha channel value
shun-iwasawa 1b1839
        dstMem[j * dim.lx + i].r =
shun-iwasawa 481b59
            m_conv->valueToExposure(val / (double)PIXEL::maxChannelValue) *
shun-iwasawa 1b1839
            ((double)pix->m / (double)PIXEL::maxChannelValue);
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
shun-iwasawa 1b1839
void BokehUtils::MyThread::run() {
shun-iwasawa 1b1839
  // get the source image size
shun-iwasawa 1b1839
  TDimensionI dim = m_layerTileRas->getSize();
shun-iwasawa 1b1839
  // int lx,ly;
shun-iwasawa 1b1839
  int lx = m_layerTileRas->getSize().lx;
shun-iwasawa 1b1839
  int ly = m_layerTileRas->getSize().ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // initialize
shun-iwasawa 1b1839
  for (int i = 0; i < dim.lx * dim.ly; i++) {
shun-iwasawa 1b1839
    m_kissfft_comp_in[i].r = 0.0;  // real part
shun-iwasawa 1b1839
    m_kissfft_comp_in[i].i = 0.0;  // imaginary part
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRaster32P ras32 = (TRaster32P)m_layerTileRas;
shun-iwasawa 1b1839
  TRaster64P ras64 = (TRaster64P)m_layerTileRas;
shun-iwasawa 481b59
  TRasterFP rasF   = (TRasterFP)m_layerTileRas;
shun-iwasawa 1b1839
  // Prepare data for FFT.
shun-iwasawa 1b1839
  // Convert the RGB values to the exposure, then multiply it by the alpha
shun-iwasawa 1b1839
  // channel value
shun-iwasawa 1b1839
  {
shun-iwasawa 1b1839
    lock.lockForRead();
shun-iwasawa 1b1839
    if (ras32)
shun-iwasawa 1b1839
      setLayerRaster<traster32p, tpixel32="">(ras32, m_kissfft_comp_in, dim);</traster32p,>
shun-iwasawa 1b1839
    else if (ras64)
shun-iwasawa 1b1839
      setLayerRaster<traster64p, tpixel64="">(ras64, m_kissfft_comp_in, dim);</traster64p,>
shun-iwasawa 481b59
    else if (rasF)
shun-iwasawa 481b59
      setLayerRaster<trasterfp, tpixelf="">(rasF, m_kissfft_comp_in, dim);</trasterfp,>
shun-iwasawa 1b1839
    else {
shun-iwasawa 1b1839
      lock.unlock();
shun-iwasawa 1b1839
      return;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    lock.unlock();
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (checkTerminationAndCleanupThread()) return;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kiss_fftnd(m_kissfft_plan_fwd, m_kissfft_comp_in, m_kissfft_comp_out);
shun-iwasawa 1b1839
  kiss_fft_free(m_kissfft_plan_fwd);  // we don't need this plan anymore
shun-iwasawa 1b1839
  m_kissfft_plan_fwd = NULL;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (checkTerminationAndCleanupThread()) return;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Filtering. Multiply by the iris FFT data
shun-iwasawa 1b1839
  {
shun-iwasawa 1b1839
    for (int i = 0; i < lx * ly; i++) {
shun-iwasawa 481b59
      kiss_fft_scalar re, im;
shun-iwasawa 1b1839
      re = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].r -
shun-iwasawa 1b1839
           m_kissfft_comp_out[i].i * m_kissfft_comp_iris[i].i;
shun-iwasawa 1b1839
      im = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].i +
shun-iwasawa 1b1839
           m_kissfft_comp_iris[i].r * m_kissfft_comp_out[i].i;
shun-iwasawa 1b1839
      m_kissfft_comp_out[i].r = re;
shun-iwasawa 1b1839
      m_kissfft_comp_out[i].i = im;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (checkTerminationAndCleanupThread()) return;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kiss_fftnd(m_kissfft_plan_bkwd, m_kissfft_comp_out,
shun-iwasawa 1b1839
             m_kissfft_comp_in);       // Backward FFT
shun-iwasawa 1b1839
  kiss_fft_free(m_kissfft_plan_bkwd);  // we don't need this plan anymore
shun-iwasawa 1b1839
  m_kissfft_plan_bkwd = NULL;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // In the backward FFT above, "m_kissfft_comp_out" is used as input and
shun-iwasawa 1b1839
  // "m_kissfft_comp_in" as output.
shun-iwasawa 1b1839
  // So we don't need "m_kissfft_comp_out" anymore.
shun-iwasawa 1b1839
  m_kissfft_comp_out_ras->unlock();
shun-iwasawa 1b1839
  m_kissfft_comp_out = 0;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (checkTerminationAndCleanupThread()) return;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  {
shun-iwasawa 1b1839
    QMutexLocker locker(&mutex);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    double* alp_p  = m_alpha_bokeh;
shun-iwasawa 1b1839
    double4* res_p = m_result;
shun-iwasawa 481b59
shun-iwasawa 1b1839
    for (int i = 0; i < dim.lx * dim.ly; i++, alp_p++, res_p++) {
shun-iwasawa 1b1839
      if ((*alp_p) < 0.00001) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      double exposure =
shun-iwasawa 1b1839
          (double)m_kissfft_comp_in[getCoord(i, dim.lx, dim.ly)].r /
shun-iwasawa 1b1839
          (double)(dim.lx * dim.ly);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // convert to layer hardness
shun-iwasawa 481b59
      if (m_masterGamma != m_layerGamma) {
shun-iwasawa 481b59
        if (isGammaBased())
shun-iwasawa 481b59
          exposure =
shun-iwasawa 481b59
              std::pow(exposure / (*alp_p), m_masterGamma / m_layerGamma) *
shun-iwasawa 481b59
              (*alp_p);
shun-iwasawa 481b59
        else  // hardness based
shun-iwasawa 481b59
          exposure =
shun-iwasawa 481b59
              std::pow(exposure / (*alp_p), m_layerGamma / m_masterGamma) *
shun-iwasawa 481b59
              (*alp_p);
shun-iwasawa 481b59
      }
shun-iwasawa 1b1839
shun-iwasawa 8e3b15
      double* res = (m_channel == Red)     ? (&((*res_p).x))
shun-iwasawa 8e3b15
                    : (m_channel == Green) ? (&((*res_p).y))
shun-iwasawa 8e3b15
                                           : (&((*res_p).z));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // composite exposure
shun-iwasawa 1b1839
      if ((*alp_p) >= 1.0 || (*res) == 0.0)
shun-iwasawa 1b1839
        (*res) = exposure;
shun-iwasawa 1b1839
      else {
shun-iwasawa 1b1839
        (*res) *= 1.0 - (*alp_p);
shun-iwasawa 1b1839
        (*res) += exposure;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // over compoite alpha
shun-iwasawa 1b1839
      if (m_channel == Red) {
shun-iwasawa 1b1839
        if ((*res_p).w < 1.0) {
shun-iwasawa 1b1839
          if ((*alp_p) > 1.0)
shun-iwasawa 1b1839
            (*res_p).w = 1.0;
shun-iwasawa 1b1839
          else
shun-iwasawa 1b1839
            (*res_p).w = (*alp_p) + (*res_p).w * (1.0 - (*alp_p));
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      //---
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
  m_kissfft_comp_in = 0;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  m_finished = true;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
bool BokehUtils::MyThread::checkTerminationAndCleanupThread() {
shun-iwasawa 1b1839
  if (!m_isTerminated) return false;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (m_kissfft_comp_in) m_kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
  if (m_kissfft_comp_out) m_kissfft_comp_out_ras->unlock();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (m_kissfft_plan_fwd) kiss_fft_free(m_kissfft_plan_fwd);
shun-iwasawa 1b1839
  if (m_kissfft_plan_bkwd) kiss_fft_free(m_kissfft_plan_bkwd);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  m_finished = true;
shun-iwasawa 1b1839
  return true;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------
shun-iwasawa 1b1839
BokehUtils::BokehRefThread::BokehRefThread(
shun-iwasawa 1b1839
    int channel, kiss_fft_cpx* fftcpx_channel_before,
shun-iwasawa 1b1839
    kiss_fft_cpx* fftcpx_channel, kiss_fft_cpx* fftcpx_alpha,
shun-iwasawa 1b1839
    kiss_fft_cpx* fftcpx_iris, double4* result_buff,
shun-iwasawa 1b1839
    kiss_fftnd_cfg kissfft_plan_fwd, kiss_fftnd_cfg kissfft_plan_bkwd,
shun-iwasawa 1b1839
    TDimensionI& dim)
shun-iwasawa 1b1839
    : m_channel(channel)
shun-iwasawa 1b1839
    , m_fftcpx_channel_before(fftcpx_channel_before)
shun-iwasawa 1b1839
    , m_fftcpx_channel(fftcpx_channel)
shun-iwasawa 1b1839
    , m_fftcpx_alpha(fftcpx_alpha)
shun-iwasawa 1b1839
    , m_fftcpx_iris(fftcpx_iris)
shun-iwasawa 1b1839
    , m_result_buff(result_buff)
shun-iwasawa 1b1839
    , m_kissfft_plan_fwd(kissfft_plan_fwd)
shun-iwasawa 1b1839
    , m_kissfft_plan_bkwd(kissfft_plan_bkwd)
shun-iwasawa 1b1839
    , m_dim(dim)
shun-iwasawa 1b1839
    , m_finished(false)
shun-iwasawa 1b1839
    , m_isTerminated(false) {}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------
shun-iwasawa 1b1839
shun-iwasawa 1b1839
void BokehUtils::BokehRefThread::run() {
shun-iwasawa 1b1839
  // execute channel fft
shun-iwasawa 1b1839
  kiss_fftnd(m_kissfft_plan_fwd, m_fftcpx_channel_before, m_fftcpx_channel);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (m_isTerminated) {
shun-iwasawa 1b1839
    m_finished = true;
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int size = m_dim.lx * m_dim.ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // multiply filter
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++) {
shun-iwasawa 481b59
    kiss_fft_scalar re, im;
shun-iwasawa 1b1839
    re = m_fftcpx_channel[i].r * m_fftcpx_iris[i].r -
shun-iwasawa 1b1839
         m_fftcpx_channel[i].i * m_fftcpx_iris[i].i;
shun-iwasawa 1b1839
    im = m_fftcpx_channel[i].r * m_fftcpx_iris[i].i +
shun-iwasawa 1b1839
         m_fftcpx_iris[i].r * m_fftcpx_channel[i].i;
shun-iwasawa 1b1839
    m_fftcpx_channel[i].r = re;
shun-iwasawa 1b1839
    m_fftcpx_channel[i].i = im;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  // execute invert fft
shun-iwasawa 1b1839
  kiss_fftnd(m_kissfft_plan_bkwd, m_fftcpx_channel, m_fftcpx_channel_before);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (m_isTerminated) {
shun-iwasawa 1b1839
    m_finished = true;
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // pixels with smaller index : normal composite exposure value
shun-iwasawa 1b1839
  // pixels with the same or larger index : replace exposure value
shun-iwasawa 1b1839
  double4* result_p = m_result_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, result_p++) {
shun-iwasawa 1b1839
    // modify fft coordinate to normal
shun-iwasawa 1b1839
    int coord = getCoord(i, m_dim.lx, m_dim.ly);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    double alpha = (double)m_fftcpx_alpha[coord].r / (double)size;
shun-iwasawa 1b1839
    // ignore transpalent pixels
shun-iwasawa 1b1839
    if (alpha < 0.00001) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    double exposure = (double)m_fftcpx_channel_before[coord].r / (double)size;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // in case of using upper layer at all
shun-iwasawa 1b1839
    if (alpha >= 1.0 || (m_channel == 0 && (*result_p).x == 0.0) ||
shun-iwasawa 1b1839
        (m_channel == 1 && (*result_p).y == 0.0) ||
shun-iwasawa 1b1839
        (m_channel == 2 && (*result_p).z == 0.0)) {
shun-iwasawa 1b1839
      // set exposure
shun-iwasawa 1b1839
      if (m_channel == 0)  // R
shun-iwasawa 1b1839
        (*result_p).x = exposure;
shun-iwasawa 1b1839
      else if (m_channel == 1)  // G
shun-iwasawa 1b1839
        (*result_p).y = exposure;
shun-iwasawa 1b1839
      else  // B
shun-iwasawa 1b1839
        (*result_p).z = exposure;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
    // in case of compositing both layers
shun-iwasawa 1b1839
    else {
shun-iwasawa 1b1839
      if (m_channel == 0)  // R
shun-iwasawa 1b1839
      {
shun-iwasawa 1b1839
        (*result_p).x *= 1.0 - alpha;
shun-iwasawa 1b1839
        (*result_p).x += exposure;
shun-iwasawa 1b1839
      } else if (m_channel == 1)  // G
shun-iwasawa 1b1839
      {
shun-iwasawa 1b1839
        (*result_p).y *= 1.0 - alpha;
shun-iwasawa 1b1839
        (*result_p).y += exposure;
shun-iwasawa 1b1839
      } else  // B
shun-iwasawa 1b1839
      {
shun-iwasawa 1b1839
        (*result_p).z *= 1.0 - alpha;
shun-iwasawa 1b1839
        (*result_p).z += exposure;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  m_finished = true;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
// normalize the source raster image to 0-1 and set to dstMem
shun-iwasawa 1b1839
// returns true if the source is (seems to be) premultiplied
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
template void BokehUtils::setSourceRaster<traster32p, tpixel32="">(</traster32p,>
shun-iwasawa 1b1839
    const TRaster32P srcRas, double4* dstMem, TDimensionI dim);
shun-iwasawa 1b1839
template void BokehUtils::setSourceRaster<traster64p, tpixel64="">(</traster64p,>
shun-iwasawa 1b1839
    const TRaster64P srcRas, double4* dstMem, TDimensionI dim);
shun-iwasawa 481b59
template void BokehUtils::setSourceRaster<trasterfp, tpixelf="">(</trasterfp,>
shun-iwasawa 481b59
    const TRasterFP srcRas, double4* dstMem, TDimensionI dim);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa 1b1839
void BokehUtils::setSourceRaster(const RASTER srcRas, double4* dstMem,
shun-iwasawa 1b1839
                                 TDimensionI dim) {
shun-iwasawa 1b1839
  double4* chann_p = dstMem;
shun-iwasawa 1b1839
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa 1b1839
    PIXEL* pix = srcRas->pixels(j);
shun-iwasawa 1b1839
    for (int i = 0; i < dim.lx; i++, pix++, chann_p++) {
shun-iwasawa 1b1839
      (*chann_p).x = (double)pix->r / (double)PIXEL::maxChannelValue;
shun-iwasawa 1b1839
      (*chann_p).y = (double)pix->g / (double)PIXEL::maxChannelValue;
shun-iwasawa 1b1839
      (*chann_p).z = (double)pix->b / (double)PIXEL::maxChannelValue;
shun-iwasawa 1b1839
      (*chann_p).w = (double)pix->m / (double)PIXEL::maxChannelValue;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
// normalize brightness of the depth reference image to unsigned char
shun-iwasawa 1b1839
// and store into detMem
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
template void BokehUtils::setDepthRaster<traster32p, tpixel32="">(</traster32p,>
shun-iwasawa 1b1839
    const TRaster32P srcRas, unsigned char* dstMem, TDimensionI dim);
shun-iwasawa 1b1839
template void BokehUtils::setDepthRaster<traster64p, tpixel64="">(</traster64p,>
shun-iwasawa 1b1839
    const TRaster64P srcRas, unsigned char* dstMem, TDimensionI dim);
shun-iwasawa 481b59
template void BokehUtils::setDepthRaster<trasterfp, tpixelf="">(</trasterfp,>
shun-iwasawa 481b59
    const TRasterFP srcRas, unsigned char* dstMem, TDimensionI dim);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa 1b1839
void BokehUtils::setDepthRaster(const RASTER srcRas, unsigned char* dstMem,
shun-iwasawa 1b1839
                                TDimensionI dim) {
shun-iwasawa 1b1839
  unsigned char* depth_p = dstMem;
shun-iwasawa 1b1839
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa 1b1839
    PIXEL* pix = srcRas->pixels(j);
shun-iwasawa 1b1839
    for (int i = 0; i < dim.lx; i++, pix++, depth_p++) {
shun-iwasawa 1b1839
      // normalize brightness to 0-1
shun-iwasawa 1b1839
      double val = ((double)pix->r * 0.3 + (double)pix->g * 0.59 +
shun-iwasawa 1b1839
                    (double)pix->b * 0.11) /
shun-iwasawa 1b1839
                   (double)PIXEL::maxChannelValue;
shun-iwasawa 481b59
      // clamp
shun-iwasawa 481b59
      val = std::min(1., std::max(0., val));
shun-iwasawa 481b59
shun-iwasawa 1b1839
      // convert to unsigned char
shun-iwasawa 1b1839
      (*depth_p) = (unsigned char)(val * (double)UCHAR_MAX + 0.5);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
// create the depth index map
shun-iwasawa 1b1839
//------------------------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::defineSegemntDepth(
shun-iwasawa 1b1839
    const unsigned char* indexMap_main, const unsigned char* indexMap_sub,
shun-iwasawa 1b1839
    const double* mainSub_ratio, const unsigned char* depth_buff,
shun-iwasawa 1b1839
    const TDimensionI& dimOut, QVector<double>& segmentDepth_main,</double>
shun-iwasawa 1b1839
    QVector<double>& segmentDepth_sub, const double focusDepth,</double>
shun-iwasawa 1b1839
    int distancePrecision, double nearDepth, double farDepth) {
shun-iwasawa 1b1839
  QSet<int> segmentValues;</int>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // histogram parameters
shun-iwasawa 1b1839
  struct HISTO {
shun-iwasawa 1b1839
    int pix_amount;
shun-iwasawa 1b1839
    int belongingSegmentValue;  // value to which temporary segmented
shun-iwasawa 1b1839
    int segmentId;
shun-iwasawa 1b1839
    int segmentId_sub;
shun-iwasawa 1b1839
  };
shun-iwasawa 1b1839
  std::array<histo, 256=""> histo;</histo,>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // initialize
shun-iwasawa 1b1839
  for (int h = 0; h < 256; h++) {
shun-iwasawa 1b1839
    histo[h].pix_amount            = 0;
shun-iwasawa 1b1839
    histo[h].belongingSegmentValue = -1;
shun-iwasawa 1b1839
    histo[h].segmentId             = -1;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int size = dimOut.lx * dimOut.ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // max and min
shun-iwasawa 1b1839
  int minHisto = (int)UCHAR_MAX;
shun-iwasawa 1b1839
  int maxHisto = 0;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  unsigned char* depth_p = (unsigned char*)depth_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, depth_p++) {
shun-iwasawa 1b1839
    histo[(int)*depth_p].pix_amount++;
shun-iwasawa 1b1839
    // update max and min
shun-iwasawa 1b1839
    if ((int)*depth_p < minHisto) minHisto = (int)*depth_p;
shun-iwasawa 1b1839
    if ((int)*depth_p > maxHisto) maxHisto = (int)*depth_p;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // the maximum and the minimum depth become the segment layers
shun-iwasawa 1b1839
  segmentValues.insert(minHisto);
shun-iwasawa 1b1839
  segmentValues.insert(maxHisto);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // The nearest depth in the histogram
shun-iwasawa 1b1839
  double minDepth =
shun-iwasawa 1b1839
      (farDepth - nearDepth) * (double)minHisto / 255.0 + nearDepth;
shun-iwasawa 1b1839
  // The furthest depth in the histogram
shun-iwasawa 1b1839
  double maxDepth =
shun-iwasawa 1b1839
      (farDepth - nearDepth) * (double)maxHisto / 255.0 + nearDepth;
shun-iwasawa 1b1839
  // focus depth becomes the segment layer as well
shun-iwasawa 1b1839
  if (minDepth < focusDepth && focusDepth < maxDepth)
shun-iwasawa 1b1839
    segmentValues.insert(std::round(
shun-iwasawa 1b1839
        (double)UCHAR_MAX * (focusDepth - nearDepth) / (farDepth - nearDepth)));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // set the initial segmentation for each depth value
shun-iwasawa 1b1839
  for (int h = 0; h < 256; h++) {
shun-iwasawa 1b1839
    for (int seg = 0; seg < segmentValues.size(); seg++) {
shun-iwasawa 1b1839
      // set the segment
shun-iwasawa 1b1839
      if (histo[h].belongingSegmentValue == -1) {
shun-iwasawa 1b1839
        histo[h].belongingSegmentValue = segmentValues.values().at(seg);
shun-iwasawa 1b1839
        continue;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      // error amount at the current registered layers
shun-iwasawa 1b1839
      int tmpError = std::abs(h - histo[h].belongingSegmentValue);
shun-iwasawa 1b1839
      if (tmpError == 0) break;
shun-iwasawa 1b1839
      // new error amount
shun-iwasawa 1b1839
      int newError = std::abs(h - segmentValues.values().at(seg));
shun-iwasawa 1b1839
      // compare the two and update
shun-iwasawa 1b1839
      if (newError < tmpError)
shun-iwasawa 1b1839
        histo[h].belongingSegmentValue = segmentValues.values().at(seg);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // add the segment layers to the distance precision value
shun-iwasawa 1b1839
  while (segmentValues.size() < distancePrecision) {
shun-iwasawa 1b1839
    // add a new segment at the value which will reduce the error amount in
shun-iwasawa 1b1839
    // maximum
shun-iwasawa 1b1839
    double tmpMaxErrorMod = 0;
shun-iwasawa 1b1839
    int tmpBestNewSegVal;
shun-iwasawa 1b1839
    bool newSegFound = false;
shun-iwasawa 1b1839
    for (int h = minHisto + 1; h < maxHisto; h++) {
shun-iwasawa 1b1839
      // if it is already set as the segment, continue
shun-iwasawa 1b1839
      if (histo[h].belongingSegmentValue == h) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      double errorModAmount = 0;
shun-iwasawa 1b1839
      // estimate how much the error will be reduced if the current h becomes
shun-iwasawa 1b1839
      // segment
shun-iwasawa 1b1839
      for (int i = minHisto + 1; i < maxHisto; i++) {
shun-iwasawa 1b1839
        // compare the current segment value and h and take the nearest value
shun-iwasawa 1b1839
        // if h is near (from i), then accumulate the estimated error reduction
shun-iwasawa 1b1839
        // amount
shun-iwasawa 1b1839
        if (std::abs(i - histo[i].belongingSegmentValue) >
shun-iwasawa 1b1839
            std::abs(i - h))  // the current segment value has
luz paz 6454c4
                              // priority, if the distance is the same
shun-iwasawa 1b1839
          errorModAmount +=
shun-iwasawa 1b1839
              (std::abs(i - histo[i].belongingSegmentValue) - std::abs(i - h)) *
shun-iwasawa 1b1839
              histo[i].pix_amount;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // if h will reduce the error, update the candidate segment value
shun-iwasawa 1b1839
      if (errorModAmount > tmpMaxErrorMod) {
shun-iwasawa 1b1839
        tmpMaxErrorMod   = errorModAmount;
shun-iwasawa 1b1839
        tmpBestNewSegVal = h;
shun-iwasawa 1b1839
        newSegFound      = true;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    if (!newSegFound) break;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // register tmpBestNewSegVal to the segment values list
shun-iwasawa 1b1839
    segmentValues.insert(tmpBestNewSegVal);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // update belongingSegmentValue
shun-iwasawa 1b1839
    for (int h = minHisto + 1; h < maxHisto; h++) {
shun-iwasawa 1b1839
      // compare the current segment value and h and take the nearest value
shun-iwasawa 1b1839
      // if tmpBestNewSegVal is near (from h), then update the
shun-iwasawa 1b1839
      // belongingSegmentValue
shun-iwasawa 1b1839
      if (std::abs(h - histo[h].belongingSegmentValue) >
shun-iwasawa 1b1839
          std::abs(h - tmpBestNewSegVal))  // the current segment value has
luz paz 6454c4
        // priority, if the distance is the same
shun-iwasawa 1b1839
        histo[h].belongingSegmentValue = tmpBestNewSegVal;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // set indices from the farthest and create the index table for each depth
shun-iwasawa 1b1839
  // value
shun-iwasawa 1b1839
  QVector<int> segValVec;</int>
shun-iwasawa 1b1839
  int tmpSegVal = -1;
shun-iwasawa 1b1839
  int tmpSegId  = -1;
shun-iwasawa 1b1839
  for (int h = 255; h >= 0; h--) {
shun-iwasawa 1b1839
    if (histo[h].belongingSegmentValue != tmpSegVal) {
shun-iwasawa 1b1839
      segmentDepth_main.push_back((double)histo[h].belongingSegmentValue /
shun-iwasawa 1b1839
                                  (double)UCHAR_MAX);
shun-iwasawa 1b1839
      tmpSegVal = histo[h].belongingSegmentValue;
shun-iwasawa 1b1839
      tmpSegId++;
shun-iwasawa 1b1839
      segValVec.push_back(tmpSegVal);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
    histo[h].segmentId = tmpSegId;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // "sub" depth segment value list for interporation
shun-iwasawa 1b1839
  for (int d = 0; d < segmentDepth_main.size() - 1; d++)
shun-iwasawa 1b1839
    segmentDepth_sub.push_back(
shun-iwasawa 1b1839
        (segmentDepth_main.at(d) + segmentDepth_main.at(d + 1)) / 2.0);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // create the "sub" index table for each depth value
shun-iwasawa 1b1839
  tmpSegId = 0;
shun-iwasawa 1b1839
  for (int seg = 0; seg < segValVec.size() - 1; seg++) {
shun-iwasawa 1b1839
    int hMax = (seg == 0) ? 255 : segValVec.at(seg);
shun-iwasawa 1b1839
    int hMin = (seg == segValVec.size() - 2) ? 0 : segValVec.at(seg + 1) + 1;
shun-iwasawa 1b1839
    for (int h = hMax; h >= hMin; h--) histo[h].segmentId_sub = tmpSegId;
shun-iwasawa 1b1839
    tmpSegId++;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // convert the depth value to the segment index by using the index table
shun-iwasawa 1b1839
  depth_p               = (unsigned char*)depth_buff;
shun-iwasawa 1b1839
  unsigned char* main_p = (unsigned char*)indexMap_main;
shun-iwasawa 1b1839
  unsigned char* sub_p  = (unsigned char*)indexMap_sub;
shun-iwasawa 1b1839
  // mainSub_ratio represents the composition ratio of the image with "main"
shun-iwasawa 1b1839
  // separation.
shun-iwasawa 1b1839
  double* ratio_p = (double*)mainSub_ratio;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, depth_p++, main_p++, sub_p++, ratio_p++) {
shun-iwasawa 1b1839
    *main_p = (unsigned char)histo[(int)*depth_p].segmentId;
shun-iwasawa 1b1839
    *sub_p  = (unsigned char)histo[(int)*depth_p].segmentId_sub;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    double depth         = (double)*depth_p / (double)UCHAR_MAX;
shun-iwasawa 1b1839
    double main_segDepth = segmentDepth_main.at(*main_p);
shun-iwasawa 1b1839
    double sub_segDepth  = segmentDepth_sub.at(*sub_p);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    if (main_segDepth == sub_segDepth)
shun-iwasawa 1b1839
      *ratio_p = 1.0;
shun-iwasawa 1b1839
    else {
shun-iwasawa 1b1839
      *ratio_p = 1.0 - (main_segDepth - depth) / (main_segDepth - sub_segDepth);
shun-iwasawa 1b1839
      *ratio_p = clamp01(*ratio_p);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// convert source image value rgb -> exposure
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::convertRGBToExposure(const double4* source_buff, int size,
shun-iwasawa 481b59
                                      const ExposureConverter& conv) {
shun-iwasawa 1b1839
  double4* source_p = (double4*)source_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, source_p++) {
shun-iwasawa 1b1839
    // continue if alpha channel is 0
shun-iwasawa 1b1839
    if ((*source_p).w == 0.0) {
shun-iwasawa 1b1839
      (*source_p).x = 0.0;
shun-iwasawa 1b1839
      (*source_p).y = 0.0;
shun-iwasawa 1b1839
      (*source_p).z = 0.0;
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // RGB value -> exposure
shun-iwasawa 481b59
    (*source_p).x = conv.valueToExposure((*source_p).x);
shun-iwasawa 481b59
    (*source_p).y = conv.valueToExposure((*source_p).y);
shun-iwasawa 481b59
    (*source_p).z = conv.valueToExposure((*source_p).z);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // multiply with alpha channel
shun-iwasawa 1b1839
    (*source_p).x *= (*source_p).w;
shun-iwasawa 1b1839
    (*source_p).y *= (*source_p).w;
shun-iwasawa 1b1839
    (*source_p).z *= (*source_p).w;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// convert result image value exposure -> rgb
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::convertExposureToRGB(const double4* result_buff, int size,
shun-iwasawa 481b59
                                      const ExposureConverter& conv) {
shun-iwasawa 1b1839
  double4* res_p = (double4*)result_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, res_p++) {
shun-iwasawa 481b59
    (*res_p).x = conv.exposureToValue((*res_p).x);
shun-iwasawa 481b59
    (*res_p).y = conv.exposureToValue((*res_p).y);
shun-iwasawa 481b59
    (*res_p).z = conv.exposureToValue((*res_p).z);
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//-----------------------------------------------------
shun-iwasawa 1b1839
// obtain iris size from the depth value
shun-iwasawa 1b1839
//-----------------------------------------------------
shun-iwasawa 1b1839
double BokehUtils::calcIrisSize(
shun-iwasawa 1b1839
    const double depth,  // relative depth where near depth is set to 0 and far
shun-iwasawa 1b1839
                         // depth is 1
shun-iwasawa 1b1839
    const double bokehPixelAmount, const double onFocusDistance,
shun-iwasawa 1b1839
    const double bokehAdjustment, double nearDepth,
shun-iwasawa 1b1839
    double farDepth)  // actual depth of black and white reference pixels
shun-iwasawa 1b1839
{
shun-iwasawa 1b1839
  double realDepth = nearDepth + (farDepth - nearDepth) * depth;
shun-iwasawa 1b1839
  return ((double)onFocusDistance - realDepth) * bokehPixelAmount *
shun-iwasawa 1b1839
         bokehAdjustment;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
namespace {
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// apply single median filter
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void doSingleMedian(const double4* source_buff,
shun-iwasawa 1b1839
                    const double4* segment_layer_buff,
shun-iwasawa 1b1839
                    const unsigned char* indexMap_mainSub, int index, int lx,
shun-iwasawa 1b1839
                    int ly, const unsigned char* generation_buff, int curGen) {
shun-iwasawa 1b1839
  double4* source_p         = (double4*)source_buff;
shun-iwasawa 1b1839
  double4* layer_p          = (double4*)segment_layer_buff;
shun-iwasawa 1b1839
  unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
shun-iwasawa 1b1839
  unsigned char* gen_p      = (unsigned char*)generation_buff;
shun-iwasawa 1b1839
  for (int posY = 0; posY < ly; posY++) {
shun-iwasawa 1b1839
    for (int posX = 0; posX < lx;
shun-iwasawa 1b1839
         posX++, source_p++, layer_p++, indexMap_p++, gen_p++) {
shun-iwasawa 1b1839
      // continue if the current pixel is at the same or far depth
shun-iwasawa 1b1839
      if ((int)(*indexMap_p) <= index) continue;
shun-iwasawa 1b1839
      // continue if the current pixel is already extended
shun-iwasawa 1b1839
      if ((*gen_p) > 0) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // check out the neighbor pixels. store brightness in neighbor[x].w
shun-iwasawa 1b1839
      double4 neighbor[8];
shun-iwasawa 1b1839
      int neighbor_amount = 0;
shun-iwasawa 1b1839
      for (int ky = posY - 1; ky <= posY + 1; ky++) {
shun-iwasawa 1b1839
        for (int kx = posX - 1; kx <= posX + 1; kx++) {
shun-iwasawa 1b1839
          // skip the current pixel itself
shun-iwasawa 1b1839
          if (kx == posX && ky == posY) continue;
shun-iwasawa 1b1839
          // border condition
shun-iwasawa 1b1839
          if (ky < 0 || ky >= ly || kx < 0 || kx >= lx) continue;
shun-iwasawa 1b1839
          // index in the buffer
shun-iwasawa 1b1839
          int neighborId = ky * lx + kx;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
          if ((int)indexMap_mainSub[neighborId] !=
shun-iwasawa 1b1839
                  index &&  // pixels from the original image can be used as
shun-iwasawa 1b1839
                            // neighbors
shun-iwasawa 1b1839
              (generation_buff[neighborId] == 0 ||  // pixels which is not yet
shun-iwasawa 1b1839
                                                    // be extended cannot be
shun-iwasawa 1b1839
                                                    // used as neighbors
shun-iwasawa 1b1839
               generation_buff[neighborId] == curGen))  // pixels which is
shun-iwasawa 1b1839
                                                        // extended in the
shun-iwasawa 1b1839
                                                        // current median
shun-iwasawa 1b1839
                                                        // generation cannot be
shun-iwasawa 1b1839
                                                        // used as neighbors
shun-iwasawa 1b1839
            continue;
shun-iwasawa 1b1839
          // compute brightness (actually, it is "pseudo" brightness
shun-iwasawa 1b1839
          // since the source buffer is already converted to exposure)
shun-iwasawa 1b1839
          double brightness = source_buff[neighborId].x * 0.3 +
shun-iwasawa 1b1839
                              source_buff[neighborId].y * 0.59 +
shun-iwasawa 1b1839
                              source_buff[neighborId].z * 0.11;
shun-iwasawa 1b1839
          // insert with sorting
shun-iwasawa 1b1839
          int ins_index;
shun-iwasawa 1b1839
          for (ins_index = 0; ins_index < neighbor_amount; ins_index++) {
shun-iwasawa 1b1839
            if (neighbor[ins_index].w < brightness) break;
shun-iwasawa 1b1839
          }
shun-iwasawa 1b1839
          // displace neighbor values from neighbor_amount-1 to ins_index
shun-iwasawa 1b1839
          for (int k = neighbor_amount - 1; k >= ins_index; k--) {
shun-iwasawa 1b1839
            neighbor[k + 1].x = neighbor[k].x;
shun-iwasawa 1b1839
            neighbor[k + 1].y = neighbor[k].y;
shun-iwasawa 1b1839
            neighbor[k + 1].z = neighbor[k].z;
shun-iwasawa 1b1839
            neighbor[k + 1].w = neighbor[k].w;
shun-iwasawa 1b1839
          }
shun-iwasawa 1b1839
          // set the neighbor value
shun-iwasawa 1b1839
          neighbor[ins_index].x = source_buff[neighborId].x;
shun-iwasawa 1b1839
          neighbor[ins_index].y = source_buff[neighborId].y;
shun-iwasawa 1b1839
          neighbor[ins_index].z = source_buff[neighborId].z;
shun-iwasawa 1b1839
          neighbor[ins_index].w = brightness;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
          // increment the count
shun-iwasawa 1b1839
          neighbor_amount++;
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // If there is no neighbor pixles available, continue
shun-iwasawa 1b1839
      if (neighbor_amount == 0) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // switch the behavior when there are even number of neighbors
shun-iwasawa 1b1839
      bool flag = ((posX + posY) % 2 == 0);
shun-iwasawa 1b1839
      // pick up the medium index
shun-iwasawa 1b1839
      int pickIndex = (flag)
shun-iwasawa 1b1839
                          ? (int)std::floor((double)(neighbor_amount - 1) / 2.0)
shun-iwasawa 1b1839
                          : (int)std::ceil((double)(neighbor_amount - 1) / 2.0);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // set the medium pixel values
shun-iwasawa 1b1839
      (*layer_p).x = neighbor[pickIndex].x;
shun-iwasawa 1b1839
      (*layer_p).y = neighbor[pickIndex].y;
shun-iwasawa 1b1839
      (*layer_p).z = neighbor[pickIndex].z;
shun-iwasawa 1b1839
      (*layer_p).w = (*source_p).w;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // set the generation
shun-iwasawa 1b1839
      (*gen_p) = (unsigned char)curGen;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
void doSingleExtend(const double4* source_buff,
shun-iwasawa 1b1839
                    const double4* segment_layer_buff,
shun-iwasawa 1b1839
                    const unsigned char* indexMap_mainSub, int index, int lx,
shun-iwasawa 1b1839
                    int ly, const unsigned char* generation_buff, int curGen) {
shun-iwasawa 1b1839
  double4* source_p         = (double4*)source_buff;
shun-iwasawa 1b1839
  double4* layer_p          = (double4*)segment_layer_buff;
shun-iwasawa 1b1839
  unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
shun-iwasawa 1b1839
  unsigned char* gen_p      = (unsigned char*)generation_buff;
shun-iwasawa 1b1839
  for (int posY = 0; posY < ly; posY++) {
shun-iwasawa 1b1839
    for (int posX = 0; posX < lx;
shun-iwasawa 1b1839
         posX++, source_p++, layer_p++, indexMap_p++, gen_p++) {
shun-iwasawa 1b1839
      // continue if the current pixel is at the same or far depth
shun-iwasawa 1b1839
      if ((int)(*indexMap_p) <= index) continue;
shun-iwasawa 1b1839
      // continue if the current pixel is already extended
shun-iwasawa 1b1839
      if ((*gen_p) > 0) continue;
shun-iwasawa 1b1839
shun-iwasawa 443318
      // check out the neighbor pixels.
shun-iwasawa 1b1839
      bool neighbor_found = false;
shun-iwasawa 1b1839
      for (int ky = posY - 1; ky <= posY + 1; ky++) {
shun-iwasawa 1b1839
        for (int kx = posX - 1; kx <= posX + 1; kx++) {
shun-iwasawa 1b1839
          // skip the current pixel itself
shun-iwasawa 1b1839
          if (kx == posX && ky == posY) continue;
shun-iwasawa 1b1839
          // border condition
shun-iwasawa 1b1839
          if (ky < 0 || ky >= ly || kx < 0 || kx >= lx) continue;
shun-iwasawa 1b1839
          // index in the buffer
shun-iwasawa 1b1839
          int neighborId = ky * lx + kx;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
          if ((int)indexMap_mainSub[neighborId] !=
shun-iwasawa 1b1839
                  index &&  // pixels from the original image can be used as
shun-iwasawa 1b1839
                            // neighbors
shun-iwasawa 1b1839
              (generation_buff[neighborId] == 0 ||  // pixels which is not yet
shun-iwasawa 1b1839
                                                    // be extended cannot be
shun-iwasawa 1b1839
                                                    // used as neighbors
shun-iwasawa 1b1839
               generation_buff[neighborId] == curGen))  // pixels which is
shun-iwasawa 1b1839
                                                        // extended in the
shun-iwasawa 1b1839
                                                        // current median
shun-iwasawa 1b1839
                                                        // generation cannot be
shun-iwasawa 1b1839
                                                        // used as neighbors
shun-iwasawa 1b1839
            continue;
shun-iwasawa 1b1839
          // increment the count
shun-iwasawa 1b1839
          neighbor_found = true;
shun-iwasawa 1b1839
          break;
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
        if (neighbor_found) break;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // If there is no neighbor pixles available, continue
shun-iwasawa 1b1839
      if (!neighbor_found) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // set the medium pixel values
shun-iwasawa 1b1839
      (*layer_p).x = (*source_p).x;
shun-iwasawa 1b1839
      (*layer_p).y = (*source_p).y;
shun-iwasawa 1b1839
      (*layer_p).z = (*source_p).z;
shun-iwasawa 1b1839
      (*layer_p).w = (*source_p).w;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // set the generation
shun-iwasawa 1b1839
      (*gen_p) = (unsigned char)curGen;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
}  // namespace
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// generate the segment layer source at the current depth
shun-iwasawa 1b1839
// considering fillGap and doMedian options
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::retrieveLayer(const double4* source_buff,
shun-iwasawa 1b1839
                               const double4* segment_layer_buff,
shun-iwasawa 1b1839
                               const unsigned char* indexMap_mainSub, int index,
shun-iwasawa 1b1839
                               int lx, int ly, bool fillGap, bool doMedian,
shun-iwasawa 1b1839
                               int margin) {
shun-iwasawa 1b1839
  // only when fillGap is ON and doMedian is OFF,
shun-iwasawa 1b1839
  // fill the region where will be behind of the near layers
shun-iwasawa 1b1839
  // bool fill = (fillGap && !doMedian);
shun-iwasawa 1b1839
  // retrieve the regions with the current depth
shun-iwasawa 1b1839
  double4* source_p         = (double4*)source_buff;
shun-iwasawa 1b1839
  double4* layer_p          = (double4*)segment_layer_buff;
shun-iwasawa 1b1839
  unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
shun-iwasawa 1b1839
  for (int i = 0; i < lx * ly; i++, source_p++, layer_p++, indexMap_p++) {
shun-iwasawa 1b1839
    // continue if the current pixel is at the far layer
shun-iwasawa 1b1839
    // consider the fill flag if the current pixel is at the near layer
shun-iwasawa 1b1839
    // if ((int)(*indexMap_p) < index || (!fill && (int)(*indexMap_p) > index))
shun-iwasawa 1b1839
    if ((int)(*indexMap_p) != index) continue;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // copy pixel values
shun-iwasawa 1b1839
    (*layer_p).x = (*source_p).x;
shun-iwasawa 1b1839
    (*layer_p).y = (*source_p).y;
shun-iwasawa 1b1839
    (*layer_p).z = (*source_p).z;
shun-iwasawa 1b1839
    (*layer_p).w = (*source_p).w;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (!fillGap && !doMedian) return;
shun-iwasawa 1b1839
  if (margin == 0) return;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // extend pixels by using median filter
shun-iwasawa 1b1839
  unsigned char* generation_buff;
shun-iwasawa 1b1839
  TRasterGR8P generation_buff_ras = allocateRasterAndLock<unsigned char="">(</unsigned>
shun-iwasawa 1b1839
      &generation_buff, TDimensionI(lx, ly));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // extend (margin * 2) pixels in order to enough cover when two adjacent
shun-iwasawa 1b1839
  // layers are both blurred in the maximum radius
shun-iwasawa 1b1839
  for (int gen = 0; gen < margin * 2; gen++) {
shun-iwasawa 1b1839
    if (doMedian)
shun-iwasawa 1b1839
      // apply single median filter
shun-iwasawa 1b1839
      doSingleMedian(source_buff, segment_layer_buff, indexMap_mainSub, index,
shun-iwasawa 1b1839
                     lx, ly, generation_buff, gen + 1);
shun-iwasawa 1b1839
    else
shun-iwasawa 1b1839
      // put the source pixel as-is
shun-iwasawa 1b1839
      doSingleExtend(source_buff, segment_layer_buff, indexMap_mainSub, index,
shun-iwasawa 1b1839
                     lx, ly, generation_buff, gen + 1);
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  generation_buff_ras->unlock();
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// normal-composite the layer as is, without filtering
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::compositeAsIs(const double4* segment_layer_buff,
shun-iwasawa 1b1839
                               const double4* result_buff_mainSub, int size) {
shun-iwasawa 1b1839
  double4* layer_p  = (double4*)segment_layer_buff;
shun-iwasawa 1b1839
  double4* result_p = (double4*)result_buff_mainSub;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, layer_p++, result_p++) {
shun-iwasawa 1b1839
    // in case the pixel is full opac
shun-iwasawa 1b1839
    if ((*layer_p).w == 1.0f) {
shun-iwasawa 1b1839
      (*result_p).x = (*layer_p).x;
shun-iwasawa 1b1839
      (*result_p).y = (*layer_p).y;
shun-iwasawa 1b1839
      (*result_p).z = (*layer_p).z;
shun-iwasawa 1b1839
      (*result_p).w = 1.0f;
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
    // in case the pixel is full transparent
shun-iwasawa 1b1839
    else if ((*layer_p).w == 0.0f)
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    // in case the pixel is semi-transparent, do normal composite
shun-iwasawa 1b1839
    else {
shun-iwasawa 1b1839
      (*result_p).x = (*layer_p).x + (*result_p).x * (1.0 - (*layer_p).w);
shun-iwasawa 1b1839
      (*result_p).y = (*layer_p).y + (*result_p).y * (1.0 - (*layer_p).w);
shun-iwasawa 1b1839
      (*result_p).z = (*layer_p).z + (*result_p).z * (1.0 - (*layer_p).w);
shun-iwasawa 1b1839
      (*result_p).w = (*layer_p).w + (*result_p).w * (1.0 - (*layer_p).w);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//------------------------------------------------
shun-iwasawa 1b1839
// Resize / flip the iris image according to the size ratio.
shun-iwasawa 1b1839
// Normalize the brightness of the iris image.
shun-iwasawa 1b1839
// Enlarge the iris to the output size.
shun-iwasawa 1b1839
void BokehUtils::convertIris(const double irisSize,
shun-iwasawa 1b1839
                             kiss_fft_cpx* kissfft_comp_iris_before,
shun-iwasawa 1b1839
                             const TDimensionI& dimOut, const TRectD& irisBBox,
shun-iwasawa 1b1839
                             const TTile& irisTile) {
shun-iwasawa 1b1839
  // the original size of iris image
shun-iwasawa 1b1839
  double2 irisOrgSize = {irisBBox.getLx(), irisBBox.getLy()};
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Get the size ratio of iris based on width. The ratio can be negative value.
shun-iwasawa 1b1839
  double irisSizeResampleRatio = irisSize / irisOrgSize.x;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Create the raster for resized iris
shun-iwasawa 1b1839
  double2 resizedIrisSize = {std::abs(irisSizeResampleRatio) * irisOrgSize.x,
shun-iwasawa 1b1839
                             std::abs(irisSizeResampleRatio) * irisOrgSize.y};
shun-iwasawa 1b1839
  // add 1 pixel margins to all sides
shun-iwasawa 1b1839
  int2 filterSize = {int(std::ceil(resizedIrisSize.x)) + 2,
shun-iwasawa 1b1839
                     int(std::ceil(resizedIrisSize.y)) + 2};
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TPointD resizeOffset((double)filterSize.x - resizedIrisSize.x,
shun-iwasawa 1b1839
                       (double)filterSize.y - resizedIrisSize.y);
shun-iwasawa 1b1839
  // Add some adjustment in order to absorb the difference of the cases when the
shun-iwasawa 1b1839
  // iris size is odd and even numbers.
shun-iwasawa 1b1839
  // Try to set the center of the iris to the center of the screen
shun-iwasawa 1b1839
  if ((dimOut.lx - filterSize.x) % 2 == 1) filterSize.x++;
shun-iwasawa 1b1839
  if ((dimOut.ly - filterSize.y) % 2 == 1) filterSize.y++;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Terminate if the filter size becomes bigger than the output size.
shun-iwasawa 1b1839
  if (filterSize.x > dimOut.lx || filterSize.y > dimOut.ly) {
shun-iwasawa 1b1839
    std::cout
shun-iwasawa 1b1839
        << "Error: The iris filter size becomes larger than the source size!"
shun-iwasawa 1b1839
        << std::endl;
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRaster64P resizedIris(TDimension(filterSize.x, filterSize.y));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Add some adjustment in order to absorb the 0.5 translation to be done in
shun-iwasawa 1b1839
  // resample()
shun-iwasawa 1b1839
  TAffine aff;
shun-iwasawa 1b1839
  TPointD affOffset(0.5, 0.5);
shun-iwasawa 1b1839
  affOffset += TPointD((dimOut.lx % 2 == 1) ? 0.5 : 0.0,
shun-iwasawa 1b1839
                       (dimOut.ly % 2 == 1) ? 0.5 : 0.0);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  aff = TTranslation(resizedIris->getCenterD() + affOffset);
shun-iwasawa 1b1839
  aff *= TScale(irisSizeResampleRatio);
shun-iwasawa 1b1839
  aff *= TTranslation(-(irisTile.getRaster()->getCenterD() + affOffset));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // resample the iris
shun-iwasawa 1b1839
  TRop::resample(resizedIris, irisTile.getRaster(), aff);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // accumulated value
shun-iwasawa 1b1839
  float irisValAmount = 0.0f;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int iris_j = 0;
shun-iwasawa 1b1839
  // Initialize
shun-iwasawa 1b1839
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
shun-iwasawa 1b1839
    kissfft_comp_iris_before[i].r = 0.0f;
shun-iwasawa 1b1839
    kissfft_comp_iris_before[i].i = 0.0f;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  for (int j = (dimOut.ly - filterSize.y) / 2; iris_j < filterSize.y;
shun-iwasawa 1b1839
       j++, iris_j++) {
shun-iwasawa 1b1839
    TPixel64* pix = resizedIris->pixels(iris_j);
shun-iwasawa 1b1839
    int iris_i    = 0;
shun-iwasawa 1b1839
    for (int i = (dimOut.lx - filterSize.x) / 2; iris_i < filterSize.x;
shun-iwasawa 1b1839
         i++, iris_i++) {
shun-iwasawa 1b1839
      // Value = 0.3R 0.59G 0.11B
shun-iwasawa 1b1839
      kissfft_comp_iris_before[j * dimOut.lx + i].r =
shun-iwasawa 1b1839
          ((float)pix->r * 0.3f + (float)pix->g * 0.59f +
shun-iwasawa 1b1839
           (float)pix->b * 0.11f) /
shun-iwasawa 1b1839
          (float)USHRT_MAX;
shun-iwasawa 1b1839
      irisValAmount += kissfft_comp_iris_before[j * dimOut.lx + i].r;
shun-iwasawa 1b1839
      pix++;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Normalize value
shun-iwasawa 1b1839
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
shun-iwasawa 1b1839
    kissfft_comp_iris_before[i].r /= irisValAmount;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// retrieve segment layer image for each channel
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::retrieveChannel(const double4* segment_layer_buff,  // src
shun-iwasawa 1b1839
                                 kiss_fft_cpx* fftcpx_r_before,      // dst
shun-iwasawa 1b1839
                                 kiss_fft_cpx* fftcpx_g_before,      // dst
shun-iwasawa 1b1839
                                 kiss_fft_cpx* fftcpx_b_before,      // dst
shun-iwasawa 1b1839
                                 kiss_fft_cpx* fftcpx_a_before,      // dst
shun-iwasawa 1b1839
                                 int size) {
shun-iwasawa 1b1839
  double4* layer_p = (double4*)segment_layer_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, layer_p++) {
shun-iwasawa 1b1839
    fftcpx_r_before[i].r = (*layer_p).x;
shun-iwasawa 1b1839
    fftcpx_g_before[i].r = (*layer_p).y;
shun-iwasawa 1b1839
    fftcpx_b_before[i].r = (*layer_p).z;
shun-iwasawa 1b1839
    fftcpx_a_before[i].r = (*layer_p).w;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// multiply filter on channel
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::multiplyFilter(kiss_fft_cpx* fftcpx_channel,  // dst
shun-iwasawa 1b1839
                                kiss_fft_cpx* fftcpx_iris,     // filter
shun-iwasawa 1b1839
                                int size) {
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++) {
shun-iwasawa 481b59
    kiss_fft_scalar re, im;
shun-iwasawa 1b1839
    re = fftcpx_channel[i].r * fftcpx_iris[i].r -
shun-iwasawa 1b1839
         fftcpx_channel[i].i * fftcpx_iris[i].i;
shun-iwasawa 1b1839
    im = fftcpx_channel[i].r * fftcpx_iris[i].i +
shun-iwasawa 1b1839
         fftcpx_channel[i].i * fftcpx_iris[i].r;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    fftcpx_channel[i].r = re;
shun-iwasawa 1b1839
    fftcpx_channel[i].i = im;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// normal comosite the alpha channel
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::compositeAlpha(const double4* result_buff,        // dst
shun-iwasawa 1b1839
                                const kiss_fft_cpx* fftcpx_alpha,  // alpha
shun-iwasawa 1b1839
                                int lx, int ly) {
shun-iwasawa 1b1839
  int size          = lx * ly;
shun-iwasawa 1b1839
  double4* result_p = (double4*)result_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size; i++, result_p++) {
shun-iwasawa 1b1839
    // modify fft coordinate to normal
shun-iwasawa 1b1839
    double alpha  = (double)fftcpx_alpha[getCoord(i, lx, ly)].r / (double)size;
shun-iwasawa 1b1839
    alpha         = clamp01(alpha);
shun-iwasawa 1b1839
    (*result_p).w = alpha + ((*result_p).w * (1.0 - alpha));
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// interpolate main and sub exposures
shun-iwasawa 1b1839
// set to result
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void BokehUtils::interpolateExposureAndConvertToRGB(
shun-iwasawa 1b1839
    const double4* result_main_buff,  // result1
shun-iwasawa 1b1839
    const double4* result_sub_buff,   // result2
shun-iwasawa 1b1839
    const double* mainSub_ratio,      // ratio
shun-iwasawa 1b1839
    const double4* result_buff,       // dst
shun-iwasawa 1b1839
    int size, double layerHardnessRatio) {
shun-iwasawa 1b1839
  double4* resultMain_p = (double4*)result_main_buff;
shun-iwasawa 1b1839
  double4* resultSub_p  = (double4*)result_sub_buff;
shun-iwasawa 1b1839
  double* ratio_p       = (double*)mainSub_ratio;
shun-iwasawa 1b1839
  double4* out_p        = (double4*)result_buff;
shun-iwasawa 1b1839
  for (int i = 0; i < size;
shun-iwasawa 1b1839
       i++, resultMain_p++, resultSub_p++, ratio_p++, out_p++) {
shun-iwasawa 1b1839
    // interpolate main and sub exposures
shun-iwasawa 1b1839
    double4 result;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    result.x =
shun-iwasawa 1b1839
        (*resultMain_p).x * (*ratio_p) + (*resultSub_p).x * (1.0 - (*ratio_p));
shun-iwasawa 1b1839
    result.y =
shun-iwasawa 1b1839
        (*resultMain_p).y * (*ratio_p) + (*resultSub_p).y * (1.0 - (*ratio_p));
shun-iwasawa 1b1839
    result.z =
shun-iwasawa 1b1839
        (*resultMain_p).z * (*ratio_p) + (*resultSub_p).z * (1.0 - (*ratio_p));
shun-iwasawa 1b1839
    result.w =
shun-iwasawa 1b1839
        (*resultMain_p).w * (*ratio_p) + (*resultSub_p).w * (1.0 - (*ratio_p));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // continue for transparent pixel
shun-iwasawa 1b1839
    if (result.w == 0.0) {
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // convert exposure by layer hardness
shun-iwasawa 1b1839
    if (layerHardnessRatio != 1.0) {
shun-iwasawa efd92b
      result.x = std::pow(result.x / result.w, layerHardnessRatio) * result.w;
shun-iwasawa efd92b
      result.y = std::pow(result.y / result.w, layerHardnessRatio) * result.w;
shun-iwasawa efd92b
      result.z = std::pow(result.z / result.w, layerHardnessRatio) * result.w;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // in case the result is replaced by the upper layer pixel
shun-iwasawa 1b1839
    if (result.w >= 1.0f) {
shun-iwasawa 1b1839
      (*out_p).x = result.x;
shun-iwasawa 1b1839
      (*out_p).y = result.y;
shun-iwasawa 1b1839
      (*out_p).z = result.z;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
    // in case the layers will be composed
shun-iwasawa 1b1839
    else {
shun-iwasawa 1b1839
      (*out_p).x = (*out_p).x * (1.0 - result.w) + result.x;
shun-iwasawa 1b1839
      (*out_p).y = (*out_p).y * (1.0 - result.w) + result.y;
shun-iwasawa 1b1839
      (*out_p).z = (*out_p).z * (1.0 - result.w) + result.z;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    (*out_p).w = (*out_p).w * (1.0 - result.w) + result.w;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
//"Over" composite the layer to the output exposure.
shun-iwasawa 1b1839
void BokehUtils::compositLayerAsIs(TTile& layerTile, double4* result,
shun-iwasawa 481b59
                                   TDimensionI& dimOut,
shun-iwasawa 481b59
                                   const ExposureConverter& conv) {
shun-iwasawa 1b1839
  double4* layer_buff;
shun-iwasawa 1b1839
  TRasterGR8P layer_buff_ras(dimOut.lx * sizeof(double4), dimOut.ly);
shun-iwasawa 1b1839
  layer_buff_ras->lock();
shun-iwasawa 1b1839
  layer_buff = (double4*)layer_buff_ras->getRawData();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRaster32P ras32 = (TRaster32P)layerTile.getRaster();
shun-iwasawa 1b1839
  TRaster64P ras64 = (TRaster64P)layerTile.getRaster();
shun-iwasawa 481b59
  TRasterFP rasF   = (TRasterFP)layerTile.getRaster();
shun-iwasawa 1b1839
  lock.lockForRead();
shun-iwasawa 1b1839
  if (ras32)
shun-iwasawa 1b1839
    BokehUtils::setSourceRaster<traster32p, tpixel32="">(ras32, layer_buff,</traster32p,>
shun-iwasawa 1b1839
                                                      dimOut);
shun-iwasawa 1b1839
  else if (ras64)
shun-iwasawa 1b1839
    BokehUtils::setSourceRaster<traster64p, tpixel64="">(ras64, layer_buff,</traster64p,>
shun-iwasawa 1b1839
                                                      dimOut);
shun-iwasawa 481b59
  else if (rasF)
shun-iwasawa 481b59
    BokehUtils::setSourceRaster<trasterfp, tpixelf="">(rasF, layer_buff, dimOut);</trasterfp,>
shun-iwasawa 1b1839
  lock.unlock();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double4* lay_p = layer_buff;
shun-iwasawa 1b1839
  double4* res_p = result;
shun-iwasawa 1b1839
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++, lay_p++, res_p++) {
shun-iwasawa 1b1839
    if ((*lay_p).w <= 0.0)
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    else if ((*lay_p).w < 1.0) {
shun-iwasawa 1b1839
      // composite exposure
shun-iwasawa 481b59
      (*res_p).x = conv.valueToExposure((*lay_p).x) * (*lay_p).w +
shun-iwasawa 1b1839
                   (*res_p).x * (1.0 - (*lay_p).w);
shun-iwasawa 481b59
      (*res_p).y = conv.valueToExposure((*lay_p).y) * (*lay_p).w +
shun-iwasawa 1b1839
                   (*res_p).y * (1.0 - (*lay_p).w);
shun-iwasawa 481b59
      (*res_p).z = conv.valueToExposure((*lay_p).z) * (*lay_p).w +
shun-iwasawa 1b1839
                   (*res_p).z * (1.0 - (*lay_p).w);
shun-iwasawa 1b1839
      // over composite alpha
shun-iwasawa 1b1839
      (*res_p).w = (*lay_p).w + ((*res_p).w * (1.0 - (*lay_p).w));
shun-iwasawa 1b1839
      (*res_p).w = clamp01((*res_p).w);
shun-iwasawa 1b1839
    } else  // replace by upper layer
shun-iwasawa 1b1839
    {
shun-iwasawa 481b59
      (*res_p).x = conv.valueToExposure((*lay_p).x);
shun-iwasawa 481b59
      (*res_p).y = conv.valueToExposure((*lay_p).y);
shun-iwasawa 481b59
      (*res_p).z = conv.valueToExposure((*lay_p).z);
shun-iwasawa 1b1839
      (*res_p).w = 1.0;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  layer_buff_ras->unlock();
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// Do FFT the alpha channel.
shun-iwasawa 1b1839
// Forward FFT -> Multiply by the iris data -> Backward FFT
shun-iwasawa 1b1839
void BokehUtils::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris,
shun-iwasawa 1b1839
                                      TTile& layerTile, double* alpha_bokeh) {
shun-iwasawa 1b1839
  // Obtain the source size
shun-iwasawa 1b1839
  int lx, ly;
shun-iwasawa 1b1839
  lx = layerTile.getRaster()->getSize().lx;
shun-iwasawa 1b1839
  ly = layerTile.getRaster()->getSize().ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Allocate the FFT data
shun-iwasawa 1b1839
  kiss_fft_cpx *kissfft_comp_in, *kissfft_comp_out;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRasterGR8P kissfft_comp_in_ras(lx * sizeof(kiss_fft_cpx), ly);
shun-iwasawa 1b1839
  kissfft_comp_in_ras->lock();
shun-iwasawa 1b1839
  kissfft_comp_in = (kiss_fft_cpx*)kissfft_comp_in_ras->getRawData();
shun-iwasawa 1b1839
  TRasterGR8P kissfft_comp_out_ras(lx * sizeof(kiss_fft_cpx), ly);
shun-iwasawa 1b1839
  kissfft_comp_out_ras->lock();
shun-iwasawa 1b1839
  kissfft_comp_out = (kiss_fft_cpx*)kissfft_comp_out_ras->getRawData();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Initialize the FFT data
shun-iwasawa 1b1839
  for (int i = 0; i < lx * ly; i++) {
shun-iwasawa 1b1839
    kissfft_comp_in[i].r = 0.0;  // real part
shun-iwasawa 1b1839
    kissfft_comp_in[i].i = 0.0;  // imaginary part
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRaster32P ras32 = (TRaster32P)layerTile.getRaster();
shun-iwasawa 1b1839
  TRaster64P ras64 = (TRaster64P)layerTile.getRaster();
shun-iwasawa 481b59
  TRasterFP rasF   = (TRasterFP)layerTile.getRaster();
shun-iwasawa 1b1839
  if (ras32) {
shun-iwasawa 1b1839
    for (int j = 0; j < ly; j++) {
shun-iwasawa 1b1839
      TPixel32* pix = ras32->pixels(j);
shun-iwasawa 1b1839
      for (int i = 0; i < lx; i++) {
shun-iwasawa 1b1839
        kissfft_comp_in[j * lx + i].r = (double)pix->m / (double)UCHAR_MAX;
shun-iwasawa 1b1839
        pix++;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  } else if (ras64) {
shun-iwasawa 1b1839
    for (int j = 0; j < ly; j++) {
shun-iwasawa 1b1839
      TPixel64* pix = ras64->pixels(j);
shun-iwasawa 1b1839
      for (int i = 0; i < lx; i++) {
shun-iwasawa 1b1839
        kissfft_comp_in[j * lx + i].r = (double)pix->m / (double)USHRT_MAX;
shun-iwasawa 1b1839
        pix++;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
    }
shun-iwasawa 481b59
  } else if (rasF) {
shun-iwasawa 481b59
    for (int j = 0; j < ly; j++) {
shun-iwasawa 481b59
      TPixelF* pix = rasF->pixels(j);
shun-iwasawa 481b59
      for (int i = 0; i < lx; i++) {
shun-iwasawa 481b59
        kissfft_comp_in[j * lx + i].r = (double)pix->m;
shun-iwasawa 481b59
        pix++;
shun-iwasawa 481b59
      }
shun-iwasawa 481b59
    }
shun-iwasawa 1b1839
  } else
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int dims[2]             = {ly, lx};
shun-iwasawa 1b1839
  int ndims               = 2;
shun-iwasawa 1b1839
  kiss_fftnd_cfg plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd(plan_fwd, kissfft_comp_in, kissfft_comp_out);
shun-iwasawa 1b1839
  kiss_fft_free(plan_fwd);  // we don't need this plan anymore
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Filtering. Multiply by the iris FFT data
shun-iwasawa 1b1839
  for (int i = 0; i < lx * ly; i++) {
shun-iwasawa 481b59
    kiss_fft_scalar re, im;
shun-iwasawa 1b1839
    re = kissfft_comp_out[i].r * kissfft_comp_iris[i].r -
shun-iwasawa 1b1839
         kissfft_comp_out[i].i * kissfft_comp_iris[i].i;
shun-iwasawa 1b1839
    im = kissfft_comp_out[i].r * kissfft_comp_iris[i].i +
shun-iwasawa 1b1839
         kissfft_comp_iris[i].r * kissfft_comp_out[i].i;
shun-iwasawa 1b1839
    kissfft_comp_out[i].r = re;
shun-iwasawa 1b1839
    kissfft_comp_out[i].i = im;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kiss_fftnd_cfg plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd(plan_bkwd, kissfft_comp_out, kissfft_comp_in);  // Backward FFT
shun-iwasawa 1b1839
  kiss_fft_free(plan_bkwd);  // we don't need this plan anymore
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // In the backward FFT above, "kissfft_comp_out" is used as input and
shun-iwasawa 1b1839
  // "kissfft_comp_in" as output.
shun-iwasawa 1b1839
  // So we don't need "kissfft_comp_out" anymore.
shun-iwasawa 1b1839
  kissfft_comp_out_ras->unlock();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // store to the buffer
shun-iwasawa 1b1839
  double* alp_p = alpha_bokeh;
shun-iwasawa 1b1839
  for (int j = 0; j < ly; j++) {
shun-iwasawa 1b1839
    for (int i = 0; i < lx; i++, alp_p++) {
shun-iwasawa 1b1839
      (*alp_p) = (double)kissfft_comp_in[getCoord(j * lx + i, lx, ly)].r /
shun-iwasawa 1b1839
                 (double)(lx * ly);
shun-iwasawa 1b1839
      (*alp_p) = clamp01(*alp_p);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kissfft_comp_in_ras->unlock();
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//-----------------------------------------------------
shun-iwasawa 1b1839
// convert to channel value and set to output
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template void BokehUtils::setOutputRaster<traster32p, tpixel32="">(</traster32p,>
shun-iwasawa 1b1839
    double4* src, const TRaster32P dstRas, TDimensionI& dim, int2 margin);
shun-iwasawa 1b1839
template void BokehUtils::setOutputRaster<traster64p, tpixel64="">(</traster64p,>
shun-iwasawa 1b1839
    double4* src, const TRaster64P dstRas, TDimensionI& dim, int2 margin);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa 1b1839
void BokehUtils::setOutputRaster(double4* src, const RASTER dstRas,
shun-iwasawa 1b1839
                                 TDimensionI& dim, int2 margin) {
shun-iwasawa 1b1839
  double4* src_p = src + (margin.y * dim.lx);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  for (int j = 0; j < dstRas->getLy(); j++) {
shun-iwasawa 1b1839
    PIXEL* outPix = dstRas->pixels(j);
shun-iwasawa 1b1839
    src_p += margin.x;
shun-iwasawa 1b1839
    for (int i = 0; i < dstRas->getLx(); i++, outPix++, src_p++) {
shun-iwasawa 1b1839
      double val;
shun-iwasawa 1b1839
      val = (*src_p).x * (double)PIXEL::maxChannelValue + 0.5;
shun-iwasawa 1b1839
      outPix->r =
shun-iwasawa 1b1839
          (typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
shun-iwasawa 1b1839
                                        ? (double)PIXEL::maxChannelValue
shun-iwasawa 8e3b15
                                    : (val < 0.) ? 0.
shun-iwasawa 8e3b15
                                                 : val);
shun-iwasawa 1b1839
      val = (*src_p).y * (double)PIXEL::maxChannelValue + 0.5;
shun-iwasawa 1b1839
      outPix->g =
shun-iwasawa 1b1839
          (typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
shun-iwasawa 1b1839
                                        ? (double)PIXEL::maxChannelValue
shun-iwasawa 8e3b15
                                    : (val < 0.) ? 0.
shun-iwasawa 8e3b15
                                                 : val);
shun-iwasawa 1b1839
      val = (*src_p).z * (double)PIXEL::maxChannelValue + 0.5;
shun-iwasawa 1b1839
      outPix->b =
shun-iwasawa 1b1839
          (typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
shun-iwasawa 1b1839
                                        ? (double)PIXEL::maxChannelValue
shun-iwasawa 8e3b15
                                    : (val < 0.) ? 0.
shun-iwasawa 8e3b15
                                                 : val);
shun-iwasawa 1b1839
      val = (*src_p).w * (double)PIXEL::maxChannelValue + 0.5;
shun-iwasawa 1b1839
      outPix->m =
shun-iwasawa 1b1839
          (typename PIXEL::Channel)((val > (double)PIXEL::maxChannelValue)
shun-iwasawa 1b1839
                                        ? (double)PIXEL::maxChannelValue
shun-iwasawa 8e3b15
                                    : (val < 0.) ? 0.
shun-iwasawa 8e3b15
                                                 : val);
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
    src_p += margin.x;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 481b59
template <>
shun-iwasawa 481b59
void BokehUtils::setOutputRaster<trasterfp, tpixelf="">(double4* src,</trasterfp,>
shun-iwasawa 481b59
                                                     const TRasterFP dstRas,
shun-iwasawa 481b59
                                                     TDimensionI& dim,
shun-iwasawa 481b59
                                                     int2 margin) {
shun-iwasawa 481b59
  double4* src_p = src + (margin.y * dim.lx);
shun-iwasawa 481b59
shun-iwasawa 481b59
  for (int j = 0; j < dstRas->getLy(); j++) {
shun-iwasawa 481b59
    TPixelF* outPix = dstRas->pixels(j);
shun-iwasawa 481b59
    src_p += margin.x;
shun-iwasawa 481b59
    for (int i = 0; i < dstRas->getLx(); i++, outPix++, src_p++) {
shun-iwasawa 481b59
      outPix->r = (typename TPixelF::Channel)(
shun-iwasawa 481b59
          (std::isfinite((*src_p).x) && (*src_p).x > 0.) ? (*src_p).x : 0.);
shun-iwasawa 481b59
      outPix->g = (typename TPixelF::Channel)(
shun-iwasawa 481b59
          (std::isfinite((*src_p).y) && (*src_p).y > 0.) ? (*src_p).y : 0.);
shun-iwasawa 481b59
      outPix->b = (typename TPixelF::Channel)(
shun-iwasawa 481b59
          (std::isfinite((*src_p).z) && (*src_p).z > 0.) ? (*src_p).z : 0.);
shun-iwasawa 481b59
shun-iwasawa 481b59
      outPix->m =
shun-iwasawa 481b59
          (typename TPixelF::Channel)(((*src_p).w > 1.) ? 1. : (*src_p).w);
shun-iwasawa 481b59
      assert(outPix->m >= 0.0);
shun-iwasawa 481b59
    }
shun-iwasawa 481b59
    src_p += margin.x;
shun-iwasawa 481b59
  }
shun-iwasawa 481b59
}
shun-iwasawa 481b59
shun-iwasawa 1b1839
//-----------------------------------------------------
shun-iwasawa 1b1839
// Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
shun-iwasawa 1b1839
double BokehUtils::getBokehPixelAmount(const double bokehAmount,
shun-iwasawa 1b1839
                                       const TAffine affine) {
shun-iwasawa 1b1839
  /*--- Convert to vector --- */
shun-iwasawa 1b1839
  TPointD vect(bokehAmount, 0.0);
shun-iwasawa 1b1839
  /*--- Apply geometrical transformation ---*/
shun-iwasawa 1b1839
  // For the following lines I referred to lines 586-592 of
shun-iwasawa 1b1839
  // sources/stdfx/motionblurfx.cpp
shun-iwasawa 1b1839
  TAffine aff(affine);
shun-iwasawa 1b1839
  aff.a13 = aff.a23 = 0; /* ignore translation */
shun-iwasawa 1b1839
  vect              = aff * vect;
shun-iwasawa 1b1839
  /*--- return the length of the vector ---*/
shun-iwasawa 1b1839
  return std::sqrt(vect.x * vect.x + vect.y * vect.y);
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//-----------------------------------------------------
shun-iwasawa 1b1839
shun-iwasawa 1b1839
Iwa_BokehCommonFx::Iwa_BokehCommonFx()
shun-iwasawa 481b59
    : m_onFocusDistance(0.5)
shun-iwasawa 481b59
    , m_bokehAmount(30.0)
shun-iwasawa 481b59
    , m_hardness(0.3)
shun-iwasawa 481b59
    , m_gamma(2.2)
shun-iwasawa 481b59
    , m_gammaAdjust(0.)
shun-iwasawa 481b59
    , m_linearizeMode(new TIntEnumParam(Gamma, "Gamma")) {
shun-iwasawa 1b1839
  addInputPort("Iris", m_iris);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Set the ranges of common parameters
shun-iwasawa 1b1839
  m_onFocusDistance->setValueRange(0.0, 10.);
shun-iwasawa 1b1839
  m_bokehAmount->setValueRange(0.0, 300.0);
shun-iwasawa 1b1839
  m_bokehAmount->setMeasureName("fxLength");
shun-iwasawa 1b1839
  m_hardness->setValueRange(0.05, 3.0);
shun-iwasawa 481b59
  m_gamma->setValueRange(1.0, 10.0);
shun-iwasawa 481b59
  m_gammaAdjust->setValueRange(-5., 5.);
shun-iwasawa 481b59
  m_linearizeMode->addItem(Hardness, "Hardness");
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
bool Iwa_BokehCommonFx::doGetBBox(double frame, TRectD& bBox,
shun-iwasawa 1b1839
                                  const TRenderSettings& info) {
shun-iwasawa 1b1839
  bBox = TConsts::infiniteRectD;
shun-iwasawa 1b1839
  return true;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
bool Iwa_BokehCommonFx::canHandle(const TRenderSettings& info, double frame) {
shun-iwasawa 1b1839
  return false;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void Iwa_BokehCommonFx::doFx(TTile& tile, double frame,
shun-iwasawa 1b1839
                             const TRenderSettings& settings,
shun-iwasawa 1b1839
                             double bokehPixelAmount, int margin,
shun-iwasawa 1b1839
                             TDimensionI& dimOut, TRectD& irisBBox,
shun-iwasawa 1b1839
                             TTile& irisTile, QList<layervalue>& layerValues,</layervalue>
shun-iwasawa 1b1839
                             QMap<int, char*="" unsigned="">& ctrls) {</int,>
shun-iwasawa 1b1839
  // This fx is relatively heavy so the multi thread computation is introduced.
shun-iwasawa 1b1839
  // Lock the mutex here in order to prevent multiple rendering tasks run at the
shun-iwasawa 1b1839
  // same time.
shun-iwasawa 1b1839
  // QMutexLocker fx_locker(&fx_mutex);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  QList<trastergr8p> rasterList;</trastergr8p>
shun-iwasawa 1b1839
  QList<kiss_fftnd_cfg> planList;</kiss_fftnd_cfg>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kiss_fft_cpx* kissfft_comp_iris;
shun-iwasawa 1b1839
  double* alpha_bokeh = nullptr;
shun-iwasawa 1b1839
  double4* result     = nullptr;
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<kiss_fft_cpx>(&kissfft_comp_iris, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double>(&alpha_bokeh, dimOut));</double>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double4>(&result, dimOut));</double4>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double4 zero = {0.0, 0.0, 0.0, 0.0};
shun-iwasawa 1b1839
  // initialize
shun-iwasawa 1b1839
  std::fill_n(result, dimOut.lx * dimOut.ly, zero);
shun-iwasawa 1b1839
shun-iwasawa 481b59
  double masterGamma;
shun-iwasawa 481b59
  if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
    masterGamma = m_hardness->getValue(frame);
shun-iwasawa 481b59
  else {  // gamma
shun-iwasawa 481b59
    masterGamma = m_gamma->getValue(frame);
shun-iwasawa 481b59
    if (tile.getRaster()->isLinear()) masterGamma /= settings.m_colorSpaceGamma;
shun-iwasawa 481b59
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
    releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // compute layers from further to nearer
shun-iwasawa 1b1839
  for (int i = 0; i < layerValues.size(); i++) {
shun-iwasawa 1b1839
    // cancel check
shun-iwasawa 1b1839
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
      releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
      return;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    LayerValue layer = layerValues.at(i);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    //-------------------
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // separate layer by the reference image
shun-iwasawa 1b1839
    int ctrlIndex = layer.depth_ref;
shun-iwasawa 1b1839
    if (ctrlIndex > 0 && ctrls.contains(ctrlIndex) && ctrls[ctrlIndex] != 0) {
shun-iwasawa 1b1839
      TTile* layerTile = layer.sourceTile;
shun-iwasawa 1b1839
      if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster());
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      doBokehRef(result, frame, settings, bokehPixelAmount, margin, dimOut,
shun-iwasawa 481b59
                 irisBBox, irisTile, kissfft_comp_iris, layer, ctrls[ctrlIndex],
shun-iwasawa 481b59
                 tile.getRaster()->isLinear());
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    //-------------------
shun-iwasawa 1b1839
shun-iwasawa 481b59
    double layerGamma = layer.layerGamma;
shun-iwasawa 1b1839
    // The iris size of the current layer
shun-iwasawa 1b1839
    double irisSize = layer.irisSize;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // in case the layer is at the focus
shun-iwasawa 1b1839
    if (-1.0 <= irisSize && 1.0 >= irisSize) {
shun-iwasawa 1b1839
      TTile* layerTile = layer.sourceTile;
shun-iwasawa 1b1839
      if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster());
shun-iwasawa 481b59
shun-iwasawa 481b59
      if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
        BokehUtils::compositLayerAsIs(
shun-iwasawa 481b59
            *layerTile, result, dimOut,
shun-iwasawa 481b59
            HardnessBasedConverter(masterGamma, settings.m_colorSpaceGamma,
shun-iwasawa 481b59
                                   layerTile->getRaster()->isLinear()));
shun-iwasawa 481b59
      else
shun-iwasawa 481b59
        BokehUtils::compositLayerAsIs(*layerTile, result, dimOut,
shun-iwasawa 481b59
                                      GammaBasedConverter(masterGamma));
shun-iwasawa 481b59
shun-iwasawa 1b1839
      // Continue to the next layer
shun-iwasawa 1b1839
      continue;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    {
shun-iwasawa 1b1839
      // prepare for iris FFT
shun-iwasawa 1b1839
      kiss_fft_cpx* kissfft_comp_iris_before;
shun-iwasawa 1b1839
      rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(</kiss_fft_cpx>
shun-iwasawa 1b1839
          &kissfft_comp_iris_before, dimOut));
shun-iwasawa 1b1839
      // Resize / flip the iris image according to the size ratio.
shun-iwasawa 1b1839
      // Normalize the brightness of the iris image.
shun-iwasawa 1b1839
      // Enlarge the iris to the output size.
shun-iwasawa 1b1839
      BokehUtils::convertIris(irisSize, kissfft_comp_iris_before, dimOut,
shun-iwasawa 1b1839
                              irisBBox, irisTile);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // cancel check
shun-iwasawa 1b1839
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // Create the FFT plan for the iris image.
shun-iwasawa 1b1839
      kiss_fftnd_cfg iris_kissfft_plan;
shun-iwasawa 1b1839
      while (1) {
shun-iwasawa 1b1839
        int dims[2]       = {dimOut.ly, dimOut.lx};
shun-iwasawa 1b1839
        int ndims         = 2;
shun-iwasawa 1b1839
        iris_kissfft_plan = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
shun-iwasawa 1b1839
        if (iris_kissfft_plan != NULL) break;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      // Do FFT the iris image.
shun-iwasawa 1b1839
      kiss_fftnd(iris_kissfft_plan, kissfft_comp_iris_before,
shun-iwasawa 1b1839
                 kissfft_comp_iris);
shun-iwasawa 1b1839
      kiss_fft_free(iris_kissfft_plan);
shun-iwasawa 1b1839
      // release the iris buffer
shun-iwasawa 1b1839
      rasterList.takeLast()->unlock();
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // Up to here, FFT-ed iris data is stored in kissfft_comp_iris
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // cancel check
shun-iwasawa 1b1839
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
      releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
      return;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    //- - - - - - - - - - - - -
shun-iwasawa 1b1839
    // Prepare the layer rasters
shun-iwasawa 1b1839
    TTile* layerTile = layer.sourceTile;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    if (!layer.premultiply) TRop::depremultiply(layerTile->getRaster());
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    //- - - - - - - - - - - - -
shun-iwasawa 1b1839
    // Do FFT the alpha channel.
shun-iwasawa 1b1839
    // Forward FFT -> Multiply by the iris data -> Backward FFT
shun-iwasawa 1b1839
    BokehUtils::calcAlfaChannelBokeh(kissfft_comp_iris, *layerTile,
shun-iwasawa 1b1839
                                     alpha_bokeh);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // cancel check
shun-iwasawa 1b1839
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
      releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
      return;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 481b59
    BokehUtils::MyThread threadR(BokehUtils::MyThread::Red,
shun-iwasawa 481b59
                                 layerTile->getRaster(), result, alpha_bokeh,
shun-iwasawa 481b59
                                 kissfft_comp_iris, layerGamma, masterGamma);
shun-iwasawa 481b59
    BokehUtils::MyThread threadG(BokehUtils::MyThread::Green,
shun-iwasawa 481b59
                                 layerTile->getRaster(), result, alpha_bokeh,
shun-iwasawa 481b59
                                 kissfft_comp_iris, layerGamma, masterGamma);
shun-iwasawa 481b59
    BokehUtils::MyThread threadB(BokehUtils::MyThread::Blue,
shun-iwasawa 481b59
                                 layerTile->getRaster(), result, alpha_bokeh,
shun-iwasawa 481b59
                                 kissfft_comp_iris, layerGamma, masterGamma);
shun-iwasawa 481b59
shun-iwasawa 481b59
    std::shared_ptr<exposureconverter> conv;</exposureconverter>
shun-iwasawa 481b59
    if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
      conv.reset(
shun-iwasawa 481b59
          new HardnessBasedConverter(layerGamma, settings.m_colorSpaceGamma,
shun-iwasawa 481b59
                                     layerTile->getRaster()->isLinear()));
shun-iwasawa 481b59
    else
shun-iwasawa 481b59
      conv.reset(new GammaBasedConverter(layerGamma));
shun-iwasawa 481b59
    threadR.setConverter(conv);
shun-iwasawa 481b59
    threadG.setConverter(conv);
shun-iwasawa 481b59
    threadB.setConverter(conv);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // If you set this flag to true, the fx will be forced to compute in single
shun-iwasawa 1b1839
    // thread.
shun-iwasawa 1b1839
    // Under some specific condition (such as calling from single-threaded
shun-iwasawa 1b1839
    // tcomposer)
shun-iwasawa 1b1839
    // we may need to use this flag... For now, I'll keep this option unused.
shun-iwasawa 1b1839
    // TODO: investigate this.
shun-iwasawa 1b1839
    bool renderInSingleThread = false;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // Start the thread when the initialization is done.
shun-iwasawa 1b1839
    // Red channel
shun-iwasawa 1b1839
    int waitCount = 0;
shun-iwasawa 1b1839
    while (1) {
shun-iwasawa 1b1839
      // cancel check
shun-iwasawa 1b1839
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun-iwasawa 1b1839
          waitCount >= 20) {
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      if (threadR.init()) {
shun-iwasawa 1b1839
        if (renderInSingleThread)
shun-iwasawa 1b1839
          threadR.run();
shun-iwasawa 1b1839
        else
shun-iwasawa 1b1839
          threadR.start();
shun-iwasawa 1b1839
        break;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      QThread::msleep(500);
shun-iwasawa 1b1839
      waitCount++;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    waitCount = 0;
shun-iwasawa 1b1839
    while (1) {
shun-iwasawa 1b1839
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun-iwasawa 1b1839
          waitCount >= 20)  // 10 seconds
shun-iwasawa 1b1839
      {
shun-iwasawa 1b1839
        if (!threadR.isFinished()) threadR.terminateThread();
shun-iwasawa 1b1839
        while (!threadR.isFinished()) {
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      if (threadG.init()) {
shun-iwasawa 1b1839
        if (renderInSingleThread)
shun-iwasawa 1b1839
          threadG.run();
shun-iwasawa 1b1839
        else
shun-iwasawa 1b1839
          threadG.start();
shun-iwasawa 1b1839
        break;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      QThread::msleep(500);
shun-iwasawa 1b1839
      waitCount++;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    waitCount = 0;
shun-iwasawa 1b1839
    while (1) {
shun-iwasawa 1b1839
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun-iwasawa 1b1839
          waitCount >= 20)  // 10 seconds
shun-iwasawa 1b1839
      {
shun-iwasawa 1b1839
        if (!threadR.isFinished()) threadR.terminateThread();
shun-iwasawa 1b1839
        if (!threadG.isFinished()) threadG.terminateThread();
shun-iwasawa 1b1839
        while (!threadR.isFinished() || !threadG.isFinished()) {
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      if (threadB.init()) {
shun-iwasawa 1b1839
        if (renderInSingleThread)
shun-iwasawa 1b1839
          threadB.run();
shun-iwasawa 1b1839
        else
shun-iwasawa 1b1839
          threadB.start();
shun-iwasawa 1b1839
        break;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      QThread::msleep(500);
shun-iwasawa 1b1839
      waitCount++;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    /*
shun-iwasawa 1b1839
     * What is done in the thread for each RGB channel:
shun-iwasawa 1b1839
     * - Convert channel value -> Exposure
shun-iwasawa 1b1839
     * - Multiply by alpha channel
shun-iwasawa 1b1839
     * - Forward FFT
shun-iwasawa 1b1839
     * - Multiply by the iris FFT data
shun-iwasawa 1b1839
     * - Backward FFT
shun-iwasawa 1b1839
     * - Convert Exposure -> channel value
shun-iwasawa 1b1839
     */
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    waitCount = 0;
shun-iwasawa 1b1839
    while (1) {
shun-iwasawa 1b1839
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun-iwasawa 1b1839
          waitCount >= 2000)  // 100 second timeout
shun-iwasawa 1b1839
      {
shun-iwasawa 1b1839
        if (!threadR.isFinished()) threadR.terminateThread();
shun-iwasawa 1b1839
        if (!threadG.isFinished()) threadG.terminateThread();
shun-iwasawa 1b1839
        if (!threadB.isFinished()) threadB.terminateThread();
shun-iwasawa 1b1839
        while (!threadR.isFinished() || !threadG.isFinished() ||
shun-iwasawa 1b1839
               !threadB.isFinished()) {
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      if (threadR.isFinished() && threadG.isFinished() && threadB.isFinished())
shun-iwasawa 1b1839
        break;
shun-iwasawa 1b1839
      QThread::msleep(50);
shun-iwasawa 1b1839
      waitCount++;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // convert result image value exposure -> rgb
shun-iwasawa 481b59
  if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
    BokehUtils::convertExposureToRGB(
shun-iwasawa 481b59
        result, dimOut.lx * dimOut.ly,
shun-iwasawa 481b59
        HardnessBasedConverter(masterGamma, settings.m_colorSpaceGamma,
shun-iwasawa 481b59
                               tile.getRaster()->isLinear()));
shun-iwasawa 481b59
  else
shun-iwasawa 481b59
    BokehUtils::convertExposureToRGB(result, dimOut.lx * dimOut.ly,
shun-iwasawa 481b59
                                     GammaBasedConverter(masterGamma));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // clear result raster
shun-iwasawa 1b1839
  tile.getRaster()->clear();
shun-iwasawa 1b1839
  TRaster32P outRas32 = (TRaster32P)tile.getRaster();
shun-iwasawa 1b1839
  TRaster64P outRas64 = (TRaster64P)tile.getRaster();
shun-iwasawa 481b59
  TRasterFP outRasF   = (TRasterFP)tile.getRaster();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int2 outMargin = {(dimOut.lx - tile.getRaster()->getSize().lx) / 2,
shun-iwasawa 1b1839
                    (dimOut.ly - tile.getRaster()->getSize().ly) / 2};
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  lock.lockForWrite();
shun-iwasawa 1b1839
  if (outRas32)
shun-iwasawa 1b1839
    BokehUtils::setOutputRaster<traster32p, tpixel32="">(result, outRas32, dimOut,</traster32p,>
shun-iwasawa 1b1839
                                                      outMargin);
shun-iwasawa 1b1839
  else if (outRas64)
shun-iwasawa 1b1839
    BokehUtils::setOutputRaster<traster64p, tpixel64="">(result, outRas64, dimOut,</traster64p,>
shun-iwasawa 1b1839
                                                      outMargin);
shun-iwasawa 481b59
  else if (outRasF)
shun-iwasawa 481b59
    BokehUtils::setOutputRaster<trasterfp, tpixelf="">(result, outRasF, dimOut,</trasterfp,>
shun-iwasawa 481b59
                                                    outMargin);
shun-iwasawa 1b1839
  lock.unlock();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 481b59
void Iwa_BokehCommonFx::doBokehRef(
shun-iwasawa 481b59
    double4* result, double frame, const TRenderSettings& settings,
shun-iwasawa 481b59
    double bokehPixelAmount, int margin, TDimensionI& dimOut, TRectD& irisBBox,
shun-iwasawa 481b59
    TTile& irisTile, kiss_fft_cpx* kissfft_comp_iris, LayerValue layer,
shun-iwasawa 481b59
    unsigned char* ctrl, const bool isLinear) {
shun-iwasawa 1b1839
  QList<trastergr8p> rasterList;</trastergr8p>
shun-iwasawa 1b1839
  QList<kiss_fftnd_cfg> planList;</kiss_fftnd_cfg>
shun-iwasawa 1b1839
  // source image
shun-iwasawa 1b1839
  double4* source_buff;
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double4>(&source_buff, dimOut));</double4>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRaster32P ras32 = (TRaster32P)layer.sourceTile->getRaster();
shun-iwasawa 1b1839
  TRaster64P ras64 = (TRaster64P)layer.sourceTile->getRaster();
shun-iwasawa 481b59
  TRasterFP rasF   = (TRasterFP)layer.sourceTile->getRaster();
shun-iwasawa 1b1839
  lock.lockForRead();
shun-iwasawa 1b1839
  if (ras32)
shun-iwasawa 1b1839
    BokehUtils::setSourceRaster<traster32p, tpixel32="">(ras32, source_buff,</traster32p,>
shun-iwasawa 1b1839
                                                      dimOut);
shun-iwasawa 1b1839
  else if (ras64)
shun-iwasawa 1b1839
    BokehUtils::setSourceRaster<traster64p, tpixel64="">(ras64, source_buff,</traster64p,>
shun-iwasawa 1b1839
                                                      dimOut);
shun-iwasawa 481b59
  else if (rasF)
shun-iwasawa 481b59
    BokehUtils::setSourceRaster<trasterfp, tpixelf="">(rasF, source_buff, dimOut);</trasterfp,>
shun-iwasawa 1b1839
  lock.unlock();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // create the index map, which indicates which layer each pixel belongs to
shun-iwasawa 1b1839
  // make two separations and interporate the results in order to avoid
shun-iwasawa 1b1839
  // artifacts appear at the layer border
shun-iwasawa 1b1839
  unsigned char* indexMap_main_buff;
shun-iwasawa 1b1839
  unsigned char* indexMap_sub_buff;
shun-iwasawa 1b1839
  double* mainSub_ratio_buff;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<unsigned char="">(&indexMap_main_buff, dimOut));</unsigned>
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<unsigned char="">(&indexMap_sub_buff, dimOut));</unsigned>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double>(&mainSub_ratio_buff, dimOut));</double>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  QVector<double> segmentDepth_main;</double>
shun-iwasawa 1b1839
  QVector<double> segmentDepth_sub;</double>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double layerDitance = layer.distance;
shun-iwasawa 1b1839
  double nearDepth    = layerDitance - layer.depthRange * layerDitance;
shun-iwasawa 1b1839
  double farDepth     = nearDepth + layer.depthRange;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int distancePrecision = layer.distancePrecision;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // create the depth index map
shun-iwasawa 1b1839
  BokehUtils::defineSegemntDepth(
shun-iwasawa 1b1839
      indexMap_main_buff, indexMap_sub_buff, mainSub_ratio_buff, ctrl, dimOut,
shun-iwasawa 1b1839
      segmentDepth_main, segmentDepth_sub, m_onFocusDistance->getValue(frame),
shun-iwasawa 1b1839
      distancePrecision, nearDepth, farDepth);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int size = dimOut.lx * dimOut.ly;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double4* layer_buff;
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double4>(&layer_buff, dimOut));</double4>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kiss_fft_cpx* kissfft_comp_iris_before;
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<kiss_fft_cpx>(&kissfft_comp_iris_before, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // alpha channel
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_alpha_before;
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_alpha;
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha_before, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // RGB channels
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_r_before;
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_g_before;
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_b_before;
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_r;
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_g;
shun-iwasawa 1b1839
  kiss_fft_cpx* fftcpx_b;
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r_before, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g_before, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(
shun-iwasawa 1b1839
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b_before, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b, dimOut));</kiss_fft_cpx>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // for accumulating result image
shun-iwasawa 1b1839
  double4* result_main_buff;
shun-iwasawa 1b1839
  double4* result_sub_buff;
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double4>(&result_main_buff, dimOut));</double4>
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<double4>(&result_sub_buff, dimOut));</double4>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
    releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // fft plans
shun-iwasawa 1b1839
  int dims[2]                      = {dimOut.ly, dimOut.lx};
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_fwd);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_bkwd);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_r_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_r_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_g_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_g_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_b_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa 1b1839
  kiss_fftnd_cfg kissfft_plan_b_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_r_fwd);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_r_bkwd);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_g_fwd);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_g_bkwd);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_b_fwd);
shun-iwasawa 1b1839
  planList.append(kissfft_plan_b_bkwd);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // initialize result memory
shun-iwasawa 1b1839
  memset(result_main_buff, 0, sizeof(double4) * size);
shun-iwasawa 1b1839
  memset(result_sub_buff, 0, sizeof(double4) * size);
shun-iwasawa 1b1839
shun-iwasawa 481b59
  double masterGamma;
shun-iwasawa 481b59
  if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
    masterGamma = m_hardness->getValue(frame);
shun-iwasawa 481b59
  else {  // gamma
shun-iwasawa 481b59
    masterGamma = m_gamma->getValue(frame);
shun-iwasawa 481b59
    if (isLinear) masterGamma /= settings.m_colorSpaceGamma;
shun-iwasawa 481b59
  }
shun-iwasawa 481b59
  double layerGamma = layer.layerGamma;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // convert source image value rgb -> exposure
shun-iwasawa 1b1839
  // note that premultiplied source image is already unpremultiplied before this
shun-iwasawa 1b1839
  // function
shun-iwasawa 481b59
  if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
    BokehUtils::convertRGBToExposure(
shun-iwasawa 481b59
        source_buff, size,
shun-iwasawa 481b59
        HardnessBasedConverter(layerGamma, settings.m_colorSpaceGamma,
shun-iwasawa 481b59
                               layer.sourceTile->getRaster()->isLinear()));
shun-iwasawa 481b59
  else
shun-iwasawa 481b59
    BokehUtils::convertRGBToExposure(source_buff, size,
shun-iwasawa 481b59
                                     GammaBasedConverter(layerGamma));
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double focus  = m_onFocusDistance->getValue(frame);
shun-iwasawa 1b1839
  double adjust = layer.bokehAdjustment;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  for (int mainSub = 0; mainSub < 2; mainSub++) {
shun-iwasawa 1b1839
    double4* result_buff_mainSub;
shun-iwasawa 1b1839
    QVector<double> segmentDepth_mainSub;</double>
shun-iwasawa 1b1839
    unsigned char* indexMap_mainSub;
shun-iwasawa 1b1839
    if (mainSub == 0) {
shun-iwasawa 1b1839
      result_buff_mainSub  = result_main_buff;
shun-iwasawa 1b1839
      segmentDepth_mainSub = segmentDepth_main;
shun-iwasawa 1b1839
      indexMap_mainSub     = indexMap_main_buff;
shun-iwasawa 1b1839
    } else {
shun-iwasawa 1b1839
      result_buff_mainSub  = result_sub_buff;
shun-iwasawa 1b1839
      segmentDepth_mainSub = segmentDepth_sub;
shun-iwasawa 1b1839
      indexMap_mainSub     = indexMap_sub_buff;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // compute from further to nearer
shun-iwasawa 1b1839
    for (int index = 0; index < segmentDepth_mainSub.size(); index++) {
shun-iwasawa 1b1839
      // cancel check
shun-iwasawa 1b1839
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // compute iris size
shun-iwasawa 1b1839
      double irisSize = BokehUtils::calcIrisSize(segmentDepth_mainSub.at(index),
shun-iwasawa 1b1839
                                                 bokehPixelAmount, focus,
shun-iwasawa 1b1839
                                                 adjust, nearDepth, farDepth);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      memset(layer_buff, 0, sizeof(double4) * size);
shun-iwasawa 1b1839
      BokehUtils::retrieveLayer(
shun-iwasawa 1b1839
          source_buff, layer_buff, indexMap_mainSub, index, dimOut.lx,
shun-iwasawa 1b1839
          dimOut.ly, layer.fillGap, layer.doMedian,
shun-iwasawa 1b1839
          (index == segmentDepth_mainSub.size() - 1) ? 0 : margin);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // in case the current segment is at the focus
shun-iwasawa 1b1839
      if (-1.0 <= irisSize && 1.0 >= irisSize) {
shun-iwasawa 1b1839
        BokehUtils::compositeAsIs(layer_buff, result_buff_mainSub, size);
shun-iwasawa 1b1839
        continue;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // Resize / flip the iris image according to the size ratio.
shun-iwasawa 1b1839
      // Normalize the brightness of the iris image.
shun-iwasawa 1b1839
      // Enlarge the iris to the output size.
shun-iwasawa 1b1839
      BokehUtils::convertIris(irisSize, kissfft_comp_iris_before, dimOut,
shun-iwasawa 1b1839
                              irisBBox, irisTile);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // cancel check
shun-iwasawa 1b1839
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
        return;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
      // Do FFT the iris image.
shun-iwasawa 1b1839
      kiss_fftnd(kissfft_plan_fwd, kissfft_comp_iris_before, kissfft_comp_iris);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // initialize alpha
shun-iwasawa 1b1839
      memset(fftcpx_alpha_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa 1b1839
      // initialize channels
shun-iwasawa 1b1839
      memset(fftcpx_r_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa 1b1839
      memset(fftcpx_g_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa 1b1839
      memset(fftcpx_b_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // retrieve segment layer image for each channel
shun-iwasawa 1b1839
      BokehUtils::retrieveChannel(layer_buff,           // src
shun-iwasawa 1b1839
                                  fftcpx_r_before,      // dst
shun-iwasawa 1b1839
                                  fftcpx_g_before,      // dst
shun-iwasawa 1b1839
                                  fftcpx_b_before,      // dst
shun-iwasawa 1b1839
                                  fftcpx_alpha_before,  // dst
shun-iwasawa 1b1839
                                  size);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // forward fft of alpha channel
shun-iwasawa 1b1839
      kiss_fftnd(kissfft_plan_fwd, fftcpx_alpha_before, fftcpx_alpha);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // multiply filter on alpha
shun-iwasawa 1b1839
      BokehUtils::multiplyFilter(fftcpx_alpha,       // dst
shun-iwasawa 1b1839
                                 kissfft_comp_iris,  // filter
shun-iwasawa 1b1839
                                 size);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // inverse fft the alpha channel
shun-iwasawa 1b1839
      // note that the result is multiplied by the image size
shun-iwasawa 1b1839
      kiss_fftnd(kissfft_plan_bkwd, fftcpx_alpha, fftcpx_alpha_before);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // over composite the alpha channel
shun-iwasawa 1b1839
      BokehUtils::compositeAlpha(result_buff_mainSub,  // dst
shun-iwasawa 1b1839
                                 fftcpx_alpha_before,  // alpha
shun-iwasawa 1b1839
                                 dimOut.lx, dimOut.ly);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // create worker threads
shun-iwasawa 1b1839
      BokehUtils::BokehRefThread threadR(
shun-iwasawa 1b1839
          0, fftcpx_r_before, fftcpx_r, fftcpx_alpha_before, kissfft_comp_iris,
shun-iwasawa 1b1839
          result_buff_mainSub, kissfft_plan_r_fwd, kissfft_plan_r_bkwd, dimOut);
shun-iwasawa 1b1839
      BokehUtils::BokehRefThread threadG(
shun-iwasawa 1b1839
          1, fftcpx_g_before, fftcpx_g, fftcpx_alpha_before, kissfft_comp_iris,
shun-iwasawa 1b1839
          result_buff_mainSub, kissfft_plan_g_fwd, kissfft_plan_g_bkwd, dimOut);
shun-iwasawa 1b1839
      BokehUtils::BokehRefThread threadB(
shun-iwasawa 1b1839
          2, fftcpx_b_before, fftcpx_b, fftcpx_alpha_before, kissfft_comp_iris,
shun-iwasawa 1b1839
          result_buff_mainSub, kissfft_plan_b_fwd, kissfft_plan_b_bkwd, dimOut);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      // If you set this flag to true, the fx will be forced to compute in
shun-iwasawa 1b1839
      // single thread.
shun-iwasawa 1b1839
      // Under some specific condition (such as calling from single-threaded
shun-iwasawa 1b1839
      // tcomposer)
shun-iwasawa 1b1839
      // we may need to use this flag... For now, I'll keep this option unused.
shun-iwasawa 1b1839
      // TODO: investigate this.
shun-iwasawa 1b1839
      bool renderInSingleThread = false;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
      if (renderInSingleThread) {
shun-iwasawa 1b1839
        threadR.run();
shun-iwasawa 1b1839
        threadG.run();
shun-iwasawa 1b1839
        threadB.run();
shun-iwasawa 1b1839
      } else {
shun-iwasawa 1b1839
        threadR.start();
shun-iwasawa 1b1839
        threadG.start();
shun-iwasawa 1b1839
        threadB.start();
shun-iwasawa 1b1839
        int waitCount = 0;
shun-iwasawa 1b1839
        while (1) {
shun-iwasawa 1b1839
          if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun-iwasawa 1b1839
              waitCount >= 2000)  // 100 second timeout
shun-iwasawa 1b1839
          {
shun-iwasawa 1b1839
            if (!threadR.isFinished()) threadR.terminateThread();
shun-iwasawa 1b1839
            if (!threadG.isFinished()) threadG.terminateThread();
shun-iwasawa 1b1839
            if (!threadB.isFinished()) threadB.terminateThread();
shun-iwasawa 1b1839
            while (!threadR.isFinished() || !threadG.isFinished() ||
shun-iwasawa 1b1839
                   !threadB.isFinished()) {
shun-iwasawa 1b1839
            }
shun-iwasawa 1b1839
            releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
            return;
shun-iwasawa 1b1839
          }
shun-iwasawa 1b1839
          if (threadR.isFinished() && threadG.isFinished() &&
shun-iwasawa 1b1839
              threadB.isFinished())
shun-iwasawa 1b1839
            break;
shun-iwasawa 1b1839
          QThread::msleep(50);
shun-iwasawa 1b1839
          waitCount++;
shun-iwasawa 1b1839
        }
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    }  // for each segment
shun-iwasawa 1b1839
  }    // main and sub
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // cancel check
shun-iwasawa 1b1839
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
    releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 481b59
  double adjustFactor = (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
                            ? layerGamma / masterGamma
shun-iwasawa 481b59
                            : masterGamma / layerGamma;
shun-iwasawa 481b59
shun-iwasawa 481b59
  BokehUtils::interpolateExposureAndConvertToRGB(result_main_buff,    // result1
shun-iwasawa 481b59
                                                 result_sub_buff,     // result2
shun-iwasawa 481b59
                                                 mainSub_ratio_buff,  // ratio
shun-iwasawa 481b59
                                                 result,              // dst
shun-iwasawa 481b59
                                                 size, adjustFactor);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // release rasters and plans
shun-iwasawa 1b1839
  releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa 481b59
}