nodes from eechlo

* glare
* tonemap
* lense distort
* fast gauss blur

http://projects.blender.org/tracker/?func=detail&atid=127&aid=7505&group_id=9

made fast gauss blur an option for the blur node rather then a separate node.
This commit is contained in:
2007-10-26 15:32:36 +00:00
parent 83eba96db2
commit 2a2453d3e2
14 changed files with 1791 additions and 85 deletions

View File

@@ -303,6 +303,10 @@ void set_node_shader_lamp_loop(void (*lamp_loop_func)(struct ShadeInput *, str
#define CMP_NODE_INVERT 251
#define CMP_NODE_NORMALIZE 252
#define CMP_NODE_GLARE 301
#define CMP_NODE_TONEMAP 302
#define CMP_NODE_LENSDIST 303
/* channel toggles */
#define CMP_CHAN_RGB 1
#define CMP_CHAN_A 2

View File

@@ -2365,6 +2365,10 @@ static void registerCompositNodes(ListBase *ntypelist)
nodeRegisterType(ntypelist, &cmp_node_flip);
nodeRegisterType(ntypelist, &cmp_node_displace);
nodeRegisterType(ntypelist, &cmp_node_mapuv);
nodeRegisterType(ntypelist, &cmp_node_glare);
nodeRegisterType(ntypelist, &cmp_node_tonemap);
nodeRegisterType(ntypelist, &cmp_node_lensdist);
}
static void registerShaderNodes(ListBase *ntypelist)

View File

@@ -161,8 +161,10 @@ static void FLOAT2RGBE(fCOLOR fcol, RGBE rgbe)
int imb_is_a_hdr(void *buf)
{
/* For recognition, Blender only loades first 32 bytes, so use #?RADIANCE id instead */
if (strstr((char*)buf, "#?RADIANCE")) return 1;
// For recognition, Blender only loads first 32 bytes, so use #?RADIANCE id instead
// update: actually, the 'RADIANCE' part is just an optional program name, the magic word is really only the '#?' part
//if (strstr((char*)buf, "#?RADIANCE")) return 1;
if (strstr((char*)buf, "#?")) return 1;
// if (strstr((char*)buf, "32-bit_rle_rgbe")) return 1;
return 0;
}
@@ -176,7 +178,6 @@ struct ImBuf *imb_loadhdr(unsigned char *mem, int size, int flags)
int found=0;
int width=0, height=0;
int x, y;
int ir, ig, ib;
unsigned char* ptr;
unsigned char* rect;
char oriY[80], oriX[80];
@@ -225,18 +226,14 @@ struct ImBuf *imb_loadhdr(unsigned char *mem, int size, int flags)
*rect_float++ = fcol[GRN];
*rect_float++ = fcol[BLU];
*rect_float++ = 1.0f;
/* Also old oldstyle for the rest of blender which is not using floats yet */
/* very weird mapping! (ton) */
fcol[RED] = 1.f-exp(fcol[RED]*-1.414213562f);
fcol[GRN] = 1.f-exp(fcol[GRN]*-1.414213562f);
fcol[BLU] = 1.f-exp(fcol[BLU]*-1.414213562f);
ir = (int)(255.f*pow(fcol[RED], 0.45454545f));
ig = (int)(255.f*pow(fcol[GRN], 0.45454545f));
ib = (int)(255.f*pow(fcol[BLU], 0.45454545f));
*rect++ = (unsigned char)((ir<0) ? 0 : ((ir>255) ? 255 : ir));
*rect++ = (unsigned char)((ig<0) ? 0 : ((ig>255) ? 255 : ig));
*rect++ = (unsigned char)((ib<0) ? 0 : ((ib>255) ? 255 : ib));
// e: changed to simpler tonemapping, previous code was rather slow (is this actually still relevant at all?)
fcol[RED] = fcol[RED]/(1.f + fcol[RED]);
fcol[GRN] = fcol[GRN]/(1.f + fcol[GRN]);
fcol[BLU] = fcol[BLU]/(1.f + fcol[BLU]);
*rect++ = (unsigned char)((fcol[RED] < 0.f) ? 0 : ((fcol[RED] > 1.f) ? 255 : (255.f*fcol[RED])));
*rect++ = (unsigned char)((fcol[GRN] < 0.f) ? 0 : ((fcol[GRN] > 1.f) ? 255 : (255.f*fcol[GRN])));
*rect++ = (unsigned char)((fcol[BLU] < 0.f) ? 0 : ((fcol[BLU] > 1.f) ? 255 : (255.f*fcol[BLU])));
*rect++ = 255;
}
}
@@ -328,10 +325,10 @@ static void writeHeader(FILE *file, int width, int height)
fputc(10, file);
fprintf(file, "# %s", "Created with Blender");
fputc(10, file);
fprintf(file, "FORMAT=32-bit_rle_rgbe");
fputc(10, file);
fprintf(file, "EXPOSURE=%25.13f", 1.0);
fputc(10, file);
fprintf(file, "FORMAT=32-bit_rle_rgbe");
fputc(10, file);
fputc(10, file);
fprintf(file, "-Y %d +X %d", height, width);
fputc(10, file);

View File

@@ -230,4 +230,24 @@ typedef struct NodeDefocus {
float fstop, maxblur, bthresh, scale;
} NodeDefocus;
/* qdn: glare node */
typedef struct NodeGlare {
char quality, type, iter;
char angle, angle_ofs, size, pad[2];
float colmod, mix, threshold, fade;
} NodeGlare;
/* qdn: tonemap node */
typedef struct NodeTonemap {
float key, offset, gamma;
float f, m, a, c;
int type;
} NodeTonemap;
/* qdn: lens distortion node */
typedef struct NodeLensDist {
short jit, proj, fit, pad;
} NodeLensDist;
#endif

View File

