diff options
author | Campbell Barton <ideasman42@gmail.com> | 2007-10-26 19:32:36 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2007-10-26 19:32:36 +0400 |
commit | 2a2453d3e20e662d5af05b359a71458fa8daabf1 (patch) | |
tree | 059356f764dcab1911e0785c098284eabcccad22 | |
parent | 83eba96db23fd438369ffae1197b5b40737164c0 (diff) |
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.
-rw-r--r-- | source/blender/blenkernel/BKE_node.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 4 | ||||
-rw-r--r-- | source/blender/imbuf/intern/radiance_hdr.c | 29 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 20 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_scene_types.h | 1 | ||||
-rw-r--r-- | source/blender/nodes/CMP_node.h | 6 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_nodes/CMP_blur.c | 131 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_nodes/CMP_defocus.c | 6 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_nodes/CMP_glare.c | 498 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_nodes/CMP_lensdist.c | 192 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_nodes/CMP_tonemap.c | 173 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_util.c | 593 | ||||
-rw-r--r-- | source/blender/nodes/intern/CMP_util.h | 39 | ||||
-rw-r--r-- | source/blender/src/drawnode.c | 178 |
14 files changed, 1790 insertions, 84 deletions
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 2b244656f75..8cd189d0eea 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -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 diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 1ee0a9c52e3..49e4269ac94 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -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) diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 75be790a4cc..3cb9ca79ffc 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -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); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 63ead419766..40ceb9f0c46 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -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 diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index bab373c2424..031a88c517a 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -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 diff --git a/source/blender/nodes/CMP_node.h b/source/blender/nodes/CMP_node.h index 6e800303887..14cb7b2f2ae 100644 --- a/source/blender/nodes/CMP_node.h +++ b/source/blender/nodes/CMP_node.h @@ -97,6 +97,8 @@ extern bNodeType cmp_node_flip; extern bNodeType cmp_node_displace; extern bNodeType cmp_node_mapuv; -#endif - +extern bNodeType cmp_node_glare; +extern bNodeType cmp_node_tonemap; +extern bNodeType cmp_node_lensdist; +#endif diff --git a/source/blender/nodes/intern/CMP_nodes/CMP_blur.c b/source/blender/nodes/intern/CMP_nodes/CMP_blur.c index 91a04e3128a..8ef4af4d219 100644 --- a/source/blender/nodes/intern/CMP_nodes/CMP_blur.c +++ b/source/blender/nodes/intern/CMP_nodes/CMP_blur.c @@ -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; + 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); } - out[0]->data= new; - } - else { + else { + 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; - if(in[1]->vec[0]<=0.001f) { /* time node inputs can be a tiny value */ - new= pass_on_compbuf(img); + } else { + /* All non fast gauss blur methods */ + if(img->type==CB_VEC2 || img->type==CB_VEC3) { + img= typecheck_compbuf(in[0]->data, CB_RGBA); } - else { - NodeBlurData *nbd= node->storage; - CompBuf *gammabuf; + + /* 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; + } + 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; } - 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) diff --git a/source/blender/nodes/intern/CMP_nodes/CMP_defocus.c b/source/blender/nodes/intern/CMP_nodes/CMP_defocus.c index ee68b81cde8..53d01ab1d12 100644 --- a/source/blender/nodes/intern/CMP_nodes/CMP_defocus.c +++ b/source/blender/nodes/intern/CMP_nodes/CMP_defocus.c @@ -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, diff --git a/source/blender/nodes/intern/CMP_nodes/CMP_glare.c b/source/blender/nodes/intern/CMP_nodes/CMP_glare.c new file mode 100644 index 00000000000..9943dd2246d --- /dev/null +++ b/source/blender/nodes/intern/CMP_nodes/CMP_glare.c @@ -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 +}; diff --git a/source/blender/nodes/intern/CMP_nodes/CMP_lensdist.c b/source/blender/nodes/intern/CMP_nodes/CMP_lensdist.c new file mode 100644 index 00000000000..5dec6115fba --- /dev/null +++ b/source/blender/nodes/intern/CMP_nodes/CMP_lensdist.c @@ -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 +}; diff --git a/source/blender/nodes/intern/CMP_nodes/CMP_tonemap.c b/source/blender/nodes/intern/CMP_nodes/CMP_tonemap.c new file mode 100644 index 00000000000..b7c0475884b --- /dev/null +++ b/source/blender/nodes/intern/CMP_nodes/CMP_tonemap.c @@ -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 +}; diff --git a/source/blender/nodes/intern/CMP_util.c b/source/blender/nodes/intern/CMP_util.c index 0c6834315aa..b6b73221384 100644 --- a/source/blender/nodes/intern/CMP_util.c +++ b/source/blender/nodes/intern/CMP_util.c @@ -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 +} + diff --git a/source/blender/nodes/intern/CMP_util.h b/source/blender/nodes/intern/CMP_util.h index 83f85160625..7cb10b75f3a 100644 --- a/source/blender/nodes/intern/CMP_util.h +++ b/source/blender/nodes/intern/CMP_util.h @@ -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 diff --git a/source/blender/src/drawnode.c b/source/blender/src/drawnode.c index 30d70ba3c43..9c82b723a04 100644 --- a/source/blender/src/drawnode.c +++ b/source/blender/src/drawnode.c @@ -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; |