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 1b1839
#include <qmap></qmap>
shun-iwasawa e2505a
#include <math.h></math.h>
shun-iwasawa e2505a
shun-iwasawa e2505a
namespace {
shun-iwasawa e2505a
QReadWriteLock lock;
shun-iwasawa 1b1839
QMutex fx_mutex;
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
// 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
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 1b1839
      double val = ((double)pix->r * 0.3 + (double)pix->g * 0.59 +
shun-iwasawa 1b1839
                    (double)pix->b * 0.11) /
shun-iwasawa 1b1839
                   (double)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      // convert to unsigned char
shun-iwasawa 1b1839
      (*depth_p) = (unsigned char)(val * (double)UCHAR_MAX + 0.5);
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 1b1839
      double val = (double)pix->value / (double)PIXEL::maxChannelValue;
shun-iwasawa e2505a
      // convert to unsigned char
shun-iwasawa 1b1839
      (*depth_p) = (unsigned char)(val * (double)UCHAR_MAX + 0.5);
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
  }
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
//--------------------------------------------
shun-iwasawa e2505a
shun-iwasawa e2505a
Iwa_BokehRefFx::Iwa_BokehRefFx()
shun-iwasawa 1b1839
    : m_distancePrecision(10), m_fillGap(true), m_doMedian(true) {
shun-iwasawa e2505a
  // Bind parameters
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 481b59
  bindParam(this, "gamma", m_gamma, false);
shun-iwasawa 481b59
  bindParam(this, "gammaAdjust", m_gammaAdjust, 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 481b59
  bindParam(this, "linearizeMode", m_linearizeMode, false);
shun-iwasawa e2505a
shun-iwasawa e2505a
  m_distancePrecision->setValueRange(3, 128);
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 481b59
}
shun-iwasawa 481b59
shun-iwasawa 481b59
//--------------------------------------------
shun-iwasawa 481b59
shun-iwasawa 481b59
void Iwa_BokehRefFx::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 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 1b1839
  double bokehPixelAmount = BokehUtils::getBokehPixelAmount(
shun-iwasawa 1b1839
      m_bokehAmount->getValue(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 1b1839
  double maxIrisSize =
shun-iwasawa e2505a
      bokehPixelAmount * std::max((1.0 - onFocusDistance), onFocusDistance);
shun-iwasawa e2505a
shun-iwasawa e2505a
  int margin =
shun-iwasawa 1b1839
      (maxIrisSize > 1.0f) ? (int)(std::ceil((maxIrisSize - 1.0) / 2.0)) : 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 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 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
  // source image buffer
shun-iwasawa 1b1839
  // double4* source_buff;
shun-iwasawa 1b1839
  // rasterList.append(allocateRasterAndLock<double4>(&source_buff, dimOut));</double4>
shun-iwasawa e2505a
shun-iwasawa 1b1839
  LayerValue layerValue;
shun-iwasawa 481b59
  ;
shun-iwasawa 1b1839
  // source tile is used only in this focus.
shun-iwasawa 1b1839
  // normalized source image data is stored in source_buff.
shun-iwasawa 1b1839
  layerValue.sourceTile = new TTile();
shun-iwasawa 1b1839
  m_source->allocateAndCompute(*layerValue.sourceTile, rectOut.getP00(), dimOut,
shun-iwasawa 481b59
                               tile.getRaster(), frame, settings);
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 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 481b59
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
  unsigned char* depth_buff;
shun-iwasawa 1b1839
  rasterList.append(allocateRasterAndLock<unsigned char="">(&depth_buff, dimOut));</unsigned>
shun-iwasawa 1b1839
  {
shun-iwasawa 1b1839
    TTile depthTile;
shun-iwasawa 1b1839
    m_depth->allocateAndCompute(depthTile, rectOut.getP00(), dimOut,
shun-iwasawa 1b1839
                                tile.getRaster(), frame, settings);
shun-iwasawa e2505a
shun-iwasawa e2505a
    // cancel check
shun-iwasawa e2505a
    if (settings.m_isCanceled && *settings.m_isCanceled) {
shun-iwasawa 1b1839
      releaseAllRasters(rasterList);
shun-iwasawa 1b1839
      tile.getRaster()->clear();
shun-iwasawa e2505a
      return;
shun-iwasawa e2505a
    }
shun-iwasawa e2505a
shun-iwasawa 1b1839
    // normalize brightness of the depth reference image to unsigned char
shun-iwasawa 1b1839
    // and store into depth_buff
shun-iwasawa 1b1839
    TRasterGR8P rasGR8   = (TRasterGR8P)depthTile.getRaster();
shun-iwasawa 1b1839
    TRasterGR16P rasGR16 = (TRasterGR16P)depthTile.getRaster();
shun-iwasawa 1b1839
    TRaster32P ras32     = (TRaster32P)depthTile.getRaster();
shun-iwasawa 1b1839
    TRaster64P ras64     = (TRaster64P)depthTile.getRaster();
shun-iwasawa 481b59
    TRasterFP rasF       = (TRasterFP)depthTile.getRaster();
shun-iwasawa 1b1839
    lock.lockForRead();
shun-iwasawa 1b1839
    if (rasGR8)
shun-iwasawa 1b1839
      setDepthRasterGray<trastergr8p, tpixelgr8="">(rasGR8, depth_buff, dimOut);</trastergr8p,>
shun-iwasawa 1b1839
    else if (rasGR16)
shun-iwasawa 1b1839
      setDepthRasterGray<trastergr16p, tpixelgr16="">(rasGR16, depth_buff, dimOut);</trastergr16p,>
shun-iwasawa 1b1839
    else if (ras32)
shun-iwasawa 1b1839
      BokehUtils::setDepthRaster<traster32p, tpixel32="">(ras32, depth_buff,</traster32p,>
shun-iwasawa 1b1839
                                                       dimOut);
shun-iwasawa 1b1839
    else if (ras64)
shun-iwasawa 1b1839
      BokehUtils::setDepthRaster<traster64p, tpixel64="">(ras64, depth_buff,</traster64p,>
shun-iwasawa 1b1839
                                                       dimOut);
shun-iwasawa 481b59
    else if (rasF)
shun-iwasawa 481b59
      BokehUtils::setDepthRaster<trasterfp, tpixelf="">(rasF, depth_buff, dimOut);</trasterfp,>
shun-iwasawa 1b1839
    lock.unlock();
shun-iwasawa e2505a
  }
shun-iwasawa 1b1839
  ctrls[1] = depth_buff;
shun-iwasawa e2505a
shun-iwasawa 8e3b15
  layerValue.premultiply       = false;
shun-iwasawa 481b59
  layerValue.layerGamma        = masterGamma;
shun-iwasawa 1b1839
  layerValue.depth_ref         = 1;
shun-iwasawa 1b1839
  layerValue.distance          = 0.5;
shun-iwasawa 1b1839
  layerValue.bokehAdjustment   = 1.0;
shun-iwasawa 1b1839
  layerValue.depthRange        = 1.0;
shun-iwasawa 1b1839
  layerValue.distancePrecision = m_distancePrecision->getValue();
shun-iwasawa 1b1839
  layerValue.fillGap           = m_fillGap->getValue();
shun-iwasawa 1b1839
  layerValue.doMedian          = m_doMedian->getValue();
shun-iwasawa 1b1839
  QList<layervalue> layerValues;</layervalue>
shun-iwasawa 1b1839
  layerValues.append(layerValue);
shun-iwasawa e2505a
shun-iwasawa 1b1839
  Iwa_BokehCommonFx::doFx(tile, frame, settings, bokehPixelAmount, margin,
shun-iwasawa 1b1839
                          dimOut, irisBBox, irisTile, layerValues, ctrls);
shun-iwasawa e2505a
shun-iwasawa 1b1839
  releaseAllRasters(rasterList);
shun-iwasawa 1b1839
  delete layerValue.sourceTile;
shun-iwasawa e2505a
}
shun-iwasawa e2505a
shun-iwasawa e2505a
FX_PLUGIN_IDENTIFIER(Iwa_BokehRefFx, "iwa_BokehRefFx")