@@ -507,6 +507,7 @@ typedef struct Scene {
#define R_FILTER_CATROM 4
#define R_FILTER_GAUSS 5
#define R_FILTER_MITCH 6
#define R_FILTER_FAST_GAUSS 7 /* note, this is only used for nodes at the moment */
/* yafray: renderer flag (not only exclusive to yafray) */
#define R_INTERN 0

View File

@@ -97,6 +97,8 @@ extern bNodeType cmp_node_flip;
extern bNodeType cmp_node_displace;
extern bNodeType cmp_node_mapuv;
extern bNodeType cmp_node_glare;
extern bNodeType cmp_node_tonemap;
extern bNodeType cmp_node_lensdist;
#endif

View File

@@ -29,8 +29,6 @@
#include "../CMP_util.h"
/* **************** BLUR ******************** */
static bNodeSocketType cmp_node_blur_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
@@ -555,8 +553,6 @@ static void blur_with_reference(bNode *node, CompBuf *new, CompBuf *img, CompBuf
free_compbuf(ref_use);
}
static void node_composit_exec_blur(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *img= in[0]->data;
@@ -564,35 +560,48 @@ static void node_composit_exec_blur(void *data, bNode *node, bNodeStack **in, bN
if(img==NULL || out[0]->hasoutput==0)
return;
if(img->type==CB_VEC2 || img->type==CB_VEC3) {
img= typecheck_compbuf(in[0]->data, CB_RGBA);
}
if (((NodeBlurData *)node->storage)->filtertype == R_FILTER_FAST_GAUSS) {
CompBuf *new, *img = in[0]->data;
/*from eeshlo's original patch, removed to fit in with the existing blur node */
/*const float sx = in[1]->vec[0], sy = in[2]->vec[0];*/
/* if fac input, we do it different */
if(in[1]->data) {
/* make output size of input image */
new= alloc_compbuf(img->x, img->y, img->type, 1); /* allocs */
/* accept image offsets from other nodes */
new->xof = img->xof;
new->yof = img->yof;
blur_with_reference(node, new, img, in[1]->data);
if(node->exec & NODE_BREAK) {
free_compbuf(new);
new= NULL;
}
out[0]->data= new;
}
else {
if(in[1]->vec[0]<=0.001f) { /* time node inputs can be a tiny value */
new= pass_on_compbuf(img);
NodeBlurData *nbd= node->storage;
const float sx = ((float)nbd->sizex)/2.0f, sy = ((float)nbd->sizey)/2.0f;
int c;
if ((img==NULL) || (out[0]->hasoutput==0)) return;
if (img->type == CB_VEC2)
new = typecheck_compbuf(img, CB_VAL);
else if (img->type == CB_VEC3)
new = typecheck_compbuf(img, CB_RGBA);
else
new = dupalloc_compbuf(img);
if ((sx == sy) && (sx > 0.f)) {
for (c=0; c<new->type; ++c)
IIR_gauss(new, sx, c, 3);
}
else {
NodeBlurData *nbd= node->storage;
CompBuf *gammabuf;
if (sx > 0.f) {
for (c=0; c<new->type; ++c)
IIR_gauss(new, sx, c, 1);
}
if (sy > 0.f) {
for (c=0; c<new->type; ++c)
IIR_gauss(new, sy, c, 2);
}
}
out[0]->data = new;
} else {
/* All non fast gauss blur methods */
if(img->type==CB_VEC2 || img->type==CB_VEC3) {
img= typecheck_compbuf(in[0]->data, CB_RGBA);
}
/* if fac input, we do it different */
if(in[1]->data) {
/* make output size of input image */
new= alloc_compbuf(img->x, img->y, img->type, 1); /* allocs */
@@ -600,33 +609,57 @@ static void node_composit_exec_blur(void *data, bNode *node, bNodeStack **in, bN
/* accept image offsets from other nodes */
new->xof = img->xof;
new->yof = img->yof;
if(nbd->gamma) {
gammabuf= dupalloc_compbuf(img);
gamma_correct_compbuf(gammabuf, 0);
}
else gammabuf= img;
if(nbd->bokeh)
bokeh_single_image(node, new, gammabuf, in[1]->vec[0]);
else if(1)
blur_single_image(node, new, gammabuf, in[1]->vec[0]);
else /* bloom experimental... */
bloom_with_reference(new, gammabuf, NULL, in[1]->vec[0], nbd);
if(nbd->gamma) {
gamma_correct_compbuf(new, 1);
free_compbuf(gammabuf);
}
blur_with_reference(node, new, img, in[1]->data);
if(node->exec & NODE_BREAK) {
free_compbuf(new);
new= NULL;
}
out[0]->data= new;
}
out[0]->data= new;
else {
if(in[1]->vec[0]<=0.001f) { /* time node inputs can be a tiny value */
new= pass_on_compbuf(img);
}
else {
NodeBlurData *nbd= node->storage;
CompBuf *gammabuf;
/* make output size of input image */
new= alloc_compbuf(img->x, img->y, img->type, 1); /* allocs */
/* accept image offsets from other nodes */
new->xof = img->xof;
new->yof = img->yof;
if(nbd->gamma) {
gammabuf= dupalloc_compbuf(img);
gamma_correct_compbuf(gammabuf, 0);
}
else gammabuf= img;
if(nbd->bokeh)
bokeh_single_image(node, new, gammabuf, in[1]->vec[0]);
else if(1)
blur_single_image(node, new, gammabuf, in[1]->vec[0]);
else /* bloom experimental... */
bloom_with_reference(new, gammabuf, NULL, in[1]->vec[0], nbd);
if(nbd->gamma) {
gamma_correct_compbuf(new, 1);
free_compbuf(gammabuf);
}
if(node->exec & NODE_BREAK) {
free_compbuf(new);
new= NULL;
}
}
out[0]->data= new;
}
if(img!=in[0]->data)
free_compbuf(img);
}
if(img!=in[0]->data)
free_compbuf(img);
}
static void node_composit_init_blur(bNode* node)

View File

@@ -143,7 +143,7 @@ static float RI_vdC(unsigned int bits, unsigned int r)
// 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(CompBuf* buf, float sigma)
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;
@@ -322,8 +322,8 @@ static void defocus_blur(bNode *node, CompBuf *new, CompBuf *img, CompBuf *zbuf,
// 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(crad, wt);
IIR_gauss(wts, wt);
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,

View File

@@ -0,0 +1,498 @@
/**
*
* ***** 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): Alfredo de Greef (eeshlo)
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "../CMP_util.h"
static bNodeSocketType cmp_node_glare_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_glare_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
// mix two images, src buffer does not have to be same size,
static void mixImages(CompBuf *dst, CompBuf *src, float mix)
{
int x, y;
fRGB c1, c2, *dcolp, *scolp;
const float mf = 2.f - 2.f*fabsf(mix - 0.5f);
if ((dst->x == src->x) && (dst->y == src->y)) {
for (y=0; y<dst->y; y++) {
dcolp = (fRGB*)&dst->rect[y*dst->x*dst->type];
scolp = (fRGB*)&src->rect[y*dst->x*dst->type];
for (x=0; x<dst->x; x++) {
fRGB_copy(c1, dcolp[x]);
fRGB_copy(c2, scolp[x]);
c1[0] += mix*(c2[0] - c1[0]);
c1[1] += mix*(c2[1] - c1[1]);
c1[2] += mix*(c2[2] - c1[2]);
if (c1[0] < 0.f) c1[0] = 0.f;
if (c1[1] < 0.f) c1[1] = 0.f;
if (c1[2] < 0.f) c1[2] = 0.f;
fRGB_mult(c1, mf);
fRGB_copy(dcolp[x], c1);
}
}
}
else {
float xr = src->x / (float)dst->x;
float yr = src->y / (float)dst->y;
for (y=0; y<dst->y; y++) {
dcolp = (fRGB*)&dst->rect[y*dst->x*dst->type];
for (x=0; x<dst->x; x++) {
fRGB_copy(c1, dcolp[x]);
qd_getPixelLerp(src, (x + 0.5f)*xr - 0.5f, (y + 0.5f)*yr - 0.5f, c2);
c1[0] += mix*(c2[0] - c1[0]);
c1[1] += mix*(c2[1] - c1[1]);
c1[2] += mix*(c2[2] - c1[2]);
if (c1[0] < 0.f) c1[0] = 0.f;
if (c1[1] < 0.f) c1[1] = 0.f;
if (c1[2] < 0.f) c1[2] = 0.f;
fRGB_mult(c1, mf);
fRGB_copy(dcolp[x], c1);
}
}
}
}
// adds src to dst image, must be of same size
static void addImage(CompBuf* dst, CompBuf* src, float scale)
{
if ((dst->x == src->x) && (dst->y == src->y)) {
int p = dst->x*dst->y*dst->type;
float *dcol = dst->rect, *scol = src->rect;
while (p--) *dcol++ += *scol++ * scale;
}
}
// returns possibly downscaled copy of all pixels above threshold
static CompBuf* BTP(CompBuf* src, float threshold, int scaledown)
{
int x, y;
CompBuf* bsrc = qd_downScaledCopy(src, scaledown);
float* cr = bsrc->rect;
for (y=0; y<bsrc->y; ++y)
for (x=0; x<bsrc->x; ++x, cr+=4) {
if ((0.212671f*cr[0] + 0.71516f*cr[1] + 0.072169f*cr[2]) >= threshold) {
cr[0] -= threshold, cr[1] -= threshold, cr[2] -= threshold;
cr[0] = MAX2(cr[0], 0.f);
cr[1] = MAX2(cr[1], 0.f);
cr[2] = MAX2(cr[2], 0.f);
}
else cr[0] = cr[1] = cr[2] = 0.f;
}
return bsrc;
}
//--------------------------------------------------------------------------------------------
// simple 4-point star filter
static void star4(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
int x, y, i, xm, xp, ym, yp;
float c[4] = {0,0,0,0}, tc[4] = {0,0,0,0};
CompBuf *tbuf1, *tbuf2, *tsrc;
const float f1 = 1.f - ndg->fade, f2 = (1.f - f1)*0.5f;
//const float t3 = ndg->threshold*3.f;
const float sc = (float)(1 << ndg->quality);
const float isc = 1.f/sc;
tsrc = BTP(src, ndg->threshold, (int)sc);
tbuf1 = dupalloc_compbuf(tsrc);
tbuf2 = dupalloc_compbuf(tsrc);
for (i=0; i<ndg->iter; i++) {
// (x || x-1, y-1) to (x || x+1, y+1)
// F
for (y=0; y<tbuf1->y; y++) {
ym = y - i;
yp = y + i;
for (x=0; x<tbuf1->x; x++) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf1, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf1, (ndg->angle ? xm : x), ym, tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf1, (ndg->angle ? xp : x), yp, tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf1, x, y, c);
}
}
// B
for (y=tbuf1->y-1; y>=0; y--) {
ym = y - i;
yp = y + i;
for (x=tbuf1->x-1; x>=0; x--) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf1, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf1, (ndg->angle ? xm : x), ym, tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf1, (ndg->angle ? xp : x), yp, tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf1, x, y, c);
}
}
// (x-1, y || y+1) to (x+1, y || y-1)
// F
for (y=0; y<tbuf2->y; y++) {
ym = y - i;
yp = y + i;
for (x=0; x<tbuf2->x; x++) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf2, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf2, xm, (ndg->angle ? yp : y), tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf2, xp, (ndg->angle ? ym : y), tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf2, x, y, c);
}
}
// B
for (y=tbuf2->y-1; y>=0; y--) {
ym = y - i;
yp = y + i;
for (x=tbuf2->x-1; x>=0; x--) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf2, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf2, xm, (ndg->angle ? yp : y), tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf2, xp, (ndg->angle ? ym : y), tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf2, x, y, c);
}
}
}
for (y=0; y<tbuf1->y; ++y)
for (x=0; x<tbuf1->x; ++x) {
unsigned int p = (x + y*tbuf1->x)*tbuf1->type;
tbuf1->rect[p] += tbuf2->rect[p];
tbuf1->rect[p+1] += tbuf2->rect[p+1];
tbuf1->rect[p+2] += tbuf2->rect[p+2];
}
for (y=0; y<dst->y; ++y) {
const float m = 0.5f + 0.5f*ndg->mix;
for (x=0; x<dst->x; ++x) {
unsigned int p = (x + y*dst->x)*dst->type;
qd_getPixelLerp(tbuf1, x*isc, y*isc, tc);
dst->rect[p] = src->rect[p] + m*(tc[0] - src->rect[p]);
dst->rect[p+1] = src->rect[p+1] + m*(tc[1] - src->rect[p+1]);
dst->rect[p+2] = src->rect[p+2] + m*(tc[2] - src->rect[p+2]);
}
}
free_compbuf(tbuf1);
free_compbuf(tbuf2);
free_compbuf(tsrc);
}
//--------------------------------------------------------------------------------------------
// streak filter
static void streaks(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
CompBuf *bsrc, *tsrc, *tdst, *sbuf;
int x, y, n;
unsigned int nump=0;
fRGB c1, c2, c3, c4;
float a, ang = 360.f/(float)ndg->angle;
bsrc = BTP(src, ndg->threshold, 1 << ndg->quality);
tsrc = dupalloc_compbuf(bsrc); // sample from buffer
tdst = alloc_compbuf(tsrc->x, tsrc->y, tsrc->type, 1); // sample to buffer
sbuf = alloc_compbuf(tsrc->x, tsrc->y, tsrc->type, 1); // streak sum buffer
for (a=0.f; a<360.f; a+=ang) {
const float an = (a + (float)ndg->angle_ofs)*(float)M_PI/180.f;
const float vx = cosf(an), vy = sinf(an);
for (n=0; n<ndg->iter; ++n) {
const float p4 = powf(4.f, n);
const float vxp = vx*p4, vyp = vy*p4;
const float wt = powf(ndg->fade, p4);
const float cmo = 1.f - powf(ndg->colmod, n+1); // colormodulation amount relative to current pass
float* tdstcol = tdst->rect;
for (y=0; y<tsrc->y; ++y) {
for (x=0; x<tsrc->x; ++x, tdstcol+=4) {
// first pass no offset, always same for every pass, exact copy,
// otherwise results in uneven brightness, only need once
if (n==0) qd_getPixel(tsrc, x, y, c1); else c1[0]=c1[1]=c1[2]=0;
qd_getPixelLerp(tsrc, x + vxp, y + vyp, c2);
qd_getPixelLerp(tsrc, x + vxp*2.f, y + vyp*2.f, c3);
qd_getPixelLerp(tsrc, x + vxp*3.f, y + vyp*3.f, c4);
// modulate color to look vaguely similar to a color spectrum
fRGB_rgbmult(c2, 1.f, cmo, cmo);
fRGB_rgbmult(c3, cmo, cmo, 1.f);
fRGB_rgbmult(c4, cmo, 1.f, cmo);
tdstcol[0] = 0.5f*(tdstcol[0] + c1[0] + wt*(c2[0] + wt*(c3[0] + wt*c4[0])));
tdstcol[1] = 0.5f*(tdstcol[1] + c1[1] + wt*(c2[1] + wt*(c3[1] + wt*c4[1])));
tdstcol[2] = 0.5f*(tdstcol[2] + c1[2] + wt*(c2[2] + wt*(c3[2] + wt*c4[2])));
}
}
memcpy(tsrc->rect, tdst->rect, sizeof(float)*tdst->x*tdst->y*tdst->type);
}
addImage(sbuf, tsrc, 1.f/(float)(6 - ndg->iter));
memset(tdst->rect, 0, tdst->x*tdst->y*tdst->type*sizeof(float));
memcpy(tsrc->rect, bsrc->rect, bsrc->x*bsrc->y*bsrc->type*sizeof(float));
nump++;
}
mixImages(dst, sbuf, 0.5f + 0.5f*ndg->mix);
free_compbuf(tsrc);
free_compbuf(tdst);
free_compbuf(sbuf);
free_compbuf(bsrc);
}
//--------------------------------------------------------------------------------------------
// Ghosts (lensflare)
static float smoothMask(float x, float y)
{
float t;
x = 2.f*x - 1.f, y = 2.f*y - 1.f;
if ((t = 1.f - sqrtf(x*x + y*y)) <= 0.f) return 0.f;
return t;
}
static void ghosts(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
// colormodulation and scale factors (cm & scalef) for 16 passes max: 64
int x, y, n, p, np;
fRGB c, tc, cm[64];
float sc, isc, u, v, sm, s, t, ofs, scalef[64];
CompBuf *tbuf1, *tbuf2, *gbuf;
const float cmo = 1.f - ndg->colmod;
const int qt = 1 << ndg->quality;
const float s1 = 4.f/(float)qt, s2 = 2.f*s1;
gbuf = BTP(src, ndg->threshold, qt);
tbuf1 = dupalloc_compbuf(gbuf);
IIR_gauss(tbuf1, s1, 0, 3);
IIR_gauss(tbuf1, s1, 1, 3);
IIR_gauss(tbuf1, s1, 2, 3);
tbuf2 = dupalloc_compbuf(tbuf1);
IIR_gauss(tbuf2, s2, 0, 3);
IIR_gauss(tbuf2, s2, 1, 3);
IIR_gauss(tbuf2, s2, 2, 3);
if (ndg->iter & 1) ofs = 0.5f; else ofs = 0.f;
for (x=0; x<(ndg->iter*4); x++) {
y = x & 3;
cm[x][0] = cm[x][1] = cm[x][2] = 1;
if (y==1) fRGB_rgbmult(cm[x], 1.f, cmo, cmo);
if (y==2) fRGB_rgbmult(cm[x], cmo, cmo, 1.f);
if (y==3) fRGB_rgbmult(cm[x], cmo, 1.f, cmo);
scalef[x] = 2.1f*(1.f-(x+ofs)/(float)(ndg->iter*4));
if (x & 1) scalef[x] = -0.99f/scalef[x];
}
sc = 2.13;
isc = -0.97;
for (y=0; y<gbuf->y; y++) {
v = (float)(y+0.5f) / (float)gbuf->y;
for (x=0; x<gbuf->x; x++) {
u = (float)(x+0.5f) / (float)gbuf->x;
s = (u-0.5f)*sc + 0.5f, t = (v-0.5f)*sc + 0.5f;
qd_getPixelLerp(tbuf1, s*gbuf->x, t*gbuf->y, c);
sm = smoothMask(s, t);
fRGB_mult(c, sm);
s = (u-0.5f)*isc + 0.5f, t = (v-0.5f)*isc + 0.5f;
qd_getPixelLerp(tbuf2, s*gbuf->x - 0.5f, t*gbuf->y - 0.5f, tc);
sm = smoothMask(s, t);
fRGB_madd(c, tc, sm);
qd_setPixel(gbuf, x, y, c);
}
}
memset(tbuf1->rect, 0, tbuf1->x*tbuf1->y*tbuf1->type*sizeof(float));
for (n=1; n<ndg->iter; n++) {
for (y=0; y<gbuf->y; y++) {
v = (float)(y+0.5f) / (float)gbuf->y;
for (x=0; x<gbuf->x; x++) {
u = (float)(x+0.5f) / (float)gbuf->x;
tc[0] = tc[1] = tc[2] = 0.f;
for (p=0;p<4;p++) {
np = (n<<2) + p;
s = (u-0.5f)*scalef[np] + 0.5f;
t = (v-0.5f)*scalef[np] + 0.5f;
qd_getPixelLerp(gbuf, s*gbuf->x - 0.5f, t*gbuf->y - 0.5f, c);
fRGB_colormult(c, cm[np]);
sm = smoothMask(s, t)*0.25f;
fRGB_madd(tc, c, sm);
}
p = (x + y*tbuf1->x)*tbuf1->type;
tbuf1->rect[p] += tc[0];
tbuf1->rect[p+1] += tc[1];
tbuf1->rect[p+2] += tc[2];
}
}
memcpy(gbuf->rect, tbuf1->rect, tbuf1->x*tbuf1->y*tbuf1->type*sizeof(float));
}
free_compbuf(tbuf1);
free_compbuf(tbuf2);
mixImages(dst, gbuf, 0.5f + 0.5f*ndg->mix);
free_compbuf(gbuf);
}
//--------------------------------------------------------------------------------------------
// Fog glow (convolution with kernel of exponential falloff)
static void fglow(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
int x, y;
float scale, u, v, r, w, d;
fRGB fcol;
CompBuf *tsrc, *ckrn;
unsigned int sz = 1 << ndg->size;
const float cs_r = 1.f, cs_g = 1.f, cs_b = 1.f;
// temp. src image
tsrc = BTP(src, ndg->threshold, 1 << ndg->quality);
// make the convolution kernel
ckrn = alloc_compbuf(sz, sz, CB_RGBA, 1);
scale = 0.25f*sqrtf(sz*sz);
for (y=0; y<sz; ++y) {
v = 2.f*(y / (float)sz) - 1.f;
for (x=0; x<sz; ++x) {
u = 2.f*(x / (float)sz) - 1.f;
r = (u*u + v*v)*scale;
d = -sqrtf(sqrtf(sqrtf(r)))*9.f;
fcol[0] = expf(d*cs_r), fcol[1] = expf(d*cs_g), fcol[2] = expf(d*cs_b);
// linear window good enough here, visual result counts, not scientific analysis
//w = (1.f-fabs(u))*(1.f-fabs(v));
// actually, Hanning window is ok, cos^2 for some reason is slower
w = (0.5f + 0.5f*cosf(u*(float)M_PI))*(0.5f + 0.5f*cosf(v*(float)M_PI));
fRGB_mult(fcol, w);
qd_setPixel(ckrn, x, y, fcol);
}
}
convolve(tsrc, tsrc, ckrn);
free_compbuf(ckrn);
mixImages(dst, tsrc, 0.5f + 0.5f*ndg->mix);
free_compbuf(tsrc);
}
//--------------------------------------------------------------------------------------------
static void node_composit_exec_glare(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *img = in[0]->data;
NodeGlare* ndg = node->storage;
if ((img == NULL) || (out[0]->hasoutput == 0)) return;
if (img->type != CB_RGBA)
new = typecheck_compbuf(img, CB_RGBA);
else
new = dupalloc_compbuf(img);
{
int x, y;
for (y=0; y<new->y; ++y) {
fRGB* col = (fRGB*)&new->rect[y*new->x*new->type];
for (x=0; x<new->x; ++x) {
col[x][0] = MAX2(col[x][0], 0.f);
col[x][1] = MAX2(col[x][1], 0.f);
col[x][2] = MAX2(col[x][2], 0.f);
}
}
}
switch (ndg->type) {
case 0:
star4(ndg, new, img);
break;
case 1:
fglow(ndg, new, img);
break;
case 3:
ghosts(ndg, new, img);
break;
case 2:
default:
streaks(ndg, new, img);
}
out[0]->data = new;
}
static void node_composit_init_glare(bNode* node)
{
NodeGlare *ndg = MEM_callocN(sizeof(NodeGlare), "node glare data");
ndg->quality = 1;
ndg->type = 2;
ndg->iter = 3;
ndg->colmod = 0.25;
ndg->mix = 0;
ndg->threshold = 1;
ndg->angle = 4;
ndg->angle_ofs = 0;
ndg->fade = 0.9;
ndg->size = 8;
node->storage = ndg;
}
bNodeType cmp_node_glare = {
/* *next,*prev */ NULL, NULL,
/* type code */ CMP_NODE_GLARE,
/* name */ "Glare",
/* width+range */ 150, 120, 200,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_glare_in,
/* output sock */ cmp_node_glare_out,
/* storage */ "NodeGlare",
/* execfunc */ node_composit_exec_glare,
/* butfunc */ NULL,
/* initfunc */ node_composit_init_glare,
/* freestoragefunc */ node_free_standard_storage,
/* copystoragefunc */ node_copy_standard_storage,
/* id */ NULL
};

View File

@@ -0,0 +1,192 @@
/**
*
* ***** 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): Alfredo de Greef (eeshlo)
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "../CMP_util.h"
static bNodeSocketType cmp_node_lensdist_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Distort", 0.f, 0.f, 0.f, 0.f, -0.999f, 1.f},
{ SOCK_VALUE, 1, "Dispersion", 0.f, 0.f, 0.f, 0.f, 0.f, 1.f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_lensdist_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void lensDistort(CompBuf* dst, CompBuf* src, float kr, float kg, float kb, int jit, int proj, int fit)
{
int x, y, z;
const float cx = 0.5f*(float)dst->x, cy = 0.5f*(float)dst->y;
if (proj) {
// shift
CompBuf* tsrc = dupalloc_compbuf(src);
for (z=0; z<tsrc->type; ++z)
IIR_gauss(tsrc, (kr+0.5f)*(kr+0.5f), z, 1);
kr *= 20.f;
for (y=0; y<dst->y; y++) {
fRGB* colp = (fRGB*)&dst->rect[y*dst->x*dst->type];
const float v = (y + 0.5f)/(float)dst->y;
for (x=0; x<dst->x; x++) {
const float u = (x + 0.5f)/(float)dst->x;
qd_getPixelLerpChan(tsrc, (u*dst->x + kr) - 0.5f, v*dst->y - 0.5f, 0, colp[x]);
if (tsrc->type == CB_VAL)
colp[x][1] = tsrc->rect[x + y*tsrc->x];
else
colp[x][1] = tsrc->rect[(x + y*tsrc->x)*tsrc->type + 1];
qd_getPixelLerpChan(tsrc, (u*dst->x - kr) - 0.5f, v*dst->y - 0.5f, 2, colp[x]+2);
}
}
free_compbuf(tsrc);
}
else {
// Spherical
// Scale factor to make bottom/top & right/left sides fit in window after deform
// so in the case of pincushion (kn < 0), corners will be outside window.
// Now also optionally scales image such that black areas are not visible when distort factor is positive
// (makes distorted corners match window corners, but really only valid if mk<=0.5)
const float mk = MAX3(kr, kg, kb);
const float sc = (fit && (mk > 0.f)) ? (1.f/(1.f + 2.f*mk)) : (1.f/(1.f + mk));
const float drg = 4.f*(kg - kr), dgb = 4.f*(kb - kg);
kr *= 4.f, kg *= 4.f, kb *= 4.f;
for (y=0; y<dst->y; y++) {
fRGB* colp = (fRGB*)&dst->rect[y*dst->x*dst->type];
const float v = sc*((y + 0.5f) - cy)/cy;
for (x=0; x<dst->x; x++) {
int dr = 0, dg = 0, db = 0;
float d, t, ln[6] = {0, 0, 0, 0, 0, 0};
fRGB c1, tc = {0, 0, 0, 0};
const float u = sc*((x + 0.5f) - cx)/cx;
int sta = 0, mid = 0, end = 0;
if ((t = 1.f - kr*(u*u + v*v)) >= 0.f) {
d = 1.f/(1.f + sqrtf(t));
ln[0] = (u*d + 0.5f)*dst->x - 0.5f, ln[1] = (v*d + 0.5f)*dst->y - 0.5f;
sta = 1;
}
if ((t = 1.f - kg*(u*u + v*v)) >= 0.f) {
d = 1.f/(1.f + sqrtf(t));
ln[2] = (u*d + 0.5f)*dst->x - 0.5f, ln[3] = (v*d + 0.5f)*dst->y - 0.5f;
mid = 1;
}
if ((t = 1.f - kb*(u*u + v*v)) >= 0.f) {
d = 1.f/(1.f + sqrtf(t));
ln[4] = (u*d + 0.5f)*dst->x - 0.5f, ln[5] = (v*d + 0.5f)*dst->y - 0.5f;
end = 1;
}
if (sta && mid && end) {
// RG
const int dx = ln[2] - ln[0], dy = ln[3] - ln[1];
const float dsf = sqrtf(dx*dx + dy*dy) + 1.f;
const int ds = (int)(jit ? ((dsf < 4.f) ? 2.f : sqrtf(dsf)) : dsf);
const float sd = 1.f/(float)ds;
for (z=0; z<ds; ++z) {
const float tz = ((float)z + (jit ? BLI_frand() : 0.5f))*sd;
t = 1.f - (kr + tz*drg)*(u*u + v*v);
d = 1.f / (1.f + sqrtf(t));
qd_getPixelLerp(src, (u*d + 0.5f)*dst->x - 0.5f, (v*d + 0.5f)*dst->y - 0.5f, c1);
if (src->type == CB_VAL) c1[1] = c1[2] = c1[0];
tc[0] += (1.f-tz)*c1[0], tc[1] += tz*c1[1];
dr++, dg++;
}
// GB
{
const int dx = ln[4] - ln[2], dy = ln[5] - ln[3];
const float dsf = sqrtf(dx*dx + dy*dy) + 1.f;
const int ds = (int)(jit ? ((dsf < 4.f) ? 2.f : sqrtf(dsf)) : dsf);
const float sd = 1.f/(float)ds;
for (z=0; z<ds; ++z) {
const float tz = ((float)z + (jit ? BLI_frand() : 0.5f))*sd;
t = 1.f - (kg + tz*dgb)*(u*u + v*v);
d = 1.f / (1.f + sqrtf(t));
qd_getPixelLerp(src, (u*d + 0.5f)*dst->x - 0.5f, (v*d + 0.5f)*dst->y - 0.5f, c1);
if (src->type == CB_VAL) c1[1] = c1[2] = c1[0];
tc[1] += (1.f-tz)*c1[1], tc[2] += tz*c1[2];
dg++, db++;
}
}
}
if (dr) colp[x][0] = 2.f*tc[0] / (float)dr;
if (dg) colp[x][1] = 2.f*tc[1] / (float)dg;
if (db) colp[x][2] = 2.f*tc[2] / (float)db;
}
}
}
}
static void node_composit_exec_lensdist(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *img = in[0]->data;
NodeLensDist* nld = node->storage;
const float k = MAX2(MIN2(in[1]->vec[0], 1.f), -0.999f);
// smaller dispersion range for somewhat more control
const float d = 0.25f*MAX2(MIN2(in[2]->vec[0], 1.f), 0.f);
const float kr = MAX2(MIN2((k+d), 1.f), -0.999f), kb = MAX2(MIN2((k-d), 1.f), -0.999f);
if ((img==NULL) || (out[0]->hasoutput==0)) return;
new = alloc_compbuf(img->x, img->y, CB_RGBA, 1);
lensDistort(new, img, (nld->proj ? d : kr), k, kb, nld->jit, nld->proj, nld->fit);
out[0]->data = new;
}
static void node_composit_init_lensdist(bNode* node)
{
NodeLensDist *nld = MEM_callocN(sizeof(NodeLensDist), "node lensdist data");
nld->jit = nld->proj = nld->fit = 0;
node->storage = nld;
}
bNodeType cmp_node_lensdist = {
/* *next,*prev */ NULL, NULL,
/* type code */ CMP_NODE_LENSDIST,
/* name */ "Lens Distortion",
/* width+range */ 150, 120, 200,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_lensdist_in,
/* output sock */ cmp_node_lensdist_out,
/* storage */ "NodeLensDist",
/* execfunc */ node_composit_exec_lensdist,
/* butfunc */ NULL,
/* initfunc */ node_composit_init_lensdist,
/* freestoragefunc */ node_free_standard_storage,
/* copystoragefunc */ node_copy_standard_storage,
/* id */ NULL
};

View File

@@ -0,0 +1,173 @@
/**
*
* ***** 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): Alfredo de Greef (eeshlo)
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "../CMP_util.h"
static bNodeSocketType cmp_node_tonemap_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_tonemap_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static float avgLogLum(CompBuf *src, float* auto_key, float* Lav, float* Cav)
{
float lsum = 0;
int p = src->x*src->y;
fRGB* bc = (fRGB*)src->rect;
float avl, maxl = -1e10f, minl = 1e10f;
const float sc = 1.f/(src->x*src->y);
*Lav = 0.f;
while (p--) {
float L = 0.212671f*bc[0][0] + 0.71516f*bc[0][1] + 0.072169f*bc[0][2];
*Lav += L;
fRGB_add(Cav, bc[0]);
lsum += logf(MAX2(L, 0.f) + 1e-5f);
maxl = (L > maxl) ? L : maxl;
minl = (L < minl) ? L : minl;
bc++;
}
*Lav *= sc;
fRGB_mult(Cav, sc);
maxl = logf(maxl + 1e-5f), minl = logf(minl + 1e-5f), avl = lsum*sc;
*auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.f;
return expf(avl);
}
void static tonemap(NodeTonemap* ntm, CompBuf* dst, CompBuf* src)
{
int x, y;
float dr, dg, db, al, igm = (ntm->gamma==0.f) ? 1 : (1.f / ntm->gamma);
float auto_key, Lav, Cav[3] = {0, 0, 0};
al = avgLogLum(src, &auto_key, &Lav, Cav);
al = (al == 0.f) ? 0.f : (ntm->key / al);
if (ntm->type == 1) {
// Reinhard/Devlin photoreceptor
const float f = expf(-ntm->f);
const float m = (ntm->m > 0.f) ? ntm->m : (0.3f + 0.7f*powf(auto_key, 1.4f));
const float ic = 1.f - ntm->c, ia = 1.f - ntm->a;
if (ntm->m == 0.f) printf("tonemap node, M: %g\n", m);
for (y=0; y<src->y; ++y) {
fRGB* sp = (fRGB*)&src->rect[y*src->x*src->type];
fRGB* dp = (fRGB*)&dst->rect[y*src->x*src->type];
for (x=0; x<src->x; ++x) {
const float L = 0.212671f*sp[x][0] + 0.71516f*sp[x][1] + 0.072169f*sp[x][2];
float I_l = sp[x][0] + ic*(L - sp[x][0]);
float I_g = Cav[0] + ic*(Lav - Cav[0]);
float I_a = I_l + ia*(I_g - I_l);
dp[x][0] /= (dp[x][0] + powf(f*I_a, m));
I_l = sp[x][1] + ic*(L - sp[x][1]);
I_g = Cav[1] + ic*(Lav - Cav[1]);
I_a = I_l + ia*(I_g - I_l);
dp[x][1] /= (dp[x][1] + powf(f*I_a, m));
I_l = sp[x][2] + ic*(L - sp[x][2]);
I_g = Cav[2] + ic*(Lav - Cav[2]);
I_a = I_l + ia*(I_g - I_l);
dp[x][2] /= (dp[x][2] + powf(f*I_a, m));
}
}
return;
}
// Reinhard simple photographic tm (simplest, not using whitepoint var)
for (y=0; y<src->y; y++) {
fRGB* sp = (fRGB*)&src->rect[y*src->x*src->type];
fRGB* dp = (fRGB*)&dst->rect[y*src->x*src->type];
for (x=0; x<src->x; x++) {
fRGB_copy(dp[x], sp[x]);
fRGB_mult(dp[x], al);
dr = dp[x][0] + ntm->offset;
dg = dp[x][1] + ntm->offset;
db = dp[x][2] + ntm->offset;
dp[x][0] /= ((dr == 0.f) ? 1.f : dr);
dp[x][1] /= ((dg == 0.f) ? 1.f : dg);
dp[x][2] /= ((db == 0.f) ? 1.f : db);
if (igm != 0.f) {
dp[x][0] = powf(MAX2(dp[x][0], 0.f), igm);
dp[x][1] = powf(MAX2(dp[x][1], 0.f), igm);
dp[x][2] = powf(MAX2(dp[x][2], 0.f), igm);
}
}
}
}
static void node_composit_exec_tonemap(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *img = in[0]->data;
if ((img==NULL) || (out[0]->hasoutput==0)) return;
if (img->type != CB_RGBA)
new = typecheck_compbuf(img, CB_RGBA);
else
new = dupalloc_compbuf(img);
tonemap(node->storage, new, img);
out[0]->data = new;
}
static void node_composit_init_tonemap(bNode* node)
{
NodeTonemap *ntm = MEM_callocN(sizeof(NodeTonemap), "node tonemap data");
ntm->type = 1;
ntm->key = 0.18;
ntm->offset = 1;
ntm->gamma = 1;
ntm->f = 0;
ntm->m = 0; // actual value is set according to input
// default a of 1 works well with natural HDR images, but not always so for cgi.
// Maybe should use 0 or at least lower initial value instead
ntm->a = 1;
ntm->c = 0;
node->storage = ntm;
}
bNodeType cmp_node_tonemap = {
/* *next,*prev */ NULL, NULL,
/* type code */ CMP_NODE_TONEMAP,
/* name */ "Tonemap",
/* width+range */ 150, 120, 200,
/* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS,
/* input sock */ cmp_node_tonemap_in,
/* output sock */ cmp_node_tonemap_out,
/* storage */ "NodeTonemap",
/* execfunc */ node_composit_exec_tonemap,
/* butfunc */ NULL,
/* initfunc */ node_composit_init_tonemap,
/* freestoragefunc */ node_free_standard_storage,
/* copystoragefunc */ node_copy_standard_storage,
/* id */ NULL
};

View File

@@ -29,9 +29,6 @@
#include "CMP_util.h"
CompBuf *alloc_compbuf(int sizex, int sizey, int type, int alloc)
{
CompBuf *cbuf= MEM_callocN(sizeof(CompBuf), "compbuf");
@@ -577,3 +574,593 @@ void gamma_correct_compbuf(CompBuf *img, int inversed)
}
}
}
/*
* 2D Fast Hartley Transform, used for convolution
*/
typedef float fREAL;
// returns next highest power of 2 of x, as well it's log2 in L2
static unsigned int nextPow2(unsigned int x, unsigned int* L2)
{
unsigned int pw, x_notpow2 = x & (x-1);
*L2 = 0;
while (x>>=1) ++(*L2);
pw = 1 << (*L2);
if (x_notpow2) { (*L2)++; pw<<=1; }
return pw;
}
//------------------------------------------------------------------------------
// from FXT library by Joerg Arndt, faster in order bitreversal
// use: r = revbin_upd(r, h) where h = N>>1
static unsigned int revbin_upd(unsigned int r, unsigned int h)
{
while (!((r^=h)&h)) h >>= 1;
return r;
}
//------------------------------------------------------------------------------
static void FHT(fREAL* data, unsigned int M, unsigned int inverse)
{
double tt, fc, dc, fs, ds, a = M_PI;
fREAL t1, t2;
int n2, bd, bl, istep, k, len = 1 << M, n = 1;
int i, j = 0;
unsigned int Nh = len >> 1;
for (i=1;i<(len-1);++i) {
j = revbin_upd(j, Nh);
if (j>i) {
t1 = data[i];
data[i] = data[j];
data[j] = t1;
}
}
do {
fREAL* data_n = &data[n];
istep = n << 1;
for (k=0; k<len; k+=istep) {
t1 = data_n[k];
data_n[k] = data[k] - t1;
data[k] += t1;
}
n2 = n >> 1;
if (n>2) {
fc = dc = cos(a);
fs = ds = sqrt(1.0 - fc*fc); //sin(a);
bd = n-2;
for (bl=1; bl<n2; bl++) {
fREAL* data_nbd = &data_n[bd];
fREAL* data_bd = &data[bd];
for (k=bl; k<len; k+=istep) {
t1 = fc*data_n[k] + fs*data_nbd[k];
t2 = fs*data_n[k] - fc*data_nbd[k];
data_n[k] = data[k] - t1;
data_nbd[k] = data_bd[k] - t2;
data[k] += t1;
data_bd[k] += t2;
}
tt = fc*dc - fs*ds;
fs = fs*dc + fc*ds;
fc = tt;
bd -= 2;
}
}
if (n>1) {
for (k=n2; k<len; k+=istep) {
t1 = data_n[k];
data_n[k] = data[k] - t1;
data[k] += t1;
}
}
n = istep;
a *= 0.5;
} while (n<len);
if (inverse) {
fREAL sc = (fREAL)1 / (fREAL)len;
for (k=0; k<len; ++k)
data[k] *= sc;
}
}
//------------------------------------------------------------------------------
/* 2D Fast Hartley Transform, Mx/My -> log2 of width/height,
nzp -> the row where zero pad data starts,
inverse -> see above */
static void FHT2D(fREAL *data, unsigned int Mx, unsigned int My,
unsigned int nzp, unsigned int inverse)
{
unsigned int i, j, Nx, Ny, maxy;
fREAL t;
Nx = 1 << Mx;
Ny = 1 << My;
// rows (forward transform skips 0 pad data)
maxy = inverse ? Ny : nzp;
for (j=0; j<maxy; ++j)
FHT(&data[Nx*j], Mx, inverse);
// transpose data
if (Nx==Ny) { // square
for (j=0; j<Ny; ++j)
for (i=j+1; i<Nx; ++i) {
unsigned int op = i + (j << Mx), np = j + (i << My);
t=data[op], data[op]=data[np], data[np]=t;
}
}
else { // rectangular
unsigned int k, Nym = Ny-1, stm = 1 << (Mx + My);
for (i=0; stm>0; i++) {
#define pred(k) (((k & Nym) << Mx) + (k >> My))
for (j=pred(i); j>i; j=pred(j));
if (j < i) continue;
for (k=i, j=pred(i); j!=i; k=j, j=pred(j), stm--)
{ t=data[j], data[j]=data[k], data[k]=t; }
#undef pred
stm--;
}
}
// swap Mx/My & Nx/Ny
i = Nx, Nx = Ny, Ny = i;
i = Mx, Mx = My, My = i;
// now columns == transposed rows
for (j=0; j<Ny; ++j)
FHT(&data[Nx*j], Mx, inverse);
// finalize
for (j=0; j<=(Ny >> 1); j++) {
unsigned int jm = (Ny - j) & (Ny-1);
unsigned int ji = j << Mx;
unsigned int jmi = jm << Mx;
for (i=0; i<=(Nx >> 1); i++) {
unsigned int im = (Nx - i) & (Nx-1);
fREAL A = data[ji + i];
fREAL B = data[jmi + i];
fREAL C = data[ji + im];
fREAL D = data[jmi + im];
fREAL E = (fREAL)0.5*((A + D) - (B + C));
data[ji + i] = A - E;
data[jmi + i] = B + E;
data[ji + im] = C + E;
data[jmi + im] = D - E;
}
}
}
//------------------------------------------------------------------------------
/* 2D convolution calc, d1 *= d2, M/N - > log2 of width/height */
static void fht_convolve(fREAL* d1, fREAL* d2, unsigned int M, unsigned int N)
{
fREAL a, b;
unsigned int i, j, k, L, mj, mL;
unsigned int m = 1 << M, n = 1 << N;
unsigned int m2 = 1 << (M-1), n2 = 1 << (N-1);
unsigned int mn2 = m << (N-1);
d1[0] *= d2[0];
d1[mn2] *= d2[mn2];
d1[m2] *= d2[m2];
d1[m2 + mn2] *= d2[m2 + mn2];
for (i=1; i<m2; i++) {
k = m - i;
a = d1[i]*d2[i] - d1[k]*d2[k];
b = d1[k]*d2[i] + d1[i]*d2[k];
d1[i] = (b + a)*(fREAL)0.5;
d1[k] = (b - a)*(fREAL)0.5;
a = d1[i + mn2]*d2[i + mn2] - d1[k + mn2]*d2[k + mn2];
b = d1[k + mn2]*d2[i + mn2] + d1[i + mn2]*d2[k + mn2];
d1[i + mn2] = (b + a)*(fREAL)0.5;
d1[k + mn2] = (b - a)*(fREAL)0.5;
}
for (j=1; j<n2; j++) {
L = n - j;
mj = j << M;
mL = L << M;
a = d1[mj]*d2[mj] - d1[mL]*d2[mL];
b = d1[mL]*d2[mj] + d1[mj]*d2[mL];
d1[mj] = (b + a)*(fREAL)0.5;
d1[mL] = (b - a)*(fREAL)0.5;
a = d1[m2 + mj]*d2[m2 + mj] - d1[m2 + mL]*d2[m2 + mL];
b = d1[m2 + mL]*d2[m2 + mj] + d1[m2 + mj]*d2[m2 + mL];
d1[m2 + mj] = (b + a)*(fREAL)0.5;
d1[m2 + mL] = (b - a)*(fREAL)0.5;
}
for (i=1; i<m2; i++) {
k = m - i;
for (j=1; j<n2; j++) {
L = n - j;
mj = j << M;
mL = L << M;
a = d1[i + mj]*d2[i + mj] - d1[k + mL]*d2[k + mL];
b = d1[k + mL]*d2[i + mj] + d1[i + mj]*d2[k + mL];
d1[i + mj] = (b + a)*(fREAL)0.5;
d1[k + mL] = (b - a)*(fREAL)0.5;
a = d1[i + mL]*d2[i + mL] - d1[k + mj]*d2[k + mj];
b = d1[k + mj]*d2[i + mL] + d1[i + mL]*d2[k + mj];
d1[i + mL] = (b + a)*(fREAL)0.5;
d1[k + mj] = (b - a)*(fREAL)0.5;
}
}
}
//------------------------------------------------------------------------------
void convolve(CompBuf* dst, CompBuf* in1, CompBuf* in2)
{
fREAL *data1, *data2, *fp;
unsigned int w2, h2, hw, hh, log2_w, log2_h;
fRGB wt, *colp;
int x, y, ch;
int xbl, ybl, nxb, nyb, xbsz, ybsz;
int in2done = 0;
CompBuf* rdst = alloc_compbuf(in1->x, in1->y, in1->type, 1);
// convolution result width & height
w2 = 2*in2->x - 1;
h2 = 2*in2->y - 1;
// FFT pow2 required size & log2
w2 = nextPow2(w2, &log2_w);
h2 = nextPow2(h2, &log2_h);
// alloc space
data1 = (fREAL*)MEM_callocN(3*w2*h2*sizeof(fREAL), "convolve_fast FHT data1");
data2 = (fREAL*)MEM_callocN(w2*h2*sizeof(fREAL), "convolve_fast FHT data2");
// normalize convolutor
wt[0] = wt[1] = wt[2] = 0.f;
for (y=0; y<in2->y; y++) {
colp = (fRGB*)&in2->rect[y*in2->x*in2->type];
for (x=0; x<in2->x; x++)
fRGB_add(wt, colp[x]);
}
if (wt[0] != 0.f) wt[0] = 1.f/wt[0];
if (wt[1] != 0.f) wt[1] = 1.f/wt[1];
if (wt[2] != 0.f) wt[2] = 1.f/wt[2];
for (y=0; y<in2->y; y++) {
colp = (fRGB*)&in2->rect[y*in2->x*in2->type];
for (x=0; x<in2->x; x++)
fRGB_colormult(colp[x], wt);
}
// copy image data, unpacking interleaved RGBA into separate channels
// only need to calc data1 once
// block add-overlap
hw = in2->x >> 1;
hh = in2->y >> 1;
xbsz = (w2 + 1) - in2->x;
ybsz = (h2 + 1) - in2->y;
nxb = in1->x / xbsz;
if (in1->x % xbsz) nxb++;
nyb = in1->y / ybsz;
if (in1->y % ybsz) nyb++;
for (ybl=0; ybl<nyb; ybl++) {
for (xbl=0; xbl<nxb; xbl++) {
// each channel one by one
for (ch=0; ch<3; ch++) {
fREAL* data1ch = &data1[ch*w2*h2];
// only need to calc fht data from in2 once, can re-use for every block
if (!in2done) {
// in2, channel ch -> data1
for (y=0; y<in2->y; y++) {
fp = &data1ch[y*w2];
colp = (fRGB*)&in2->rect[y*in2->x*in2->type];
for (x=0; x<in2->x; x++)
fp[x] = colp[x][ch];
}
}
// in1, channel ch -> data2
memset(data2, 0, w2*h2*sizeof(fREAL));
for (y=0; y<ybsz; y++) {
int yy = ybl*ybsz + y;
if (yy >= in1->y) continue;
fp = &data2[y*w2];
colp = (fRGB*)&in1->rect[yy*in1->x*in1->type];
for (x=0; x<xbsz; x++) {
int xx = xbl*xbsz + x;
if (xx >= in1->x) continue;
fp[x] = colp[xx][ch];
}
}
// forward FHT
// zero pad data start is different for each == height+1
if (!in2done) FHT2D(data1ch, log2_w, log2_h, in2->y+1, 0);
FHT2D(data2, log2_w, log2_h, in2->y+1, 0);
// FHT2D transposed data, row/col now swapped
// convolve & inverse FHT
fht_convolve(data2, data1ch, log2_h, log2_w);
FHT2D(data2, log2_h, log2_w, 0, 1);
// data again transposed, so in order again
// overlap-add result
for (y=0; y<(int)h2; y++) {
const int yy = ybl*ybsz + y - hh;
if ((yy < 0) || (yy >= in1->y)) continue;
fp = &data2[y*w2];
colp = (fRGB*)&rdst->rect[yy*in1->x*in1->type];
for (x=0; x<(int)w2; x++) {
const int xx = xbl*xbsz + x - hw;
if ((xx < 0) || (xx >= in1->x)) continue;
colp[xx][ch] += fp[x];
}
}
}
in2done = 1;
}
}
MEM_freeN(data2);
MEM_freeN(data1);
memcpy(dst->rect, rdst->rect, sizeof(float)*dst->x*dst->y*dst->type);
free_compbuf(rdst);
}
/*
*
* Utility functions qd_* should probably be intergrated better with other functions here.
*
*/
// sets fcol to pixelcolor at (x, y)
void qd_getPixel(CompBuf* src, int x, int y, float* col)
{
if ((x >= 0) && (x < src->x) && (y >= 0) && (y < src->y)) {
float* bc = &src->rect[(x + y*src->x)*src->type];
col[0] = bc[0], col[1] = bc[1], col[2] = bc[2];
}
else col[0] = col[1] = col[2] = 0.f;
}
// sets pixel (x, y) to color col
void qd_setPixel(CompBuf* src, int x, int y, float* col)
{
if ((x >= 0) && (x < src->x) && (y >= 0) && (y < src->y)) {
float* bc = &src->rect[(x + y*src->x)*src->type];
bc[0] = col[0], bc[1] = col[1], bc[2] = col[2];
}
}
// adds fcol to pixelcolor (x, y)
void qd_addPixel(CompBuf* src, int x, int y, float* col)
{
if ((x >= 0) && (x < src->x) && (y >= 0) && (y < src->y)) {
float* bc = &src->rect[(x + y*src->x)*src->type];
bc[0] += col[0], bc[1] += col[1], bc[2] += col[2];
}
}
// multiplies pixel by factor value f
void qd_multPixel(CompBuf* src, int x, int y, float f)
{
if ((x >= 0) && (x < src->x) && (y >= 0) && (y < src->y)) {
float* bc = &src->rect[(x + y*src->x)*src->type];
bc[0] *= f, bc[1] *= f, bc[2] *= f;
}
}
// bilinear interpolation with wraparound
void qd_getPixelLerpWrap(CompBuf* src, float u, float v, float* col)
{
const float ufl = floorf(u), vfl = floorf(v);
const int nx = (int)ufl % src->x, ny = (int)vfl % src->y;
const int x1 = (nx < 0) ? (nx + src->x) : nx;
const int y1 = (ny < 0) ? (ny + src->y) : ny;
const int x2 = (x1 + 1) % src->x, y2 = (y1 + 1) % src->y;
const float* c00 = &src->rect[(x1 + y1*src->x)*src->type];
const float* c10 = &src->rect[(x2 + y1*src->x)*src->type];
const float* c01 = &src->rect[(x1 + y2*src->x)*src->type];
const float* c11 = &src->rect[(x2 + y2*src->x)*src->type];
const float uf = u - ufl, vf = v - vfl;
const float w00=(1.f-uf)*(1.f-vf), w10=uf*(1.f-vf), w01=(1.f-uf)*vf, w11=uf*vf;
col[0] = w00*c00[0] + w10*c10[0] + w01*c01[0] + w11*c11[0];
if (src->type != CB_VAL) {
col[1] = w00*c00[1] + w10*c10[1] + w01*c01[1] + w11*c11[1];
col[2] = w00*c00[2] + w10*c10[2] + w01*c01[2] + w11*c11[2];
col[3] = w00*c00[3] + w10*c10[3] + w01*c01[3] + w11*c11[3];
}
}
// as above, without wrap around
void qd_getPixelLerp(CompBuf* src, float u, float v, float* col)
{
const float ufl = floorf(u), vfl = floorf(v);
const int x1 = (int)ufl, y1 = (int)vfl;
const int x2 = (int)ceilf(u), y2 = (int)ceilf(v);
if ((x2 >= 0) && (y2 >= 0) && (x1 < src->x) && (y1 < src->y)) {
const float B[4] = {0,0,0,0};
const int ox1 = (x1 < 0), oy1 = (y1 < 0), ox2 = (x2 >= src->x), oy2 = (y2 >= src->y);
const float* c00 = (ox1 || oy1) ? B : &src->rect[(x1 + y1*src->x)*src->type];
const float* c10 = (ox2 || oy1) ? B : &src->rect[(x2 + y1*src->x)*src->type];
const float* c01 = (ox1 || oy2) ? B : &src->rect[(x1 + y2*src->x)*src->type];
const float* c11 = (ox2 || oy2) ? B : &src->rect[(x2 + y2*src->x)*src->type];
const float uf = u - ufl, vf = v - vfl;
const float w00=(1.f-uf)*(1.f-vf), w10=uf*(1.f-vf), w01=(1.f-uf)*vf, w11=uf*vf;
col[0] = w00*c00[0] + w10*c10[0] + w01*c01[0] + w11*c11[0];
if (src->type != CB_VAL) {
col[1] = w00*c00[1] + w10*c10[1] + w01*c01[1] + w11*c11[1];
col[2] = w00*c00[2] + w10*c10[2] + w01*c01[2] + w11*c11[2];
col[3] = w00*c00[3] + w10*c10[3] + w01*c01[3] + w11*c11[3];
}
}
else col[0] = col[1] = col[2] = col[3] = 0.f;
}
// as above, sampling only one channel
void qd_getPixelLerpChan(CompBuf* src, float u, float v, int chan, float* out)
{
const float ufl = floorf(u), vfl = floorf(v);
const int x1 = (int)ufl, y1 = (int)vfl;
const int x2 = (int)ceilf(u), y2 = (int)ceilf(v);
if (chan >= src->type) chan = 0;
if ((x2 >= 0) && (y2 >= 0) && (x1 < src->x) && (y1 < src->y)) {
const float B[4] = {0,0,0,0};
const int ox1 = (x1 < 0), oy1 = (y1 < 0), ox2 = (x2 >= src->x), oy2 = (y2 >= src->y);
const float* c00 = (ox1 || oy1) ? B : &src->rect[(x1 + y1*src->x)*src->type + chan];
const float* c10 = (ox2 || oy1) ? B : &src->rect[(x2 + y1*src->x)*src->type + chan];
const float* c01 = (ox1 || oy2) ? B : &src->rect[(x1 + y2*src->x)*src->type + chan];
const float* c11 = (ox2 || oy2) ? B : &src->rect[(x2 + y2*src->x)*src->type + chan];
const float uf = u - ufl, vf = v - vfl;
const float w00=(1.f-uf)*(1.f-vf), w10=uf*(1.f-vf), w01=(1.f-uf)*vf, w11=uf*vf;
out[0] = w00*c00[0] + w10*c10[0] + w01*c01[0] + w11*c11[0];
}
else *out = 0.f;
}
CompBuf* qd_downScaledCopy(CompBuf* src, int scale)
{
CompBuf* fbuf;
if (scale <= 1)
fbuf = dupalloc_compbuf(src);
else {
int nw = src->x/scale, nh = src->y/scale;
if ((2*(src->x % scale)) > scale) nw++;
if ((2*(src->y % scale)) > scale) nh++;
fbuf = alloc_compbuf(nw, nh, src->type, 1);
{
int x, y, xx, yy, sx, sy, mx, my;
float colsum[4];
float fscale = 1.f/(float)(scale*scale);
for (y=0; y<nh; y++) {
fRGB* fcolp = (fRGB*)&fbuf->rect[y*fbuf->x*fbuf->type];
yy = y*scale;
my = yy + scale;
if (my > src->y) my = src->y;
for (x=0; x<nw; x++) {
xx = x*scale;
mx = xx + scale;
if (mx > src->x) mx = src->x;
colsum[0] = colsum[1] = colsum[2] = 0.f;
for (sy=yy; sy<my; sy++) {
fRGB* scolp = (fRGB*)&src->rect[sy*src->x*src->type];
for (sx=xx; sx<mx; sx++)
fRGB_add(colsum, scolp[sx]);
}
fRGB_mult(colsum, fscale);
fRGB_copy(fcolp[x], colsum);
}
}
}
}
return fbuf;
}
// fast g.blur, per channel
// xy var. bits 1 & 2 ca be used to blur in x or y direction separately
void IIR_gauss(CompBuf* src, float sigma, int chan, int xy)
{
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
float *X, *Y, *W;
int i, x, y, sz;
// <0.5 not valid, though can have a possibly useful sort of sharpening effect
if (sigma < 0.5) return;
if ((xy < 1) || (xy > 3)) xy = 3;
// 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(src->x, src->y);
X = MEM_callocN(sz*sizeof(float), "IIR_gauss X buf");
Y = MEM_callocN(sz*sizeof(float), "IIR_gauss Y buf");
W = MEM_callocN(sz*sizeof(float), "IIR_gauss W buf");
if (xy & 1) { // H
for (y=0; y<src->y; ++y) {
const int yx = y*src->x;
for (x=0; x<src->x; ++x)
X[x] = src->rect[(x + yx)*src->type + chan];
YVV(src->x);
for (x=0; x<src->x; ++x)
src->rect[(x + yx)*src->type + chan] = Y[x];
}
}
if (xy & 2) { // V
for (x=0; x<src->x; ++x) {
for (y=0; y<src->y; ++y)
X[y] = src->rect[(x + y*src->x)*src->type + chan];
YVV(src->y);
for (y=0; y<src->y; ++y)
src->rect[(x + y*src->x)*src->type + chan] = Y[y];
}
}
MEM_freeN(X);
MEM_freeN(W);
MEM_freeN(Y);
#undef YVV
}

View File

@@ -176,7 +176,46 @@ void do_hsva_to_rgba(bNode *node, float *out, float *in);
void do_ycca_to_rgba(bNode *node, float *out, float *in);
void gamma_correct_compbuf(CompBuf *img, int inversed);
void convolve(CompBuf* dst, CompBuf* in1, CompBuf* in2);
extern void node_ID_title_cb(void *node_v, void *unused_v);
/* utility functions used by glare, tonemap and lense distortion */
/* soms macros for color handling */
typedef float fRGB[4];
/* clear color */
#define fRGB_clear(c) { c[0]=c[1]=c[2]=0.f; }
/* copy c2 to c1 */
#define fRGB_copy(c1, c2) { c1[0]=c2[0]; c1[1]=c2[1]; c1[2]=c2[2]; }
/* add c2 to c1 */
#define fRGB_add(c1, c2) { c1[0]+=c2[0]; c1[1]+=c2[1]; c1[2]+=c2[2]; }
/* subtract c2 from c1 */
#define fRGB_sub(c1, c2) { c1[0]-=c2[0]; c1[1]-=c2[1]; c1[2]-=c2[2]; }
/* multiply c by float value s */
#define fRGB_mult(c, s) { c[0]*=s; c[1]*=s; c[2]*=s; }
/* multiply c2 by s and add to c1 */
#define fRGB_madd(c1, c2, s) { c1[0]+=c2[0]*s; c1[1]+=c2[1]*s; c1[2]+=c2[2]*s; }
/* multiply c2 by color c1 */
#define fRGB_colormult(c, cs) { c[0]*=cs[0]; c[1]*=cs[1]; c[2]*=cs[2]; }
/* multiply c2 by color c3 and add to c1 */
#define fRGB_colormadd(c1, c2, c3) { c1[0]+=c2[0]*c3[0]; c1[1]+=c2[1]*c3[1]; c1[2]+=c2[2]*c3[2]; }
/* multiply c2 by color rgb, rgb as separate arguments */
#define fRGB_rgbmult(c, r, g, b) { c[0]*=(r); c[1]*=(g); c[2]*=(b); }
/* swap colors c1 & c2 */
#define fRGB_swap(c1, c2) { float _t=c1[0]; c1[0]=c2[0]; c2[0]=_t;\
_t=c1[1]; c1[1]=c2[1]; c2[1]=_t;\
_t=c1[2]; c1[2]=c2[2]; c2[2]=_t; }
void qd_getPixel(CompBuf* src, int x, int y, float* col);
void qd_setPixel(CompBuf* src, int x, int y, float* col);
void qd_addPixel(CompBuf* src, int x, int y, float* col);
void qd_multPixel(CompBuf* src, int x, int y, float f);
void qd_getPixelLerpWrap(CompBuf* src, float u, float v, float* col);
void qd_getPixelLerp(CompBuf* src, float u, float v, float* col);
void qd_getPixelLerpChan(CompBuf* src, float u, float v, int chan, float* out);
CompBuf* qd_downScaledCopy(CompBuf* src, int scale);
void IIR_gauss(CompBuf* src, float sigma, int chan, int xy);
/* end utility funcs */
#endif

