shun_iwasawa a35b8f
#include "iwa_bokehfx.h"
shun_iwasawa a35b8f
shun_iwasawa a35b8f
#include "trop.h"
shun_iwasawa a35b8f
#include "tdoubleparam.h"
shun_iwasawa a35b8f
#include "trasterfx.h"
shun_iwasawa a35b8f
#include "trasterimage.h"
shun_iwasawa a35b8f
shun_iwasawa a35b8f
#include "kiss_fft.h"
shun_iwasawa a35b8f
shun_iwasawa a35b8f
#include <qpair></qpair>
shun_iwasawa a35b8f
#include <qvector></qvector>
shun_iwasawa a35b8f
#include <qreadwritelock></qreadwritelock>
shun_iwasawa a35b8f
#include <qmutexlocker></qmutexlocker>
shun_iwasawa a35b8f
#include <qmap></qmap>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
namespace {
shun_iwasawa a35b8f
QReadWriteLock lock;
shun_iwasawa a35b8f
QMutex mutex, fx_mutex;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
bool isFurtherLayer(const QPair<int, float=""> val1,</int,>
shun_iwasawa a35b8f
                    const QPair<int, float=""> val2) {</int,>
shun_iwasawa a35b8f
  return val1.second > val2.second;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// FFT coordinate -> Normal corrdinate
shun_iwasawa a35b8f
inline int getCoord(int i, int j, int lx, int ly) {
shun_iwasawa a35b8f
  int cx = i - lx / 2;
shun_iwasawa a35b8f
  int cy = j - ly / 2;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (cx < 0) cx += lx;
shun_iwasawa a35b8f
  if (cy < 0) cy += ly;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  return cy * lx + cx;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// RGB value <--> Exposure
shun_iwasawa a35b8f
inline float valueToExposure(float value, float filmGamma) {
shun_iwasawa a35b8f
  float logVal = (value - 0.5) / filmGamma;
shun_iwasawa a35b8f
  return pow(10, logVal);
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
inline float exposureToValue(float exposure, float filmGamma) {
shun_iwasawa a35b8f
  return log10(exposure) * filmGamma + 0.5;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
};
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//--------------------------------------------
shun_iwasawa a35b8f
// Threads used for FFT computation for each RGB channel
shun_iwasawa a35b8f
//--------------------------------------------
shun_iwasawa a35b8f
shun_iwasawa a35b8f
MyThread::MyThread(Channel channel, TRasterP layerTileRas, TRasterP outTileRas,
shun_iwasawa a35b8f
                   TRasterP tmpAlphaRas, kiss_fft_cpx* kissfft_comp_iris,
shun_iwasawa a35b8f
                   float filmGamma,
shun_iwasawa a35b8f
                   bool doLightenComp)  // not used for now
shun_iwasawa a35b8f
    : m_channel(channel),
shun_iwasawa a35b8f
      m_layerTileRas(layerTileRas),
shun_iwasawa a35b8f
      m_outTileRas(outTileRas),
shun_iwasawa a35b8f
      m_tmpAlphaRas(tmpAlphaRas),
shun_iwasawa a35b8f
      m_kissfft_comp_iris(kissfft_comp_iris),
shun_iwasawa a35b8f
      m_filmGamma(filmGamma),
shun_iwasawa a35b8f
      m_finished(false),
shun_iwasawa a35b8f
      m_kissfft_comp_in(0),
shun_iwasawa a35b8f
      m_kissfft_comp_out(0),
shun_iwasawa a35b8f
      m_isTerminated(false),
shun_iwasawa a35b8f
      m_doLightenComp(doLightenComp)  // not used for now
shun_iwasawa a35b8f
{}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
bool MyThread::init() {
shun_iwasawa a35b8f
  // get the source size
shun_iwasawa a35b8f
  int lx, ly;
shun_iwasawa a35b8f
  lx = m_layerTileRas->getSize().lx;
shun_iwasawa a35b8f
  ly = m_layerTileRas->getSize().ly;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // memory allocation for input
shun_iwasawa a35b8f
  m_kissfft_comp_in_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly);
shun_iwasawa a35b8f
  m_kissfft_comp_in_ras->lock();
shun_iwasawa a35b8f
  m_kissfft_comp_in = (kiss_fft_cpx*)m_kissfft_comp_in_ras->getRawData();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // allocation check
shun_iwasawa a35b8f
  if (m_kissfft_comp_in == 0) return false;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // cancel check
shun_iwasawa a35b8f
  if (m_isTerminated) {
shun_iwasawa a35b8f
    m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
    return false;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // memory allocation for output
shun_iwasawa a35b8f
  m_kissfft_comp_out_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly);
shun_iwasawa a35b8f
  m_kissfft_comp_out_ras->lock();
shun_iwasawa a35b8f
  m_kissfft_comp_out = (kiss_fft_cpx*)m_kissfft_comp_out_ras->getRawData();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // allocation check
shun_iwasawa a35b8f
  if (m_kissfft_comp_out == 0) {
shun_iwasawa a35b8f
    m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_in = 0;
shun_iwasawa a35b8f
    return false;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // cancel check
shun_iwasawa a35b8f
  if (m_isTerminated) {
shun_iwasawa a35b8f
    m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_in = 0;
shun_iwasawa a35b8f
    m_kissfft_comp_out_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_out = 0;
shun_iwasawa a35b8f
    return false;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // create the forward FFT plan
shun_iwasawa a35b8f
  int dims[2]        = {ly, lx};
shun_iwasawa a35b8f
  int ndims          = 2;
shun_iwasawa a35b8f
  m_kissfft_plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
shun_iwasawa a35b8f
  // allocation and cancel check
shun_iwasawa a35b8f
  if (m_kissfft_plan_fwd == NULL || m_isTerminated) {
shun_iwasawa a35b8f
    m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_in = 0;
shun_iwasawa a35b8f
    m_kissfft_comp_out_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_out = 0;
shun_iwasawa a35b8f
    return false;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // create the backward FFT plan
shun_iwasawa a35b8f
  m_kissfft_plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0);
shun_iwasawa a35b8f
  // allocation and cancel check
shun_iwasawa a35b8f
  if (m_kissfft_plan_bkwd == NULL || m_isTerminated) {
shun_iwasawa a35b8f
    m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_in = 0;
shun_iwasawa a35b8f
    m_kissfft_comp_out_ras->unlock();
shun_iwasawa a35b8f
    m_kissfft_comp_out = 0;
shun_iwasawa a35b8f
    kiss_fft_free(m_kissfft_plan_fwd);
shun_iwasawa a35b8f
    m_kissfft_plan_fwd = NULL;
shun_iwasawa a35b8f
    return false;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // return true if all the initializations are done
shun_iwasawa a35b8f
  return true;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//------------------------------------------------------------
shun_iwasawa a35b8f
// Convert the pixels from RGB values to exposures and multiply it by alpha
shun_iwasawa a35b8f
// channel value.
shun_iwasawa a35b8f
// Store the results in the real part of kiss_fft_cpx.
shun_iwasawa a35b8f
//------------------------------------------------------------
shun_iwasawa a35b8f
template <typename pixel="" raster,="" typename=""></typename>
shun_iwasawa a35b8f
void MyThread::setLayerRaster(const RASTER srcRas, kiss_fft_cpx* dstMem,
shun_iwasawa a35b8f
                              TDimensionI dim) {
shun_iwasawa a35b8f
  for (int j = 0; j < dim.ly; j++) {
shun_iwasawa a35b8f
    PIXEL* pix = srcRas->pixels(j);
shun_iwasawa a35b8f
    for (int i = 0; i < dim.lx; i++, pix++) {
shun_iwasawa a35b8f
      if (pix->m != 0) {
shun_iwasawa a35b8f
        float val = (m_channel == Red)
shun_iwasawa a35b8f
                        ? (float)pix->r
shun_iwasawa a35b8f
                        : (m_channel == Green) ? (float)pix->g : (float)pix->b;
shun_iwasawa a35b8f
        // multiply the exposure by alpha channel value
shun_iwasawa a35b8f
        dstMem[j * dim.lx + i].r =
shun_iwasawa a35b8f
            valueToExposure(val / (float)PIXEL::maxChannelValue) *
shun_iwasawa a35b8f
            ((float)pix->m / (float)PIXEL::maxChannelValue);
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//------------------------------------------------------------
shun_iwasawa a35b8f
// Composite the bokeh layer to the result
shun_iwasawa a35b8f
//------------------------------------------------------------
shun_iwasawa a35b8f
template <typename a_pixel="" a_raster,="" pixel,="" raster,="" typename=""></typename>
shun_iwasawa a35b8f
void MyThread::compositLayerToTile(const RASTER layerRas,
shun_iwasawa a35b8f
                                   const RASTER outTileRas,
shun_iwasawa a35b8f
                                   const A_RASTER alphaRas, TDimensionI dim,
shun_iwasawa a35b8f
                                   int2 margin) {
shun_iwasawa a35b8f
  int j = margin.y;
shun_iwasawa a35b8f
  for (int out_j = 0; out_j < outTileRas->getLy(); j++, out_j++) {
shun_iwasawa a35b8f
    PIXEL* outPix     = outTileRas->pixels(out_j);
shun_iwasawa a35b8f
    A_PIXEL* alphaPix = alphaRas->pixels(j);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    alphaPix += margin.x;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    int i = margin.x;
shun_iwasawa a35b8f
    for (int out_i = 0; out_i < outTileRas->getLx(); i++, out_i++) {
shun_iwasawa a35b8f
      // If the layer pixel is transparent, keep the result pizel as-is.
shun_iwasawa a35b8f
      float alpha = (float)alphaPix->value / (float)PIXEL::maxChannelValue;
shun_iwasawa a35b8f
      if (alpha == 0.0f) {
shun_iwasawa a35b8f
        alphaPix++;
shun_iwasawa a35b8f
        outPix++;
shun_iwasawa a35b8f
        continue;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
      // Composite the upper layer exposure with the bottom layers. Then,
shun_iwasawa a35b8f
      // convert the exposure to RGB values.
shun_iwasawa 3f4710
      typename PIXEL::Channel dnVal =
shun_iwasawa 3f4710
          (m_channel == Red) ? outPix->r
shun_iwasawa 3f4710
                             : (m_channel == Green) ? outPix->g : outPix->b;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
      float exposure;
shun_iwasawa a35b8f
      double val;
shun_iwasawa a35b8f
      if (alpha == 1.0 || dnVal == 0.0) {
shun_iwasawa a35b8f
        exposure = (m_kissfft_comp_in[getCoord(i, j, dim.lx, dim.ly)].r /
shun_iwasawa a35b8f
                    (dim.lx * dim.ly));
shun_iwasawa a35b8f
        val = exposureToValue(exposure) * (float)PIXEL::maxChannelValue + 0.5f;
shun_iwasawa a35b8f
      } else {
shun_iwasawa a35b8f
        exposure =
shun_iwasawa a35b8f
            (m_kissfft_comp_in[getCoord(i, j, dim.lx, dim.ly)].r /
shun_iwasawa a35b8f
             (dim.lx * dim.ly)) +
shun_iwasawa a35b8f
            valueToExposure((float)dnVal / (float)PIXEL::maxChannelValue) *
shun_iwasawa a35b8f
                (1 - alpha);
shun_iwasawa a35b8f
        val = exposureToValue(exposure) * (float)PIXEL::maxChannelValue + 0.5f;
shun_iwasawa a35b8f
        // not used for now
shun_iwasawa a35b8f
        if (m_doLightenComp) val = std::max(val, (double)dnVal);
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
      // clamp
shun_iwasawa a35b8f
      if (val < 0.0)
shun_iwasawa a35b8f
        val = 0.0;
shun_iwasawa a35b8f
      else if (val > (float)PIXEL::maxChannelValue)
shun_iwasawa a35b8f
        val = (float)PIXEL::maxChannelValue;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
      switch (m_channel) {
shun_iwasawa a35b8f
      case Red:
shun_iwasawa 3f4710
        outPix->r = (typename PIXEL::Channel)val;
shun_iwasawa a35b8f
        //"over" composite the alpha channel here
shun_iwasawa a35b8f
        if (outPix->m != A_PIXEL::maxChannelValue) {
shun_iwasawa a35b8f
          if (alphaPix->value == A_PIXEL::maxChannelValue)
shun_iwasawa a35b8f
            outPix->m = A_PIXEL::maxChannelValue;
shun_iwasawa a35b8f
          else
shun_iwasawa 3f4710
            outPix->m =
shun_iwasawa 3f4710
                alphaPix->value +
shun_iwasawa 3f4710
                (typename A_PIXEL::Channel)(
shun_iwasawa 3f4710
                    (float)outPix->m *
shun_iwasawa 3f4710
                    (float)(A_PIXEL::maxChannelValue - alphaPix->value) /
shun_iwasawa 3f4710
                    (float)A_PIXEL::maxChannelValue);
shun_iwasawa a35b8f
        }
shun_iwasawa a35b8f
        break;
shun_iwasawa a35b8f
      case Green:
shun_iwasawa 3f4710
        outPix->g = (typename PIXEL::Channel)val;
shun_iwasawa a35b8f
        break;
shun_iwasawa a35b8f
      case Blue:
shun_iwasawa 3f4710
        outPix->b = (typename PIXEL::Channel)val;
shun_iwasawa a35b8f
        break;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
      alphaPix++;
shun_iwasawa a35b8f
      outPix++;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//------------------------------------------------------------
shun_iwasawa a35b8f
shun_iwasawa a35b8f
void MyThread::run() {
shun_iwasawa a35b8f
  // get the source image size
shun_iwasawa a35b8f
  TDimensionI dim = m_layerTileRas->getSize();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  int2 margin = {(dim.lx - m_outTileRas->getSize().lx) / 2,
shun_iwasawa a35b8f
                 (dim.ly - m_outTileRas->getSize().ly) / 2};
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // initialize
shun_iwasawa a35b8f
  for (int i = 0; i < dim.lx * dim.ly; i++) {
shun_iwasawa a35b8f
    m_kissfft_comp_in[i].r = 0.0;  // real part
shun_iwasawa a35b8f
    m_kissfft_comp_in[i].i = 0.0;  // imaginary part
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  TRaster32P ras32 = (TRaster32P)m_layerTileRas;
shun_iwasawa a35b8f
  TRaster64P ras64 = (TRaster64P)m_layerTileRas;
shun_iwasawa a35b8f
  // Prepare data for FFT.
shun_iwasawa a35b8f
  // Convert the RGB values to the exposure, then multiply it by the alpha
shun_iwasawa a35b8f
  // channel value
shun_iwasawa a35b8f
  {
shun_iwasawa a35b8f
    lock.lockForRead();
shun_iwasawa a35b8f
    if (ras32)
shun_iwasawa a35b8f
      setLayerRaster<traster32p, tpixel32="">(ras32, m_kissfft_comp_in, dim);</traster32p,>
shun_iwasawa a35b8f
    else if (ras64)
shun_iwasawa a35b8f
      setLayerRaster<traster64p, tpixel64="">(ras64, m_kissfft_comp_in, dim);</traster64p,>
shun_iwasawa a35b8f
    else {
shun_iwasawa a35b8f
      lock.unlock();
shun_iwasawa a35b8f
      return;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    lock.unlock();
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (checkTerminationAndCleanupThread()) return;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  kiss_fftnd(m_kissfft_plan_fwd, m_kissfft_comp_in, m_kissfft_comp_out);
shun_iwasawa a35b8f
  kiss_fft_free(m_kissfft_plan_fwd);  // we don't need this plan anymore
shun_iwasawa a35b8f
  m_kissfft_plan_fwd = NULL;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (checkTerminationAndCleanupThread()) return;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Filtering. Multiply by the iris FFT data
shun_iwasawa a35b8f
  {
shun_iwasawa a35b8f
    for (int i = 0; i < dim.lx * dim.ly; i++) {
shun_iwasawa a35b8f
      float re, im;
shun_iwasawa a35b8f
      re = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].r -
shun_iwasawa a35b8f
           m_kissfft_comp_out[i].i * m_kissfft_comp_iris[i].i;
shun_iwasawa a35b8f
      im = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].i +
shun_iwasawa a35b8f
           m_kissfft_comp_iris[i].r * m_kissfft_comp_out[i].i;
shun_iwasawa a35b8f
      m_kissfft_comp_out[i].r = re;
shun_iwasawa a35b8f
      m_kissfft_comp_out[i].i = im;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (checkTerminationAndCleanupThread()) return;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  kiss_fftnd(m_kissfft_plan_bkwd, m_kissfft_comp_out,
shun_iwasawa a35b8f
             m_kissfft_comp_in);       // Backward FFT
shun_iwasawa a35b8f
  kiss_fft_free(m_kissfft_plan_bkwd);  // we don't need this plan anymore
shun_iwasawa a35b8f
  m_kissfft_plan_bkwd = NULL;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // In the backward FFT above, "m_kissfft_comp_out" is used as input and
shun_iwasawa a35b8f
  // "m_kissfft_comp_in" as output.
shun_iwasawa a35b8f
  // So we don't need "m_kissfft_comp_out" anymore.
shun_iwasawa a35b8f
  m_kissfft_comp_out_ras->unlock();
shun_iwasawa a35b8f
  m_kissfft_comp_out = 0;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (checkTerminationAndCleanupThread()) return;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  {
shun_iwasawa a35b8f
    QMutexLocker locker(&mutex);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    TRaster32P ras32 = (TRaster32P)m_layerTileRas;
shun_iwasawa a35b8f
    TRaster64P ras64 = (TRaster64P)m_layerTileRas;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    if (ras32) {
shun_iwasawa a35b8f
      compositLayerToTile<traster32p, tpixel32,="" tpixelgr8="" trastergr8p,="">(</traster32p,>
shun_iwasawa a35b8f
          ras32, (TRaster32P)m_outTileRas, (TRasterGR8P)m_tmpAlphaRas, dim,
shun_iwasawa a35b8f
          margin);
shun_iwasawa a35b8f
    } else if (ras64) {
shun_iwasawa a35b8f
      compositLayerToTile<traster64p, tpixel64,="" tpixelgr16="" trastergr16p,="">(</traster64p,>
shun_iwasawa a35b8f
          ras64, (TRaster64P)m_outTileRas, (TRasterGR16P)m_tmpAlphaRas, dim,
shun_iwasawa a35b8f
          margin);
shun_iwasawa a35b8f
    } else {
shun_iwasawa a35b8f
      lock.unlock();
shun_iwasawa a35b8f
      return;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Now we don't need "m_kissfft_comp_in" anymore.
shun_iwasawa a35b8f
  m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
  m_kissfft_comp_in = 0;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  m_finished = true;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// RGB value <--> Exposure
shun_iwasawa a35b8f
inline float MyThread::valueToExposure(float value) {
shun_iwasawa a35b8f
  float logVal = (value - 0.5) / m_filmGamma;
shun_iwasawa a35b8f
  return pow(10, logVal);
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
inline float MyThread::exposureToValue(float exposure) {
shun_iwasawa a35b8f
  return log10(exposure) * m_filmGamma + 0.5;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// Release the raster memory and FFT plans on cancel rendering.
shun_iwasawa a35b8f
bool MyThread::checkTerminationAndCleanupThread() {
shun_iwasawa a35b8f
  if (!m_isTerminated) return false;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (m_kissfft_comp_in) m_kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
  if (m_kissfft_comp_out) m_kissfft_comp_out_ras->unlock();
shun_iwasawa a35b8f
  if (m_kissfft_plan_fwd) kiss_fft_free(m_kissfft_plan_fwd);
shun_iwasawa a35b8f
  if (m_kissfft_plan_bkwd) kiss_fft_free(m_kissfft_plan_bkwd);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  m_finished = true;
shun_iwasawa a35b8f
  return true;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//--------------------------------------------
shun_iwasawa a35b8f
// Iwa_BokehFx
shun_iwasawa a35b8f
//--------------------------------------------
shun_iwasawa a35b8f
shun_iwasawa a35b8f
Iwa_BokehFx::Iwa_BokehFx()
shun_iwasawa a35b8f
    : m_onFocusDistance(0.5), m_bokehAmount(30.0), m_hardness(0.3) {
shun_iwasawa a35b8f
  // Bind the common parameters
shun_iwasawa a35b8f
  addInputPort("Iris", m_iris);
shun_iwasawa a35b8f
  bindParam(this, "on_focus_distance", m_onFocusDistance, false);
shun_iwasawa a35b8f
  bindParam(this, "bokeh_amount", m_bokehAmount, false);
shun_iwasawa a35b8f
  bindParam(this, "hardness", m_hardness, false);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Set the ranges of common parameters
shun_iwasawa a35b8f
  m_onFocusDistance->setValueRange(0, 1);
shun_iwasawa a35b8f
  m_bokehAmount->setValueRange(0, 300);
shun_iwasawa a35b8f
  m_bokehAmount->setMeasureName("fxLength");
shun_iwasawa a35b8f
  m_hardness->setValueRange(0.05, 3.0);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Bind the layer parameters
shun_iwasawa a35b8f
  for (int layer = 0; layer < LAYER_NUM; layer++) {
shun_iwasawa a35b8f
    m_layerParams[layer].m_premultiply     = TBoolParamP(false);
shun_iwasawa a35b8f
    m_layerParams[layer].m_distance        = TDoubleParamP(0.5);
shun_iwasawa a35b8f
    m_layerParams[layer].m_bokehAdjustment = TDoubleParamP(1);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    std::string str = QString("Source%1").arg(layer + 1).toStdString();
shun_iwasawa a35b8f
    addInputPort(str, m_layerParams[layer].m_source);
shun_iwasawa a35b8f
    bindParam(this, QString("premultiply%1").arg(layer + 1).toStdString(),
shun_iwasawa a35b8f
              m_layerParams[layer].m_premultiply, false);
shun_iwasawa a35b8f
    bindParam(this, QString("distance%1").arg(layer + 1).toStdString(),
shun_iwasawa a35b8f
              m_layerParams[layer].m_distance, false);
shun_iwasawa a35b8f
    bindParam(this, QString("bokeh_adjustment%1").arg(layer + 1).toStdString(),
shun_iwasawa a35b8f
              m_layerParams[layer].m_bokehAdjustment, false);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    m_layerParams[layer].m_distance->setValueRange(0, 1);
shun_iwasawa a35b8f
    m_layerParams[layer].m_bokehAdjustment->setValueRange(0, 2);
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
void Iwa_BokehFx::doCompute(TTile& tile, double frame,
shun_iwasawa a35b8f
                            const TRenderSettings& settings) {
shun_iwasawa a35b8f
  // If the iris is not connected, then do nothing
shun_iwasawa a35b8f
  if (!m_iris.isConnected()) {
shun_iwasawa a35b8f
    tile.getRaster()->clear();
shun_iwasawa a35b8f
    return;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
  // If none of the source ports is connected, then do nothing
shun_iwasawa a35b8f
  bool sourceIsConnected = false;
shun_iwasawa a35b8f
  for (int i = 0; i < LAYER_NUM; i++) {
shun_iwasawa a35b8f
    if (m_layerParams[i].m_source.isConnected()) {
shun_iwasawa a35b8f
      sourceIsConnected = true;
shun_iwasawa a35b8f
      break;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
  if (!sourceIsConnected) {
shun_iwasawa a35b8f
    tile.getRaster()->clear();
shun_iwasawa a35b8f
    return;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Sort source layers by distance
shun_iwasawa a35b8f
  QList<int> sourceIndices = getSortedSourceIndices(frame);</int>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
shun_iwasawa a35b8f
  float bokehPixelAmount = getBokehPixelAmount(frame, settings.m_affine);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Compute the bokeh size for each layer. The source tile will be enlarged by
shun_iwasawa a35b8f
  // the largest size of them.
shun_iwasawa a35b8f
  float maxIrisSize;
shun_iwasawa a35b8f
  QVector<float> irisSizes =</float>
shun_iwasawa a35b8f
      getIrisSizes(frame, sourceIndices, bokehPixelAmount, maxIrisSize);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  int margin = tceil(maxIrisSize / 2.0);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Range of computation
shun_iwasawa a35b8f
  TRectD _rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
shun_iwasawa a35b8f
                                          tile.getRaster()->getLy()));
shun_iwasawa a35b8f
  _rectOut = _rectOut.enlarge(static_cast<double>(margin));</double>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  TDimensionI dimOut(static_cast<int>(_rectOut.getLx() + 0.5),</int>
shun_iwasawa a35b8f
                     static_cast<int>(_rectOut.getLy() + 0.5));</int>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Enlarge the size to the "fast size" for kissfft which has no factors other
shun_iwasawa a35b8f
  // than 2,3, or 5.
shun_iwasawa a35b8f
  if (dimOut.lx < 10000 && dimOut.ly < 10000) {
shun_iwasawa a35b8f
    int new_x = kiss_fft_next_fast_size(dimOut.lx);
shun_iwasawa a35b8f
    int new_y = kiss_fft_next_fast_size(dimOut.ly);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    _rectOut = _rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0,</double>
shun_iwasawa a35b8f
                                static_cast<double>(new_y - dimOut.ly) / 2.0);</double>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    dimOut.lx = new_x;
shun_iwasawa a35b8f
    dimOut.ly = new_y;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  //----------------------------
shun_iwasawa a35b8f
  // Compute the input tiles first
shun_iwasawa a35b8f
  QMap<int, ttile*=""> sourceTiles;</int,>
shun_iwasawa a35b8f
  for (int i = 0; i < sourceIndices.size(); i++) {
shun_iwasawa a35b8f
    int index      = sourceIndices.at(i);
shun_iwasawa a35b8f
    float irisSize = irisSizes.at(i);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    TTile* layerTile = new TTile();
shun_iwasawa a35b8f
    // Layer to be composited as-is
shun_iwasawa a35b8f
    if (-1.0 <= irisSize && 1.0 >= irisSize) {
shun_iwasawa a35b8f
      m_layerParams[index].m_source->allocateAndCompute(
shun_iwasawa a35b8f
          *layerTile, tile.m_pos,
shun_iwasawa a35b8f
          TDimension(tile.getRaster()->getLx(), tile.getRaster()->getLy()),
shun_iwasawa a35b8f
          tile.getRaster(), frame, settings);
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    // Layer to be off-focused
shun_iwasawa a35b8f
    else {
shun_iwasawa a35b8f
      m_layerParams[index].m_source->allocateAndCompute(
shun_iwasawa a35b8f
          *layerTile, _rectOut.getP00(), dimOut, tile.getRaster(), frame,
shun_iwasawa a35b8f
          settings);
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    sourceTiles[index] = layerTile;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Get the original size of Iris image
shun_iwasawa a35b8f
  TRectD irisBBox;
shun_iwasawa a35b8f
  m_iris->getBBox(frame, irisBBox, settings);
shun_iwasawa a35b8f
  // Compute the iris tile.
shun_iwasawa a35b8f
  TTile irisTile;
shun_iwasawa a35b8f
  m_iris->allocateAndCompute(
shun_iwasawa a35b8f
      irisTile, irisBBox.getP00(),
shun_iwasawa a35b8f
      TDimension(static_cast<int>(irisBBox.getLx() + 0.5),</int>
shun_iwasawa a35b8f
                 static_cast<int>(irisBBox.getLy() + 0.5)),</int>
shun_iwasawa a35b8f
      tile.getRaster(), frame, settings);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // This fx is relatively heavy so the multi thread computation is introduced.
shun_iwasawa a35b8f
  // Lock the mutex here in order to prevent multiple rendering tasks run at the
shun_iwasawa a35b8f
  // same time.
shun_iwasawa a35b8f
  QMutexLocker fx_locker(&fx_mutex);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  kiss_fft_cpx* kissfft_comp_iris;
shun_iwasawa a35b8f
  // create the iris data for FFT (in the same size as the source tile)
shun_iwasawa a35b8f
  TRasterGR8P kissfft_comp_iris_ras(dimOut.lx * sizeof(kiss_fft_cpx),
shun_iwasawa a35b8f
                                    dimOut.ly);
shun_iwasawa a35b8f
  kissfft_comp_iris_ras->lock();
shun_iwasawa a35b8f
  kissfft_comp_iris = (kiss_fft_cpx*)kissfft_comp_iris_ras->getRawData();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // obtain the film gamma
shun_iwasawa a35b8f
  double filmGamma = m_hardness->getValue(frame);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // clear the raster memory
shun_iwasawa a35b8f
  tile.getRaster()->clear();
shun_iwasawa a35b8f
  TRaster32P raster32 = tile.getRaster();
shun_iwasawa a35b8f
  if (raster32)
shun_iwasawa a35b8f
    raster32->fill(TPixel32::Transparent);
shun_iwasawa a35b8f
  else {
shun_iwasawa a35b8f
    TRaster64P ras64 = tile.getRaster();
shun_iwasawa a35b8f
    if (ras64) ras64->fill(TPixel64::Transparent);
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // cancel check
shun_iwasawa a35b8f
  if (settings.m_isCanceled && *settings.m_isCanceled) {
shun_iwasawa a35b8f
    kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
    tile.getRaster()->clear();
shun_iwasawa a35b8f
    return;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Compute from from the most distant layer
shun_iwasawa a35b8f
  for (int i = 0; i < sourceIndices.size(); i++) {
shun_iwasawa a35b8f
    // cancel check
shun_iwasawa a35b8f
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun_iwasawa a35b8f
      kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
      tile.getRaster()->clear();
shun_iwasawa a35b8f
      return;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    int index = sourceIndices.at(i);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // The iris size of the current layer
shun_iwasawa a35b8f
    float irisSize = irisSizes.at(i);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // If the size of iris is less than 1 (i.e. the layer is at focal position),
shun_iwasawa a35b8f
    // composite the layer as-is.
shun_iwasawa a35b8f
    if (-1.0 <= irisSize && 1.0 >= irisSize) {
shun_iwasawa a35b8f
      //"Over" composite the layer to the output raster.
shun_iwasawa a35b8f
      TTile* layerTile = sourceTiles.value(index);
shun_iwasawa a35b8f
      compositLayerAsIs(tile, *layerTile, frame, settings, index);
shun_iwasawa a35b8f
      sourceTiles.remove(index);
shun_iwasawa a35b8f
      // Continue to the next layer
shun_iwasawa a35b8f
      continue;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    {
shun_iwasawa a35b8f
      // Create the Iris image for FFT
shun_iwasawa a35b8f
      kiss_fft_cpx* kissfft_comp_iris_before;
shun_iwasawa a35b8f
      TRasterGR8P kissfft_comp_iris_before_ras(dimOut.lx * sizeof(kiss_fft_cpx),
shun_iwasawa a35b8f
                                               dimOut.ly);
shun_iwasawa a35b8f
      kissfft_comp_iris_before_ras->lock();
shun_iwasawa a35b8f
      kissfft_comp_iris_before =
shun_iwasawa a35b8f
          (kiss_fft_cpx*)kissfft_comp_iris_before_ras->getRawData();
shun_iwasawa a35b8f
      // Resize / flip the iris image according to the size ratio.
shun_iwasawa a35b8f
      // Normalize the brightness of the iris image.
shun_iwasawa a35b8f
      // Enlarge the iris to the output size.
shun_iwasawa a35b8f
      convertIris(irisSize, kissfft_comp_iris_before, dimOut, irisBBox,
shun_iwasawa a35b8f
                  irisTile);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
      if (settings.m_isCanceled && *settings.m_isCanceled) {
shun_iwasawa a35b8f
        kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
        kissfft_comp_iris_before_ras->unlock();
shun_iwasawa a35b8f
        tile.getRaster()->clear();
shun_iwasawa a35b8f
        return;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
      // Create the FFT plan for the iris image.
shun_iwasawa a35b8f
      kiss_fftnd_cfg iris_kissfft_plan;
shun_iwasawa a35b8f
      while (1) {
shun_iwasawa a35b8f
        int dims[2]       = {dimOut.ly, dimOut.lx};
shun_iwasawa a35b8f
        int ndims         = 2;
shun_iwasawa a35b8f
        iris_kissfft_plan = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
shun_iwasawa a35b8f
        if (iris_kissfft_plan != NULL) break;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
      // Do FFT the iris image.
shun_iwasawa a35b8f
      kiss_fftnd(iris_kissfft_plan, kissfft_comp_iris_before,
shun_iwasawa a35b8f
                 kissfft_comp_iris);
shun_iwasawa a35b8f
      kiss_fft_free(iris_kissfft_plan);
shun_iwasawa a35b8f
      kissfft_comp_iris_before_ras->unlock();
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // Up to here, FFT-ed iris data is stored in kissfft_comp_iris
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // cancel check
shun_iwasawa a35b8f
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun_iwasawa a35b8f
      kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
      tile.getRaster()->clear();
shun_iwasawa a35b8f
      return;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    // Prepare the layer rasters
shun_iwasawa a35b8f
    TTile* layerTile = sourceTiles.value(index);
shun_iwasawa a35b8f
    // Unpremultiply the source if needed
shun_iwasawa a35b8f
    if (!m_layerParams[index].m_premultiply->getValue())
shun_iwasawa a35b8f
      TRop::depremultiply(layerTile->getRaster());
shun_iwasawa a35b8f
    // Create the raster memory for storing alpha channel
shun_iwasawa a35b8f
    TRasterP tmpAlphaRas;
shun_iwasawa a35b8f
    {
shun_iwasawa a35b8f
      TRaster32P ras32(tile.getRaster());
shun_iwasawa a35b8f
      TRaster64P ras64(tile.getRaster());
shun_iwasawa a35b8f
      if (ras32)
shun_iwasawa a35b8f
        tmpAlphaRas = TRasterGR8P(dimOut);
shun_iwasawa a35b8f
      else if (ras64)
shun_iwasawa a35b8f
        tmpAlphaRas = TRasterGR16P(dimOut);
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    tmpAlphaRas->lock();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // Do FFT the alpha channel.
shun_iwasawa a35b8f
    // Forward FFT -> Multiply by the iris data -> Backward FFT
shun_iwasawa a35b8f
    calcAlfaChannelBokeh(kissfft_comp_iris, *layerTile, tmpAlphaRas);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun_iwasawa a35b8f
      kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
      tile.getRaster()->clear();
shun_iwasawa a35b8f
      tmpAlphaRas->unlock();
shun_iwasawa a35b8f
      return;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // Create the threads for RGB channels
shun_iwasawa a35b8f
    MyThread threadR(MyThread::Red, layerTile->getRaster(), tile.getRaster(),
shun_iwasawa a35b8f
                     tmpAlphaRas, kissfft_comp_iris, filmGamma);
shun_iwasawa a35b8f
    MyThread threadG(MyThread::Green, layerTile->getRaster(), tile.getRaster(),
shun_iwasawa a35b8f
                     tmpAlphaRas, kissfft_comp_iris, filmGamma);
shun_iwasawa a35b8f
    MyThread threadB(MyThread::Blue, layerTile->getRaster(), tile.getRaster(),
shun_iwasawa a35b8f
                     tmpAlphaRas, kissfft_comp_iris, filmGamma);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // If you set this flag to true, the fx will be forced to compute in single
shun_iwasawa a35b8f
    // thread.
shun_iwasawa a35b8f
    // Under some specific condition (such as calling from single-threaded
shun_iwasawa a35b8f
    // tcomposer)
shun_iwasawa a35b8f
    // we may need to use this flag... For now, I'll keep this option unused.
shun_iwasawa a35b8f
    // TODO: investigate this.
shun_iwasawa a35b8f
    bool renderInSingleThread = false;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // Start the thread when the initialization is done.
shun_iwasawa a35b8f
    // Red channel
shun_iwasawa a35b8f
    int waitCount = 0;
shun_iwasawa a35b8f
    while (1) {
shun_iwasawa a35b8f
      // cancel & timeout check
shun_iwasawa a35b8f
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun_iwasawa a35b8f
          waitCount >= 20)  // 10 second timeout
shun_iwasawa a35b8f
      {
shun_iwasawa a35b8f
        kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
        tile.getRaster()->clear();
shun_iwasawa a35b8f
        tmpAlphaRas->unlock();
shun_iwasawa a35b8f
        return;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
      if (threadR.init()) {
shun_iwasawa a35b8f
        if (renderInSingleThread)
shun_iwasawa a35b8f
          threadR.run();
shun_iwasawa a35b8f
        else
shun_iwasawa a35b8f
          threadR.start();
shun_iwasawa a35b8f
        break;
shun_iwasawa a35b8f
      }
shun_iwasawa 3f4710
      QThread::msleep(500);
shun_iwasawa a35b8f
      waitCount++;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    // Green channel
shun_iwasawa a35b8f
    waitCount = 0;
shun_iwasawa a35b8f
    while (1) {
shun_iwasawa a35b8f
      // cancel & timeout check
shun_iwasawa a35b8f
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun_iwasawa a35b8f
          waitCount >= 20)  // 10 second timeout
shun_iwasawa a35b8f
      {
shun_iwasawa a35b8f
        if (!threadR.isFinished()) threadR.terminateThread();
shun_iwasawa a35b8f
        while (!threadR.isFinished()) {
shun_iwasawa a35b8f
        }
shun_iwasawa a35b8f
        kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
        tile.getRaster()->clear();
shun_iwasawa a35b8f
        tmpAlphaRas->unlock();
shun_iwasawa a35b8f
        return;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
      if (threadG.init()) {
shun_iwasawa a35b8f
        if (renderInSingleThread)
shun_iwasawa a35b8f
          threadG.run();
shun_iwasawa a35b8f
        else
shun_iwasawa a35b8f
          threadG.start();
shun_iwasawa a35b8f
        break;
shun_iwasawa a35b8f
      }
shun_iwasawa 3f4710
      QThread::msleep(500);
shun_iwasawa a35b8f
      waitCount++;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    // Blue channel
shun_iwasawa a35b8f
    waitCount = 0;
shun_iwasawa a35b8f
    while (1) {
shun_iwasawa a35b8f
      // cancel & timeout check
shun_iwasawa a35b8f
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun_iwasawa a35b8f
          waitCount >= 20)  // 10 second timeout
shun_iwasawa a35b8f
      {
shun_iwasawa a35b8f
        if (!threadR.isFinished()) threadR.terminateThread();
shun_iwasawa a35b8f
        if (!threadG.isFinished()) threadG.terminateThread();
shun_iwasawa a35b8f
        while (!threadR.isFinished() || !threadG.isFinished()) {
shun_iwasawa a35b8f
        }
shun_iwasawa a35b8f
        kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
        tile.getRaster()->clear();
shun_iwasawa a35b8f
        tmpAlphaRas->unlock();
shun_iwasawa a35b8f
        return;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
      if (threadB.init()) {
shun_iwasawa a35b8f
        if (renderInSingleThread)
shun_iwasawa a35b8f
          threadB.run();
shun_iwasawa a35b8f
        else
shun_iwasawa a35b8f
          threadB.start();
shun_iwasawa a35b8f
        break;
shun_iwasawa a35b8f
      }
shun_iwasawa 3f4710
      QThread::msleep(500);
shun_iwasawa a35b8f
      waitCount++;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    /*
shun_iwasawa a35b8f
    * What is done in the thread for each RGB channel:
shun_iwasawa a35b8f
    * - Convert channel value -> Exposure
shun_iwasawa a35b8f
    * - Multiply by alpha channel
shun_iwasawa a35b8f
    * - Forward FFT
shun_iwasawa a35b8f
    * - Multiply by the iris FFT data
shun_iwasawa a35b8f
    * - Backward FFT
shun_iwasawa a35b8f
    * - Convert Exposure -> channel value
shun_iwasawa a35b8f
    */
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    waitCount = 0;
shun_iwasawa a35b8f
    while (1) {
shun_iwasawa a35b8f
      // cancel & timeout check
shun_iwasawa a35b8f
      if ((settings.m_isCanceled && *settings.m_isCanceled) ||
shun_iwasawa a35b8f
          waitCount >= 2000)  // 100 second timeout
shun_iwasawa a35b8f
      {
shun_iwasawa a35b8f
        if (!threadR.isFinished()) threadR.terminateThread();
shun_iwasawa a35b8f
        if (!threadG.isFinished()) threadG.terminateThread();
shun_iwasawa a35b8f
        if (!threadB.isFinished()) threadB.terminateThread();
shun_iwasawa a35b8f
        while (!threadR.isFinished() || !threadG.isFinished() ||
shun_iwasawa a35b8f
               !threadB.isFinished()) {
shun_iwasawa a35b8f
        }
shun_iwasawa a35b8f
        kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
        tile.getRaster()->clear();
shun_iwasawa a35b8f
        tmpAlphaRas->unlock();
shun_iwasawa a35b8f
        return;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
      if (threadR.isFinished() && threadG.isFinished() && threadB.isFinished())
shun_iwasawa a35b8f
        break;
shun_iwasawa 3f4710
      QThread::msleep(50);
shun_iwasawa a35b8f
      waitCount++;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
    tmpAlphaRas->unlock();
shun_iwasawa a35b8f
    sourceTiles.remove(index);
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  kissfft_comp_iris_ras->unlock();
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
bool Iwa_BokehFx::doGetBBox(double frame, TRectD& bBox,
shun_iwasawa a35b8f
                            const TRenderSettings& info) {
shun_iwasawa a35b8f
  bBox = TConsts::infiniteRectD;
shun_iwasawa a35b8f
  return true;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
bool Iwa_BokehFx::canHandle(const TRenderSettings& info, double frame) {
shun_iwasawa a35b8f
  return false;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// Sort the layers by distances
shun_iwasawa a35b8f
QList<int> Iwa_BokehFx::getSortedSourceIndices(double frame) {</int>
shun_iwasawa a35b8f
  QList<qpair<int, float="">> usedSourceList;</qpair<int,>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Gather the source layers connected to the ports
shun_iwasawa a35b8f
  for (int i = 0; i < LAYER_NUM; i++) {
shun_iwasawa a35b8f
    if (m_layerParams[i].m_source.isConnected())
shun_iwasawa a35b8f
      usedSourceList.push_back(
shun_iwasawa a35b8f
          QPair<int, float="">(i, m_layerParams[i].m_distance->getValue(frame)));</int,>
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  if (usedSourceList.empty()) return QList<int>();</int>
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Sort the layers in descending distance order
shun_iwasawa a35b8f
  qSort(usedSourceList.begin(), usedSourceList.end(), isFurtherLayer);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  QList<int> indicesList;</int>
shun_iwasawa a35b8f
  for (int i = 0; i < usedSourceList.size(); i++) {
shun_iwasawa a35b8f
    indicesList.push_back(usedSourceList.at(i).first);
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  return indicesList;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// Get the pixel size of bokehAmount ( referenced ino_blur.cpp )
shun_iwasawa a35b8f
float Iwa_BokehFx::getBokehPixelAmount(const double frame,
shun_iwasawa a35b8f
                                       const TAffine affine) {
shun_iwasawa a35b8f
  /*--- Convert to vector --- */
shun_iwasawa a35b8f
  TPointD vect;
shun_iwasawa a35b8f
  vect.x = m_bokehAmount->getValue(frame);
shun_iwasawa a35b8f
  vect.y = 0.0;
shun_iwasawa a35b8f
  /*--- Apply geometrical transformation ---*/
shun-iwasawa 0e4a7a
  // For the following lines I referred to lines 586-592 of
shun-iwasawa 0e4a7a
  // sources/stdfx/motionblurfx.cpp
shun_iwasawa a35b8f
  TAffine aff(affine);
shun_iwasawa a35b8f
  aff.a13 = aff.a23 = 0; /* ignore translation */
shun_iwasawa a35b8f
  vect              = aff * vect;
shun_iwasawa a35b8f
  /*--- return the length of the vector ---*/
shun_iwasawa a35b8f
  return sqrt(vect.x * vect.x + vect.y * vect.y);
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// Compute the bokeh size for each layer. The source tile will be enlarged by
shun_iwasawa a35b8f
// the largest size of them.
shun_iwasawa a35b8f
QVector<float> Iwa_BokehFx::getIrisSizes(const double frame,</float>
shun_iwasawa a35b8f
                                         const QList<int> sourceIndices,</int>
shun_iwasawa a35b8f
                                         const float bokehPixelAmount,
shun_iwasawa a35b8f
                                         float& maxIrisSize) {
shun_iwasawa a35b8f
  float max = 0.0;
shun_iwasawa a35b8f
  QVector<float> irisSizes;</float>
shun_iwasawa a35b8f
  for (int s = 0; s < sourceIndices.size(); s++) {
shun_iwasawa a35b8f
    int index      = sourceIndices.at(s);
shun_iwasawa a35b8f
    float irisSize = (m_onFocusDistance->getValue(frame) -
shun_iwasawa a35b8f
                      m_layerParams[index].m_distance->getValue(frame)) *
shun_iwasawa a35b8f
                     bokehPixelAmount *
shun_iwasawa a35b8f
                     m_layerParams[index].m_bokehAdjustment->getValue(frame);
shun_iwasawa a35b8f
    irisSizes.push_back(irisSize);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // Update the maximum size
shun_iwasawa a35b8f
    if (max < fabs(irisSize)) max = fabs(irisSize);
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
  maxIrisSize = max;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  return irisSizes;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//"Over" composite the layer to the output raster.
shun_iwasawa a35b8f
void Iwa_BokehFx::compositLayerAsIs(TTile& tile, TTile& layerTile,
shun_iwasawa a35b8f
                                    const double frame,
shun_iwasawa a35b8f
                                    const TRenderSettings& settings,
shun_iwasawa a35b8f
                                    const int index) {
shun_iwasawa a35b8f
  // Premultiply the source if needed
shun_iwasawa a35b8f
  if (m_layerParams[index].m_premultiply->getValue())
shun_iwasawa a35b8f
    TRop::premultiply(layerTile.getRaster());
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  TRop::over(tile.getRaster(), layerTile.getRaster());
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//------------------------------------------------
shun_iwasawa a35b8f
// Resize / flip the iris image according to the size ratio.
shun_iwasawa a35b8f
// Normalize the brightness of the iris image.
shun_iwasawa a35b8f
// Enlarge the iris to the output size.
shun_iwasawa a35b8f
void Iwa_BokehFx::convertIris(const float irisSize,
shun_iwasawa a35b8f
                              kiss_fft_cpx* kissfft_comp_iris_before,
shun_iwasawa a35b8f
                              const TDimensionI& dimOut, const TRectD& irisBBox,
shun_iwasawa a35b8f
                              const TTile& irisTile) {
shun_iwasawa a35b8f
  // the original size of iris image
shun_iwasawa a35b8f
  double2 irisOrgSize = {irisBBox.getLx(), irisBBox.getLy()};
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Get the size ratio of iris based on width. The ratio can be negative value.
shun_iwasawa a35b8f
  double irisSizeResampleRatio = irisSize / irisOrgSize.x;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Create the raster for resized iris
shun-iwasawa 0e4a7a
  double2 resizedIrisSize = {std::abs(irisSizeResampleRatio) * irisOrgSize.x,
shun-iwasawa 0e4a7a
                             std::abs(irisSizeResampleRatio) * irisOrgSize.y};
shun_iwasawa a35b8f
  int2 filterSize = {tceil(resizedIrisSize.x), tceil(resizedIrisSize.y)};
shun_iwasawa a35b8f
  TPointD resizeOffset((double)filterSize.x - resizedIrisSize.x,
shun_iwasawa a35b8f
                       (double)filterSize.y - resizedIrisSize.y);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Add some adjustment in order to absorb the difference of the cases when the
shun_iwasawa a35b8f
  // iris size is odd and even numbers.
shun_iwasawa a35b8f
  bool isIrisOffset[2] = {false, false};
shun_iwasawa a35b8f
  // Try to set the center of the iris to the center of the screen
shun_iwasawa a35b8f
  if ((dimOut.lx - filterSize.x) % 2 == 1) {
shun_iwasawa a35b8f
    filterSize.x++;
shun_iwasawa a35b8f
    isIrisOffset[0] = true;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
  if ((dimOut.ly - filterSize.y) % 2 == 1) {
shun_iwasawa a35b8f
    filterSize.y++;
shun_iwasawa a35b8f
    isIrisOffset[1] = true;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Terminate if the filter size becomes bigger than the output size.
shun_iwasawa a35b8f
  if (filterSize.x > dimOut.lx || filterSize.y > dimOut.ly) {
shun_iwasawa a35b8f
    std::cout
shun_iwasawa a35b8f
        << "Error: The iris filter size becomes larger than the source size!"
shun_iwasawa a35b8f
        << std::endl;
shun_iwasawa a35b8f
    return;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  TRaster64P resizedIris(TDimension(filterSize.x, filterSize.y));
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Add some adjustment in order to absorb the 0.5 translation to be done in
shun_iwasawa a35b8f
  // resample()
shun_iwasawa a35b8f
  TAffine aff;
shun_iwasawa a35b8f
  TPointD affOffset((isIrisOffset[0]) ? 0.5 : 1.0,
shun_iwasawa a35b8f
                    (isIrisOffset[1]) ? 0.5 : 1.0);
shun_iwasawa a35b8f
  if (!isIrisOffset[0]) affOffset.x -= resizeOffset.x / 2;
shun_iwasawa a35b8f
  if (!isIrisOffset[1]) affOffset.y -= resizeOffset.y / 2;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  aff = TTranslation(resizedIris->getCenterD() + affOffset);
shun_iwasawa a35b8f
  aff *= TScale(irisSizeResampleRatio);
shun_iwasawa a35b8f
  aff *= TTranslation(-(irisTile.getRaster()->getCenterD() + affOffset));
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // resample the iris
shun_iwasawa a35b8f
  TRop::resample(resizedIris, irisTile.getRaster(), aff);
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // accumulated value
shun_iwasawa a35b8f
  float irisValAmount = 0.0;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  int iris_j = 0;
shun_iwasawa a35b8f
  // Initialize
shun_iwasawa a35b8f
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
shun_iwasawa a35b8f
    kissfft_comp_iris_before[i].r = 0.0;
shun_iwasawa a35b8f
    kissfft_comp_iris_before[i].i = 0.0;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
  for (int j = (dimOut.ly - filterSize.y) / 2; iris_j < filterSize.y;
shun_iwasawa a35b8f
       j++, iris_j++) {
shun_iwasawa a35b8f
    TPixel64* pix = resizedIris->pixels(iris_j);
shun_iwasawa a35b8f
    int iris_i    = 0;
shun_iwasawa a35b8f
    for (int i = (dimOut.lx - filterSize.x) / 2; iris_i < filterSize.x;
shun_iwasawa a35b8f
         i++, iris_i++) {
shun_iwasawa a35b8f
      // Value = 0.3R 0.59G 0.11B
shun_iwasawa a35b8f
      kissfft_comp_iris_before[j * dimOut.lx + i].r =
shun_iwasawa a35b8f
          ((float)pix->r * 0.3f + (float)pix->g * 0.59f +
shun_iwasawa a35b8f
           (float)pix->b * 0.11f) /
shun_iwasawa a35b8f
          (float)USHRT_MAX;
shun_iwasawa a35b8f
      irisValAmount += kissfft_comp_iris_before[j * dimOut.lx + i].r;
shun_iwasawa a35b8f
      pix++;
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Normalize value
shun_iwasawa a35b8f
  for (int i = 0; i < dimOut.lx * dimOut.ly; i++) {
shun_iwasawa a35b8f
    kissfft_comp_iris_before[i].r /= irisValAmount;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
// Do FFT the alpha channel.
shun_iwasawa a35b8f
// Forward FFT -> Multiply by the iris data -> Backward FFT
shun_iwasawa a35b8f
void Iwa_BokehFx::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris,
shun_iwasawa a35b8f
                                       TTile& layerTile, TRasterP tmpAlphaRas) {
shun_iwasawa a35b8f
  // Obtain the source size
shun_iwasawa a35b8f
  int lx, ly;
shun_iwasawa a35b8f
  lx = layerTile.getRaster()->getSize().lx;
shun_iwasawa a35b8f
  ly = layerTile.getRaster()->getSize().ly;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Allocate the FFT data
shun_iwasawa a35b8f
  kiss_fft_cpx *kissfft_comp_in, *kissfft_comp_out;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  TRasterGR8P kissfft_comp_in_ras(lx * sizeof(kiss_fft_cpx), ly);
shun_iwasawa a35b8f
  kissfft_comp_in_ras->lock();
shun_iwasawa a35b8f
  kissfft_comp_in = (kiss_fft_cpx*)kissfft_comp_in_ras->getRawData();
shun_iwasawa a35b8f
  TRasterGR8P kissfft_comp_out_ras(lx * sizeof(kiss_fft_cpx), ly);
shun_iwasawa a35b8f
  kissfft_comp_out_ras->lock();
shun_iwasawa a35b8f
  kissfft_comp_out = (kiss_fft_cpx*)kissfft_comp_out_ras->getRawData();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Initialize the FFT data
shun_iwasawa a35b8f
  for (int i = 0; i < lx * ly; i++) {
shun_iwasawa a35b8f
    kissfft_comp_in[i].r = 0.0;  // real part
shun_iwasawa a35b8f
    kissfft_comp_in[i].i = 0.0;  // imaginary part
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  TRaster32P ras32 = (TRaster32P)layerTile.getRaster();
shun_iwasawa a35b8f
  TRaster64P ras64 = (TRaster64P)layerTile.getRaster();
shun_iwasawa a35b8f
  if (ras32) {
shun_iwasawa a35b8f
    for (int j = 0; j < ly; j++) {
shun_iwasawa a35b8f
      TPixel32* pix = ras32->pixels(j);
shun_iwasawa a35b8f
      for (int i = 0; i < lx; i++) {
shun_iwasawa a35b8f
        kissfft_comp_in[j * lx + i].r = (float)pix->m / (float)UCHAR_MAX;
shun_iwasawa a35b8f
        pix++;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  } else if (ras64) {
shun_iwasawa a35b8f
    for (int j = 0; j < ly; j++) {
shun_iwasawa a35b8f
      TPixel64* pix = ras64->pixels(j);
shun_iwasawa a35b8f
      for (int i = 0; i < lx; i++) {
shun_iwasawa a35b8f
        kissfft_comp_in[j * lx + i].r = (float)pix->m / (float)USHRT_MAX;
shun_iwasawa a35b8f
        pix++;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  } else
shun_iwasawa a35b8f
    return;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  int dims[2]             = {ly, lx};
shun_iwasawa a35b8f
  int ndims               = 2;
shun_iwasawa a35b8f
  kiss_fftnd_cfg plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0);
shun_iwasawa a35b8f
  kiss_fftnd(plan_fwd, kissfft_comp_in, kissfft_comp_out);
shun_iwasawa a35b8f
  kiss_fft_free(plan_fwd);  // we don't need this plan anymore
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Filtering. Multiply by the iris FFT data
shun_iwasawa a35b8f
  for (int i = 0; i < lx * ly; i++) {
shun_iwasawa a35b8f
    float re, im;
shun_iwasawa a35b8f
    re = kissfft_comp_out[i].r * kissfft_comp_iris[i].r -
shun_iwasawa a35b8f
         kissfft_comp_out[i].i * kissfft_comp_iris[i].i;
shun_iwasawa a35b8f
    im = kissfft_comp_out[i].r * kissfft_comp_iris[i].i +
shun_iwasawa a35b8f
         kissfft_comp_iris[i].r * kissfft_comp_out[i].i;
shun_iwasawa a35b8f
    kissfft_comp_out[i].r = re;
shun_iwasawa a35b8f
    kissfft_comp_out[i].i = im;
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  kiss_fftnd_cfg plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0);
shun_iwasawa a35b8f
  kiss_fftnd(plan_bkwd, kissfft_comp_out, kissfft_comp_in);  // Backward FFT
shun_iwasawa a35b8f
  kiss_fft_free(plan_bkwd);  // we don't need this plan anymore
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // In the backward FFT above, "kissfft_comp_out" is used as input and
shun_iwasawa a35b8f
  // "kissfft_comp_in" as output.
shun_iwasawa a35b8f
  // So we don't need "kissfft_comp_out" anymore.
shun_iwasawa a35b8f
  kissfft_comp_out_ras->unlock();
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  // Store the result into the alpha channel of layer tile
shun_iwasawa a35b8f
  if (ras32) {
shun_iwasawa a35b8f
    TRasterGR8P alphaRas8(tmpAlphaRas);
shun_iwasawa a35b8f
    for (int j = 0; j < ly; j++) {
shun_iwasawa a35b8f
      TPixelGR8* pix = alphaRas8->pixels(j);
shun_iwasawa a35b8f
      for (int i = 0; i < lx; i++) {
shun_iwasawa a35b8f
        float val =
shun_iwasawa a35b8f
            kissfft_comp_in[getCoord(i, j, lx, ly)].r / (lx * ly) * 256.0;
shun_iwasawa a35b8f
        if (val < 0.0)
shun_iwasawa a35b8f
          val = 0.0;
shun_iwasawa a35b8f
        else if (val > 255.0)
shun_iwasawa a35b8f
          val = 255.0;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
        pix->value = (unsigned char)val;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
        pix++;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  } else if (ras64) {
shun_iwasawa a35b8f
    TRasterGR16P alphaRas16(tmpAlphaRas);
shun_iwasawa a35b8f
    for (int j = 0; j < ly; j++) {
shun_iwasawa a35b8f
      TPixelGR16* pix = alphaRas16->pixels(j);
shun_iwasawa a35b8f
      for (int i = 0; i < lx; i++) {
shun_iwasawa a35b8f
        float val =
shun_iwasawa a35b8f
            kissfft_comp_in[getCoord(i, j, lx, ly)].r / (lx * ly) * 65536.0;
shun_iwasawa a35b8f
        if (val < 0.0)
shun_iwasawa a35b8f
          val = 0.0;
shun_iwasawa a35b8f
        else if (val > 65535.0)
shun_iwasawa a35b8f
          val = 65535.0;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
        pix->value = (unsigned short)val;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
        pix++;
shun_iwasawa a35b8f
      }
shun_iwasawa a35b8f
    }
shun_iwasawa a35b8f
  } else
shun_iwasawa a35b8f
    return;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
  kissfft_comp_in_ras->unlock();
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun_iwasawa a35b8f
FX_PLUGIN_IDENTIFIER(Iwa_BokehFx, "iwa_BokehFx")