|
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 |
e917fd |
}; // namespace
|
|
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 |
e917fd |
: m_channel(channel)
|
|
shun-iwasawa |
e917fd |
, m_layerTileRas(layerTileRas)
|
|
shun-iwasawa |
e917fd |
, m_outTileRas(outTileRas)
|
|
shun-iwasawa |
e917fd |
, m_tmpAlphaRas(tmpAlphaRas)
|
|
shun-iwasawa |
e917fd |
, m_kissfft_comp_iris(kissfft_comp_iris)
|
|
shun-iwasawa |
e917fd |
, m_filmGamma(filmGamma)
|
|
shun-iwasawa |
e917fd |
, m_finished(false)
|
|
shun-iwasawa |
e917fd |
, m_kissfft_comp_in(0)
|
|
shun-iwasawa |
e917fd |
, m_kissfft_comp_out(0)
|
|
shun-iwasawa |
e917fd |
, m_isTerminated(false)
|
|
shun-iwasawa |
e917fd |
, 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 |
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 |
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 |
e917fd |
* What is done in the thread for each RGB channel:
|
|
shun-iwasawa |
e917fd |
* - Convert channel value -> Exposure
|
|
shun-iwasawa |
e917fd |
* - Multiply by alpha channel
|
|
shun-iwasawa |
e917fd |
* - Forward FFT
|
|
shun-iwasawa |
e917fd |
* - Multiply by the iris FFT data
|
|
shun-iwasawa |
e917fd |
* - Backward FFT
|
|
shun-iwasawa |
e917fd |
* - Convert Exposure -> channel value
|
|
shun-iwasawa |
e917fd |
*/
|
|
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
|
|
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 |
// 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 |
e917fd |
// add 1 pixel margins to all sides
|
|
shun-iwasawa |
e917fd |
int2 filterSize = {int(std::ceil(resizedIrisSize.x)) + 2,
|
|
shun-iwasawa |
e917fd |
int(std::ceil(resizedIrisSize.y)) + 2};
|
|
shun-iwasawa |
e917fd |
|
|
shun_iwasawa |
a35b8f |
TPointD resizeOffset((double)filterSize.x - resizedIrisSize.x,
|
|
shun_iwasawa |
a35b8f |
(double)filterSize.y - resizedIrisSize.y);
|
|
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 |
// Try to set the center of the iris to the center of the screen
|
|
shun-iwasawa |
e917fd |
if ((dimOut.lx - filterSize.x) % 2 == 1) filterSize.x++;
|
|
shun-iwasawa |
e917fd |
if ((dimOut.ly - filterSize.y) % 2 == 1) filterSize.y++;
|
|
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 |
e917fd |
TPointD affOffset(0.5, 0.5);
|
|
shun-iwasawa |
e917fd |
affOffset += TPointD((dimOut.lx % 2 == 1) ? 0.5 : 0.0,
|
|
shun-iwasawa |
e917fd |
(dimOut.ly % 2 == 1) ? 0.5 : 0.0);
|
|
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")
|