View File

@@ -1057,18 +1057,22 @@ static int node_composit_buts_blur(uiBlock *block, bNodeTree *ntree, bNode *node
char str[256];
uiBlockBeginAlign(block);
sprintf(str, "Filter Type%%t|Flat %%x%d|Tent %%x%d|Quad %%x%d|Cubic %%x%d|Gauss %%x%d|CatRom %%x%d|Mitch %%x%d", R_FILTER_BOX, R_FILTER_TENT, R_FILTER_QUAD, R_FILTER_CUBIC, R_FILTER_GAUSS, R_FILTER_CATROM, R_FILTER_MITCH);
uiDefButS(block, MENU, B_NODE_EXEC+node->nr,str,
sprintf(str, "Filter Type%%t|Flat %%x%d|Tent %%x%d|Quad %%x%d|Cubic %%x%d|Gauss %%x%d|Fast Gauss%%x%d|CatRom %%x%d|Mitch %%x%d", R_FILTER_BOX, R_FILTER_TENT, R_FILTER_QUAD, R_FILTER_CUBIC, R_FILTER_GAUSS, R_FILTER_FAST_GAUSS, R_FILTER_CATROM, R_FILTER_MITCH);
uiDefButS(block, MENU, B_NODE_EXEC+node->nr,str,
butr->xmin, dy, dx*2, 19,
&nbd->filtertype, 0, 0, 0, 0, "Set sampling filter for blur");
dy-=19;
uiDefButC(block, TOG, B_NODE_EXEC+node->nr, "Bokeh",
butr->xmin, dy, dx, 19,
&nbd->bokeh, 0, 0, 0, 0, "Uses circular filter, warning it's slow!");
uiDefButC(block, TOG, B_NODE_EXEC+node->nr, "Gamma",
butr->xmin+dx, dy, dx, 19,
&nbd->gamma, 0, 0, 0, 0, "Applies filter on gamma corrected values");
dy-=19;
if (nbd->filtertype != R_FILTER_FAST_GAUSS) {
uiDefButC(block, TOG, B_NODE_EXEC+node->nr, "Bokeh",
butr->xmin, dy, dx, 19,
&nbd->bokeh, 0, 0, 0, 0, "Uses circular filter, warning it's slow!");
uiDefButC(block, TOG, B_NODE_EXEC+node->nr, "Gamma",
butr->xmin+dx, dy, dx, 19,
&nbd->gamma, 0, 0, 0, 0, "Applies filter on gamma corrected values");
} else {
uiBlockEndAlign(block);
uiBlockBeginAlign(block);
}
dy-=19;
bt=uiDefButS(block, NUM, B_NODE_EXEC+node->nr, "X:",
butr->xmin, dy, dx, 19,
@@ -1076,6 +1080,7 @@ static int node_composit_buts_blur(uiBlock *block, bNodeTree *ntree, bNode *node
bt=uiDefButS(block, NUM, B_NODE_EXEC+node->nr, "Y:",
butr->xmin+dx, dy, dx, 19,
&nbd->sizey, 0, 256, 0, 0, "");
uiBlockEndAlign(block);
}
return 57;
}
@@ -1134,6 +1139,145 @@ static int node_composit_buts_defocus(uiBlock *block, bNodeTree *ntree, bNode *n
return 228;
}
/* qdn: glare node */
static int node_composit_buts_glare(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *butr)
{
if(block) {
NodeGlare *ndg = node->storage;
short dy = butr->ymin + 152, dx = butr->xmax - butr->xmin;
char* mn1 = "Type%t|Ghosts%x3|Streaks%x2|Fog Glow%x1|Simple Star%x0";
char* mn2 = "Quality/Speed%t|High/Slow%x0|Medium/Medium%x1|Low/Fast%x2";
uiDefButC(block, MENU, B_NODE_EXEC+node->nr, mn1,
butr->xmin, dy, dx, 19,
&ndg->type, 0, 0, 0, 0, "Glow/Flare/Bloom type");
uiDefButC(block, MENU, B_NODE_EXEC+node->nr, mn2,
butr->xmin, dy-19, dx, 19,
&ndg->quality, 0, 0, 0, 0,
"Quality speed trade off, if not set to high quality, effect will be applied to low-res copy of source image");
if (ndg->type != 1) {
uiDefButC(block, NUM, B_NODE_EXEC+node->nr, "Iterations:",
butr->xmin, dy-38, dx, 19,
&ndg->iter, 2, 5, 1, 0,
"higher values will generate longer/more streaks/ghosts");
if (ndg->type != 0)
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "ColMod:",
butr->xmin, dy-57, dx, 19,
&ndg->colmod, 0, 1, 10, 0,
"Amount of Color Modulation, modulates colors of streaks and ghosts for a spectral dispersion effect");
}
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Mix:",
butr->xmin, dy-76, dx, 19,
&ndg->mix, -1, 1, 10, 0,
"Mix balance, -1 is original image only, 0 is exact 50/50 mix, 1 is processed image only");
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Threshold:",
butr->xmin, dy-95, dx, 19,
&ndg->threshold, 0, 1000, 10, 0,
"Brightness threshold, the glarefilter will be applied only to pixels brighter than this value");
if ((ndg->type == 2) || (ndg->type == 0))
{
if (ndg->type == 2) {
uiDefButC(block, NUM, B_NODE_EXEC+node->nr, "streaks:",
butr->xmin, dy-114, dx, 19,
&ndg->angle, 2, 16, 1000, 0,
"Total number of streaks");
uiDefButC(block, NUM, B_NODE_EXEC+node->nr, "AngOfs:",
butr->xmin, dy-133, dx, 19,
&ndg->angle_ofs, 0, 180, 1000, 0,
"Streak angle rotation offset in degrees");
}
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Fade:",
butr->xmin, dy-152, dx, 19,
&ndg->fade, 0.75, 1, 5, 0,
"Streak fade out factor");
}
if (ndg->type == 0)
uiDefButC(block, TOG, B_NODE_EXEC+node->nr, "Rot45",
butr->xmin, dy-114, dx, 19,
&ndg->angle, 0, 0, 0, 0,
"simple star filter, add 45 degree rotation offset");
if ((ndg->type == 1) || (ndg->type > 3)) // PBGH and fog glow
uiDefButC(block, NUM, B_NODE_EXEC+node->nr, "Size:",
butr->xmin, dy-114, dx, 19,
&ndg->size, 6, 9, 1000, 0,
"glow/glare size (not actual size, relative to initial size of bright area of pixels)");
}
return 171;
}
/* qdn: tonemap node */
static int node_composit_buts_tonemap(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *butr)
{
if(block) {
NodeTonemap *ntm = node->storage;
short dy = butr->ymin + 76, dx = butr->xmax - butr->xmin;
char* mn = "Type%t|R/D Photoreceptor%x1|Rh Simple%x0";
uiBlockBeginAlign(block);
uiDefButI(block, MENU, B_NODE_EXEC+node->nr, mn,
butr->xmin, dy, dx, 19,
&ntm->type, 0, 0, 0, 0,
"Tone mapping type");
if (ntm->type == 0) {
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Key:",
butr->xmin, dy-19, dx, 19,
&ntm->key, 0, 1, 5, 0,
"The value the average luminance is mapped to");
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Offset:",
butr->xmin, dy-38, dx, 19,
&ntm->offset, 0.001, 10, 5, 0,
"Tonemap offset, normally always 1, but can be used as an extra control to alter the brightness curve");
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Gamma:",
butr->xmin, dy-57, dx, 19,
&ntm->gamma, 0.001, 3, 5, 0,
"Gamma factor, if not used, set to 1");
}
else {
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Intensity:",
butr->xmin, dy-19, dx, 19,
&ntm->f, -8, 8, 10, 0, "if less than zero, darkens image, otherwise makes it brighter");
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Contrast:",
butr->xmin, dy-38, dx, 19,
&ntm->m, 0, 1, 5, 0, "Set to 0 to use estimate from input image");
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "Adaptation:",
butr->xmin, dy-57, dx, 19,
&ntm->a, 0, 1, 5, 0, "if 0, global, if 1, based on pixel intensity");
uiDefButF(block, NUM, B_NODE_EXEC+node->nr, "ColCorrect:",
butr->xmin, dy-76, dx, 19,
&ntm->c, 0, 1, 5, 0, "color correction, if 0, same for all channels, if 1, each independent");
}
uiBlockEndAlign(block);
}
return 95;
}
/* qdn: lens distortion node */
static int node_composit_buts_lensdist(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *butr)
{
if(block) {
NodeLensDist *nld = node->storage;
short dy = butr->ymin + 19, dx = butr->xmax - butr->xmin;
uiBlockBeginAlign(block);
uiDefButS(block, TOG, B_NODE_EXEC+node->nr, "Projector",
butr->xmin, dy, dx, 19,
&nld->proj, 0, 0, 0, 0,
"Enable/disable projector mode, effect is applied in horizontal direction only");
if (!nld->proj) {
uiDefButS(block, TOG, B_NODE_EXEC+node->nr, "Jitter",
butr->xmin, dy-19, dx/2, 19,
&nld->jit, 0, 0, 0, 0,
"Enable/disable jittering, faster, but also noisier");
uiDefButS(block, TOG, B_NODE_EXEC+node->nr, "Fit",
butr->xmin+dx/2, dy-19, dx/2, 19,
&nld->fit, 0, 0, 0, 0,
"For positive distortion factor only, scale image such that black areas are not visible");
}
uiBlockEndAlign(block);
}
return 38;
}
static int node_composit_buts_vecblur(uiBlock *block, bNodeTree *ntree, bNode *node, rctf *butr)
{
if(block) {
@@ -1617,10 +1761,22 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_BLUR:
ntype->butfunc= node_composit_buts_blur;
break;
/* qdn: defocus node */
/* qdn: defocus node */
case CMP_NODE_DEFOCUS:
ntype->butfunc = node_composit_buts_defocus;
break;
/* qdn: glare node */
case CMP_NODE_GLARE:
ntype->butfunc = node_composit_buts_glare;
break;
/* qdn: tonemap node */
case CMP_NODE_TONEMAP:
ntype->butfunc = node_composit_buts_tonemap;
break;
/* qdn: lens distortion node */
case CMP_NODE_LENSDIST:
ntype->butfunc = node_composit_buts_lensdist;
break;
case CMP_NODE_VECBLUR:
ntype->butfunc= node_composit_buts_vecblur;
break;