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 1b1839
QMutex fx_mutex;
shun_iwasawa a35b8f
shun-iwasawa 1b1839
bool isFurtherLayer(const QPair<int, double=""> val1,</int,>
shun-iwasawa 1b1839
                    const QPair<int, double=""> val2) {</int,>
shun-iwasawa 1b1839
  // if the layers are at the same depth, then put the layer with smaller index
shun-iwasawa 1b1839
  // above
shun-iwasawa 1b1839
  if (val1.second == val2.second) return val1.first > val2.first;
shun_iwasawa a35b8f
  return val1.second > val2.second;
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun-iwasawa 1b1839
template <typename t=""></typename>
shun-iwasawa 1b1839
TRasterGR8P allocateRasterAndLock(T** buf, TDimensionI dim) {
shun-iwasawa 1b1839
  TRasterGR8P ras(dim.lx * sizeof(T), dim.ly);
shun-iwasawa 1b1839
  ras->lock();
shun-iwasawa 1b1839
  *buf = (T*)ras->getRawData();
shun-iwasawa 1b1839
  return ras;
shun_iwasawa a35b8f
}
shun-iwasawa 1b1839
// release all registered raster memories and free all fft plans
shun-iwasawa 1b1839
void releaseAllRastersAndPlans(QList<trastergr8p>& rasterList,</trastergr8p>
shun-iwasawa 1b1839
                               QList<kiss_fftnd_cfg>& planList) {</kiss_fftnd_cfg>
shun-iwasawa 1b1839
  for (int r = 0; r < rasterList.size(); r++) rasterList.at(r)->unlock();
shun-iwasawa 1b1839
  for (int p = 0; p < planList.size(); p++) kiss_fft_free(planList.at(p));
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun-iwasawa 1b1839
};  // namespace
shun_iwasawa a35b8f
shun_iwasawa a35b8f
//--------------------------------------------
shun_iwasawa a35b8f
// Iwa_BokehFx
shun_iwasawa a35b8f
//--------------------------------------------
shun_iwasawa a35b8f
shun-iwasawa 1b1839
Iwa_BokehFx::Iwa_BokehFx() {
shun_iwasawa a35b8f
  // Bind the common parameters
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 481b59
  bindParam(this, "gamma", m_gamma, false);
shun-iwasawa 481b59
  bindParam(this, "gammaAdjust", m_gammaAdjust, false);
shun-iwasawa 481b59
  bindParam(this, "linearizeMode", m_linearizeMode, false);
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_distance        = TDoubleParamP(0.5);
shun-iwasawa 1b1839
    m_layerParams[layer].m_bokehAdjustment = TDoubleParamP(1.0);
shun-iwasawa 1b1839
    // The premultiply option is not displayed anymore for simplicity
shun-iwasawa 1b1839
    m_layerParams[layer].m_premultiply = TBoolParamP(false);
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("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 1b1839
    bindParam(this, QString("premultiply%1").arg(layer + 1).toStdString(),
shun-iwasawa 1b1839
              m_layerParams[layer].m_premultiply, false);
shun_iwasawa a35b8f
shun-iwasawa 1b1839
    m_layerParams[layer].m_distance->setValueRange(0.0, 1.0);
shun-iwasawa 1b1839
    m_layerParams[layer].m_bokehAdjustment->setValueRange(0.0, 2.0);
shun_iwasawa a35b8f
  }
shun-iwasawa 481b59
shun-iwasawa 481b59
  enableComputeInFloat(true);
shun-iwasawa 481b59
shun-iwasawa 481b59
  // Version 1: Exposure is computed by using Hardness
shun-iwasawa 481b59
  //            E = std::pow(10.0, (value - 0.5) / hardness)
shun-iwasawa 481b59
  // Version 2: Exposure can also be computed by using Gamma, for easier
shun-iwasawa 481b59
  // combination with the linear color space
shun-iwasawa 481b59
  //            E = std::pow(value, gamma)
shun-iwasawa 481b59
  // this must be called after binding the parameters (see onFxVersionSet())
shun-iwasawa 481b59
  // Version 3: Gamma is computed by rs.m_colorSpaceGamma + gammaAdjust
shun-iwasawa 481b59
  setFxVersion(3);
shun_iwasawa a35b8f
}
shun_iwasawa a35b8f
shun-iwasawa 481b59
//--------------------------------------------
shun-iwasawa 481b59
shun-iwasawa 481b59
void Iwa_BokehFx::onFxVersionSet() {
shun-iwasawa 481b59
  bool useGamma = getFxVersion() == 2;
shun-iwasawa 481b59
  if (getFxVersion() == 1) {
shun-iwasawa 481b59
    m_linearizeMode->setValue(Hardness);
shun-iwasawa 481b59
    setFxVersion(3);
shun-iwasawa 481b59
  } else if (getFxVersion() == 2) {
shun-iwasawa 481b59
    // Automatically update version
shun-iwasawa 481b59
    if (m_linearizeMode->getValue() == Hardness ||
shun-iwasawa 481b59
        (m_gamma->getKeyframeCount() == 0 &&
shun-iwasawa 481b59
         areAlmostEqual(m_gamma->getDefaultValue(), 2.2))) {
shun-iwasawa 481b59
      useGamma = false;
shun-iwasawa 481b59
      setFxVersion(3);
shun-iwasawa 481b59
    }
shun-iwasawa 481b59
  }
shun-iwasawa 481b59
  getParams()->getParamVar("gamma")->setIsHidden(!useGamma);
shun-iwasawa 481b59
  getParams()->getParamVar("gammaAdjust")->setIsHidden(useGamma);
shun-iwasawa 481b59
}
shun-iwasawa 481b59
shun-iwasawa 481b59
//--------------------------------------------
shun-iwasawa 481b59
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 1b1839
  double bokehPixelAmount = BokehUtils::getBokehPixelAmount(
shun-iwasawa 1b1839
      m_bokehAmount->getValue(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 1b1839
  double maxIrisSize;
shun-iwasawa 1b1839
  QMap<int, double=""> irisSizes =</int,>
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 e917fd
    // margin should be integer
shun-iwasawa e917fd
    while ((new_x - dimOut.lx) % 2 != 0)
shun-iwasawa e917fd
      new_x = kiss_fft_next_fast_size(new_x + 1);
shun-iwasawa e917fd
    while ((new_y - dimOut.ly) % 2 != 0)
shun-iwasawa e917fd
      new_y = kiss_fft_next_fast_size(new_y + 1);
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 1b1839
  for (auto index : sourceIndices) {
shun_iwasawa a35b8f
    TTile* layerTile = new TTile();
shun-iwasawa 1b1839
    m_layerParams[index].m_source->allocateAndCompute(
shun-iwasawa 481b59
        *layerTile, _rectOut.getP00(), dimOut, tile.getRaster(), frame,
shun-iwasawa 481b59
        settings);
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 481b59
  double masterGamma;
shun-iwasawa 481b59
  if (m_linearizeMode->getValue() == Hardness)
shun-iwasawa 481b59
    masterGamma = m_hardness->getValue(frame);
shun-iwasawa 481b59
  else {  // Gamma
shun-iwasawa 481b59
    if (getFxVersion() == 2)
shun-iwasawa 481b59
      masterGamma = m_gamma->getValue(frame);
shun-iwasawa 481b59
    else
shun-iwasawa 481b59
      masterGamma = std::max(
shun-iwasawa 481b59
          1., settings.m_colorSpaceGamma + m_gammaAdjust->getValue(frame));
shun-iwasawa 481b59
    if (tile.getRaster()->isLinear()) masterGamma /= settings.m_colorSpaceGamma;
shun-iwasawa 481b59
  }
shun_iwasawa a35b8f
shun-iwasawa 1b1839
  QMap<int, char*="" unsigned=""> ctrls;</int,>
shun_iwasawa a35b8f
shun-iwasawa 1b1839
  QList<layervalue> layerValues;</layervalue>
shun-iwasawa 1b1839
  for (auto index : sourceIndices) {
shun-iwasawa 1b1839
    LayerValue layerValue;
shun-iwasawa 481b59
    layerValue.sourceTile  = sourceTiles[index];
shun-iwasawa 481b59
    layerValue.premultiply = m_layerParams[index].m_premultiply->getValue();
shun-iwasawa 481b59
    layerValue.layerGamma  = masterGamma;
shun-iwasawa 481b59
    layerValue.depth_ref   = 0;
shun-iwasawa 481b59
    layerValue.irisSize    = irisSizes.value(index);
shun-iwasawa 481b59
    layerValue.distance    = m_layerParams[index].m_distance->getValue(frame);
shun-iwasawa 1b1839
    layerValue.bokehAdjustment =
shun-iwasawa 1b1839
        m_layerParams[index].m_bokehAdjustment->getValue(frame);
shun-iwasawa 1b1839
    layerValues.append(layerValue);
shun_iwasawa a35b8f
  }
shun_iwasawa a35b8f
shun-iwasawa 1b1839
  Iwa_BokehCommonFx::doFx(tile, frame, settings, bokehPixelAmount, margin,
shun-iwasawa 1b1839
                          dimOut, irisBBox, irisTile, layerValues, ctrls);
shun-iwasawa 1b1839
  qDeleteAll(sourceTiles);
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 1b1839
  QList<qpair<int, double="">> 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 1b1839
          QPair<int, double="">(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
Tact Yoshida b8554a
  std::sort(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
// Compute the bokeh size for each layer. The source tile will be enlarged by
shun_iwasawa a35b8f
// the largest size of them.
shun-iwasawa 1b1839
QMap<int, double=""> Iwa_BokehFx::getIrisSizes(const double frame,</int,>
shun-iwasawa 1b1839
                                            const QList<int> sourceIndices,</int>
shun-iwasawa 1b1839
                                            const double bokehPixelAmount,
shun-iwasawa 1b1839
                                            double& maxIrisSize) {
shun-iwasawa 1b1839
  double max = 0.0;
shun-iwasawa 1b1839
  QMap<int, double=""> irisSizes;</int,>
shun_iwasawa a35b8f
  for (int s = 0; s < sourceIndices.size(); s++) {
shun-iwasawa 1b1839
    int index       = sourceIndices.at(s);
shun-iwasawa 1b1839
    double irisSize = (m_onFocusDistance->getValue(frame) -
shun-iwasawa 1b1839
                       m_layerParams[index].m_distance->getValue(frame)) *
shun-iwasawa 1b1839
                      bokehPixelAmount *
shun-iwasawa 1b1839
                      m_layerParams[index].m_bokehAdjustment->getValue(frame);
shun-iwasawa 1b1839
    irisSizes[index] = irisSize;
shun_iwasawa a35b8f
shun_iwasawa a35b8f
    // Update the maximum size
shun-iwasawa 1b1839
    if (max < std::abs(irisSize)) max = std::abs(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
FX_PLUGIN_IDENTIFIER(Iwa_BokehFx, "iwa_BokehFx")