shun-iwasawa 1b1839
#include "iwa_bokeh_advancedfx.h"
shun-iwasawa 1b1839
shun-iwasawa 1b1839
#include "trop.h"
shun-iwasawa 1b1839
#include "trasterfx.h"
shun-iwasawa 1b1839
#include "trasterimage.h"
shun-iwasawa 1b1839
shun-iwasawa 1b1839
#include <qreadwritelock></qreadwritelock>
shun-iwasawa 1b1839
#include <qmutexlocker></qmutexlocker>
shun-iwasawa 1b1839
#include <qvector></qvector>
shun-iwasawa 1b1839
#include <qmap></qmap>
shun-iwasawa 1b1839
#include <qset></qset>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
namespace {
shun-iwasawa 1b1839
QReadWriteLock lock;
shun-iwasawa 1b1839
QMutex fx_mutex;
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 1b1839
  return val1.second > val2.second;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
template <typename t=""></typename>
shun-iwasawa 1b1839
TRasterGR8P allocateRasterAndLock(T** buf, TDimensionI dim) {
shun-iwasawa 1b1839
  TRasterGR8P ras(dim.lx * sizeof(T), dim.ly);
shun-iwasawa 1b1839
  ras->lock();
shun-iwasawa 1b1839
  *buf = (T*)ras->getRawData();
shun-iwasawa 1b1839
  return ras;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
// release all registered raster memories and free all fft plans
shun-iwasawa 1b1839
void releaseAllRastersAndPlans(QList<trastergr8p>& rasterList,</trastergr8p>
shun-iwasawa 1b1839
                               QList<kiss_fftnd_cfg>& planList) {</kiss_fftnd_cfg>
shun-iwasawa 1b1839
  for (int r = 0; r < rasterList.size(); r++) rasterList.at(r)->unlock();
shun-iwasawa 1b1839
  for (int p = 0; p < planList.size(); p++) kiss_fft_free(planList.at(p));
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
};  // namespace
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// sort source images
shun-iwasawa 1b1839
QList<int> Iwa_BokehAdvancedFx::getSortedSourceIndices(double frame) {</int>
shun-iwasawa 1b1839
  QList<qpair<int, double="">> usedSourceList;</qpair<int,>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // gather connected sources
shun-iwasawa 1b1839
  for (int i = 0; i < LAYER_NUM; i++) {
shun-iwasawa 1b1839
    if (m_layerParams[i].m_source.isConnected())
shun-iwasawa 1b1839
      usedSourceList.push_back(
shun-iwasawa 1b1839
          QPair<int, double="">(i, m_layerParams[i].m_distance->getValue(frame)));</int,>
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  if (usedSourceList.empty()) return QList<int>();</int>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // sort by distance
shun-iwasawa 1b1839
  std::sort(usedSourceList.begin(), usedSourceList.end(), isFurtherLayer);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  QList<int> indicesList;</int>
shun-iwasawa 1b1839
  for (int i = 0; i < usedSourceList.size(); i++) {
shun-iwasawa 1b1839
    indicesList.push_back(usedSourceList.at(i).first);
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  return indicesList;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
// Compute the bokeh size for each layer. The source tile will be enlarged by
shun-iwasawa 1b1839
// the largest size of them.
shun-iwasawa 1b1839
QMap<int, double=""> Iwa_BokehAdvancedFx::getIrisSizes(</int,>
shun-iwasawa 1b1839
    const double frame, const QList<int> sourceIndices,</int>
shun-iwasawa 1b1839
    const double bokehPixelAmount, double& maxIrisSize) {
shun-iwasawa 1b1839
  // create a list of available reference image ports
shun-iwasawa 1b1839
  QList<int> availableCtrPorts;</int>
shun-iwasawa 1b1839
  for (int i = 0; i < getInputPortCount(); ++i) {
shun-iwasawa 1b1839
    QString portName = QString::fromStdString(getInputPortName(i));
shun-iwasawa 1b1839
    if (portName.startsWith("Depth") && getInputPort(i)->isConnected())
shun-iwasawa 1b1839
      availableCtrPorts.push_back(portName.remove(0, 5).toInt());
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double focus = m_onFocusDistance->getValue(frame);
shun-iwasawa 1b1839
  double max   = 0.0;
shun-iwasawa 1b1839
  QMap<int, double=""> irisSizes;</int,>
shun-iwasawa 1b1839
  for (int s = 0; s < sourceIndices.size(); s++) {
shun-iwasawa 1b1839
    int index = sourceIndices.at(s);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    double layer  = m_layerParams[index].m_distance->getValue(frame);
shun-iwasawa 1b1839
    double adjust = m_layerParams[index].m_bokehAdjustment->getValue(frame);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    double irisSize;
shun-iwasawa 1b1839
    // In case there is no available reference image
shun-iwasawa 1b1839
    if (m_layerParams[index].m_depth_ref->getValue() == 0 ||
shun-iwasawa 1b1839
        !availableCtrPorts.contains(
shun-iwasawa 1b1839
            m_layerParams[index].m_depth_ref->getValue())) {
shun-iwasawa 1b1839
      irisSize = (focus - layer) * bokehPixelAmount * adjust;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
    // in case using the reference image
shun-iwasawa 1b1839
    else {
shun-iwasawa 1b1839
      double refRangeHalf =
shun-iwasawa 1b1839
          m_layerParams[index].m_depthRange->getValue(frame) * 0.5;
shun-iwasawa 1b1839
      double nearToFocus = focus - layer + refRangeHalf;
shun-iwasawa 1b1839
      double farToFocus  = focus - layer - refRangeHalf;
shun-iwasawa 1b1839
      // take further point from the focus distance
shun-iwasawa 1b1839
      if (std::abs(nearToFocus) > std::abs(farToFocus))
shun-iwasawa 1b1839
        irisSize = nearToFocus * bokehPixelAmount * adjust;
shun-iwasawa 1b1839
      else
shun-iwasawa 1b1839
        irisSize = farToFocus * bokehPixelAmount * adjust;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    irisSizes[index] = irisSize;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    // update the maximum
shun-iwasawa 1b1839
    if (max < std::abs(irisSize)) max = std::abs(irisSize);
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  maxIrisSize = max;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  return irisSizes;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//-----------------------------------------------------
shun-iwasawa 1b1839
// return true if the control image is available and used
shun-iwasawa 1b1839
bool Iwa_BokehAdvancedFx::portIsUsed(int portIndex) {
shun-iwasawa 1b1839
  for (int layer = 0; layer < LAYER_NUM; layer++) {
shun-iwasawa 1b1839
    if (m_layerParams[layer].m_source.isConnected() &&
shun-iwasawa 1b1839
        m_layerParams[layer].m_depth_ref->getValue() == portIndex)
shun-iwasawa 1b1839
      return true;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  return false;
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
Iwa_BokehAdvancedFx::Iwa_BokehAdvancedFx()
shun-iwasawa 1b1839
    : m_hardnessPerSource(false), m_control("Depth") {
shun-iwasawa 1b1839
  // Bind common parameters
shun-iwasawa 1b1839
  bindParam(this, "on_focus_distance", m_onFocusDistance, false);
shun-iwasawa 1b1839
  bindParam(this, "bokeh_amount", m_bokehAmount, false);
shun-iwasawa 1b1839
  bindParam(this, "masterHardness", m_hardness, false);
shun-iwasawa 1b1839
  bindParam(this, "hardnessPerSource", m_hardnessPerSource, false);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Bind layer parameters
shun-iwasawa 1b1839
  for (int layer = 0; layer < LAYER_NUM; layer++) {
shun-iwasawa 1b1839
    m_layerParams[layer].m_distance        = TDoubleParamP(0.5);
shun-iwasawa 1b1839
    m_layerParams[layer].m_bokehAdjustment = TDoubleParamP(1);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    m_layerParams[layer].m_hardness   = TDoubleParamP(0.3);
shun-iwasawa 1b1839
    m_layerParams[layer].m_depth_ref  = TIntParamP(0);
shun-iwasawa 1b1839
    m_layerParams[layer].m_depthRange = TDoubleParamP(1.0);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    std::string str = QString("Source%1").arg(layer + 1).toStdString();
shun-iwasawa 1b1839
    addInputPort(str, m_layerParams[layer].m_source);
shun-iwasawa 1b1839
    bindParam(this, QString("distance%1").arg(layer + 1).toStdString(),
shun-iwasawa 1b1839
              m_layerParams[layer].m_distance);
shun-iwasawa 1b1839
    bindParam(this, QString("bokeh_adjustment%1").arg(layer + 1).toStdString(),
shun-iwasawa 1b1839
              m_layerParams[layer].m_bokehAdjustment);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    bindParam(this, QString("hardness%1").arg(layer + 1).toStdString(),
shun-iwasawa 1b1839
              m_layerParams[layer].m_hardness);
shun-iwasawa 1b1839
    bindParam(this, QString("depth_ref%1").arg(layer + 1).toStdString(),
shun-iwasawa 1b1839
              m_layerParams[layer].m_depth_ref);
shun-iwasawa 1b1839
    bindParam(this, QString("depthRange%1").arg(layer + 1).toStdString(),
shun-iwasawa 1b1839
              m_layerParams[layer].m_depthRange);
shun-iwasawa 1b1839
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 1b1839
shun-iwasawa 1b1839
    m_layerParams[layer].m_hardness->setValueRange(0.05, 3.0);
shun-iwasawa 1b1839
    m_layerParams[layer].m_depthRange->setValueRange(0.0, 1.0);
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  addInputPort("Depth1", new TRasterFxPort, 0);
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
void Iwa_BokehAdvancedFx::doCompute(TTile& tile, double frame,
shun-iwasawa 1b1839
                                    const TRenderSettings& settings) {
shun-iwasawa 1b1839
  // If the iris is not connected, then do nothing
shun-iwasawa 1b1839
  if (!m_iris.isConnected()) {
shun-iwasawa 1b1839
    tile.getRaster()->clear();
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  // If none of the source ports is connected, then do nothing
shun-iwasawa 1b1839
  bool sourceIsConnected = false;
shun-iwasawa 1b1839
  for (int i = 0; i < LAYER_NUM; i++) {
shun-iwasawa 1b1839
    if (m_layerParams[i].m_source.isConnected()) {
shun-iwasawa 1b1839
      sourceIsConnected = true;
shun-iwasawa 1b1839
      break;
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
  if (!sourceIsConnected) {
shun-iwasawa 1b1839
    tile.getRaster()->clear();
shun-iwasawa 1b1839
    return;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Sort source layers by distance
shun-iwasawa 1b1839
  QList<int> sourceIndices = getSortedSourceIndices(frame);</int>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // 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 1b1839
shun-iwasawa 1b1839
  // Compute the bokeh size for each layer. The source tile will be enlarged by
shun-iwasawa 1b1839
  // the largest size of them.
shun-iwasawa 1b1839
  double maxIrisSize;
shun-iwasawa 1b1839
  QMap<int, double=""> irisSizes =</int,>
shun-iwasawa 1b1839
      getIrisSizes(frame, sourceIndices, bokehPixelAmount, maxIrisSize);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  int margin = (int)std::ceil(maxIrisSize * 0.5);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TRectD _rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(),
shun-iwasawa 1b1839
                                          tile.getRaster()->getLy()));
shun-iwasawa 1b1839
  _rectOut = _rectOut.enlarge(static_cast<double>(margin));</double>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  TDimensionI dimOut(static_cast<int>(_rectOut.getLx() + 0.5),</int>
shun-iwasawa 1b1839
                     static_cast<int>(_rectOut.getLy() + 0.5));</int>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // Enlarge the size to the "fast size" for kissfft which has no factors other
shun-iwasawa 1b1839
  // than 2,3, or 5.
shun-iwasawa 1b1839
  if (dimOut.lx < 10000 && dimOut.ly < 10000) {
shun-iwasawa 1b1839
    int new_x = kiss_fft_next_fast_size(dimOut.lx);
shun-iwasawa 1b1839
    int new_y = kiss_fft_next_fast_size(dimOut.ly);
shun-iwasawa 1b1839
    // margin should be integer
shun-iwasawa 1b1839
    while ((new_x - dimOut.lx) % 2 != 0)
shun-iwasawa 1b1839
      new_x = kiss_fft_next_fast_size(new_x + 1);
shun-iwasawa 1b1839
    while ((new_y - dimOut.ly) % 2 != 0)
shun-iwasawa 1b1839
      new_y = kiss_fft_next_fast_size(new_y + 1);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    _rectOut = _rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0,</double>
shun-iwasawa 1b1839
                                static_cast<double>(new_y - dimOut.ly) / 2.0);</double>
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    dimOut.lx = new_x;
shun-iwasawa 1b1839
    dimOut.ly = new_y;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // compute the input tiles
shun-iwasawa 1b1839
  QMap<int, ttile*=""> sourceTiles;</int,>
shun-iwasawa 32ddcd
  TRenderSettings infoOnInput(settings);
shun-iwasawa 32ddcd
  infoOnInput.m_bpp = 64;
shun-iwasawa 1b1839
  for (auto index : sourceIndices) {
shun-iwasawa 1b1839
    TTile* layerTile = new TTile();
shun-iwasawa 1b1839
    m_layerParams[index].m_source->allocateAndCompute(
shun-iwasawa 32ddcd
        *layerTile, _rectOut.getP00(), dimOut, 0, frame, infoOnInput);
shun-iwasawa 1b1839
    sourceTiles[index] = layerTile;
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // obtain pixel size of original iris image
shun-iwasawa 1b1839
  TRectD irisBBox;
shun-iwasawa 1b1839
  m_iris->getBBox(frame, irisBBox, settings);
shun-iwasawa 1b1839
  // compute the iris tile
shun-iwasawa 1b1839
  TTile irisTile;
shun-iwasawa 1b1839
  m_iris->allocateAndCompute(
shun-iwasawa 1b1839
      irisTile, irisBBox.getP00(),
shun-iwasawa 1b1839
      TDimension(static_cast<int>(irisBBox.getLx() + 0.5),</int>
shun-iwasawa 1b1839
                 static_cast<int>(irisBBox.getLy() + 0.5)),</int>
shun-iwasawa 1b1839
      tile.getRaster(), frame, settings);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // compute the reference image
shun-iwasawa 1b1839
  std::vector<trastergr8p> ctrl_rasters;  // to be stored in uchar</trastergr8p>
shun-iwasawa 1b1839
  QMap<int, char*="" unsigned=""></int,>
shun-iwasawa 1b1839
      ctrls;  // container of [port number, reference image buffer in uchar]
shun-iwasawa 1b1839
  for (int i = 0; i < getInputPortCount(); ++i) {
shun-iwasawa 1b1839
    QString portName = QString::fromStdString(getInputPortName(i));
shun-iwasawa 1b1839
    if (portName.startsWith("Depth") && getInputPort(i)->isConnected()) {
shun-iwasawa 1b1839
      int portIndex = portName.remove(0, 5).toInt();
shun-iwasawa 1b1839
      if (portIsUsed(portIndex)) {
shun-iwasawa 1b1839
        TTile tmpTile;
shun-iwasawa 1b1839
        TRasterFxPort* tmpCtrl = dynamic_cast<trasterfxport*>(getInputPort(i));</trasterfxport*>
shun-iwasawa 1b1839
        (*tmpCtrl)->allocateAndCompute(tmpTile, _rectOut.getP00(), dimOut,
shun-iwasawa 1b1839
                                       tile.getRaster(), frame, settings);
shun-iwasawa 1b1839
        TRasterGR8P ctrlRas(dimOut.lx, dimOut.ly);
shun-iwasawa 1b1839
        ctrlRas->lock();
shun-iwasawa 1b1839
        unsigned char* ctrl_mem = (unsigned char*)ctrlRas->getRawData();
shun-iwasawa 1b1839
        TRaster32P ras32        = (TRaster32P)tmpTile.getRaster();
shun-iwasawa 1b1839
        TRaster64P ras64        = (TRaster64P)tmpTile.getRaster();
shun-iwasawa 1b1839
        lock.lockForRead();
shun-iwasawa 1b1839
        if (ras32)
shun-iwasawa 1b1839
          BokehUtils::setDepthRaster<traster32p, tpixel32="">(ras32, ctrl_mem,</traster32p,>
shun-iwasawa 1b1839
                                                           dimOut);
shun-iwasawa 1b1839
        else if (ras64)
shun-iwasawa 1b1839
          BokehUtils::setDepthRaster<traster64p, tpixel64="">(ras64, ctrl_mem,</traster64p,>
shun-iwasawa 1b1839
                                                           dimOut);
shun-iwasawa 1b1839
        lock.unlock();
shun-iwasawa 1b1839
        ctrl_rasters.push_back(ctrlRas);
shun-iwasawa 1b1839
        ctrls[portIndex] = ctrl_mem;
shun-iwasawa 1b1839
      }
shun-iwasawa 1b1839
    }
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  double masterHardness = m_hardness->getValue(frame);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  QList<layervalue> layerValues;</layervalue>
shun-iwasawa 1b1839
  for (auto index : sourceIndices) {
shun-iwasawa 1b1839
    LayerValue layerValue;
shun-iwasawa 1b1839
    layerValue.sourceTile = sourceTiles[index];
shun-iwasawa 1b1839
    layerValue.premultiply =
shun-iwasawa 1b1839
        false;  // assuming input images are always premultiplied
shun-iwasawa 1b1839
    layerValue.layerHardness =
shun-iwasawa 1b1839
        (m_hardnessPerSource->getValue())
shun-iwasawa 1b1839
            ? m_layerParams[index].m_hardness->getValue(frame)
shun-iwasawa 1b1839
            : masterHardness;
shun-iwasawa 1b1839
    layerValue.depth_ref = m_layerParams[index].m_depth_ref->getValue();
shun-iwasawa 1b1839
    layerValue.irisSize  = irisSizes.value(index);
shun-iwasawa 1b1839
    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
    layerValue.depthRange = m_layerParams[index].m_depthRange->getValue(frame);
shun-iwasawa 1b1839
    layerValue.distancePrecision = 10;
shun-iwasawa 1b1839
    layerValue.fillGap           = true;
shun-iwasawa 1b1839
    layerValue.doMedian          = false;
shun-iwasawa 1b1839
shun-iwasawa 1b1839
    layerValues.append(layerValue);
shun-iwasawa 1b1839
  }
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  Iwa_BokehCommonFx::doFx(tile, frame, settings, bokehPixelAmount, margin,
shun-iwasawa 1b1839
                          dimOut, irisBBox, irisTile, layerValues, ctrls);
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  // release control image buffers
shun-iwasawa 1b1839
  for (int r = 0; r < ctrl_rasters.size(); r++) ctrl_rasters[r]->unlock();
shun-iwasawa 1b1839
shun-iwasawa 1b1839
  qDeleteAll(sourceTiles);
shun-iwasawa 1b1839
}
shun-iwasawa 1b1839
shun-iwasawa 1b1839
//--------------------------------------------
shun-iwasawa 1b1839
FX_PLUGIN_IDENTIFIER(Iwa_BokehAdvancedFx, "iwa_BokehAdvancedFx")