| #include "iwa_bokehfx.h" |
| |
| #include "trop.h" |
| #include "tdoubleparam.h" |
| #include "trasterfx.h" |
| #include "trasterimage.h" |
| |
| #include "kiss_fft.h" |
| |
| #include <QPair> |
| #include <QVector> |
| #include <QReadWriteLock> |
| #include <QMutexLocker> |
| #include <QMap> |
| |
| namespace { |
| QReadWriteLock lock; |
| QMutex mutex, fx_mutex; |
| |
| bool isFurtherLayer(const QPair<int, float> val1, |
| const QPair<int, float> val2) { |
| return val1.second > val2.second; |
| } |
| |
| |
| inline int getCoord(int i, int j, int lx, int ly) { |
| int cx = i - lx / 2; |
| int cy = j - ly / 2; |
| |
| if (cx < 0) cx += lx; |
| if (cy < 0) cy += ly; |
| |
| return cy * lx + cx; |
| } |
| |
| |
| inline float valueToExposure(float value, float filmGamma) { |
| float logVal = (value - 0.5) / filmGamma; |
| return pow(10, logVal); |
| } |
| inline float exposureToValue(float exposure, float filmGamma) { |
| return log10(exposure) * filmGamma + 0.5; |
| } |
| }; |
| |
| |
| |
| |
| |
| MyThread::MyThread(Channel channel, TRasterP layerTileRas, TRasterP outTileRas, |
| TRasterP tmpAlphaRas, kiss_fft_cpx* kissfft_comp_iris, |
| float filmGamma, |
| bool doLightenComp) |
| : m_channel(channel), |
| m_layerTileRas(layerTileRas), |
| m_outTileRas(outTileRas), |
| m_tmpAlphaRas(tmpAlphaRas), |
| m_kissfft_comp_iris(kissfft_comp_iris), |
| m_filmGamma(filmGamma), |
| m_finished(false), |
| m_kissfft_comp_in(0), |
| m_kissfft_comp_out(0), |
| m_isTerminated(false), |
| m_doLightenComp(doLightenComp) |
| {} |
| |
| bool MyThread::init() { |
| |
| int lx, ly; |
| lx = m_layerTileRas->getSize().lx; |
| ly = m_layerTileRas->getSize().ly; |
| |
| |
| m_kissfft_comp_in_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly); |
| m_kissfft_comp_in_ras->lock(); |
| m_kissfft_comp_in = (kiss_fft_cpx*)m_kissfft_comp_in_ras->getRawData(); |
| |
| |
| if (m_kissfft_comp_in == 0) return false; |
| |
| |
| if (m_isTerminated) { |
| m_kissfft_comp_in_ras->unlock(); |
| return false; |
| } |
| |
| |
| m_kissfft_comp_out_ras = TRasterGR8P(lx * sizeof(kiss_fft_cpx), ly); |
| m_kissfft_comp_out_ras->lock(); |
| m_kissfft_comp_out = (kiss_fft_cpx*)m_kissfft_comp_out_ras->getRawData(); |
| |
| |
| if (m_kissfft_comp_out == 0) { |
| m_kissfft_comp_in_ras->unlock(); |
| m_kissfft_comp_in = 0; |
| return false; |
| } |
| |
| |
| if (m_isTerminated) { |
| m_kissfft_comp_in_ras->unlock(); |
| m_kissfft_comp_in = 0; |
| m_kissfft_comp_out_ras->unlock(); |
| m_kissfft_comp_out = 0; |
| return false; |
| } |
| |
| |
| int dims[2] = {ly, lx}; |
| int ndims = 2; |
| m_kissfft_plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0); |
| |
| if (m_kissfft_plan_fwd == NULL || m_isTerminated) { |
| m_kissfft_comp_in_ras->unlock(); |
| m_kissfft_comp_in = 0; |
| m_kissfft_comp_out_ras->unlock(); |
| m_kissfft_comp_out = 0; |
| return false; |
| } |
| |
| |
| m_kissfft_plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0); |
| |
| if (m_kissfft_plan_bkwd == NULL || m_isTerminated) { |
| m_kissfft_comp_in_ras->unlock(); |
| m_kissfft_comp_in = 0; |
| m_kissfft_comp_out_ras->unlock(); |
| m_kissfft_comp_out = 0; |
| kiss_fft_free(m_kissfft_plan_fwd); |
| m_kissfft_plan_fwd = NULL; |
| return false; |
| } |
| |
| |
| return true; |
| } |
| |
| |
| |
| |
| |
| |
| template <typename RASTER, typename PIXEL> |
| void MyThread::setLayerRaster(const RASTER srcRas, kiss_fft_cpx* dstMem, |
| TDimensionI dim) { |
| for (int j = 0; j < dim.ly; j++) { |
| PIXEL* pix = srcRas->pixels(j); |
| for (int i = 0; i < dim.lx; i++, pix++) { |
| if (pix->m != 0) { |
| float val = (m_channel == Red) |
| ? (float)pix->r |
| : (m_channel == Green) ? (float)pix->g : (float)pix->b; |
| |
| dstMem[j * dim.lx + i].r = |
| valueToExposure(val / (float)PIXEL::maxChannelValue) * |
| ((float)pix->m / (float)PIXEL::maxChannelValue); |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| template <typename RASTER, typename PIXEL, typename A_RASTER, typename A_PIXEL> |
| void MyThread::compositLayerToTile(const RASTER layerRas, |
| const RASTER outTileRas, |
| const A_RASTER alphaRas, TDimensionI dim, |
| int2 margin) { |
| int j = margin.y; |
| for (int out_j = 0; out_j < outTileRas->getLy(); j++, out_j++) { |
| PIXEL* outPix = outTileRas->pixels(out_j); |
| A_PIXEL* alphaPix = alphaRas->pixels(j); |
| |
| alphaPix += margin.x; |
| |
| int i = margin.x; |
| for (int out_i = 0; out_i < outTileRas->getLx(); i++, out_i++) { |
| |
| float alpha = (float)alphaPix->value / (float)PIXEL::maxChannelValue; |
| if (alpha == 0.0f) { |
| alphaPix++; |
| outPix++; |
| continue; |
| } |
| |
| |
| typename PIXEL::Channel dnVal = |
| (m_channel == Red) ? outPix->r |
| : (m_channel == Green) ? outPix->g : outPix->b; |
| |
| float exposure; |
| double val; |
| if (alpha == 1.0 || dnVal == 0.0) { |
| exposure = (m_kissfft_comp_in[getCoord(i, j, dim.lx, dim.ly)].r / |
| (dim.lx * dim.ly)); |
| val = exposureToValue(exposure) * (float)PIXEL::maxChannelValue + 0.5f; |
| } else { |
| exposure = |
| (m_kissfft_comp_in[getCoord(i, j, dim.lx, dim.ly)].r / |
| (dim.lx * dim.ly)) + |
| valueToExposure((float)dnVal / (float)PIXEL::maxChannelValue) * |
| (1 - alpha); |
| val = exposureToValue(exposure) * (float)PIXEL::maxChannelValue + 0.5f; |
| |
| if (m_doLightenComp) val = std::max(val, (double)dnVal); |
| } |
| |
| |
| if (val < 0.0) |
| val = 0.0; |
| else if (val > (float)PIXEL::maxChannelValue) |
| val = (float)PIXEL::maxChannelValue; |
| |
| switch (m_channel) { |
| case Red: |
| outPix->r = (typename PIXEL::Channel)val; |
| |
| if (outPix->m != A_PIXEL::maxChannelValue) { |
| if (alphaPix->value == A_PIXEL::maxChannelValue) |
| outPix->m = A_PIXEL::maxChannelValue; |
| else |
| outPix->m = |
| alphaPix->value + |
| (typename A_PIXEL::Channel)( |
| (float)outPix->m * |
| (float)(A_PIXEL::maxChannelValue - alphaPix->value) / |
| (float)A_PIXEL::maxChannelValue); |
| } |
| break; |
| case Green: |
| outPix->g = (typename PIXEL::Channel)val; |
| break; |
| case Blue: |
| outPix->b = (typename PIXEL::Channel)val; |
| break; |
| } |
| |
| alphaPix++; |
| outPix++; |
| } |
| } |
| } |
| |
| |
| |
| void MyThread::run() { |
| |
| TDimensionI dim = m_layerTileRas->getSize(); |
| |
| int2 margin = {(dim.lx - m_outTileRas->getSize().lx) / 2, |
| (dim.ly - m_outTileRas->getSize().ly) / 2}; |
| |
| |
| for (int i = 0; i < dim.lx * dim.ly; i++) { |
| m_kissfft_comp_in[i].r = 0.0; |
| m_kissfft_comp_in[i].i = 0.0; |
| } |
| |
| TRaster32P ras32 = (TRaster32P)m_layerTileRas; |
| TRaster64P ras64 = (TRaster64P)m_layerTileRas; |
| |
| |
| |
| { |
| lock.lockForRead(); |
| if (ras32) |
| setLayerRaster<TRaster32P, TPixel32>(ras32, m_kissfft_comp_in, dim); |
| else if (ras64) |
| setLayerRaster<TRaster64P, TPixel64>(ras64, m_kissfft_comp_in, dim); |
| else { |
| lock.unlock(); |
| return; |
| } |
| |
| lock.unlock(); |
| } |
| |
| if (checkTerminationAndCleanupThread()) return; |
| |
| kiss_fftnd(m_kissfft_plan_fwd, m_kissfft_comp_in, m_kissfft_comp_out); |
| kiss_fft_free(m_kissfft_plan_fwd); |
| m_kissfft_plan_fwd = NULL; |
| |
| if (checkTerminationAndCleanupThread()) return; |
| |
| |
| { |
| for (int i = 0; i < dim.lx * dim.ly; i++) { |
| float re, im; |
| re = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].r - |
| m_kissfft_comp_out[i].i * m_kissfft_comp_iris[i].i; |
| im = m_kissfft_comp_out[i].r * m_kissfft_comp_iris[i].i + |
| m_kissfft_comp_iris[i].r * m_kissfft_comp_out[i].i; |
| m_kissfft_comp_out[i].r = re; |
| m_kissfft_comp_out[i].i = im; |
| } |
| } |
| |
| if (checkTerminationAndCleanupThread()) return; |
| |
| kiss_fftnd(m_kissfft_plan_bkwd, m_kissfft_comp_out, |
| m_kissfft_comp_in); |
| kiss_fft_free(m_kissfft_plan_bkwd); |
| m_kissfft_plan_bkwd = NULL; |
| |
| |
| |
| |
| m_kissfft_comp_out_ras->unlock(); |
| m_kissfft_comp_out = 0; |
| |
| if (checkTerminationAndCleanupThread()) return; |
| |
| { |
| QMutexLocker locker(&mutex); |
| |
| TRaster32P ras32 = (TRaster32P)m_layerTileRas; |
| TRaster64P ras64 = (TRaster64P)m_layerTileRas; |
| |
| if (ras32) { |
| compositLayerToTile<TRaster32P, TPixel32, TRasterGR8P, TPixelGR8>( |
| ras32, (TRaster32P)m_outTileRas, (TRasterGR8P)m_tmpAlphaRas, dim, |
| margin); |
| } else if (ras64) { |
| compositLayerToTile<TRaster64P, TPixel64, TRasterGR16P, TPixelGR16>( |
| ras64, (TRaster64P)m_outTileRas, (TRasterGR16P)m_tmpAlphaRas, dim, |
| margin); |
| } else { |
| lock.unlock(); |
| return; |
| } |
| } |
| |
| |
| m_kissfft_comp_in_ras->unlock(); |
| m_kissfft_comp_in = 0; |
| |
| m_finished = true; |
| } |
| |
| |
| inline float MyThread::valueToExposure(float value) { |
| float logVal = (value - 0.5) / m_filmGamma; |
| return pow(10, logVal); |
| } |
| inline float MyThread::exposureToValue(float exposure) { |
| return log10(exposure) * m_filmGamma + 0.5; |
| } |
| |
| |
| bool MyThread::checkTerminationAndCleanupThread() { |
| if (!m_isTerminated) return false; |
| |
| if (m_kissfft_comp_in) m_kissfft_comp_in_ras->unlock(); |
| if (m_kissfft_comp_out) m_kissfft_comp_out_ras->unlock(); |
| if (m_kissfft_plan_fwd) kiss_fft_free(m_kissfft_plan_fwd); |
| if (m_kissfft_plan_bkwd) kiss_fft_free(m_kissfft_plan_bkwd); |
| |
| m_finished = true; |
| return true; |
| } |
| |
| |
| |
| |
| |
| Iwa_BokehFx::Iwa_BokehFx() |
| : m_onFocusDistance(0.5), m_bokehAmount(30.0), m_hardness(0.3) { |
| |
| addInputPort("Iris", m_iris); |
| bindParam(this, "on_focus_distance", m_onFocusDistance, false); |
| bindParam(this, "bokeh_amount", m_bokehAmount, false); |
| bindParam(this, "hardness", m_hardness, false); |
| |
| |
| m_onFocusDistance->setValueRange(0, 1); |
| m_bokehAmount->setValueRange(0, 300); |
| m_bokehAmount->setMeasureName("fxLength"); |
| m_hardness->setValueRange(0.05, 3.0); |
| |
| |
| for (int layer = 0; layer < LAYER_NUM; layer++) { |
| m_layerParams[layer].m_premultiply = TBoolParamP(false); |
| m_layerParams[layer].m_distance = TDoubleParamP(0.5); |
| m_layerParams[layer].m_bokehAdjustment = TDoubleParamP(1); |
| |
| std::string str = QString("Source%1").arg(layer + 1).toStdString(); |
| addInputPort(str, m_layerParams[layer].m_source); |
| bindParam(this, QString("premultiply%1").arg(layer + 1).toStdString(), |
| m_layerParams[layer].m_premultiply, false); |
| bindParam(this, QString("distance%1").arg(layer + 1).toStdString(), |
| m_layerParams[layer].m_distance, false); |
| bindParam(this, QString("bokeh_adjustment%1").arg(layer + 1).toStdString(), |
| m_layerParams[layer].m_bokehAdjustment, false); |
| |
| m_layerParams[layer].m_distance->setValueRange(0, 1); |
| m_layerParams[layer].m_bokehAdjustment->setValueRange(0, 2); |
| } |
| } |
| |
| void Iwa_BokehFx::doCompute(TTile& tile, double frame, |
| const TRenderSettings& settings) { |
| |
| if (!m_iris.isConnected()) { |
| tile.getRaster()->clear(); |
| return; |
| } |
| |
| bool sourceIsConnected = false; |
| for (int i = 0; i < LAYER_NUM; i++) { |
| if (m_layerParams[i].m_source.isConnected()) { |
| sourceIsConnected = true; |
| break; |
| } |
| } |
| if (!sourceIsConnected) { |
| tile.getRaster()->clear(); |
| return; |
| } |
| |
| |
| QList<int> sourceIndices = getSortedSourceIndices(frame); |
| |
| |
| float bokehPixelAmount = getBokehPixelAmount(frame, settings.m_affine); |
| |
| |
| |
| float maxIrisSize; |
| QVector<float> irisSizes = |
| getIrisSizes(frame, sourceIndices, bokehPixelAmount, maxIrisSize); |
| |
| int margin = tceil(maxIrisSize / 2.0); |
| |
| |
| TRectD _rectOut(tile.m_pos, TDimensionD(tile.getRaster()->getLx(), |
| tile.getRaster()->getLy())); |
| _rectOut = _rectOut.enlarge(static_cast<double>(margin)); |
| |
| TDimensionI dimOut(static_cast<int>(_rectOut.getLx() + 0.5), |
| static_cast<int>(_rectOut.getLy() + 0.5)); |
| |
| |
| |
| if (dimOut.lx < 10000 && dimOut.ly < 10000) { |
| int new_x = kiss_fft_next_fast_size(dimOut.lx); |
| int new_y = kiss_fft_next_fast_size(dimOut.ly); |
| |
| _rectOut = _rectOut.enlarge(static_cast<double>(new_x - dimOut.lx) / 2.0, |
| static_cast<double>(new_y - dimOut.ly) / 2.0); |
| |
| dimOut.lx = new_x; |
| dimOut.ly = new_y; |
| } |
| |
| |
| |
| QMap<int, TTile*> sourceTiles; |
| for (int i = 0; i < sourceIndices.size(); i++) { |
| int index = sourceIndices.at(i); |
| float irisSize = irisSizes.at(i); |
| |
| TTile* layerTile = new TTile(); |
| |
| if (-1.0 <= irisSize && 1.0 >= irisSize) { |
| m_layerParams[index].m_source->allocateAndCompute( |
| *layerTile, tile.m_pos, |
| TDimension(tile.getRaster()->getLx(), tile.getRaster()->getLy()), |
| tile.getRaster(), frame, settings); |
| } |
| |
| else { |
| m_layerParams[index].m_source->allocateAndCompute( |
| *layerTile, _rectOut.getP00(), dimOut, tile.getRaster(), frame, |
| settings); |
| } |
| sourceTiles[index] = layerTile; |
| } |
| |
| |
| TRectD irisBBox; |
| m_iris->getBBox(frame, irisBBox, settings); |
| |
| TTile irisTile; |
| m_iris->allocateAndCompute( |
| irisTile, irisBBox.getP00(), |
| TDimension(static_cast<int>(irisBBox.getLx() + 0.5), |
| static_cast<int>(irisBBox.getLy() + 0.5)), |
| tile.getRaster(), frame, settings); |
| |
| |
| |
| |
| QMutexLocker fx_locker(&fx_mutex); |
| |
| kiss_fft_cpx* kissfft_comp_iris; |
| |
| TRasterGR8P kissfft_comp_iris_ras(dimOut.lx * sizeof(kiss_fft_cpx), |
| dimOut.ly); |
| kissfft_comp_iris_ras->lock(); |
| kissfft_comp_iris = (kiss_fft_cpx*)kissfft_comp_iris_ras->getRawData(); |
| |
| |
| double filmGamma = m_hardness->getValue(frame); |
| |
| |
| tile.getRaster()->clear(); |
| TRaster32P raster32 = tile.getRaster(); |
| if (raster32) |
| raster32->fill(TPixel32::Transparent); |
| else { |
| TRaster64P ras64 = tile.getRaster(); |
| if (ras64) ras64->fill(TPixel64::Transparent); |
| } |
| |
| |
| if (settings.m_isCanceled && *settings.m_isCanceled) { |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| return; |
| } |
| |
| |
| for (int i = 0; i < sourceIndices.size(); i++) { |
| |
| if (settings.m_isCanceled && *settings.m_isCanceled) { |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| return; |
| } |
| |
| int index = sourceIndices.at(i); |
| |
| |
| float irisSize = irisSizes.at(i); |
| |
| |
| |
| if (-1.0 <= irisSize && 1.0 >= irisSize) { |
| |
| TTile* layerTile = sourceTiles.value(index); |
| compositLayerAsIs(tile, *layerTile, frame, settings, index); |
| sourceTiles.remove(index); |
| |
| continue; |
| } |
| |
| { |
| |
| kiss_fft_cpx* kissfft_comp_iris_before; |
| TRasterGR8P kissfft_comp_iris_before_ras(dimOut.lx * sizeof(kiss_fft_cpx), |
| dimOut.ly); |
| kissfft_comp_iris_before_ras->lock(); |
| kissfft_comp_iris_before = |
| (kiss_fft_cpx*)kissfft_comp_iris_before_ras->getRawData(); |
| |
| |
| |
| convertIris(irisSize, kissfft_comp_iris_before, dimOut, irisBBox, |
| irisTile); |
| |
| if (settings.m_isCanceled && *settings.m_isCanceled) { |
| kissfft_comp_iris_ras->unlock(); |
| kissfft_comp_iris_before_ras->unlock(); |
| tile.getRaster()->clear(); |
| return; |
| } |
| |
| |
| kiss_fftnd_cfg iris_kissfft_plan; |
| while (1) { |
| int dims[2] = {dimOut.ly, dimOut.lx}; |
| int ndims = 2; |
| iris_kissfft_plan = kiss_fftnd_alloc(dims, ndims, false, 0, 0); |
| if (iris_kissfft_plan != NULL) break; |
| } |
| |
| kiss_fftnd(iris_kissfft_plan, kissfft_comp_iris_before, |
| kissfft_comp_iris); |
| kiss_fft_free(iris_kissfft_plan); |
| kissfft_comp_iris_before_ras->unlock(); |
| } |
| |
| |
| |
| |
| if (settings.m_isCanceled && *settings.m_isCanceled) { |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| return; |
| } |
| |
| TTile* layerTile = sourceTiles.value(index); |
| |
| if (!m_layerParams[index].m_premultiply->getValue()) |
| TRop::depremultiply(layerTile->getRaster()); |
| |
| TRasterP tmpAlphaRas; |
| { |
| TRaster32P ras32(tile.getRaster()); |
| TRaster64P ras64(tile.getRaster()); |
| if (ras32) |
| tmpAlphaRas = TRasterGR8P(dimOut); |
| else if (ras64) |
| tmpAlphaRas = TRasterGR16P(dimOut); |
| } |
| tmpAlphaRas->lock(); |
| |
| |
| |
| calcAlfaChannelBokeh(kissfft_comp_iris, *layerTile, tmpAlphaRas); |
| |
| if (settings.m_isCanceled && *settings.m_isCanceled) { |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| tmpAlphaRas->unlock(); |
| return; |
| } |
| |
| |
| MyThread threadR(MyThread::Red, layerTile->getRaster(), tile.getRaster(), |
| tmpAlphaRas, kissfft_comp_iris, filmGamma); |
| MyThread threadG(MyThread::Green, layerTile->getRaster(), tile.getRaster(), |
| tmpAlphaRas, kissfft_comp_iris, filmGamma); |
| MyThread threadB(MyThread::Blue, layerTile->getRaster(), tile.getRaster(), |
| tmpAlphaRas, kissfft_comp_iris, filmGamma); |
| |
| |
| |
| |
| |
| |
| |
| bool renderInSingleThread = false; |
| |
| |
| |
| int waitCount = 0; |
| while (1) { |
| |
| if ((settings.m_isCanceled && *settings.m_isCanceled) || |
| waitCount >= 20) |
| { |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| tmpAlphaRas->unlock(); |
| return; |
| } |
| if (threadR.init()) { |
| if (renderInSingleThread) |
| threadR.run(); |
| else |
| threadR.start(); |
| break; |
| } |
| QThread::msleep(500); |
| waitCount++; |
| } |
| |
| waitCount = 0; |
| while (1) { |
| |
| if ((settings.m_isCanceled && *settings.m_isCanceled) || |
| waitCount >= 20) |
| { |
| if (!threadR.isFinished()) threadR.terminateThread(); |
| while (!threadR.isFinished()) { |
| } |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| tmpAlphaRas->unlock(); |
| return; |
| } |
| if (threadG.init()) { |
| if (renderInSingleThread) |
| threadG.run(); |
| else |
| threadG.start(); |
| break; |
| } |
| QThread::msleep(500); |
| waitCount++; |
| } |
| |
| waitCount = 0; |
| while (1) { |
| |
| if ((settings.m_isCanceled && *settings.m_isCanceled) || |
| waitCount >= 20) |
| { |
| if (!threadR.isFinished()) threadR.terminateThread(); |
| if (!threadG.isFinished()) threadG.terminateThread(); |
| while (!threadR.isFinished() || !threadG.isFinished()) { |
| } |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| tmpAlphaRas->unlock(); |
| return; |
| } |
| if (threadB.init()) { |
| if (renderInSingleThread) |
| threadB.run(); |
| else |
| threadB.start(); |
| break; |
| } |
| QThread::msleep(500); |
| waitCount++; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| waitCount = 0; |
| while (1) { |
| |
| if ((settings.m_isCanceled && *settings.m_isCanceled) || |
| waitCount >= 2000) |
| { |
| if (!threadR.isFinished()) threadR.terminateThread(); |
| if (!threadG.isFinished()) threadG.terminateThread(); |
| if (!threadB.isFinished()) threadB.terminateThread(); |
| while (!threadR.isFinished() || !threadG.isFinished() || |
| !threadB.isFinished()) { |
| } |
| kissfft_comp_iris_ras->unlock(); |
| tile.getRaster()->clear(); |
| tmpAlphaRas->unlock(); |
| return; |
| } |
| if (threadR.isFinished() && threadG.isFinished() && threadB.isFinished()) |
| break; |
| QThread::msleep(50); |
| waitCount++; |
| } |
| tmpAlphaRas->unlock(); |
| sourceTiles.remove(index); |
| } |
| |
| kissfft_comp_iris_ras->unlock(); |
| } |
| |
| bool Iwa_BokehFx::doGetBBox(double frame, TRectD& bBox, |
| const TRenderSettings& info) { |
| bBox = TConsts::infiniteRectD; |
| return true; |
| } |
| |
| bool Iwa_BokehFx::canHandle(const TRenderSettings& info, double frame) { |
| return false; |
| } |
| |
| |
| QList<int> Iwa_BokehFx::getSortedSourceIndices(double frame) { |
| QList<QPair<int, float>> usedSourceList; |
| |
| |
| for (int i = 0; i < LAYER_NUM; i++) { |
| if (m_layerParams[i].m_source.isConnected()) |
| usedSourceList.push_back( |
| QPair<int, float>(i, m_layerParams[i].m_distance->getValue(frame))); |
| } |
| |
| if (usedSourceList.empty()) return QList<int>(); |
| |
| |
| qSort(usedSourceList.begin(), usedSourceList.end(), isFurtherLayer); |
| |
| QList<int> indicesList; |
| for (int i = 0; i < usedSourceList.size(); i++) { |
| indicesList.push_back(usedSourceList.at(i).first); |
| } |
| |
| return indicesList; |
| } |
| |
| |
| float Iwa_BokehFx::getBokehPixelAmount(const double frame, |
| const TAffine affine) { |
| |
| TPointD vect; |
| vect.x = m_bokehAmount->getValue(frame); |
| vect.y = 0.0; |
| |
| |
| |
| TAffine aff(affine); |
| aff.a13 = aff.a23 = 0; |
| vect = aff * vect; |
| |
| return sqrt(vect.x * vect.x + vect.y * vect.y); |
| } |
| |
| |
| |
| QVector<float> Iwa_BokehFx::getIrisSizes(const double frame, |
| const QList<int> sourceIndices, |
| const float bokehPixelAmount, |
| float& maxIrisSize) { |
| float max = 0.0; |
| QVector<float> irisSizes; |
| for (int s = 0; s < sourceIndices.size(); s++) { |
| int index = sourceIndices.at(s); |
| float irisSize = (m_onFocusDistance->getValue(frame) - |
| m_layerParams[index].m_distance->getValue(frame)) * |
| bokehPixelAmount * |
| m_layerParams[index].m_bokehAdjustment->getValue(frame); |
| irisSizes.push_back(irisSize); |
| |
| |
| if (max < fabs(irisSize)) max = fabs(irisSize); |
| } |
| maxIrisSize = max; |
| |
| return irisSizes; |
| } |
| |
| |
| void Iwa_BokehFx::compositLayerAsIs(TTile& tile, TTile& layerTile, |
| const double frame, |
| const TRenderSettings& settings, |
| const int index) { |
| |
| if (m_layerParams[index].m_premultiply->getValue()) |
| TRop::premultiply(layerTile.getRaster()); |
| |
| TRop::over(tile.getRaster(), layerTile.getRaster()); |
| } |
| |
| |
| |
| |
| |
| void Iwa_BokehFx::convertIris(const float irisSize, |
| kiss_fft_cpx* kissfft_comp_iris_before, |
| const TDimensionI& dimOut, const TRectD& irisBBox, |
| const TTile& irisTile) { |
| |
| double2 irisOrgSize = {irisBBox.getLx(), irisBBox.getLy()}; |
| |
| |
| double irisSizeResampleRatio = irisSize / irisOrgSize.x; |
| |
| |
| double2 resizedIrisSize = {std::abs(irisSizeResampleRatio) * irisOrgSize.x, |
| std::abs(irisSizeResampleRatio) * irisOrgSize.y}; |
| int2 filterSize = {tceil(resizedIrisSize.x), tceil(resizedIrisSize.y)}; |
| TPointD resizeOffset((double)filterSize.x - resizedIrisSize.x, |
| (double)filterSize.y - resizedIrisSize.y); |
| |
| |
| |
| bool isIrisOffset[2] = {false, false}; |
| |
| if ((dimOut.lx - filterSize.x) % 2 == 1) { |
| filterSize.x++; |
| isIrisOffset[0] = true; |
| } |
| if ((dimOut.ly - filterSize.y) % 2 == 1) { |
| filterSize.y++; |
| isIrisOffset[1] = true; |
| } |
| |
| |
| if (filterSize.x > dimOut.lx || filterSize.y > dimOut.ly) { |
| std::cout |
| << "Error: The iris filter size becomes larger than the source size!" |
| << std::endl; |
| return; |
| } |
| |
| TRaster64P resizedIris(TDimension(filterSize.x, filterSize.y)); |
| |
| |
| |
| TAffine aff; |
| TPointD affOffset((isIrisOffset[0]) ? 0.5 : 1.0, |
| (isIrisOffset[1]) ? 0.5 : 1.0); |
| if (!isIrisOffset[0]) affOffset.x -= resizeOffset.x / 2; |
| if (!isIrisOffset[1]) affOffset.y -= resizeOffset.y / 2; |
| |
| aff = TTranslation(resizedIris->getCenterD() + affOffset); |
| aff *= TScale(irisSizeResampleRatio); |
| aff *= TTranslation(-(irisTile.getRaster()->getCenterD() + affOffset)); |
| |
| |
| TRop::resample(resizedIris, irisTile.getRaster(), aff); |
| |
| |
| float irisValAmount = 0.0; |
| |
| int iris_j = 0; |
| |
| for (int i = 0; i < dimOut.lx * dimOut.ly; i++) { |
| kissfft_comp_iris_before[i].r = 0.0; |
| kissfft_comp_iris_before[i].i = 0.0; |
| } |
| for (int j = (dimOut.ly - filterSize.y) / 2; iris_j < filterSize.y; |
| j++, iris_j++) { |
| TPixel64* pix = resizedIris->pixels(iris_j); |
| int iris_i = 0; |
| for (int i = (dimOut.lx - filterSize.x) / 2; iris_i < filterSize.x; |
| i++, iris_i++) { |
| |
| kissfft_comp_iris_before[j * dimOut.lx + i].r = |
| ((float)pix->r * 0.3f + (float)pix->g * 0.59f + |
| (float)pix->b * 0.11f) / |
| (float)USHRT_MAX; |
| irisValAmount += kissfft_comp_iris_before[j * dimOut.lx + i].r; |
| pix++; |
| } |
| } |
| |
| |
| for (int i = 0; i < dimOut.lx * dimOut.ly; i++) { |
| kissfft_comp_iris_before[i].r /= irisValAmount; |
| } |
| } |
| |
| |
| |
| void Iwa_BokehFx::calcAlfaChannelBokeh(kiss_fft_cpx* kissfft_comp_iris, |
| TTile& layerTile, TRasterP tmpAlphaRas) { |
| |
| int lx, ly; |
| lx = layerTile.getRaster()->getSize().lx; |
| ly = layerTile.getRaster()->getSize().ly; |
| |
| |
| kiss_fft_cpx *kissfft_comp_in, *kissfft_comp_out; |
| |
| TRasterGR8P kissfft_comp_in_ras(lx * sizeof(kiss_fft_cpx), ly); |
| kissfft_comp_in_ras->lock(); |
| kissfft_comp_in = (kiss_fft_cpx*)kissfft_comp_in_ras->getRawData(); |
| TRasterGR8P kissfft_comp_out_ras(lx * sizeof(kiss_fft_cpx), ly); |
| kissfft_comp_out_ras->lock(); |
| kissfft_comp_out = (kiss_fft_cpx*)kissfft_comp_out_ras->getRawData(); |
| |
| |
| for (int i = 0; i < lx * ly; i++) { |
| kissfft_comp_in[i].r = 0.0; |
| kissfft_comp_in[i].i = 0.0; |
| } |
| |
| TRaster32P ras32 = (TRaster32P)layerTile.getRaster(); |
| TRaster64P ras64 = (TRaster64P)layerTile.getRaster(); |
| if (ras32) { |
| for (int j = 0; j < ly; j++) { |
| TPixel32* pix = ras32->pixels(j); |
| for (int i = 0; i < lx; i++) { |
| kissfft_comp_in[j * lx + i].r = (float)pix->m / (float)UCHAR_MAX; |
| pix++; |
| } |
| } |
| } else if (ras64) { |
| for (int j = 0; j < ly; j++) { |
| TPixel64* pix = ras64->pixels(j); |
| for (int i = 0; i < lx; i++) { |
| kissfft_comp_in[j * lx + i].r = (float)pix->m / (float)USHRT_MAX; |
| pix++; |
| } |
| } |
| } else |
| return; |
| |
| int dims[2] = {ly, lx}; |
| int ndims = 2; |
| kiss_fftnd_cfg plan_fwd = kiss_fftnd_alloc(dims, ndims, false, 0, 0); |
| kiss_fftnd(plan_fwd, kissfft_comp_in, kissfft_comp_out); |
| kiss_fft_free(plan_fwd); |
| |
| |
| for (int i = 0; i < lx * ly; i++) { |
| float re, im; |
| re = kissfft_comp_out[i].r * kissfft_comp_iris[i].r - |
| kissfft_comp_out[i].i * kissfft_comp_iris[i].i; |
| im = kissfft_comp_out[i].r * kissfft_comp_iris[i].i + |
| kissfft_comp_iris[i].r * kissfft_comp_out[i].i; |
| kissfft_comp_out[i].r = re; |
| kissfft_comp_out[i].i = im; |
| } |
| |
| kiss_fftnd_cfg plan_bkwd = kiss_fftnd_alloc(dims, ndims, true, 0, 0); |
| kiss_fftnd(plan_bkwd, kissfft_comp_out, kissfft_comp_in); |
| kiss_fft_free(plan_bkwd); |
| |
| |
| |
| |
| kissfft_comp_out_ras->unlock(); |
| |
| |
| if (ras32) { |
| TRasterGR8P alphaRas8(tmpAlphaRas); |
| for (int j = 0; j < ly; j++) { |
| TPixelGR8* pix = alphaRas8->pixels(j); |
| for (int i = 0; i < lx; i++) { |
| float val = |
| kissfft_comp_in[getCoord(i, j, lx, ly)].r / (lx * ly) * 256.0; |
| if (val < 0.0) |
| val = 0.0; |
| else if (val > 255.0) |
| val = 255.0; |
| |
| pix->value = (unsigned char)val; |
| |
| pix++; |
| } |
| } |
| } else if (ras64) { |
| TRasterGR16P alphaRas16(tmpAlphaRas); |
| for (int j = 0; j < ly; j++) { |
| TPixelGR16* pix = alphaRas16->pixels(j); |
| for (int i = 0; i < lx; i++) { |
| float val = |
| kissfft_comp_in[getCoord(i, j, lx, ly)].r / (lx * ly) * 65536.0; |
| if (val < 0.0) |
| val = 0.0; |
| else if (val > 65535.0) |
| val = 65535.0; |
| |
| pix->value = (unsigned short)val; |
| |
| pix++; |
| } |
| } |
| } else |
| return; |
| |
| kissfft_comp_in_ras->unlock(); |
| } |
| |
| FX_PLUGIN_IDENTIFIER(Iwa_BokehFx, "iwa_BokehFx") |