Think global, act local! The old favorite G.scene gone! Man... that took almost 2 days. Also removed G.curscreen and G.edbo. Not everything could get solved; here's some notes. - modifiers now store current scene in ModifierData. This is not meant for permanent, but it can probably stick there until we cleaned the anim system and depsgraph to cope better with timing issues. - Game engine G.scene should become an argument for staring it. Didn't solve this yet. - Texture nodes should get scene cfra, but the current implementation is too tightly wrapped to do it easily.
863 lines
27 KiB
C
863 lines
27 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2006 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
#include "../CMP_util.h"
|
|
|
|
/* ************ qdn: Defocus node ****************** */
|
|
static bNodeSocketType cmp_node_defocus_in[]= {
|
|
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
|
|
{ SOCK_VALUE, 1, "Z", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
static bNodeSocketType cmp_node_defocus_out[]= {
|
|
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
|
|
|
|
// line coefs for point sampling & scancon. data.
|
|
typedef struct BokehCoeffs {
|
|
float x0, y0, dx, dy;
|
|
float ls_x, ls_y;
|
|
float min_x, min_y, max_x, max_y;
|
|
} BokehCoeffs;
|
|
|
|
// returns array of BokehCoeffs
|
|
// returns length of array in 'len_bkh',
|
|
// radius squared of inscribed disk in 'inradsq', needed in getWeight() test,
|
|
// BKH[8] is the data returned for the bokeh shape & bkh_b[4] is it's 2d bound
|
|
static void makeBokeh(char bktype, char ro, int* len_bkh, float* inradsq, BokehCoeffs BKH[8], float bkh_b[4])
|
|
{
|
|
float x0, x1, y0, y1, dx, dy, iDxy;
|
|
float w = MAX2(1e-5f, ro)*M_PI/180.f; // never reported stangely enough, but a zero offset causes missing center line...
|
|
float wi = (360.f/bktype)*M_PI/180.f;
|
|
int i, ov, nv;
|
|
|
|
// bktype must be at least 3 & <= 8
|
|
bktype = (bktype<3) ? 3 : ((bktype>8) ? 8 : bktype);
|
|
*len_bkh = bktype;
|
|
*inradsq = -1.f;
|
|
|
|
for (i=0; i<(*len_bkh); i++) {
|
|
x0 = cos(w);
|
|
y0 = sin(w);
|
|
w += wi;
|
|
x1 = cos(w);
|
|
y1 = sin(w);
|
|
if ((*inradsq)<0.f) {
|
|
// radius squared of inscribed disk
|
|
float idx=(x0+x1)*0.5f, idy=(y0+y1)*0.5f;
|
|
*inradsq = idx*idx + idy*idy;
|
|
}
|
|
BKH[i].x0 = x0;
|
|
BKH[i].y0 = y0;
|
|
dx = x1-x0, dy = y1-y0;
|
|
iDxy = 1.f / sqrt(dx*dx + dy*dy);
|
|
dx *= iDxy;
|
|
dy *= iDxy;
|
|
BKH[i].dx = dx;
|
|
BKH[i].dy = dy;
|
|
}
|
|
|
|
// precalc scanconversion data
|
|
// bokeh bound, not transformed, for scanconvert
|
|
bkh_b[0] = bkh_b[2] = 1e10f; // xmin/ymin
|
|
bkh_b[1] = bkh_b[3] = -1e10f; // xmax/ymax
|
|
ov = (*len_bkh) - 1;
|
|
for (nv=0; nv<(*len_bkh); nv++) {
|
|
bkh_b[0] = MIN2(bkh_b[0], BKH[nv].x0); // xmin
|
|
bkh_b[1] = MAX2(bkh_b[1], BKH[nv].x0); // xmax
|
|
bkh_b[2] = MIN2(bkh_b[2], BKH[nv].y0); // ymin
|
|
bkh_b[3] = MAX2(bkh_b[3], BKH[nv].y0); // ymax
|
|
BKH[nv].min_x = MIN2(BKH[ov].x0, BKH[nv].x0);
|
|
BKH[nv].max_x = MAX2(BKH[ov].x0, BKH[nv].x0);
|
|
BKH[nv].min_y = MIN2(BKH[ov].y0, BKH[nv].y0);
|
|
BKH[nv].max_y = MAX2(BKH[ov].y0, BKH[nv].y0);
|
|
dy = BKH[nv].y0 - BKH[ov].y0;
|
|
BKH[nv].ls_x = (BKH[nv].x0 - BKH[ov].x0) / ((dy==0.f) ? 1.f : dy);
|
|
BKH[nv].ls_y = (BKH[nv].ls_x==0.f) ? 1.f : (1.f/BKH[nv].ls_x);
|
|
ov = nv;
|
|
}
|
|
}
|
|
|
|
// test if u/v inside shape & returns weight value
|
|
static float getWeight(BokehCoeffs* BKH, int len_bkh, float u, float v, float rad, float inradsq)
|
|
{
|
|
BokehCoeffs* bc = BKH;
|
|
float cdist, irad = (rad==0.f) ? 1.f : (1.f/rad);
|
|
u *= irad;
|
|
v *= irad;
|
|
|
|
// early out test1: if point outside outer unit disk, it cannot be inside shape
|
|
cdist = u*u + v*v;
|
|
if (cdist>1.f) return 0.f;
|
|
|
|
// early out test2: if point inside or on inner disk, point must be inside shape
|
|
if (cdist<=inradsq) return 1.f;
|
|
|
|
while (len_bkh--) {
|
|
if ((bc->dy*(u - bc->x0) - bc->dx*(v - bc->y0)) > 0.f) return 0.f;
|
|
bc++;
|
|
}
|
|
return 1.f;
|
|
}
|
|
|
|
// QMC.seq. for sampling, A.Keller, EMS
|
|
static float RI_vdC(unsigned int bits, unsigned int r)
|
|
{
|
|
bits = ( bits << 16) | ( bits >> 16);
|
|
bits = ((bits & 0x00ff00ff) << 8) | ((bits & 0xff00ff00) >> 8);
|
|
bits = ((bits & 0x0f0f0f0f) << 4) | ((bits & 0xf0f0f0f0) >> 4);
|
|
bits = ((bits & 0x33333333) << 2) | ((bits & 0xcccccccc) >> 2);
|
|
bits = ((bits & 0x55555555) << 1) | ((bits & 0xaaaaaaaa) >> 1);
|
|
bits ^= r;
|
|
return (float)((double)bits / 4294967296.0);
|
|
}
|
|
|
|
// single channel IIR gaussian filtering
|
|
// much faster than anything else, constant time independent of width
|
|
// should extend to multichannel and make this a node, could be useful
|
|
static void IIR_gauss_single(CompBuf* buf, float sigma)
|
|
{
|
|
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
|
|
float *X, *Y, *W;
|
|
int i, x, y, sz;
|
|
|
|
// single channel only for now
|
|
if (buf->type != CB_VAL) return;
|
|
|
|
// <0.5 not valid, though can have a possibly useful sort of sharpening effect
|
|
if (sigma < 0.5) return;
|
|
|
|
// see "Recursive Gabor Filtering" by Young/VanVliet
|
|
// all factors here in double.prec. Required, because for single.prec it seems to blow up if sigma > ~200
|
|
if (sigma >= 3.556)
|
|
q = 0.9804*(sigma - 3.556) + 2.5091;
|
|
else // sigma >= 0.5
|
|
q = (0.0561*sigma + 0.5784)*sigma - 0.2568;
|
|
q2 = q*q;
|
|
sc = (1.1668 + q)*(3.203729649 + (2.21566 + q)*q);
|
|
// no gabor filtering here, so no complex multiplies, just the regular coefs.
|
|
// all negated here, so as not to have to recalc Triggs/Sdika matrix
|
|
cf[1] = q*(5.788961737 + (6.76492 + 3.0*q)*q)/ sc;
|
|
cf[2] = -q2*(3.38246 + 3.0*q)/sc;
|
|
// 0 & 3 unchanged
|
|
cf[3] = q2*q/sc;
|
|
cf[0] = 1.0 - cf[1] - cf[2] - cf[3];
|
|
|
|
// Triggs/Sdika border corrections,
|
|
// it seems to work, not entirely sure if it is actually totally correct,
|
|
// Besides J.M.Geusebroek's anigauss.c (see http://www.science.uva.nl/~mark),
|
|
// found one other implementation by Cristoph Lampert,
|
|
// but neither seem to be quite the same, result seems to be ok sofar anyway.
|
|
// Extra scale factor here to not have to do it in filter,
|
|
// though maybe this had something to with the precision errors
|
|
sc = cf[0]/((1.0 + cf[1] - cf[2] + cf[3])*(1.0 - cf[1] - cf[2] - cf[3])*(1.0 + cf[2] + (cf[1] - cf[3])*cf[3]));
|
|
tsM[0] = sc*(-cf[3]*cf[1] + 1.0 - cf[3]*cf[3] - cf[2]);
|
|
tsM[1] = sc*((cf[3] + cf[1])*(cf[2] + cf[3]*cf[1]));
|
|
tsM[2] = sc*(cf[3]*(cf[1] + cf[3]*cf[2]));
|
|
tsM[3] = sc*(cf[1] + cf[3]*cf[2]);
|
|
tsM[4] = sc*(-(cf[2] - 1.0)*(cf[2] + cf[3]*cf[1]));
|
|
tsM[5] = sc*(-(cf[3]*cf[1] + cf[3]*cf[3] + cf[2] - 1.0)*cf[3]);
|
|
tsM[6] = sc*(cf[3]*cf[1] + cf[2] + cf[1]*cf[1] - cf[2]*cf[2]);
|
|
tsM[7] = sc*(cf[1]*cf[2] + cf[3]*cf[2]*cf[2] - cf[1]*cf[3]*cf[3] - cf[3]*cf[3]*cf[3] - cf[3]*cf[2] + cf[3]);
|
|
tsM[8] = sc*(cf[3]*(cf[1] + cf[3]*cf[2]));
|
|
|
|
#define YVV(L)\
|
|
{\
|
|
W[0] = cf[0]*X[0] + cf[1]*X[0] + cf[2]*X[0] + cf[3]*X[0];\
|
|
W[1] = cf[0]*X[1] + cf[1]*W[0] + cf[2]*X[0] + cf[3]*X[0];\
|
|
W[2] = cf[0]*X[2] + cf[1]*W[1] + cf[2]*W[0] + cf[3]*X[0];\
|
|
for (i=3; i<L; i++)\
|
|
W[i] = cf[0]*X[i] + cf[1]*W[i-1] + cf[2]*W[i-2] + cf[3]*W[i-3];\
|
|
tsu[0] = W[L-1] - X[L-1];\
|
|
tsu[1] = W[L-2] - X[L-1];\
|
|
tsu[2] = W[L-3] - X[L-1];\
|
|
tsv[0] = tsM[0]*tsu[0] + tsM[1]*tsu[1] + tsM[2]*tsu[2] + X[L-1];\
|
|
tsv[1] = tsM[3]*tsu[0] + tsM[4]*tsu[1] + tsM[5]*tsu[2] + X[L-1];\
|
|
tsv[2] = tsM[6]*tsu[0] + tsM[7]*tsu[1] + tsM[8]*tsu[2] + X[L-1];\
|
|
Y[L-1] = cf[0]*W[L-1] + cf[1]*tsv[0] + cf[2]*tsv[1] + cf[3]*tsv[2];\
|
|
Y[L-2] = cf[0]*W[L-2] + cf[1]*Y[L-1] + cf[2]*tsv[0] + cf[3]*tsv[1];\
|
|
Y[L-3] = cf[0]*W[L-3] + cf[1]*Y[L-2] + cf[2]*Y[L-1] + cf[3]*tsv[0];\
|
|
for (i=L-4; i>=0; i--)\
|
|
Y[i] = cf[0]*W[i] + cf[1]*Y[i+1] + cf[2]*Y[i+2] + cf[3]*Y[i+3];\
|
|
}
|
|
|
|
// intermediate buffers
|
|
sz = MAX2(buf->x, buf->y);
|
|
Y = MEM_callocN(sz*sizeof(float), "IIR_gauss Y buf");
|
|
W = MEM_callocN(sz*sizeof(float), "IIR_gauss W buf");
|
|
// H
|
|
for (y=0; y<buf->y; y++) {
|
|
X = &buf->rect[y*buf->x];
|
|
YVV(buf->x);
|
|
memcpy(X, Y, sizeof(float)*buf->x);
|
|
}
|
|
// V
|
|
X = MEM_callocN(buf->y*sizeof(float), "IIR_gauss X buf");
|
|
for (x=0; x<buf->x; x++) {
|
|
for (y=0; y<buf->y; y++)
|
|
X[y] = buf->rect[x + y*buf->x];
|
|
YVV(buf->y);
|
|
for (y=0; y<buf->y; y++)
|
|
buf->rect[x + y*buf->x] = Y[y];
|
|
}
|
|
MEM_freeN(X);
|
|
|
|
MEM_freeN(W);
|
|
MEM_freeN(Y);
|
|
#undef YVV
|
|
}
|
|
|
|
static void defocus_blur(bNode *node, CompBuf *new, CompBuf *img, CompBuf *zbuf, float inpval, int no_zbuf)
|
|
{
|
|
NodeDefocus *nqd = node->storage;
|
|
CompBuf *wts; // weights buffer
|
|
CompBuf *crad; // CoC radius buffer
|
|
BokehCoeffs BKH[8]; // bokeh shape data, here never > 8 pts.
|
|
float bkh_b[4] = {0}; // shape 2D bound
|
|
unsigned int p, px, p4, zp, cp, cp4;
|
|
float *ctcol, u, v, iZ, ct_crad, lwt, wt=0, cR2=0;
|
|
float dof_sp, maxfgc, bk_hn_theta=0, inradsq=0;
|
|
float cam_fdist=1, cam_invfdist=1, cam_lens=35;
|
|
int x, y, sx, sy, len_bkh=0;
|
|
float aspect, aperture;
|
|
int minsz;
|
|
//float bcrad, nmaxc, scf;
|
|
|
|
// get some required params from the current scene camera
|
|
// (ton) this is wrong, needs fixed
|
|
Object* camob = NULL; // XXX G.scene->camera;
|
|
if (camob && camob->type==OB_CAMERA) {
|
|
Camera* cam = (Camera*)camob->data;
|
|
cam_lens = cam->lens;
|
|
cam_fdist = dof_camera(camob);
|
|
if (cam_fdist==0.0) cam_fdist = 1e10f; /* if the dof is 0.0 then set it be be far away */
|
|
cam_invfdist = 1.f/cam_fdist;
|
|
}
|
|
|
|
// guess work here.. best match with raytraced result
|
|
minsz = MIN2(img->x, img->y);
|
|
dof_sp = (float)minsz / (16.f / cam_lens); // <- == aspect * MIN2(img->x, img->y) / tan(0.5f * fov);
|
|
|
|
// aperture
|
|
aspect = (img->x > img->y) ? (img->y / (float)img->x) : (img->x / (float)img->y);
|
|
aperture = 0.5f*(cam_lens / (aspect*32.f)) / nqd->fstop;
|
|
|
|
// if not disk, make bokeh coefficients and other needed data
|
|
if (nqd->bktype!=0) {
|
|
makeBokeh(nqd->bktype, nqd->rotation, &len_bkh, &inradsq, BKH, bkh_b);
|
|
bk_hn_theta = 0.5 * nqd->bktype * sin(2.0 * M_PI / nqd->bktype); // weight factor
|
|
}
|
|
|
|
// accumulated weights
|
|
wts = alloc_compbuf(img->x, img->y, CB_VAL, 1);
|
|
// CoC radius buffer
|
|
crad = alloc_compbuf(img->x, img->y, CB_VAL, 1);
|
|
|
|
// if 'no_zbuf' flag set (which is always set if input is not an image),
|
|
// values are instead interpreted directly as blur radius values
|
|
if (no_zbuf) {
|
|
// to prevent *reaaallly* big radius values and impossible calculation times,
|
|
// limit the maximum to half the image width or height, whichever is smaller
|
|
float maxr = 0.5f*(float)MIN2(img->x, img->y);
|
|
for (p=0; p<(unsigned int)(img->x*img->y); p++) {
|
|
crad->rect[p] = zbuf ? (zbuf->rect[p]*nqd->scale) : inpval;
|
|
// bug #5921, limit minimum
|
|
crad->rect[p] = MAX2(1e-5f, crad->rect[p]);
|
|
crad->rect[p] = MIN2(crad->rect[p], maxr);
|
|
// if maxblur!=0, limit maximum
|
|
if (nqd->maxblur != 0.f) crad->rect[p] = MIN2(crad->rect[p], nqd->maxblur);
|
|
}
|
|
}
|
|
else {
|
|
// actual zbuffer.
|
|
// separate foreground from background CoC's
|
|
// then blur background and blend in again with foreground,
|
|
// improves the 'blurred foreground overlapping in-focus midground' sharp boundary problem.
|
|
// wts buffer here used for blendmask
|
|
maxfgc = 0.f; // maximum foreground CoC radius
|
|
for (y=0; y<img->y; y++) {
|
|
p = y * img->x;
|
|
for (x=0; x<img->x; x++) {
|
|
px = p + x;
|
|
iZ = (zbuf->rect[px]==0.f) ? 0.f : (1.f/zbuf->rect[px]);
|
|
crad->rect[px] = 0.5f*(aperture*(dof_sp*(cam_invfdist - iZ) - 1.f));
|
|
if (crad->rect[px] <= 0.f) {
|
|
wts->rect[px] = 1.f;
|
|
crad->rect[px] = -crad->rect[px];
|
|
if (crad->rect[px] > maxfgc) maxfgc = crad->rect[px];
|
|
}
|
|
else crad->rect[px] = wts->rect[px] = 0;
|
|
}
|
|
}
|
|
|
|
// fast blur...
|
|
// bug #6656 part 1, probably when previous node_composite.c was split into separate files, it was not properly updated
|
|
// to include recent cvs commits (well, at least not defocus node), so this part was missing...
|
|
wt = aperture*128.f;
|
|
IIR_gauss_single(crad, wt);
|
|
IIR_gauss_single(wts, wt);
|
|
|
|
// bug #6656 part 2a, although foreground blur is not based anymore on closest object,
|
|
// the rescaling op below was still based on that anyway, and unlike the comment in below code,
|
|
// the difference is therefore not always that small at all...
|
|
// so for now commented out, not sure if this is going to cause other future problems, lets just wait and see...
|
|
/*
|
|
// find new maximum to scale it back to original
|
|
// (could skip this, not strictly necessary, in general, difference is quite small, but just in case...)
|
|
nmaxc = 0;
|
|
for (p=0; p<(img->x*img->y); p++)
|
|
if (crad->rect[p] > nmaxc) nmaxc = crad->rect[p];
|
|
// rescale factor
|
|
scf = (nmaxc==0.f) ? 1.f: (maxfgc / nmaxc);
|
|
*/
|
|
|
|
// and blend...
|
|
for (y=0; y<img->y; y++) {
|
|
p = y*img->x;
|
|
for (x=0; x<img->x; x++) {
|
|
px = p + x;
|
|
if (zbuf->rect[px]!=0.f) {
|
|
iZ = (zbuf->rect[px]==0.f) ? 0.f : (1.f/zbuf->rect[px]);
|
|
|
|
// bug #6656 part 2b, do not rescale
|
|
/*
|
|
bcrad = 0.5f*fabs(aperture*(dof_sp*(cam_invfdist - iZ) - 1.f));
|
|
// scale crad back to original maximum and blend
|
|
crad->rect[px] = bcrad + wts->rect[px]*(scf*crad->rect[px] - bcrad);
|
|
*/
|
|
crad->rect[px] = 0.5f*fabs(aperture*(dof_sp*(cam_invfdist - iZ) - 1.f));
|
|
|
|
// 'bug' #6615, limit minimum radius to 1 pixel, not really a solution, but somewhat mitigates the problem
|
|
crad->rect[px] = MAX2(crad->rect[px], 0.5f);
|
|
// if maxblur!=0, limit maximum
|
|
if (nqd->maxblur != 0.f) crad->rect[px] = MIN2(crad->rect[px], nqd->maxblur);
|
|
}
|
|
else crad->rect[px] = 0.f;
|
|
// clear weights for next part
|
|
wts->rect[px] = 0.f;
|
|
}
|
|
// esc set by main calling process
|
|
if(node->exec & NODE_BREAK)
|
|
break;
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------
|
|
// main loop
|
|
for (y=0; y<img->y; y++) {
|
|
// some sort of visual feedback would be nice, or at least this text in the renderwin header
|
|
// but for now just print some info in the console every 8 scanlines.
|
|
if (((y & 7)==0) || (y==(img->y-1))) {
|
|
if(G.background==0) {
|
|
printf("\rdefocus: Processing Line %d of %d ... ", y+1, img->y);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
// esc set by main calling process
|
|
if(node->exec & NODE_BREAK)
|
|
break;
|
|
|
|
zp = y * img->x;
|
|
for (x=0; x<img->x; x++) {
|
|
cp = zp + x;
|
|
cp4 = cp * img->type;
|
|
|
|
// Circle of Confusion radius for current pixel
|
|
cR2 = ct_crad = crad->rect[cp];
|
|
// skip if zero (border render)
|
|
if (ct_crad==0.f) {
|
|
// related to bug #5921, forgot output image when skipping 0 radius values
|
|
new->rect[cp4] = img->rect[cp4];
|
|
if (new->type != CB_VAL) {
|
|
new->rect[cp4+1] = img->rect[cp4+1];
|
|
new->rect[cp4+2] = img->rect[cp4+2];
|
|
new->rect[cp4+3] = img->rect[cp4+3];
|
|
}
|
|
continue;
|
|
}
|
|
cR2 *= cR2;
|
|
|
|
// pixel color
|
|
ctcol = &img->rect[cp4];
|
|
|
|
if (!nqd->preview) {
|
|
int xs, xe, ys, ye;
|
|
float lwt, wtcol[4] = {0}, aacol[4] = {0};
|
|
|
|
// shape weight
|
|
if (nqd->bktype==0) // disk
|
|
wt = 1.f/((float)M_PI*cR2);
|
|
else
|
|
wt = 1.f/(cR2*bk_hn_theta);
|
|
|
|
// weighted color
|
|
wtcol[0] = wt*ctcol[0];
|
|
if (new->type != CB_VAL) {
|
|
wtcol[1] = wt*ctcol[1];
|
|
wtcol[2] = wt*ctcol[2];
|
|
wtcol[3] = wt*ctcol[3];
|
|
}
|
|
|
|
// macro for background blur overlap test
|
|
// unfortunately, since this is done per pixel,
|
|
// it has a very significant negative impact on processing time...
|
|
// (eg. aa disk blur without test: 112 sec, vs with test: 176 sec...)
|
|
// iff center blur radius > threshold
|
|
// and if overlap pixel in focus, do nothing, else add color/weigbt
|
|
// (threshold constant is dependant on amount of blur)
|
|
#define TESTBG1(c, w) {\
|
|
if (ct_crad > nqd->bthresh) {\
|
|
if (crad->rect[p] > nqd->bthresh) {\
|
|
new->rect[p] += c[0];\
|
|
wts->rect[p] += w;\
|
|
}\
|
|
}\
|
|
else {\
|
|
new->rect[p] += c[0];\
|
|
wts->rect[p] += w;\
|
|
}\
|
|
}
|
|
#define TESTBG4(c, w) {\
|
|
if (ct_crad > nqd->bthresh) {\
|
|
if (crad->rect[p] > nqd->bthresh) {\
|
|
new->rect[p4] += c[0];\
|
|
new->rect[p4+1] += c[1];\
|
|
new->rect[p4+2] += c[2];\
|
|
new->rect[p4+3] += c[3];\
|
|
wts->rect[p] += w;\
|
|
}\
|
|
}\
|
|
else {\
|
|
new->rect[p4] += c[0];\
|
|
new->rect[p4+1] += c[1];\
|
|
new->rect[p4+2] += c[2];\
|
|
new->rect[p4+3] += c[3];\
|
|
wts->rect[p] += w;\
|
|
}\
|
|
}
|
|
if (nqd->bktype == 0) {
|
|
// Disk
|
|
int _x, i, j, di;
|
|
float Dj, T;
|
|
// AA pixel
|
|
#define AAPIX(a, b) {\
|
|
int _ny = b;\
|
|
if ((_ny >= 0) && (_ny < new->y)) {\
|
|
int _nx = a;\
|
|
if ((_nx >=0) && (_nx < new->x)) {\
|
|
p = _ny*new->x + _nx;\
|
|
if (new->type==CB_VAL) {\
|
|
TESTBG1(aacol, lwt);\
|
|
}\
|
|
else {\
|
|
p4 = p * new->type;\
|
|
TESTBG4(aacol, lwt);\
|
|
}\
|
|
}\
|
|
}\
|
|
}
|
|
// circle scanline
|
|
#define CSCAN(a, b) {\
|
|
int _ny = y + b;\
|
|
if ((_ny >= 0) && (_ny < new->y)) {\
|
|
xs = x - a + 1;\
|
|
if (xs < 0) xs = 0;\
|
|
xe = x + a;\
|
|
if (xe > new->x) xe = new->x;\
|
|
p = _ny*new->x + xs;\
|
|
if (new->type==CB_VAL) {\
|
|
for (_x=xs; _x<xe; _x++, p++) TESTBG1(wtcol, wt);\
|
|
}\
|
|
else {\
|
|
p4 = p * new->type;\
|
|
for (_x=xs; _x<xe; _x++, p++, p4+=new->type) TESTBG4(wtcol, wt);\
|
|
}\
|
|
}\
|
|
}
|
|
i = ceil(ct_crad);
|
|
j = 0;
|
|
T = 0;
|
|
while (i > j) {
|
|
Dj = sqrt(cR2 - j*j);
|
|
Dj -= floor(Dj);
|
|
di = 0;
|
|
if (Dj > T) { i--; di = 1; }
|
|
T = Dj;
|
|
aacol[0] = wtcol[0]*Dj;
|
|
if (new->type != CB_VAL) {
|
|
aacol[1] = wtcol[1]*Dj;
|
|
aacol[2] = wtcol[2]*Dj;
|
|
aacol[3] = wtcol[3]*Dj;
|
|
}
|
|
lwt = wt*Dj;
|
|
if (i!=j) {
|
|
// outer pixels
|
|
AAPIX(x+j, y+i);
|
|
AAPIX(x+j, y-i);
|
|
if (j) {
|
|
AAPIX(x-j, y+i); // BL
|
|
AAPIX(x-j, y-i); // TL
|
|
}
|
|
if (di) { // only when i changed, interior of outer section
|
|
CSCAN(j, i); // bottom
|
|
CSCAN(j, -i); // top
|
|
}
|
|
}
|
|
// lower mid section
|
|
AAPIX(x+i, y+j);
|
|
if (i) AAPIX(x-i, y+j);
|
|
CSCAN(i, j);
|
|
// upper mid section
|
|
if (j) {
|
|
AAPIX(x+i, y-j);
|
|
if (i) AAPIX(x-i, y-j);
|
|
CSCAN(i, -j);
|
|
}
|
|
j++;
|
|
}
|
|
#undef CSCAN
|
|
#undef AAPIX
|
|
}
|
|
else {
|
|
// n-agonal
|
|
int ov, nv;
|
|
float mind, maxd, lwt;
|
|
ys = MAX2((int)floor(bkh_b[2]*ct_crad + y), 0);
|
|
ye = MIN2((int)ceil(bkh_b[3]*ct_crad + y), new->y - 1);
|
|
for (sy=ys; sy<=ye; sy++) {
|
|
float fxs = 1e10f, fxe = -1e10f;
|
|
float yf = (sy - y)/ct_crad;
|
|
int found = 0;
|
|
ov = len_bkh - 1;
|
|
mind = maxd = 0;
|
|
for (nv=0; nv<len_bkh; nv++) {
|
|
if ((BKH[nv].max_y >= yf) && (BKH[nv].min_y <= yf)) {
|
|
float tx = BKH[ov].x0 + BKH[nv].ls_x*(yf - BKH[ov].y0);
|
|
if (tx < fxs) { fxs = tx; mind = BKH[nv].ls_x; }
|
|
if (tx > fxe) { fxe = tx; maxd = BKH[nv].ls_x; }
|
|
if (++found == 2) break;
|
|
}
|
|
ov = nv;
|
|
}
|
|
if (found) {
|
|
fxs = fxs*ct_crad + x;
|
|
fxe = fxe*ct_crad + x;
|
|
xs = (int)floor(fxs), xe = (int)ceil(fxe);
|
|
// AA hack for first and last x pixel, near vertical edges only
|
|
if (fabs(mind) <= 1.f) {
|
|
if ((xs >= 0) && (xs < new->x)) {
|
|
lwt = 1.f-(fxs - xs);
|
|
aacol[0] = wtcol[0]*lwt;
|
|
p = xs + sy*new->x;
|
|
if (new->type==CB_VAL) {
|
|
lwt *= wt;
|
|
TESTBG1(aacol, lwt);
|
|
}
|
|
else {
|
|
p4 = p * new->type;
|
|
aacol[1] = wtcol[1]*lwt;
|
|
aacol[2] = wtcol[2]*lwt;
|
|
aacol[3] = wtcol[3]*lwt;
|
|
lwt *= wt;
|
|
TESTBG4(aacol, lwt);
|
|
}
|
|
}
|
|
}
|
|
if (fabs(maxd) <= 1.f) {
|
|
if ((xe >= 0) && (xe < new->x)) {
|
|
lwt = 1.f-(xe - fxe);
|
|
aacol[0] = wtcol[0]*lwt;
|
|
p = xe + sy*new->x;
|
|
if (new->type==CB_VAL) {
|
|
lwt *= wt;
|
|
TESTBG1(aacol, lwt);
|
|
}
|
|
else {
|
|
p4 = p * new->type;
|
|
aacol[1] = wtcol[1]*lwt;
|
|
aacol[2] = wtcol[2]*lwt;
|
|
aacol[3] = wtcol[3]*lwt;
|
|
lwt *= wt;
|
|
TESTBG4(aacol, lwt);
|
|
}
|
|
}
|
|
}
|
|
xs = MAX2(xs+1, 0);
|
|
xe = MIN2(xe, new->x);
|
|
// remaining interior scanline
|
|
p = sy*new->x + xs;
|
|
if (new->type==CB_VAL) {
|
|
for (sx=xs; sx<xe; sx++, p++) TESTBG1(wtcol, wt);
|
|
}
|
|
else {
|
|
p4 = p * new->type;
|
|
for (sx=xs; sx<xe; sx++, p++, p4+=new->type) TESTBG4(wtcol, wt);
|
|
}
|
|
}
|
|
}
|
|
|
|
// now traverse in opposite direction, y scanlines,
|
|
// but this time only draw the near horizontal edges,
|
|
// applying same AA hack as above
|
|
xs = MAX2((int)floor(bkh_b[0]*ct_crad + x), 0);
|
|
xe = MIN2((int)ceil(bkh_b[1]*ct_crad + x), img->x - 1);
|
|
for (sx=xs; sx<=xe; sx++) {
|
|
float xf = (sx - x)/ct_crad;
|
|
float fys = 1e10f, fye = -1e10f;
|
|
int found = 0;
|
|
ov = len_bkh - 1;
|
|
mind = maxd = 0;
|
|
for (nv=0; nv<len_bkh; nv++) {
|
|
if ((BKH[nv].max_x >= xf) && (BKH[nv].min_x <= xf)) {
|
|
float ty = BKH[ov].y0 + BKH[nv].ls_y*(xf - BKH[ov].x0);
|
|
if (ty < fys) { fys = ty; mind = BKH[nv].ls_y; }
|
|
if (ty > fye) { fye = ty; maxd = BKH[nv].ls_y; }
|
|
if (++found == 2) break;
|
|
}
|
|
ov = nv;
|
|
}
|
|
if (found) {
|
|
fys = fys*ct_crad + y;
|
|
fye = fye*ct_crad + y;
|
|
// near horizontal edges only, line slope <= 1
|
|
if (fabs(mind) <= 1.f) {
|
|
int iys = (int)floor(fys);
|
|
if ((iys >= 0) && (iys < new->y)) {
|
|
lwt = 1.f - (fys - iys);
|
|
aacol[0] = wtcol[0]*lwt;
|
|
p = sx + iys*new->x;
|
|
if (new->type==CB_VAL) {
|
|
lwt *= wt;
|
|
TESTBG1(aacol, lwt);
|
|
}
|
|
else {
|
|
p4 = p * new->type;
|
|
aacol[1] = wtcol[1]*lwt;
|
|
aacol[2] = wtcol[2]*lwt;
|
|
aacol[3] = wtcol[3]*lwt;
|
|
lwt *= wt;
|
|
TESTBG4(aacol, lwt);
|
|
}
|
|
}
|
|
}
|
|
if (fabs(maxd) <= 1.f) {
|
|
int iye = ceil(fye);
|
|
if ((iye >= 0) && (iye < new->y)) {
|
|
lwt = 1.f - (iye - fye);
|
|
aacol[0] = wtcol[0]*lwt;
|
|
p = sx + iye*new->x;
|
|
if (new->type==CB_VAL) {
|
|
lwt *= wt;
|
|
TESTBG1(aacol, lwt);
|
|
}
|
|
else {
|
|
p4 = p * new->type;
|
|
aacol[1] = wtcol[1]*lwt;
|
|
aacol[2] = wtcol[2]*lwt;
|
|
aacol[3] = wtcol[3]*lwt;
|
|
lwt *= wt;
|
|
TESTBG4(aacol, lwt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
#undef TESTBG4
|
|
#undef TESTBG1
|
|
|
|
}
|
|
else {
|
|
// sampled, simple rejection sampling here, good enough
|
|
unsigned int maxsam, s, ui = BLI_rand()*BLI_rand();
|
|
float wcor, cpr = BLI_frand();
|
|
if (no_zbuf)
|
|
maxsam = nqd->samples; // no zbuffer input, use sample value directly
|
|
else {
|
|
// depth adaptive sampling hack, the more out of focus, the more samples taken, 16 minimum.
|
|
maxsam = (int)(0.5f + nqd->samples*(1.f-(float)exp(-fabs(zbuf->rect[cp] - cam_fdist))));
|
|
if (maxsam < 16) maxsam = 16;
|
|
}
|
|
wcor = 1.f/(float)maxsam;
|
|
for (s=0; s<maxsam; ++s) {
|
|
u = ct_crad*(2.f*RI_vdC(s, ui) - 1.f);
|
|
v = ct_crad*(2.f*(s + cpr)/(float)maxsam - 1.f);
|
|
sx = (int)(x + u + 0.5f), sy = (int)(y + v + 0.5f);
|
|
if ((sx<0) || (sx >= new->x) || (sy<0) || (sy >= new->y)) continue;
|
|
p = sx + sy*new->x;
|
|
p4 = p * new->type;
|
|
if (nqd->bktype==0) // Disk
|
|
lwt = ((u*u + v*v)<=cR2) ? wcor : 0.f;
|
|
else // AA not needed here
|
|
lwt = wcor * getWeight(BKH, len_bkh, u, v, ct_crad, inradsq);
|
|
// prevent background bleeding onto in-focus pixels, user-option
|
|
if (ct_crad > nqd->bthresh) { // if center blur > threshold
|
|
if (crad->rect[p] > nqd->bthresh) { // if overlap pixel in focus, do nothing, else add color/weigbt
|
|
new->rect[p4] += ctcol[0] * lwt;
|
|
if (new->type != CB_VAL) {
|
|
new->rect[p4+1] += ctcol[1] * lwt;
|
|
new->rect[p4+2] += ctcol[2] * lwt;
|
|
new->rect[p4+3] += ctcol[3] * lwt;
|
|
}
|
|
wts->rect[p] += lwt;
|
|
}
|
|
}
|
|
else {
|
|
new->rect[p4] += ctcol[0] * lwt;
|
|
if (new->type != CB_VAL) {
|
|
new->rect[p4+1] += ctcol[1] * lwt;
|
|
new->rect[p4+2] += ctcol[2] * lwt;
|
|
new->rect[p4+3] += ctcol[3] * lwt;
|
|
}
|
|
wts->rect[p] += lwt;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// finally, normalize
|
|
for (y=0; y<new->y; y++) {
|
|
p = y * new->x;
|
|
p4 = p * new->type;
|
|
for (x=0; x<new->x; x++) {
|
|
float dv = (wts->rect[p]==0.f) ? 1.f : (1.f/wts->rect[p]);
|
|
new->rect[p4] *= dv;
|
|
if (new->type!=CB_VAL) {
|
|
new->rect[p4+1] *= dv;
|
|
new->rect[p4+2] *= dv;
|
|
new->rect[p4+3] *= dv;
|
|
}
|
|
p++;
|
|
p4 += new->type;
|
|
}
|
|
}
|
|
|
|
free_compbuf(crad);
|
|
free_compbuf(wts);
|
|
|
|
printf("Done\n");
|
|
}
|
|
|
|
|
|
static void node_composit_exec_defocus(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
CompBuf *new, *old, *zbuf_use = NULL, *img = in[0]->data, *zbuf = in[1]->data;
|
|
NodeDefocus *nqd = node->storage;
|
|
int no_zbuf = nqd->no_zbuf;
|
|
|
|
if ((img==NULL) || (out[0]->hasoutput==0)) return;
|
|
|
|
// if image not valid type or fstop==infinite (128), nothing to do, pass in to out
|
|
if (((img->type!=CB_RGBA) && (img->type!=CB_VAL)) || ((no_zbuf==0) && (nqd->fstop==128.f))) {
|
|
out[0]->data = pass_on_compbuf(img);
|
|
return;
|
|
}
|
|
|
|
if (zbuf!=NULL) {
|
|
// Zbuf input, check to make sure, single channel, same size
|
|
// doesn't have to be actual zbuffer, but must be value type
|
|
if ((zbuf->x != img->x) || (zbuf->y != img->y)) {
|
|
// could do a scale here instead...
|
|
printf("Z input must be same size as image !\n");
|
|
return;
|
|
}
|
|
zbuf_use = typecheck_compbuf(zbuf, CB_VAL);
|
|
}
|
|
else no_zbuf = 1; // no zbuffer input
|
|
|
|
// ok, process
|
|
old = img;
|
|
if (nqd->gamco) {
|
|
// gamma correct, blender func is simplified, fixed value & RGBA only,
|
|
// should make user param. also depremul and premul afterwards, gamma
|
|
// correction can't work with premul alpha
|
|
old = dupalloc_compbuf(img);
|
|
premul_compbuf(old, 1);
|
|
gamma_correct_compbuf(old, 0);
|
|
premul_compbuf(old, 0);
|
|
}
|
|
|
|
new = alloc_compbuf(old->x, old->y, old->type, 1);
|
|
defocus_blur(node, new, old, zbuf_use, in[1]->vec[0]*nqd->scale, no_zbuf);
|
|
|
|
if (nqd->gamco) {
|
|
premul_compbuf(new, 1);
|
|
gamma_correct_compbuf(new, 1);
|
|
premul_compbuf(new, 0);
|
|
free_compbuf(old);
|
|
}
|
|
if(node->exec & NODE_BREAK) {
|
|
free_compbuf(new);
|
|
new= NULL;
|
|
}
|
|
out[0]->data = new;
|
|
if (zbuf_use && (zbuf_use != zbuf)) free_compbuf(zbuf_use);
|
|
}
|
|
|
|
static void node_composit_init_defocus(bNode* node)
|
|
{
|
|
/* qdn: defocus node */
|
|
NodeDefocus *nbd = MEM_callocN(sizeof(NodeDefocus), "node defocus data");
|
|
nbd->bktype = 0;
|
|
nbd->rotation = 0.f;
|
|
nbd->preview = 1;
|
|
nbd->gamco = 0;
|
|
nbd->samples = 16;
|
|
nbd->fstop = 128.f;
|
|
nbd->maxblur = 0;
|
|
nbd->bthresh = 1.f;
|
|
nbd->scale = 1.f;
|
|
nbd->no_zbuf = 1;
|
|
node->storage = nbd;
|
|
}
|
|
|
|
bNodeType cmp_node_defocus = {
|
|
/* *next,*prev */ NULL, NULL,
|
|
/* type code */ CMP_NODE_DEFOCUS,
|
|
/* name */ "Defocus",
|
|
/* width+range */ 150, 120, 200,
|
|
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
|
|
/* input sock */ cmp_node_defocus_in,
|
|
/* output sock */ cmp_node_defocus_out,
|
|
/* storage */ "NodeDefocus",
|
|
/* execfunc */ node_composit_exec_defocus,
|
|
/* butfunc */ NULL,
|
|
/* initfunc */ node_composit_init_defocus,
|
|
/* freestoragefunc */ node_free_standard_storage,
|
|
/* copystoragefunc */ node_copy_standard_storage,
|
|
/* id */ NULL
|
|
};
|
|
|
|
|