shun-iwasawa e2505a
#include "iwa_bokehreffx.h"
shun-iwasawa e2505a
shun-iwasawa e2505a
#include "trop.h"
shun-iwasawa e2505a
shun-iwasawa e2505a
#include <qreadwritelock></qreadwritelock>
shun-iwasawa e2505a
#include <qset></qset>
shun-iwasawa e2505a
#include <math.h></math.h>
shun-iwasawa e2505a
shun-iwasawa e2505a
namespace {
shun-iwasawa e2505a
QMutex fx_mutex;
shun-iwasawa e2505a
QReadWriteLock lock;
shun-iwasawa e2505a
shun-iwasawa e2505a
template <typename t=""></typename>
shun-iwasawa e2505a
TRasterGR8P allocateRasterAndLock(T** buf, TDimensionI dim) {
shun-iwasawa e2505a
  TRasterGR8P ras(dim.lx * sizeof(T), dim.ly);
shun-iwasawa e2505a
  ras->lock();
shun-iwasawa e2505a
  *buf = (T*)ras->getRawData();
shun-iwasawa e2505a
  return ras;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
// modify fft coordinate to normal
shun-iwasawa e2505a
inline int getCoord(int index, int lx, int ly) {
shun-iwasawa e2505a
  int i = index % lx;
shun-iwasawa e2505a
  int j = index / lx;
shun-iwasawa e2505a
shun-iwasawa e2505a
  int cx = i - lx / 2;
shun-iwasawa e2505a
  int cy = j - ly / 2;
shun-iwasawa e2505a
shun-iwasawa e2505a
  if (cx < 0) cx += lx;
shun-iwasawa e2505a
  if (cy < 0) cy += ly;
shun-iwasawa e2505a
shun-iwasawa e2505a
  return cy * lx + cx;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
// release all registered raster memories
shun-iwasawa e2505a
void releaseAllRasters(QList<trastergr8p>& rasterList) {</trastergr8p>
shun-iwasawa e2505a
  for (int r = 0; r < rasterList.size(); r++) rasterList.at(r)->unlock();
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
// release all registered raster memories and free all fft plans
shun-iwasawa e2505a
void releaseAllRastersAndPlans(QList<trastergr8p>& rasterList,</trastergr8p>
shun-iwasawa e2505a
                               QList<kiss_fftnd_cfg>& planList) {</kiss_fftnd_cfg>
shun-iwasawa e2505a
  releaseAllRasters(rasterList);
shun-iwasawa e2505a
  for (int p = 0; p < planList.size(); p++) kiss_fft_free(planList.at(p));
shun-iwasawa e2505a
}
shun-iwasawa 09e972
};  // namespace
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------
shun-iwasawa e2505a
BokehRefThread::BokehRefThread(int channel, kiss_fft_cpx* fftcpx_channel_before,
shun-iwasawa e2505a
                               kiss_fft_cpx* fftcpx_channel,
shun-iwasawa e2505a
                               kiss_fft_cpx* fftcpx_alpha,
shun-iwasawa e2505a
                               kiss_fft_cpx* fftcpx_iris, float4* result_buff,
shun-iwasawa e2505a
                               kiss_fftnd_cfg kissfft_plan_fwd,
shun-iwasawa e2505a
                               kiss_fftnd_cfg kissfft_plan_bkwd,
shun-iwasawa e2505a
                               TDimensionI& dim)
shun-iwasawa e2505a
    : m_channel(channel)
shun-iwasawa e2505a
    , m_fftcpx_channel_before(fftcpx_channel_before)
shun-iwasawa e2505a
    , m_fftcpx_channel(fftcpx_channel)
shun-iwasawa e2505a
    , m_fftcpx_alpha(fftcpx_alpha)
shun-iwasawa e2505a
    , m_fftcpx_iris(fftcpx_iris)
shun-iwasawa e2505a
    , m_result_buff(result_buff)
shun-iwasawa e2505a
    , m_kissfft_plan_fwd(kissfft_plan_fwd)
shun-iwasawa e2505a
    , m_kissfft_plan_bkwd(kissfft_plan_bkwd)
shun-iwasawa e2505a
    , m_dim(dim)
shun-iwasawa e2505a
    , m_finished(false)
shun-iwasawa e2505a
    , m_isTerminated(false) {}
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
void BokehRefThread::run() {
shun-iwasawa e2505a
  // execute channel fft
shun-iwasawa e2505a
  kiss_fftnd(m_kissfft_plan_fwd, m_fftcpx_channel_before, m_fftcpx_channel);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (m_isTerminated) {
shun-iwasawa e2505a
    m_finished = true;
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  int size = m_dim.lx * m_dim.ly;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // multiply filter
shun-iwasawa e2505a
  for (int i = 0; i < size; i++) {
shun-iwasawa e2505a
    float re, im;
shun-iwasawa e2505a
    re = m_fftcpx_channel[i].r * m_fftcpx_iris[i].r -
shun-iwasawa e2505a
         m_fftcpx_channel[i].i * m_fftcpx_iris[i].i;
shun-iwasawa e2505a
    im = m_fftcpx_channel[i].r * m_fftcpx_iris[i].i +
shun-iwasawa e2505a
         m_fftcpx_iris[i].r * m_fftcpx_channel[i].i;
shun-iwasawa e2505a
    m_fftcpx_channel[i].r = re;
shun-iwasawa e2505a
    m_fftcpx_channel[i].i = im;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
  // execute invert fft
shun-iwasawa e2505a
  kiss_fftnd(m_kissfft_plan_bkwd, m_fftcpx_channel, m_fftcpx_channel_before);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (m_isTerminated) {
shun-iwasawa e2505a
    m_finished = true;
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // normal composite exposure value
shun-iwasawa e2505a
  float4* result_p = m_result_buff;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, result_p++) {
shun-iwasawa e2505a
    // modify fft coordinate to normal
shun-iwasawa e2505a
    int coord = getCoord(i, m_dim.lx, m_dim.ly);
shun-iwasawa e2505a
shun-iwasawa e2505a
    float alpha = m_fftcpx_alpha[coord].r / (float)size;
shun-iwasawa e2505a
    // ignore transpalent pixels
shun-iwasawa e2505a
    if (alpha == 0.0f) continue;
shun-iwasawa e2505a
shun-iwasawa e2505a
    float exposure = m_fftcpx_channel_before[coord].r / (float)size;
shun-iwasawa e2505a
shun-iwasawa e2505a
    // in case of using upper layer at all
shun-iwasawa e2505a
    if (alpha >= 1.0f || (m_channel == 0 && (*result_p).x == 0.0f) ||
shun-iwasawa e2505a
        (m_channel == 1 && (*result_p).y == 0.0f) ||
shun-iwasawa e2505a
        (m_channel == 2 && (*result_p).z == 0.0f)) {
shun-iwasawa e2505a
      // set exposure
shun-iwasawa e2505a
      if (m_channel == 0)  // R
shun-iwasawa e2505a
        (*result_p).x = exposure;
shun-iwasawa e2505a
      else if (m_channel == 1)  // G
shun-iwasawa e2505a
        (*result_p).y = exposure;
shun-iwasawa e2505a
      else  // B
shun-iwasawa e2505a
        (*result_p).z = exposure;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
    // in case of compositing both layers
shun-iwasawa e2505a
    else {
shun-iwasawa e2505a
      if (m_channel == 0)  // R
shun-iwasawa e2505a
      {
shun-iwasawa e2505a
        (*result_p).x *= 1.0f - alpha;
shun-iwasawa e2505a
        (*result_p).x += exposure;
shun-iwasawa e2505a
      } else if (m_channel == 1)  // G
shun-iwasawa e2505a
      {
shun-iwasawa e2505a
        (*result_p).y *= 1.0f - alpha;
shun-iwasawa e2505a
        (*result_p).y += exposure;
shun-iwasawa e2505a
      } else  // B
shun-iwasawa e2505a
      {
shun-iwasawa e2505a
        (*result_p).z *= 1.0f - alpha;
shun-iwasawa e2505a
        (*result_p).z += exposure;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
  m_finished = true;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//============================================================
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
// Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
shun-iwasawa e2505a
// DONE
shun-iwasawa e2505a
float Iwa_BokehRefFx::getBokehPixelAmount(const double frame,
shun-iwasawa e2505a
                                          const TAffine affine) {
shun-iwasawa e2505a
  // Convert to vector
shun-iwasawa e2505a
  TPointD vect;
shun-iwasawa e2505a
  vect.x = m_bokehAmount->getValue(frame);
shun-iwasawa e2505a
  vect.y = 0.0;
shun-iwasawa e2505a
  // Apply geometrical transformation
shun-iwasawa e2505a
  TAffine aff(affine);
shun-iwasawa e2505a
  aff.a13 = aff.a23 = 0;  // ignore translation
shun-iwasawa e2505a
  vect              = aff * vect;
shun-iwasawa e2505a
  // return the length of the vector
shun-iwasawa e2505a
  return sqrtf((float)(vect.x * vect.x + vect.y * vect.y));
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
// normalize the source raster image to 0-1 and set to dstMem
shun-iwasawa e2505a
// returns true if the source is (seems to be) premultiplied
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa e2505a
bool Iwa_BokehRefFx::setSourceRaster(const RASTER srcRas, float4* dstMem,
shun-iwasawa e2505a
                                     TDimensionI dim) {
shun-iwasawa e2505a
  bool isPremultiplied = true;
shun-iwasawa e2505a
shun-iwasawa e2505a
  float4* chann_p = dstMem;
shun-iwasawa e2505a
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa e2505a
    PIXEL* pix = srcRas->pixels(j);
shun-iwasawa e2505a
    for (int i = 0; i < dim.lx; i++, pix++, chann_p++) {
shun-iwasawa e2505a
      (*chann_p).x = (float)pix->r / (float)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      (*chann_p).y = (float)pix->g / (float)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      (*chann_p).z = (float)pix->b / (float)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      (*chann_p).w = (float)pix->m / (float)PIXEL::maxChannelValue;
shun-iwasawa e2505a
shun-iwasawa e2505a
      // if there is at least one pixel of which any of RGB channels has
shun-iwasawa e2505a
      // higher value than alpha channel, the source image can be jusged
shun-iwasawa e2505a
      // as NON-premultiplied.
shun-iwasawa e2505a
      if (isPremultiplied &&
shun-iwasawa e2505a
          ((*chann_p).x > (*chann_p).w || (*chann_p).y > (*chann_p).w ||
shun-iwasawa e2505a
           (*chann_p).z > (*chann_p).w))
shun-iwasawa e2505a
        isPremultiplied = false;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
  return isPremultiplied;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
// normalize brightness of the depth reference image to unsigned char
shun-iwasawa e2505a
// and store into detMem
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa e2505a
void Iwa_BokehRefFx::setDepthRaster(const RASTER srcRas, unsigned char* dstMem,
shun-iwasawa e2505a
                                    TDimensionI dim) {
shun-iwasawa e2505a
  unsigned char* depth_p = dstMem;
shun-iwasawa e2505a
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa e2505a
    PIXEL* pix = srcRas->pixels(j);
shun-iwasawa e2505a
    for (int i = 0; i < dim.lx; i++, pix++, depth_p++) {
shun-iwasawa e2505a
      // normalize brightness to 0-1
shun-iwasawa e2505a
      float val = ((float)pix->r * 0.3f + (float)pix->g * 0.59f +
shun-iwasawa e2505a
                   (float)pix->b * 0.11f) /
shun-iwasawa e2505a
                  (float)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      // convert to unsigned char
shun-iwasawa e2505a
      (*depth_p) = (unsigned char)(val * (float)UCHAR_MAX + 0.5f);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa e2505a
void Iwa_BokehRefFx::setDepthRasterGray(const RASTER srcRas,
shun-iwasawa e2505a
                                        unsigned char* dstMem,
shun-iwasawa e2505a
                                        TDimensionI dim) {
shun-iwasawa e2505a
  unsigned char* depth_p = dstMem;
shun-iwasawa e2505a
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa e2505a
    PIXEL* pix = srcRas->pixels(j);
shun-iwasawa e2505a
    for (int i = 0; i < dim.lx; i++, pix++, depth_p++) {
shun-iwasawa e2505a
      // normalize brightness to 0-1
shun-iwasawa e2505a
      float val = (float)pix->value / (float)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      // convert to unsigned char
shun-iwasawa e2505a
      (*depth_p) = (unsigned char)(val * (float)UCHAR_MAX + 0.5f);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
// create the depth index map based on the histogram
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::defineSegemntDepth(
shun-iwasawa e2505a
    const unsigned char* indexMap_main, const unsigned char* indexMap_sub,
shun-iwasawa e2505a
    const float* mainSub_ratio, const unsigned char* depth_buff,
shun-iwasawa e2505a
    const TDimensionI& dimOut, const double frame,
shun-iwasawa e2505a
    QVector<float>& segmentDepth_main, QVector<float>& segmentDepth_sub) {</float></float>
shun-iwasawa e2505a
  QSet<int> segmentValues;</int>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // histogram parameters
shun-iwasawa e2505a
  struct HISTO {
shun-iwasawa e2505a
    int pix_amount;
shun-iwasawa e2505a
    int belongingSegmentValue;  // value to which temporary segmented
shun-iwasawa e2505a
    int segmentId;
shun-iwasawa e2505a
    int segmentId_sub;
shun-iwasawa e2505a
  } histo[256];
shun-iwasawa e2505a
shun-iwasawa e2505a
  // initialize
shun-iwasawa e2505a
  for (int h = 0; h < 256; h++) {
shun-iwasawa e2505a
    histo[h].pix_amount            = 0;
shun-iwasawa e2505a
    histo[h].belongingSegmentValue = -1;
shun-iwasawa e2505a
    histo[h].segmentId             = -1;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  int size = dimOut.lx * dimOut.ly;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // max and min
shun-iwasawa e2505a
  int minHisto = (int)UCHAR_MAX;
shun-iwasawa e2505a
  int maxHisto = 0;
shun-iwasawa e2505a
shun-iwasawa e2505a
  unsigned char* depth_p = (unsigned char*)depth_buff;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, depth_p++) {
shun-iwasawa e2505a
    histo[(int)*depth_p].pix_amount++;
shun-iwasawa e2505a
    // update max and min
shun-iwasawa e2505a
    if ((int)*depth_p < minHisto) minHisto = (int)*depth_p;
shun-iwasawa e2505a
    if ((int)*depth_p > maxHisto) maxHisto = (int)*depth_p;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // the maximum and the minimum depth become the segment layers
shun-iwasawa e2505a
  segmentValues.insert(minHisto);
shun-iwasawa e2505a
  segmentValues.insert(maxHisto);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // focus depth becomes the segment layer as well
shun-iwasawa e2505a
  int focusVal = (int)(m_onFocusDistance->getValue(frame) * (double)UCHAR_MAX);
shun-iwasawa e2505a
  if (minHisto < focusVal && focusVal < maxHisto)
shun-iwasawa e2505a
    segmentValues.insert(focusVal);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // set the initial segmentation for each depth value
shun-iwasawa e2505a
  for (int h = 0; h < 256; h++) {
shun-iwasawa e2505a
    for (int seg = 0; seg < segmentValues.size(); seg++) {
shun-iwasawa e2505a
      // set the segment
shun-iwasawa e2505a
      if (histo[h].belongingSegmentValue == -1) {
shun-iwasawa e2505a
        histo[h].belongingSegmentValue = segmentValues.values().at(seg);
shun-iwasawa e2505a
        continue;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
      // error amount at the current registered layers
shun-iwasawa 0e4a7a
      int tmpError = std::abs(h - histo[h].belongingSegmentValue);
shun-iwasawa e2505a
      if (tmpError == 0) break;
shun-iwasawa e2505a
      // new error amount
shun-iwasawa 0e4a7a
      int newError = std::abs(h - segmentValues.values().at(seg));
shun-iwasawa e2505a
      // compare the two and update
shun-iwasawa e2505a
      if (newError < tmpError)
shun-iwasawa e2505a
        histo[h].belongingSegmentValue = segmentValues.values().at(seg);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // add the segment layers to the distance precision value
shun-iwasawa e2505a
  while (segmentValues.size() < m_distancePrecision->getValue()) {
shun-iwasawa e2505a
    // add a new segment at the value which will reduce the error amount in
shun-iwasawa e2505a
    // maximum
shun-iwasawa e2505a
    double tmpMaxErrorMod = 0;
shun-iwasawa e2505a
    int tmpBestNewSegVal;
shun-iwasawa e2505a
    bool newSegFound = false;
shun-iwasawa e2505a
    for (int h = minHisto + 1; h < maxHisto; h++) {
shun-iwasawa e2505a
      // if it is already set as the segment, continue
shun-iwasawa e2505a
      if (histo[h].belongingSegmentValue == h) continue;
shun-iwasawa e2505a
shun-iwasawa e2505a
      double errorModAmount = 0;
shun-iwasawa e2505a
      // estimate how much the error will be reduced if the current h becomes
shun-iwasawa e2505a
      // segment
shun-iwasawa e2505a
      for (int i = minHisto + 1; i < maxHisto; i++) {
shun-iwasawa e2505a
        // compare the current segment value and h and take the nearest value
shun-iwasawa e2505a
        // if h is near (from i), then accumulate the estimated error reduction
shun-iwasawa e2505a
        // amount
shun-iwasawa 0e4a7a
        if (std::abs(i - histo[i].belongingSegmentValue) >
shun-iwasawa 0e4a7a
            std::abs(i - h))  // the current segment value has
shun-iwasawa 0e4a7a
                              // proirity, if the distance is the same
shun-iwasawa e2505a
          errorModAmount +=
shun-iwasawa 0e4a7a
              (std::abs(i - histo[i].belongingSegmentValue) - std::abs(i - h)) *
shun-iwasawa e2505a
              histo[i].pix_amount;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // if h will reduce the error, update the candidate segment value
shun-iwasawa e2505a
      if (errorModAmount > tmpMaxErrorMod) {
shun-iwasawa e2505a
        tmpMaxErrorMod   = errorModAmount;
shun-iwasawa e2505a
        tmpBestNewSegVal = h;
shun-iwasawa e2505a
        newSegFound      = true;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa e2505a
    if (!newSegFound) break;
shun-iwasawa e2505a
shun-iwasawa e2505a
    // register tmpBestNewSegVal to the segment values list
shun-iwasawa e2505a
    segmentValues.insert(tmpBestNewSegVal);
shun-iwasawa e2505a
    // std::cout << "insert  " << tmpBestNewSegVal << std::endl;
shun-iwasawa e2505a
shun-iwasawa e2505a
    // update belongingSegmentValue
shun-iwasawa e2505a
    for (int h = minHisto + 1; h < maxHisto; h++) {
shun-iwasawa e2505a
      // compare the current segment value and h and take the nearest value
shun-iwasawa e2505a
      // if tmpBestNewSegVal is near (from h), then update the
shun-iwasawa e2505a
      // belongingSegmentValue
shun-iwasawa 0e4a7a
      if (std::abs(h - histo[h].belongingSegmentValue) >
shun-iwasawa 0e4a7a
          std::abs(h - tmpBestNewSegVal))  // the current segment value has
shun-iwasawa 0e4a7a
        // proirity, if the distance is the same
shun-iwasawa e2505a
        histo[h].belongingSegmentValue = tmpBestNewSegVal;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // set indices from the farthest and create the index table for each depth
shun-iwasawa e2505a
  // value
shun-iwasawa e2505a
  QVector<int> segValVec;</int>
shun-iwasawa e2505a
  int tmpSegVal = -1;
shun-iwasawa e2505a
  int tmpSegId  = -1;
shun-iwasawa e2505a
  for (int h = 255; h >= 0; h--) {
shun-iwasawa e2505a
    if (histo[h].belongingSegmentValue != tmpSegVal) {
shun-iwasawa e2505a
      segmentDepth_main.push_back((float)histo[h].belongingSegmentValue /
shun-iwasawa e2505a
                                  (float)UCHAR_MAX);
shun-iwasawa e2505a
      tmpSegVal = histo[h].belongingSegmentValue;
shun-iwasawa e2505a
      tmpSegId++;
shun-iwasawa e2505a
      segValVec.push_back(tmpSegVal);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
    histo[h].segmentId = tmpSegId;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // "sub" depth segment value list for interporation
shun-iwasawa e2505a
  for (int d = 0; d < segmentDepth_main.size() - 1; d++)
shun-iwasawa e2505a
    segmentDepth_sub.push_back(
shun-iwasawa e2505a
        (segmentDepth_main.at(d) + segmentDepth_main.at(d + 1)) / 2.0f);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // create the "sub" index table for each depth value
shun-iwasawa e2505a
  tmpSegId = 0;
shun-iwasawa e2505a
  for (int seg = 0; seg < segValVec.size() - 1; seg++) {
shun-iwasawa e2505a
    int hMax = (seg == 0) ? 255 : segValVec.at(seg);
shun-iwasawa e2505a
    int hMin = (seg == segValVec.size() - 2) ? 0 : segValVec.at(seg + 1) + 1;
shun-iwasawa e2505a
    for (int h = hMax; h >= hMin; h--) histo[h].segmentId_sub = tmpSegId;
shun-iwasawa e2505a
    tmpSegId++;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // convert the depth value to the segment index by using the index table
shun-iwasawa e2505a
  depth_p               = (unsigned char*)depth_buff;
shun-iwasawa e2505a
  unsigned char* main_p = (unsigned char*)indexMap_main;
shun-iwasawa e2505a
  unsigned char* sub_p  = (unsigned char*)indexMap_sub;
shun-iwasawa e2505a
  // mainSub_ratio represents the composition ratio of the image with "main"
shun-iwasawa e2505a
  // separation.
shun-iwasawa e2505a
  float* ratio_p = (float*)mainSub_ratio;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, depth_p++, main_p++, sub_p++, ratio_p++) {
shun-iwasawa e2505a
    *main_p = (unsigned char)histo[(int)*depth_p].segmentId;
shun-iwasawa e2505a
    *sub_p  = (unsigned char)histo[(int)*depth_p].segmentId_sub;
shun-iwasawa e2505a
shun-iwasawa e2505a
    float depth         = (float)*depth_p / (float)UCHAR_MAX;
shun-iwasawa e2505a
    float main_segDepth = segmentDepth_main.at(*main_p);
shun-iwasawa e2505a
    float sub_segDepth  = segmentDepth_sub.at(*sub_p);
shun-iwasawa e2505a
shun-iwasawa e2505a
    if (main_segDepth == sub_segDepth)
shun-iwasawa e2505a
      *ratio_p = 1.0f;
shun-iwasawa e2505a
    else {
shun-iwasawa e2505a
      *ratio_p =
shun-iwasawa e2505a
          1.0f - (main_segDepth - depth) / (main_segDepth - sub_segDepth);
shun-iwasawa e2505a
      if (*ratio_p > 1.0f) *ratio_p = 1.0f;
shun-iwasawa e2505a
      if (*ratio_p < 0.0f) *ratio_p = 0.0f;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
// set the result
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa e2505a
void Iwa_BokehRefFx::setOutputRaster(float4* srcMem, const RASTER dstRas,
shun-iwasawa e2505a
                                     TDimensionI dim, TDimensionI margin) {
shun-iwasawa e2505a
  int out_j = 0;
shun-iwasawa e2505a
  for (int j = margin.ly; j < dstRas->getLy() + margin.ly; j++, out_j++) {
shun-iwasawa e2505a
    PIXEL* pix     = dstRas->pixels(out_j);
shun-iwasawa e2505a
    float4* chan_p = srcMem;
shun-iwasawa e2505a
    chan_p += j * dim.lx + margin.lx;
shun-iwasawa e2505a
    for (int i = 0; i < dstRas->getLx(); i++, pix++, chan_p++) {
shun-iwasawa e2505a
      float val;
shun-iwasawa e2505a
      val    = (*chan_p).x * (float)PIXEL::maxChannelValue + 0.5f;
shun-iwasawa e2505a
      pix->r = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
shun-iwasawa e2505a
                                             ? (float)PIXEL::maxChannelValue
shun-iwasawa e2505a
                                             : val);
shun-iwasawa e2505a
      val    = (*chan_p).y * (float)PIXEL::maxChannelValue + 0.5f;
shun-iwasawa e2505a
      pix->g = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
shun-iwasawa e2505a
                                             ? (float)PIXEL::maxChannelValue
shun-iwasawa e2505a
                                             : val);
shun-iwasawa e2505a
      val    = (*chan_p).z * (float)PIXEL::maxChannelValue + 0.5f;
shun-iwasawa e2505a
      pix->b = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
shun-iwasawa e2505a
                                             ? (float)PIXEL::maxChannelValue
shun-iwasawa e2505a
                                             : val);
shun-iwasawa e2505a
      val    = (*chan_p).w * (float)PIXEL::maxChannelValue + 0.5f;
shun-iwasawa e2505a
      pix->m = (typename PIXEL::Channel)((val > (float)PIXEL::maxChannelValue)
shun-iwasawa e2505a
                                             ? (float)PIXEL::maxChannelValue
shun-iwasawa e2505a
                                             : val);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
// obtain iris size from the depth value
shun-iwasawa e2505a
//------------------------------------------------------------
shun-iwasawa e2505a
inline float Iwa_BokehRefFx::calcIrisSize(const float depth,
shun-iwasawa e2505a
                                          const float bokehPixelAmount,
shun-iwasawa e2505a
                                          const double onFocusDistance) {
shun-iwasawa e2505a
  return ((float)onFocusDistance - depth) * bokehPixelAmount;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// resize/invert the iris according to the size ratio
shun-iwasawa e2505a
// normalize the brightness
shun-iwasawa e2505a
// resize to the output size
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::convertIris(const float irisSize, const TRectD& irisBBox,
shun-iwasawa e2505a
                                 const TTile& irisTile,
shun-iwasawa e2505a
                                 const TDimensionI& dimOut,
shun-iwasawa e2505a
                                 kiss_fft_cpx* fftcpx_iris_before) {
shun-iwasawa e2505a
  // original size of the iris image
shun-iwasawa e2505a
  TDimensionD irisOrgSize = irisBBox.getSize();
shun-iwasawa e2505a
shun-iwasawa e2505a
  // obtain size ratio based on width
shun-iwasawa e2505a
  double irisSizeResampleRatio = irisSize / irisOrgSize.lx;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // create the raster for resizing
shun-iwasawa 0e4a7a
  TDimensionD resizedIrisSize(std::abs(irisSizeResampleRatio) * irisOrgSize.lx,
shun-iwasawa 0e4a7a
                              std::abs(irisSizeResampleRatio) * irisOrgSize.ly);
shun-iwasawa e2505a
  TDimensionI filterSize((int)std::ceil(resizedIrisSize.lx),
shun-iwasawa e2505a
                         (int)std::ceil(resizedIrisSize.ly));
shun-iwasawa e2505a
  TPointD resizeOffset((double)filterSize.lx - resizedIrisSize.lx,
shun-iwasawa e2505a
                       (double)filterSize.ly - resizedIrisSize.ly);
shun-iwasawa e2505a
shun-iwasawa e2505a
  bool isIrisOffset[2] = {false, false};
shun-iwasawa e2505a
  // iris shape must be exactly at the center of the image
shun-iwasawa e2505a
  if ((dimOut.lx - filterSize.lx) % 2 == 1) {
shun-iwasawa e2505a
    filterSize.lx++;
shun-iwasawa e2505a
    isIrisOffset[0] = true;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
  if ((dimOut.ly - filterSize.ly) % 2 == 1) {
shun-iwasawa e2505a
    filterSize.ly++;
shun-iwasawa e2505a
    isIrisOffset[1] = true;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // if the filter size becomes larger than the output size, return
shun-iwasawa e2505a
  if (filterSize.lx > dimOut.lx || filterSize.ly > dimOut.ly) {
shun-iwasawa e2505a
    std::cout
shun-iwasawa e2505a
        << "Error: The iris filter size becomes larger than the source size!"
shun-iwasawa e2505a
        << std::endl;
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  TRaster64P resizedIris(filterSize);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // offset
shun-iwasawa e2505a
  TAffine aff;
shun-iwasawa e2505a
  TPointD affOffset((isIrisOffset[0]) ? 0.5 : 1.0,
shun-iwasawa e2505a
                    (isIrisOffset[1]) ? 0.5 : 1.0);
shun-iwasawa e2505a
  if (!isIrisOffset[0]) affOffset.x -= resizeOffset.x / 2;
shun-iwasawa e2505a
  if (!isIrisOffset[1]) affOffset.y -= resizeOffset.y / 2;
shun-iwasawa e2505a
shun-iwasawa e2505a
  aff = TTranslation(resizedIris->getCenterD() + affOffset);
shun-iwasawa e2505a
  aff *= TScale(irisSizeResampleRatio);
shun-iwasawa e2505a
  aff *= TTranslation(-(irisTile.getRaster()->getCenterD() + affOffset));
shun-iwasawa e2505a
shun-iwasawa e2505a
  // resample the iris
shun-iwasawa e2505a
  TRop::resample(resizedIris, irisTile.getRaster(), aff);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // sum of the value
shun-iwasawa e2505a
  float irisValAmount = 0.0;
shun-iwasawa e2505a
shun-iwasawa e2505a
  int iris_j = 0;
shun-iwasawa e2505a
  // initialize
shun-iwasawa e2505a
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
shun-iwasawa e2505a
    fftcpx_iris_before[i].r = 0.0;
shun-iwasawa e2505a
    fftcpx_iris_before[i].i = 0.0;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
  for (int j = (dimOut.ly - filterSize.ly) / 2; iris_j < filterSize.ly;
shun-iwasawa e2505a
       j++, iris_j++) {
shun-iwasawa e2505a
    TPixel64* pix = resizedIris->pixels(iris_j);
shun-iwasawa e2505a
    int iris_i    = 0;
shun-iwasawa e2505a
    for (int i = (dimOut.lx - filterSize.lx) / 2; iris_i < filterSize.lx;
shun-iwasawa e2505a
         i++, iris_i++) {
shun-iwasawa e2505a
      // Value = 0.3R 0.59G 0.11B
shun-iwasawa e2505a
      fftcpx_iris_before[j * dimOut.lx + i].r =
shun-iwasawa e2505a
          ((float)pix->r * 0.3f + (float)pix->g * 0.59f +
shun-iwasawa e2505a
           (float)pix->b * 0.11f) /
shun-iwasawa e2505a
          (float)USHRT_MAX;
shun-iwasawa e2505a
      irisValAmount += fftcpx_iris_before[j * dimOut.lx + i].r;
shun-iwasawa e2505a
      pix++;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Normalize value
shun-iwasawa e2505a
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++)
shun-iwasawa e2505a
    fftcpx_iris_before[i].r /= irisValAmount;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// convert source image value rgb -> exposure
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::convertRGBToExposure(const float4* source_buff, int size,
shun-iwasawa e2505a
                                          float filmGamma,
shun-iwasawa e2505a
                                          bool sourceIsPremultiplied) {
shun-iwasawa e2505a
  float4* source_p = (float4*)source_buff;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, source_p++) {
shun-iwasawa e2505a
    // continue if alpha channel is 0
shun-iwasawa e2505a
    if ((*source_p).w == 0.0f) {
shun-iwasawa e2505a
      (*source_p).x = 0.0f;
shun-iwasawa e2505a
      (*source_p).y = 0.0f;
shun-iwasawa e2505a
      (*source_p).z = 0.0f;
shun-iwasawa e2505a
      continue;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa e2505a
    // unmultiply for premultiplied source
shun-iwasawa e2505a
    // this will not be applied to non-premultiplied image such as "DigiBook"
shun-iwasawa e2505a
    // (digital overlay)
shun-iwasawa e2505a
    if (sourceIsPremultiplied) {
shun-iwasawa e2505a
      // unpremultiply
shun-iwasawa e2505a
      (*source_p).x /= (*source_p).w;
shun-iwasawa e2505a
      (*source_p).y /= (*source_p).w;
shun-iwasawa e2505a
      (*source_p).z /= (*source_p).w;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa e2505a
    // RGB value -> exposure
shun-iwasawa e2505a
    (*source_p).x = pow(10, ((*source_p).x - 0.5f) / filmGamma);
shun-iwasawa e2505a
    (*source_p).y = pow(10, ((*source_p).y - 0.5f) / filmGamma);
shun-iwasawa e2505a
    (*source_p).z = pow(10, ((*source_p).z - 0.5f) / filmGamma);
shun-iwasawa e2505a
shun-iwasawa e2505a
    // multiply with alpha channel
shun-iwasawa e2505a
    (*source_p).x *= (*source_p).w;
shun-iwasawa e2505a
    (*source_p).y *= (*source_p).w;
shun-iwasawa e2505a
    (*source_p).z *= (*source_p).w;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// generate the segment layer source at the current depth
shun-iwasawa e2505a
// considering fillGap and doMedian options
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::retrieveLayer(const float4* source_buff,
shun-iwasawa e2505a
                                   const float4* segment_layer_buff,
shun-iwasawa e2505a
                                   const unsigned char* indexMap_mainSub,
shun-iwasawa e2505a
                                   int index, int lx, int ly, bool fillGap,
shun-iwasawa e2505a
                                   bool doMedian, int margin) {
shun-iwasawa e2505a
  // only when fillGap is ON and doMedian is OFF,
shun-iwasawa e2505a
  // fill the region where will be behind of the near layers
shun-iwasawa e2505a
  bool fill = (fillGap && !doMedian);
shun-iwasawa e2505a
  // retrieve the regions with the current depth
shun-iwasawa e2505a
  float4* source_p          = (float4*)source_buff;
shun-iwasawa e2505a
  float4* layer_p           = (float4*)segment_layer_buff;
shun-iwasawa e2505a
  unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
shun-iwasawa e2505a
  for (int i = 0; i < lx * ly; i++, source_p++, layer_p++, indexMap_p++) {
shun-iwasawa e2505a
    // continue if the current pixel is at the far layer
shun-iwasawa e2505a
    // consider the fill flag if the current pixel is at the near layer
shun-iwasawa e2505a
    if ((int)(*indexMap_p) < index || (!fill && (int)(*indexMap_p) > index))
shun-iwasawa e2505a
      continue;
shun-iwasawa e2505a
shun-iwasawa e2505a
    // copy pixel values
shun-iwasawa e2505a
    (*layer_p).x = (*source_p).x;
shun-iwasawa e2505a
    (*layer_p).y = (*source_p).y;
shun-iwasawa e2505a
    (*layer_p).z = (*source_p).z;
shun-iwasawa e2505a
    (*layer_p).w = (*source_p).w;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  if (!fillGap || !doMedian) return;
shun-iwasawa e2505a
  if (margin == 0) return;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // extend pixels by using median filter
shun-iwasawa e2505a
  unsigned char* generation_buff;
shun-iwasawa e2505a
  TRasterGR8P generation_buff_ras = allocateRasterAndLock<unsigned char="">(</unsigned>
shun-iwasawa e2505a
      &generation_buff, TDimensionI(lx, ly));
shun-iwasawa e2505a
shun-iwasawa 09e972
  // extend (margin * 2) pixels in order to enough cover when two adjacent
shun-iwasawa 09e972
  // layers are both blurred in the maximum radius
shun-iwasawa 09e972
  for (int gen = 0; gen < margin * 2; gen++) {
shun-iwasawa e2505a
    // apply single median filter
shun-iwasawa e2505a
    doSingleMedian(source_buff, segment_layer_buff, indexMap_mainSub, index, lx,
shun-iwasawa e2505a
                   ly, generation_buff, gen + 1);
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  generation_buff_ras->unlock();
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// apply single median filter
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::doSingleMedian(const float4* source_buff,
shun-iwasawa e2505a
                                    const float4* segment_layer_buff,
shun-iwasawa e2505a
                                    const unsigned char* indexMap_mainSub,
shun-iwasawa e2505a
                                    int index, int lx, int ly,
shun-iwasawa e2505a
                                    const unsigned char* generation_buff,
shun-iwasawa e2505a
                                    int curGen) {
shun-iwasawa e2505a
  float4* source_p          = (float4*)source_buff;
shun-iwasawa e2505a
  float4* layer_p           = (float4*)segment_layer_buff;
shun-iwasawa e2505a
  unsigned char* indexMap_p = (unsigned char*)indexMap_mainSub;
shun-iwasawa e2505a
  unsigned char* gen_p      = (unsigned char*)generation_buff;
shun-iwasawa e2505a
  for (int posY = 0; posY < ly; posY++) {
shun-iwasawa e2505a
    for (int posX = 0; posX < lx;
shun-iwasawa e2505a
         posX++, source_p++, layer_p++, indexMap_p++, gen_p++) {
shun-iwasawa e2505a
      // continue if the current pixel is at the same or far depth
shun-iwasawa e2505a
      if ((int)(*indexMap_p) <= index) continue;
shun-iwasawa e2505a
      // continue if the current pixel is already extended
shun-iwasawa e2505a
      if ((*gen_p) > 0) continue;
shun-iwasawa e2505a
shun-iwasawa e2505a
      // check out the neighbor pixels. store brightness in neighbor[x].w
shun-iwasawa e2505a
      float4 neighbor[8];
shun-iwasawa e2505a
      int neighbor_amount = 0;
shun-iwasawa e2505a
      for (int ky = posY - 1; ky <= posY + 1; ky++) {
shun-iwasawa e2505a
        for (int kx = posX - 1; kx <= posX + 1; kx++) {
shun-iwasawa e2505a
          // skip the current pixel itself
shun-iwasawa e2505a
          if (kx == posX && ky == posY) continue;
shun-iwasawa e2505a
          // border condition
shun-iwasawa e2505a
          if (ky < 0 || ky >= ly || kx < 0 || kx >= lx) continue;
shun-iwasawa e2505a
          // index in the buffer
shun-iwasawa e2505a
          int neighborId = ky * lx + kx;
shun-iwasawa e2505a
shun-iwasawa e2505a
          if ((int)indexMap_mainSub[neighborId] !=
shun-iwasawa e2505a
                  index &&  // pixels from the original image can be used as
shun-iwasawa e2505a
                            // neighbors
shun-iwasawa e2505a
              (generation_buff[neighborId] == 0 ||  // pixels which is not yet
shun-iwasawa e2505a
                                                    // be extended cannot be
shun-iwasawa e2505a
                                                    // used as neighbors
shun-iwasawa e2505a
               generation_buff[neighborId] == curGen))  // pixels which is
shun-iwasawa e2505a
                                                        // extended in the
shun-iwasawa e2505a
                                                        // current median
shun-iwasawa e2505a
                                                        // generation cannot be
shun-iwasawa e2505a
                                                        // used as neighbors
shun-iwasawa e2505a
            continue;
shun-iwasawa e2505a
          // compute brightness (actually, it is "pseudo" brightness
shun-iwasawa e2505a
          // since the source buffer is already converted to exposure)
shun-iwasawa e2505a
          float brightness = source_buff[neighborId].x * 0.3f +
shun-iwasawa e2505a
                             source_buff[neighborId].y * 0.59f +
shun-iwasawa e2505a
                             source_buff[neighborId].z * 0.11f;
shun-iwasawa e2505a
          // insert with sorting
shun-iwasawa e2505a
          int ins_index;
shun-iwasawa e2505a
          for (ins_index = 0; ins_index < neighbor_amount; ins_index++) {
shun-iwasawa e2505a
            if (neighbor[ins_index].w < brightness) break;
shun-iwasawa e2505a
          }
shun-iwasawa e2505a
          // displace neighbor values from neighbor_amount-1 to ins_index
shun-iwasawa e2505a
          for (int k = neighbor_amount - 1; k >= ins_index; k--) {
shun-iwasawa e2505a
            neighbor[k + 1].x = neighbor[k].x;
shun-iwasawa e2505a
            neighbor[k + 1].y = neighbor[k].y;
shun-iwasawa e2505a
            neighbor[k + 1].z = neighbor[k].z;
shun-iwasawa e2505a
            neighbor[k + 1].w = neighbor[k].w;
shun-iwasawa e2505a
          }
shun-iwasawa e2505a
          // set the neighbor value
shun-iwasawa e2505a
          neighbor[ins_index].x = source_buff[neighborId].x;
shun-iwasawa e2505a
          neighbor[ins_index].y = source_buff[neighborId].y;
shun-iwasawa e2505a
          neighbor[ins_index].z = source_buff[neighborId].z;
shun-iwasawa e2505a
          neighbor[ins_index].w = brightness;
shun-iwasawa e2505a
shun-iwasawa e2505a
          // increment the count
shun-iwasawa e2505a
          neighbor_amount++;
shun-iwasawa e2505a
        }
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // If there is no neighbor pixles available, continue
shun-iwasawa e2505a
      if (neighbor_amount == 0) continue;
shun-iwasawa e2505a
shun-iwasawa e2505a
      // switch the behavior when there are even number of neighbors
shun-iwasawa e2505a
      bool flag = ((posX + posY) % 2 == 0);
shun-iwasawa e2505a
      // pick up the medium index
shun-iwasawa e2505a
      int pickIndex = (flag)
shun-iwasawa e2505a
                          ? (int)std::floor((float)(neighbor_amount - 1) / 2.0f)
shun-iwasawa e2505a
                          : (int)std::ceil((float)(neighbor_amount - 1) / 2.0f);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // set the medium pixel values
shun-iwasawa e2505a
      (*layer_p).x = neighbor[pickIndex].x;
shun-iwasawa e2505a
      (*layer_p).y = neighbor[pickIndex].y;
shun-iwasawa e2505a
      (*layer_p).z = neighbor[pickIndex].z;
shun-iwasawa e2505a
      (*layer_p).w = (*source_p).w;
shun-iwasawa e2505a
shun-iwasawa e2505a
      // set the generation
shun-iwasawa e2505a
      (*gen_p) = (unsigned char)curGen;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// normal-composite the layer as is, without filtering
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::compositeAsIs(const float4* segment_layer_buff,
shun-iwasawa e2505a
                                   const float4* result_buff_mainSub,
shun-iwasawa e2505a
                                   int size) {
shun-iwasawa e2505a
  float4* layer_p  = (float4*)segment_layer_buff;
shun-iwasawa e2505a
  float4* result_p = (float4*)result_buff_mainSub;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, layer_p++, result_p++) {
shun-iwasawa e2505a
    // in case the pixel is full opac
shun-iwasawa e2505a
    if ((*layer_p).w == 1.0f) {
shun-iwasawa e2505a
      (*result_p).x = (*layer_p).x;
shun-iwasawa e2505a
      (*result_p).y = (*layer_p).y;
shun-iwasawa e2505a
      (*result_p).z = (*layer_p).z;
shun-iwasawa e2505a
      (*result_p).w = 1.0f;
shun-iwasawa e2505a
      continue;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
    // in case the pixel is full transparent
shun-iwasawa e2505a
    else if ((*layer_p).w == 0.0f)
shun-iwasawa e2505a
      continue;
shun-iwasawa e2505a
    // in case the pixel is semi-transparent, do normal composite
shun-iwasawa e2505a
    else {
shun-iwasawa e2505a
      (*result_p).x = (*layer_p).x + (*result_p).x * (1.0f - (*layer_p).w);
shun-iwasawa e2505a
      (*result_p).y = (*layer_p).y + (*result_p).y * (1.0f - (*layer_p).w);
shun-iwasawa e2505a
      (*result_p).z = (*layer_p).z + (*result_p).z * (1.0f - (*layer_p).w);
shun-iwasawa e2505a
      (*result_p).w = (*layer_p).w + (*result_p).w * (1.0f - (*layer_p).w);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// retrieve segment layer image for each channel
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::retrieveChannel(const float4* segment_layer_buff,  // src
shun-iwasawa e2505a
                                     kiss_fft_cpx* fftcpx_r_before,     // dst
shun-iwasawa e2505a
                                     kiss_fft_cpx* fftcpx_g_before,     // dst
shun-iwasawa e2505a
                                     kiss_fft_cpx* fftcpx_b_before,     // dst
shun-iwasawa e2505a
                                     kiss_fft_cpx* fftcpx_a_before,     // dst
shun-iwasawa e2505a
                                     int size) {
shun-iwasawa e2505a
  float4* layer_p = (float4*)segment_layer_buff;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, layer_p++) {
shun-iwasawa e2505a
    fftcpx_r_before[i].r = (*layer_p).x;
shun-iwasawa e2505a
    fftcpx_g_before[i].r = (*layer_p).y;
shun-iwasawa e2505a
    fftcpx_b_before[i].r = (*layer_p).z;
shun-iwasawa e2505a
    fftcpx_a_before[i].r = (*layer_p).w;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// multiply filter on channel
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::multiplyFilter(kiss_fft_cpx* fftcpx_channel,  // dst
shun-iwasawa e2505a
                                    kiss_fft_cpx* fftcpx_iris,     // filter
shun-iwasawa e2505a
                                    int size) {
shun-iwasawa e2505a
  for (int i = 0; i < size; i++) {
shun-iwasawa e2505a
    float re, im;
shun-iwasawa e2505a
    re = fftcpx_channel[i].r * fftcpx_iris[i].r -
shun-iwasawa e2505a
         fftcpx_channel[i].i * fftcpx_iris[i].i;
shun-iwasawa e2505a
    im = fftcpx_channel[i].r * fftcpx_iris[i].i +
shun-iwasawa e2505a
         fftcpx_channel[i].i * fftcpx_iris[i].r;
shun-iwasawa e2505a
shun-iwasawa e2505a
    fftcpx_channel[i].r = re;
shun-iwasawa e2505a
    fftcpx_channel[i].i = im;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// normal comosite the alpha channel
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::compositeAlpha(const float4* result_buff,         // dst
shun-iwasawa e2505a
                                    const kiss_fft_cpx* fftcpx_alpha,  // alpha
shun-iwasawa e2505a
                                    int lx, int ly) {
shun-iwasawa e2505a
  int size         = lx * ly;
shun-iwasawa e2505a
  float4* result_p = (float4*)result_buff;
shun-iwasawa e2505a
  for (int i = 0; i < size; i++, result_p++) {
shun-iwasawa e2505a
    // modify fft coordinate to normal
shun-iwasawa e2505a
    float alpha = fftcpx_alpha[getCoord(i, lx, ly)].r / (float)size;
shun-iwasawa e2505a
shun-iwasawa e2505a
    if ((*result_p).w < 1.0f) {
shun-iwasawa e2505a
      if (alpha >= 1.0f)
shun-iwasawa e2505a
        (*result_p).w = 1.0f;
shun-iwasawa e2505a
      else
shun-iwasawa e2505a
        (*result_p).w = alpha + ((*result_p).w * (1.0f - alpha));
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
// interpolate main and sub exposures
shun-iwasawa e2505a
// convert exposure -> value (0-1)
shun-iwasawa e2505a
// set to the result
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
void Iwa_BokehRefFx::interpolateExposureAndConvertToRGB(
shun-iwasawa e2505a
    const float4* result_main_buff,  // result1
shun-iwasawa e2505a
    const float4* result_sub_buff,   // result2
shun-iwasawa e2505a
    const float* mainSub_ratio,      // ratio
shun-iwasawa e2505a
    float filmGamma,
shun-iwasawa e2505a
    const float4* source_buff,  // dst
shun-iwasawa e2505a
    int size) {
shun-iwasawa e2505a
  float4* resultMain_p = (float4*)result_main_buff;
shun-iwasawa e2505a
  float4* resultSub_p  = (float4*)result_sub_buff;
shun-iwasawa e2505a
  float* ratio_p       = (float*)mainSub_ratio;
shun-iwasawa e2505a
  float4* out_p        = (float4*)source_buff;
shun-iwasawa e2505a
  for (int i = 0; i < size;
shun-iwasawa e2505a
       i++, resultMain_p++, resultSub_p++, ratio_p++, out_p++) {
shun-iwasawa e2505a
    // interpolate main and sub exposures
shun-iwasawa e2505a
    float4 result;
shun-iwasawa e2505a
shun-iwasawa e2505a
    result.x =
shun-iwasawa e2505a
        (*resultMain_p).x * (*ratio_p) + (*resultSub_p).x * (1.0f - (*ratio_p));
shun-iwasawa e2505a
    result.y =
shun-iwasawa e2505a
        (*resultMain_p).y * (*ratio_p) + (*resultSub_p).y * (1.0f - (*ratio_p));
shun-iwasawa e2505a
    result.z =
shun-iwasawa e2505a
        (*resultMain_p).z * (*ratio_p) + (*resultSub_p).z * (1.0f - (*ratio_p));
shun-iwasawa e2505a
    result.w =
shun-iwasawa e2505a
        (*resultMain_p).w * (*ratio_p) + (*resultSub_p).w * (1.0f - (*ratio_p));
shun-iwasawa e2505a
shun-iwasawa e2505a
    (*out_p).w = result.w;
shun-iwasawa e2505a
shun-iwasawa e2505a
    // convert exposure -> value (0-1)
shun-iwasawa e2505a
shun-iwasawa e2505a
    // continue for transparent pixel
shun-iwasawa e2505a
    if (result.w == 0.0f) {
shun-iwasawa e2505a
      (*out_p).x = 0.0f;
shun-iwasawa e2505a
      (*out_p).y = 0.0f;
shun-iwasawa e2505a
      (*out_p).z = 0.0f;
shun-iwasawa e2505a
      continue;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa e2505a
    // convert Exposure to value
shun-iwasawa e2505a
    result.x = log10(result.x) * filmGamma + 0.5f;
shun-iwasawa e2505a
    result.y = log10(result.y) * filmGamma + 0.5f;
shun-iwasawa e2505a
    result.z = log10(result.z) * filmGamma + 0.5f;
shun-iwasawa e2505a
shun-iwasawa e2505a
    (*out_p).x =
shun-iwasawa e2505a
        (result.x > 1.0f) ? 1.0f : ((result.x < 0.0f) ? 0.0f : result.x);
shun-iwasawa e2505a
    (*out_p).y =
shun-iwasawa e2505a
        (result.y > 1.0f) ? 1.0f : ((result.y < 0.0f) ? 0.0f : result.y);
shun-iwasawa e2505a
    (*out_p).z =
shun-iwasawa e2505a
        (result.z > 1.0f) ? 1.0f : ((result.z < 0.0f) ? 0.0f : result.z);
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
Iwa_BokehRefFx::Iwa_BokehRefFx()
shun-iwasawa e2505a
    : m_onFocusDistance(0.5)
shun-iwasawa e2505a
    , m_bokehAmount(30.0)
shun-iwasawa e2505a
    , m_hardness(0.3)
shun-iwasawa e2505a
    , m_distancePrecision(10)
shun-iwasawa e2505a
    , m_fillGap(true)
shun-iwasawa e2505a
    , m_doMedian(true) {
shun-iwasawa e2505a
  // Bind parameters
shun-iwasawa e2505a
  addInputPort("Iris", m_iris);
shun-iwasawa e2505a
  addInputPort("Source", m_source);
shun-iwasawa e2505a
  addInputPort("Depth", m_depth);
shun-iwasawa e2505a
shun-iwasawa e2505a
  bindParam(this, "on_focus_distance", m_onFocusDistance, false);
shun-iwasawa e2505a
  bindParam(this, "bokeh_amount", m_bokehAmount, false);
shun-iwasawa e2505a
  bindParam(this, "hardness", m_hardness, false);
shun-iwasawa e2505a
  bindParam(this, "distance_precision", m_distancePrecision, false);
shun-iwasawa e2505a
  bindParam(this, "fill_gap", m_fillGap, false);
shun-iwasawa e2505a
  bindParam(this, "fill_gap_with_median_filter", m_doMedian, false);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Set the ranges of parameters
shun-iwasawa e2505a
  m_onFocusDistance->setValueRange(0, 1);
shun-iwasawa e2505a
  m_bokehAmount->setValueRange(0, 300);
shun-iwasawa e2505a
  m_bokehAmount->setMeasureName("fxLength");
shun-iwasawa e2505a
  m_hardness->setValueRange(0.05, 20.0);
shun-iwasawa e2505a
  m_distancePrecision->setValueRange(3, 128);
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
void Iwa_BokehRefFx::doCompute(TTile& tile, double frame,
shun-iwasawa e2505a
                               const TRenderSettings& settings) {
shun-iwasawa e2505a
  // If any of input is not connected, then do nothing
shun-iwasawa e2505a
  if (!m_iris.isConnected() || !m_source.isConnected() ||
shun-iwasawa e2505a
      !m_depth.isConnected()) {
shun-iwasawa e2505a
    tile.getRaster()->clear();
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  QList<trastergr8p> rasterList;</trastergr8p>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
shun-iwasawa e2505a
  float bokehPixelAmount = getBokehPixelAmount(frame, settings.m_affine);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Obtain the larger size of bokeh between the nearest (black) point and
shun-iwasawa e2505a
  // the farthest (white) point, based on the focus distance.
shun-iwasawa e2505a
  double onFocusDistance = m_onFocusDistance->getValue(frame);
shun-iwasawa e2505a
  float maxIrisSize =
shun-iwasawa e2505a
      bokehPixelAmount * std::max((1.0 - onFocusDistance), onFocusDistance);
shun-iwasawa e2505a
shun-iwasawa e2505a
  int margin =
shun-iwasawa e2505a
      (maxIrisSize > 1.0f) ? (int)(std::ceil((maxIrisSize - 1.0f) / 2.0f)) : 0;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Range of computation
shun-iwasawa e2505a
  TRectD rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
shun-iwasawa e2505a
                                         tile.getRaster()->getLy()));
shun-iwasawa e2505a
  rectOut = rectOut.enlarge(static_cast<double>(margin));</double>
shun-iwasawa e2505a
shun-iwasawa e2505a
  TDimensionI dimOut(static_cast<int>(rectOut.getLx() + 0.5),</int>
shun-iwasawa e2505a
                     static_cast<int>(rectOut.getLy() + 0.5));</int>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Enlarge the size to the "fast size" for kissfft which has no factors other
shun-iwasawa e2505a
  // than 2,3, or 5.
shun-iwasawa e2505a
  if (dimOut.lx < 10000 && dimOut.ly < 10000) {
shun-iwasawa e2505a
    int new_x = kiss_fft_next_fast_size(dimOut.lx);
shun-iwasawa e2505a
    int new_y = kiss_fft_next_fast_size(dimOut.ly);
shun-iwasawa e2505a
shun-iwasawa e2505a
    rectOut = rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0,</double>
shun-iwasawa e2505a
                              static_cast<double>(new_y - dimOut.ly) / 2.0);</double>
shun-iwasawa e2505a
shun-iwasawa e2505a
    dimOut.lx = new_x;
shun-iwasawa e2505a
    dimOut.ly = new_y;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // - - - Compute the input tiles - - -
shun-iwasawa e2505a
shun-iwasawa e2505a
  // Automatically judge whether the source image is premultiplied or not
shun-iwasawa e2505a
  bool isPremultiplied;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // source image buffer
shun-iwasawa e2505a
  float4* source_buff;
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<float4>(&source_buff, dimOut));</float4>
shun-iwasawa e2505a
shun-iwasawa e2505a
  {
shun-iwasawa e2505a
    // source tile is used only in this focus.
shun-iwasawa e2505a
    // normalized source image data is stored in source_buff.
shun-iwasawa e2505a
    TTile sourceTile;
shun-iwasawa e2505a
    m_source->allocateAndCompute(sourceTile, rectOut.getP00(), dimOut,
shun-iwasawa e2505a
                                 tile.getRaster(), frame, settings);
shun-iwasawa e2505a
    // normalize the tile image to 0-1 and set to source_buff
shun-iwasawa e2505a
    TRaster32P ras32 = (TRaster32P)sourceTile.getRaster();
shun-iwasawa e2505a
    TRaster64P ras64 = (TRaster64P)sourceTile.getRaster();
shun-iwasawa e2505a
    lock.lockForRead();
shun-iwasawa e2505a
    if (ras32)
shun-iwasawa e2505a
      isPremultiplied =
shun-iwasawa e2505a
          setSourceRaster<traster32p, tpixel32="">(ras32, source_buff, dimOut);</traster32p,>
shun-iwasawa e2505a
    else if (ras64)
shun-iwasawa e2505a
      isPremultiplied =
shun-iwasawa e2505a
          setSourceRaster<traster64p, tpixel64="">(ras64, source_buff, dimOut);</traster64p,>
shun-iwasawa e2505a
    lock.unlock();
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRasters(rasterList);
shun-iwasawa e2505a
    tile.getRaster()->clear();
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // create the index map, which indicates which layer each pixel belongs to
shun-iwasawa e2505a
  // make two separations and interporate the results in order to avoid
shun-iwasawa e2505a
  // artifacts appear at the layer border
shun-iwasawa e2505a
  unsigned char* indexMap_main;
shun-iwasawa e2505a
  unsigned char* indexMap_sub;
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<unsigned char="">(&indexMap_main, dimOut));</unsigned>
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<unsigned char="">(&indexMap_sub, dimOut));</unsigned>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // interporation ratio between two results
shun-iwasawa e2505a
  float* mainSub_ratio;
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<float>(&mainSub_ratio, dimOut));</float>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // - - - depth segmentation - - -
shun-iwasawa e2505a
  QVector<float> segmentDepth_main;</float>
shun-iwasawa e2505a
  QVector<float> segmentDepth_sub;</float>
shun-iwasawa e2505a
  {
shun-iwasawa e2505a
    // depth image stored in 256 levels
shun-iwasawa e2505a
    unsigned char* depth_buff;
shun-iwasawa e2505a
    TRasterGR8P depth_buff_ras =
shun-iwasawa e2505a
        allocateRasterAndLock<unsigned char="">(&depth_buff, dimOut);</unsigned>
shun-iwasawa e2505a
    {
shun-iwasawa e2505a
      TTile depthTile;
shun-iwasawa e2505a
      m_depth->allocateAndCompute(depthTile, rectOut.getP00(), dimOut,
shun-iwasawa e2505a
                                  tile.getRaster(), frame, settings);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRasters(rasterList);
shun-iwasawa e2505a
        depth_buff_ras->unlock();
shun-iwasawa e2505a
        tile.getRaster()->clear();
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // normalize brightness of the depth reference image to unsigned char
shun-iwasawa e2505a
      // and store into depth_buff
shun-iwasawa e2505a
      TRasterGR8P rasGR8   = (TRasterGR8P)depthTile.getRaster();
shun-iwasawa e2505a
      TRasterGR16P rasGR16 = (TRasterGR16P)depthTile.getRaster();
shun-iwasawa e2505a
      TRaster32P ras32     = (TRaster32P)depthTile.getRaster();
shun-iwasawa e2505a
      TRaster64P ras64     = (TRaster64P)depthTile.getRaster();
shun-iwasawa e2505a
      lock.lockForRead();
shun-iwasawa e2505a
      if (rasGR8)
shun-iwasawa e2505a
        setDepthRasterGray<trastergr8p, tpixelgr8="">(rasGR8, depth_buff, dimOut);</trastergr8p,>
shun-iwasawa e2505a
      else if (rasGR16)
shun-iwasawa e2505a
        setDepthRasterGray<trastergr16p, tpixelgr16="">(rasGR16, depth_buff,</trastergr16p,>
shun-iwasawa e2505a
                                                     dimOut);
shun-iwasawa e2505a
      else if (ras32)
shun-iwasawa e2505a
        setDepthRaster<traster32p, tpixel32="">(ras32, depth_buff, dimOut);</traster32p,>
shun-iwasawa e2505a
      else if (ras64)
shun-iwasawa e2505a
        setDepthRaster<traster64p, tpixel64="">(ras64, depth_buff, dimOut);</traster64p,>
shun-iwasawa e2505a
      lock.unlock();
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
    // create the depth index map
shun-iwasawa e2505a
    defineSegemntDepth(indexMap_main, indexMap_sub, mainSub_ratio, depth_buff,
shun-iwasawa e2505a
                       dimOut, frame, segmentDepth_main, segmentDepth_sub);
shun-iwasawa e2505a
    // depth image is not needed anymore. release it
shun-iwasawa e2505a
    depth_buff_ras->unlock();
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRasters(rasterList);
shun-iwasawa e2505a
    tile.getRaster()->clear();
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // - - - iris image - - -
shun-iwasawa e2505a
  // Get the original size of Iris image
shun-iwasawa e2505a
  TRectD irisBBox;
shun-iwasawa e2505a
  m_iris->getBBox(frame, irisBBox, settings);
shun-iwasawa e2505a
  // Compute the iris tile.
shun-iwasawa e2505a
  TTile irisTile;
shun-iwasawa e2505a
  m_iris->allocateAndCompute(
shun-iwasawa e2505a
      irisTile, irisBBox.getP00(),
shun-iwasawa e2505a
      TDimension(static_cast<int>(irisBBox.getLx() + 0.5),</int>
shun-iwasawa e2505a
                 static_cast<int>(irisBBox.getLy() + 0.5)),</int>
shun-iwasawa e2505a
      tile.getRaster(), frame, settings);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRasters(rasterList);
shun-iwasawa e2505a
    tile.getRaster()->clear();
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // for now only CPU computation is supported
shun-iwasawa e2505a
  // when introduced GPGPU capability, computation will be separated here
shun-iwasawa e2505a
  doCompute_CPU(frame, settings, bokehPixelAmount, maxIrisSize, margin, dimOut,
shun-iwasawa e2505a
                source_buff, indexMap_main, indexMap_sub, mainSub_ratio,
shun-iwasawa e2505a
                segmentDepth_main, segmentDepth_sub, irisTile, irisBBox,
shun-iwasawa e2505a
                isPremultiplied);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRasters(rasterList);
shun-iwasawa e2505a
    tile.getRaster()->clear();
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  TDimensionI actualMargin((dimOut.lx - tile.getRaster()->getSize().lx) / 2,
shun-iwasawa e2505a
                           (dimOut.ly - tile.getRaster()->getSize().ly) / 2);
shun-iwasawa e2505a
  // clear the tile raster
shun-iwasawa e2505a
  tile.getRaster()->clear();
shun-iwasawa e2505a
  TRaster32P outRas32 = (TRaster32P)tile.getRaster();
shun-iwasawa e2505a
  TRaster64P outRas64 = (TRaster64P)tile.getRaster();
shun-iwasawa e2505a
  lock.lockForWrite();
shun-iwasawa e2505a
  if (outRas32)
shun-iwasawa e2505a
    setOutputRaster<traster32p, tpixel32="">(source_buff, outRas32, dimOut,</traster32p,>
shun-iwasawa e2505a
                                          actualMargin);
shun-iwasawa e2505a
  else if (outRas64)
shun-iwasawa e2505a
    setOutputRaster<traster64p, tpixel64="">(source_buff, outRas64, dimOut,</traster64p,>
shun-iwasawa e2505a
                                          actualMargin);
shun-iwasawa e2505a
  lock.unlock();
shun-iwasawa e2505a
shun-iwasawa e2505a
  // release all rasters
shun-iwasawa e2505a
  releaseAllRasters(rasterList);
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
void Iwa_BokehRefFx::doCompute_CPU(
shun-iwasawa e2505a
    const double frame, const TRenderSettings& settings, float bokehPixelAmount,
shun-iwasawa e2505a
    float maxIrisSize, int margin, TDimensionI& dimOut, float4* source_buff,
shun-iwasawa e2505a
    unsigned char* indexMap_main, unsigned char* indexMap_sub,
shun-iwasawa e2505a
    float* mainSub_ratio, QVector<float>& segmentDepth_main,</float>
shun-iwasawa e2505a
    QVector<float>& segmentDepth_sub, TTile& irisTile, TRectD& irisBBox,</float>
shun-iwasawa e2505a
    bool sourceIsPremultiplied) {
shun-iwasawa e2505a
  QList<trastergr8p> rasterList;</trastergr8p>
shun-iwasawa e2505a
  QList<kiss_fftnd_cfg> planList;</kiss_fftnd_cfg>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // This fx is relatively heavy so the multi thread computation is introduced.
shun-iwasawa e2505a
  // Lock the mutex here in order to prevent multiple rendering tasks run at the
shun-iwasawa e2505a
  // same time.
shun-iwasawa e2505a
  QMutexLocker fx_locker(&fx_mutex);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // - - - memory allocation for FFT - - -
shun-iwasawa e2505a
shun-iwasawa e2505a
  // iris image
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_iris_before;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_iris;
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_iris_before, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_iris, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // segment layers
shun-iwasawa e2505a
  float4* segment_layer_buff;
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<float4>(&segment_layer_buff, dimOut));</float4>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // alpha channel
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_alpha_before;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_alpha;
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha_before, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_alpha, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRasters(rasterList);
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // RGB channels
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_r_before;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_g_before;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_b_before;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_r;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_g;
shun-iwasawa e2505a
  kiss_fft_cpx* fftcpx_b;
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r_before, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g_before, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(
shun-iwasawa e2505a
      allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b_before, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_r, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_g, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<kiss_fft_cpx>(&fftcpx_b, dimOut));</kiss_fft_cpx>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // for accumulating result image
shun-iwasawa e2505a
  float4* result_main_buff;
shun-iwasawa e2505a
  float4* result_sub_buff;
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<float4>(&result_main_buff, dimOut));</float4>
shun-iwasawa e2505a
  rasterList.append(allocateRasterAndLock<float4>(&result_sub_buff, dimOut));</float4>
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRasters(rasterList);
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // fft plans
shun-iwasawa e2505a
  int dims[2]                      = {dimOut.ly, dimOut.lx};
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa e2505a
  planList.append(kissfft_plan_fwd);
shun-iwasawa e2505a
  planList.append(kissfft_plan_bkwd);
shun-iwasawa e2505a
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_r_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_r_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_g_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_g_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_b_fwd  = kiss_fftnd_alloc(dims, 2, false, 0, 0);
shun-iwasawa e2505a
  kiss_fftnd_cfg kissfft_plan_b_bkwd = kiss_fftnd_alloc(dims, 2, true, 0, 0);
shun-iwasawa e2505a
  planList.append(kissfft_plan_r_fwd);
shun-iwasawa e2505a
  planList.append(kissfft_plan_r_bkwd);
shun-iwasawa e2505a
  planList.append(kissfft_plan_g_fwd);
shun-iwasawa e2505a
  planList.append(kissfft_plan_g_bkwd);
shun-iwasawa e2505a
  planList.append(kissfft_plan_b_fwd);
shun-iwasawa e2505a
  planList.append(kissfft_plan_b_bkwd);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  int size = dimOut.lx * dimOut.ly;
shun-iwasawa e2505a
shun-iwasawa e2505a
  // initialize result memory
shun-iwasawa e2505a
  memset(result_main_buff, 0, sizeof(float4) * size);
shun-iwasawa e2505a
  memset(result_sub_buff, 0, sizeof(float4) * size);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // obtain parameters
shun-iwasawa e2505a
  float filmGamma = (float)m_hardness->getValue(frame);
shun-iwasawa e2505a
  bool fillGap    = m_fillGap->getValue();
shun-iwasawa e2505a
  bool doMedian   = m_doMedian->getValue();
shun-iwasawa e2505a
shun-iwasawa e2505a
  // convert source image value rgb -> exposure
shun-iwasawa e2505a
  convertRGBToExposure(source_buff, size, filmGamma, sourceIsPremultiplied);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // compute twice (main and sub) for interpolation
shun-iwasawa e2505a
  for (int mainSub = 0; mainSub < 2; mainSub++) {
shun-iwasawa e2505a
    // cancel check
shun-iwasawa e2505a
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
      releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
      return;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa e2505a
    float4* result_buff_mainSub;
shun-iwasawa e2505a
    QVector<float> segmentDepth_mainSub;</float>
shun-iwasawa e2505a
    unsigned char* indexMap_mainSub;
shun-iwasawa e2505a
    if (mainSub == 0)  // main process
shun-iwasawa e2505a
    {
shun-iwasawa e2505a
      result_buff_mainSub  = result_main_buff;
shun-iwasawa e2505a
      segmentDepth_mainSub = segmentDepth_main;
shun-iwasawa e2505a
      indexMap_mainSub     = indexMap_main;
shun-iwasawa e2505a
    } else  // sub process
shun-iwasawa e2505a
    {
shun-iwasawa e2505a
      result_buff_mainSub  = result_sub_buff;
shun-iwasawa e2505a
      segmentDepth_mainSub = segmentDepth_sub;
shun-iwasawa e2505a
      indexMap_mainSub     = indexMap_sub;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa e2505a
    // Compute from the most distant segment layer
shun-iwasawa e2505a
    for (int index = 0; index < segmentDepth_mainSub.size(); index++) {
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // obtain iris size from the depth value
shun-iwasawa e2505a
      float irisSize =
shun-iwasawa e2505a
          calcIrisSize(segmentDepth_mainSub.at(index), bokehPixelAmount,
shun-iwasawa e2505a
                       m_onFocusDistance->getValue(frame));
shun-iwasawa e2505a
shun-iwasawa e2505a
      // initialize the layer memory
shun-iwasawa e2505a
      memset(segment_layer_buff, 0, sizeof(float4) * size);
shun-iwasawa e2505a
      // generate the segment layer source at the current depth
shun-iwasawa e2505a
      // considering fillGap and doMedian options
shun-iwasawa e2505a
      retrieveLayer(source_buff, segment_layer_buff, indexMap_mainSub, index,
shun-iwasawa e2505a
                    dimOut.lx, dimOut.ly, fillGap, doMedian,
shun-iwasawa e2505a
                    (index == segmentDepth_mainSub.size() - 1) ? 0 : margin);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // if the layer is at (almost) the focus position
shun-iwasawa e2505a
      if (-1.0 <= irisSize && 1.0 >= irisSize) {
shun-iwasawa e2505a
        // normal-composite the layer as is, without filtering
shun-iwasawa e2505a
        compositeAsIs(segment_layer_buff, result_buff_mainSub, size);
shun-iwasawa e2505a
        // continue to next layer
shun-iwasawa e2505a
        continue;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // resize the iris image
shun-iwasawa e2505a
shun-iwasawa e2505a
      // resize/invert the iris according to the size ratio
shun-iwasawa e2505a
      // normalize the brightness
shun-iwasawa e2505a
      // resize to the output size
shun-iwasawa e2505a
      convertIris(irisSize, irisBBox, irisTile, dimOut, fftcpx_iris_before);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // Do FFT the iris image.
shun-iwasawa e2505a
      kiss_fftnd(kissfft_plan_fwd, fftcpx_iris_before, fftcpx_iris);
shun-iwasawa e2505a
      // fftwf_execute_dft(fftw_plan_fwd_r, iris_host, iris_host);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // initialize alpha
shun-iwasawa e2505a
      memset(fftcpx_alpha_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa e2505a
      // initialize channels
shun-iwasawa e2505a
      memset(fftcpx_r_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa e2505a
      memset(fftcpx_g_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa e2505a
      memset(fftcpx_b_before, 0, sizeof(kiss_fft_cpx) * size);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // retrieve segment layer image for each channel
shun-iwasawa e2505a
      retrieveChannel(segment_layer_buff,   // src
shun-iwasawa e2505a
                      fftcpx_r_before,      // dst
shun-iwasawa e2505a
                      fftcpx_g_before,      // dst
shun-iwasawa e2505a
                      fftcpx_b_before,      // dst
shun-iwasawa e2505a
                      fftcpx_alpha_before,  // dst
shun-iwasawa e2505a
                      size);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // forward fft of alpha channel
shun-iwasawa e2505a
      kiss_fftnd(kissfft_plan_fwd, fftcpx_alpha_before, fftcpx_alpha);
shun-iwasawa e2505a
      // fftwf_execute_dft(fftw_plan_fwd_r, alphaBokeh_host, alphaBokeh_host);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // multiply filter on alpha
shun-iwasawa e2505a
      multiplyFilter(fftcpx_alpha,  // dst
shun-iwasawa e2505a
                     fftcpx_iris,   // filter
shun-iwasawa e2505a
                     size);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // inverse fft the alpha channel
shun-iwasawa e2505a
      // note that the result is multiplied by the image size
shun-iwasawa e2505a
      kiss_fftnd(kissfft_plan_bkwd, fftcpx_alpha, fftcpx_alpha_before);
shun-iwasawa e2505a
      // fftwf_execute_dft(fftw_plan_bkwd_r, alphaBokeh_host, alphaBokeh_host);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // normal composite the alpha channel
shun-iwasawa e2505a
      compositeAlpha(result_buff_mainSub,  // dst
shun-iwasawa e2505a
                     fftcpx_alpha_before,  // alpha
shun-iwasawa e2505a
                     dimOut.lx, dimOut.ly);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // cancel check
shun-iwasawa e2505a
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
        releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
        return;
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
      // create worker threads
shun-iwasawa e2505a
      BokehRefThread threadR(0, fftcpx_r_before, fftcpx_r, fftcpx_alpha_before,
shun-iwasawa e2505a
                             fftcpx_iris, result_buff_mainSub,
shun-iwasawa e2505a
                             kissfft_plan_r_fwd, kissfft_plan_r_bkwd, dimOut);
shun-iwasawa e2505a
      BokehRefThread threadG(1, fftcpx_g_before, fftcpx_g, fftcpx_alpha_before,
shun-iwasawa e2505a
                             fftcpx_iris, result_buff_mainSub,
shun-iwasawa e2505a
                             kissfft_plan_g_fwd, kissfft_plan_g_bkwd, dimOut);
shun-iwasawa e2505a
      BokehRefThread threadB(2, fftcpx_b_before, fftcpx_b, fftcpx_alpha_before,
shun-iwasawa e2505a
                             fftcpx_iris, result_buff_mainSub,
shun-iwasawa e2505a
                             kissfft_plan_b_fwd, kissfft_plan_b_bkwd, dimOut);
shun-iwasawa e2505a
shun-iwasawa e2505a
      // If you set this flag to true, the fx will be forced to compute in
shun-iwasawa e2505a
      // single
shun-iwasawa e2505a
      // thread.
shun-iwasawa e2505a
      // Under some specific condition (such as calling from single-threaded
shun-iwasawa e2505a
      // tcomposer)
shun-iwasawa e2505a
      // we may need to use this flag... For now, I'll keep this option unused.
shun-iwasawa e2505a
      // TODO: investigate this.
shun-iwasawa e2505a
      bool renderInSingleThread = false;
shun-iwasawa e2505a
shun-iwasawa e2505a
      if (renderInSingleThread) {
shun-iwasawa e2505a
        threadR.run();
shun-iwasawa e2505a
        threadG.run();
shun-iwasawa e2505a
        threadB.run();
shun-iwasawa e2505a
      } else {
shun-iwasawa e2505a
        threadR.start();
shun-iwasawa e2505a
        threadG.start();
shun-iwasawa e2505a
        threadB.start();
shun-iwasawa e2505a
        int waitCount = 0;
shun-iwasawa e2505a
        while (1) {
shun-iwasawa e2505a
          if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun-iwasawa e2505a
              waitCount >= 2000)  // 100 second timeout
shun-iwasawa e2505a
          {
shun-iwasawa e2505a
            if (!threadR.isFinished()) threadR.terminateThread();
shun-iwasawa e2505a
            if (!threadG.isFinished()) threadG.terminateThread();
shun-iwasawa e2505a
            if (!threadB.isFinished()) threadB.terminateThread();
shun-iwasawa e2505a
            while (!threadR.isFinished() || !threadG.isFinished() ||
shun-iwasawa e2505a
                   !threadB.isFinished()) {
shun-iwasawa e2505a
            }
shun-iwasawa e2505a
            releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
            return;
shun-iwasawa e2505a
          }
shun-iwasawa e2505a
          if (threadR.isFinished() && threadG.isFinished() &&
shun-iwasawa e2505a
              threadB.isFinished())
shun-iwasawa e2505a
            break;
shun-iwasawa e2505a
          QThread::msleep(50);
shun-iwasawa e2505a
          waitCount++;
shun-iwasawa e2505a
        }
shun-iwasawa e2505a
      }
shun-iwasawa e2505a
shun-iwasawa e2505a
    }  // for each layer
shun-iwasawa e2505a
  }    // for main and sub
shun-iwasawa e2505a
shun-iwasawa e2505a
  // cancel check
shun-iwasawa e2505a
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa e2505a
    releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
    return;
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
shun-iwasawa e2505a
  // interpolate main and sub exposures
shun-iwasawa e2505a
  // convert exposure -> RGB (0-1)
shun-iwasawa e2505a
  // set to the result
shun-iwasawa e2505a
  interpolateExposureAndConvertToRGB(result_main_buff,  // result1
shun-iwasawa e2505a
                                     result_sub_buff,   // result2
shun-iwasawa e2505a
                                     mainSub_ratio,     // ratio
shun-iwasawa e2505a
                                     filmGamma,
shun-iwasawa e2505a
                                     source_buff,  // dst
shun-iwasawa e2505a
                                     size);
shun-iwasawa e2505a
shun-iwasawa e2505a
  // release rasters and plans
shun-iwasawa e2505a
  releaseAllRastersAndPlans(rasterList, planList);
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
bool Iwa_BokehRefFx::doGetBBox(double frame, TRectD& bBox,
shun-iwasawa e2505a
                               const TRenderSettings& info) {
shun-iwasawa e2505a
  bBox = TConsts::infiniteRectD;
shun-iwasawa e2505a
  return true;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
bool Iwa_BokehRefFx::canHandle(const TRenderSettings& info, double frame) {
shun-iwasawa e2505a
  return false;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
FX_PLUGIN_IDENTIFIER(Iwa_BokehRefFx, "iwa_BokehRefFx")