This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/node_composite.c
Alfredo de Greef cbbd736a9d bug #5772, now whenever the 'no zbuffer' option is enabled (which shouldn't
have been in this particular case anyway), the blur radius will automatically
be limited to half the width or height of the image.

Nothing to do with this bug, but also now skips image region outside
borders when border rendering is enabled.
2007-01-22 06:08:19 +00:00

6053 lines
167 KiB
C

/**
* $Id$
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "MEM_guardedalloc.h"
#include "DNA_camera_types.h" /* qdn: defocus node, need camera info */
#include "DNA_ID.h"
#include "DNA_image_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_material_types.h"
#include "DNA_scene_types.h"
#include "DNA_texture_types.h"
#include "DNA_vec_types.h"
#include "BKE_blender.h"
#include "BKE_colortools.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_texture.h"
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "BLI_rand.h"
#include "BLI_threads.h"
#include "IMB_imbuf_types.h"
#include "IMB_imbuf.h"
#include "RE_pipeline.h"
#include "RE_shader_ext.h" /* <- TexResult */
#include "RE_render_ext.h" /* <- ibuf_sample() */
/* *************************** operations support *************************** */
/* general signal that's in output sockets, and goes over the wires */
typedef struct CompBuf {
float *rect;
int x, y, xrad, yrad;
short type, malloc;
rcti disprect; /* cropped part of image */
int xof, yof; /* relative to center of target image */
void (*rect_procedural)(struct CompBuf *, float *, float, float);
bNode *node; /* only in use for procedural bufs */
struct CompBuf *next, *prev; /* for pass-on, works nicer than reference counting */
} CompBuf;
/* defines also used for pixel size */
#define CB_RGBA 4
#define CB_VEC4 4
#define CB_VEC3 3
#define CB_VEC2 2
#define CB_VAL 1
/* defines for RGBA channels */
#define CHAN_R 0
#define CHAN_G 1
#define CHAN_B 2
#define CHAN_A 3
static CompBuf *alloc_compbuf(int sizex, int sizey, int type, int alloc)
{
CompBuf *cbuf= MEM_callocN(sizeof(CompBuf), "compbuf");
cbuf->x= sizex;
cbuf->y= sizey;
cbuf->xrad= sizex/2;
cbuf->yrad= sizey/2;
cbuf->type= type;
if(alloc) {
if(cbuf->type==CB_RGBA)
cbuf->rect= MEM_mapallocN(4*sizeof(float)*sizex*sizey, "compbuf RGBA rect");
else if(cbuf->type==CB_VEC3)
cbuf->rect= MEM_mapallocN(3*sizeof(float)*sizex*sizey, "compbuf Vector3 rect");
else if(cbuf->type==CB_VEC2)
cbuf->rect= MEM_mapallocN(2*sizeof(float)*sizex*sizey, "compbuf Vector2 rect");
else
cbuf->rect= MEM_mapallocN(sizeof(float)*sizex*sizey, "compbuf Fac rect");
cbuf->malloc= 1;
}
cbuf->disprect.xmin= 0;
cbuf->disprect.ymin= 0;
cbuf->disprect.xmax= sizex;
cbuf->disprect.ymax= sizey;
return cbuf;
}
static CompBuf *dupalloc_compbuf(CompBuf *cbuf)
{
CompBuf *dupbuf= alloc_compbuf(cbuf->x, cbuf->y, cbuf->type, 1);
if(dupbuf) {
memcpy(dupbuf->rect, cbuf->rect, cbuf->type*sizeof(float)*cbuf->x*cbuf->y);
dupbuf->xof= cbuf->xof;
dupbuf->yof= cbuf->yof;
}
return dupbuf;
}
/* instead of reference counting, we create a list */
static CompBuf *pass_on_compbuf(CompBuf *cbuf)
{
CompBuf *dupbuf= alloc_compbuf(cbuf->x, cbuf->y, cbuf->type, 0);
CompBuf *lastbuf;
if(dupbuf) {
dupbuf->rect= cbuf->rect;
dupbuf->xof= cbuf->xof;
dupbuf->yof= cbuf->yof;
dupbuf->malloc= 0;
/* get last buffer in list, and append dupbuf */
for(lastbuf= dupbuf; lastbuf; lastbuf= lastbuf->next)
if(lastbuf->next==NULL)
break;
lastbuf->next= dupbuf;
dupbuf->prev= lastbuf;
}
return dupbuf;
}
void free_compbuf(CompBuf *cbuf)
{
/* check referencing, then remove from list and set malloc tag */
if(cbuf->prev || cbuf->next) {
if(cbuf->prev)
cbuf->prev->next= cbuf->next;
if(cbuf->next)
cbuf->next->prev= cbuf->prev;
if(cbuf->malloc) {
if(cbuf->prev)
cbuf->prev->malloc= 1;
else
cbuf->next->malloc= 1;
cbuf->malloc= 0;
}
}
if(cbuf->malloc && cbuf->rect)
MEM_freeN(cbuf->rect);
MEM_freeN(cbuf);
}
void print_compbuf(char *str, CompBuf *cbuf)
{
printf("Compbuf %s %d %d %p\n", str, cbuf->x, cbuf->y, cbuf->rect);
}
static CompBuf *get_cropped_compbuf(rcti *drect, float *rectf, int rectx, int recty, int type)
{
CompBuf *cbuf;
rcti disprect= *drect;
float *outfp;
int dx, y;
if(disprect.xmax>rectx) disprect.xmax= rectx;
if(disprect.ymax>recty) disprect.ymax= recty;
if(disprect.xmin>= disprect.xmax) return NULL;
if(disprect.ymin>= disprect.ymax) return NULL;
cbuf= alloc_compbuf(disprect.xmax-disprect.xmin, disprect.ymax-disprect.ymin, type, 1);
outfp= cbuf->rect;
rectf += type*(disprect.ymin*rectx + disprect.xmin);
dx= type*cbuf->x;
for(y=cbuf->y; y>0; y--, outfp+=dx, rectf+=type*rectx)
memcpy(outfp, rectf, sizeof(float)*dx);
return cbuf;
}
static CompBuf *scalefast_compbuf(CompBuf *inbuf, int newx, int newy)
{
CompBuf *outbuf;
float *rectf, *newrectf, *rf;
int x, y, c, pixsize= inbuf->type;
int ofsx, ofsy, stepx, stepy;
if(inbuf->x==newx && inbuf->y==newy)
return dupalloc_compbuf(inbuf);
outbuf= alloc_compbuf(newx, newy, inbuf->type, 1);
newrectf= outbuf->rect;
stepx = (65536.0 * (inbuf->x - 1.0) / (newx - 1.0)) + 0.5;
stepy = (65536.0 * (inbuf->y - 1.0) / (newy - 1.0)) + 0.5;
ofsy = 32768;
for (y = newy; y > 0 ; y--){
rectf = inbuf->rect;
rectf += pixsize * (ofsy >> 16) * inbuf->x;
ofsy += stepy;
ofsx = 32768;
for (x = newx ; x>0 ; x--) {
rf= rectf + pixsize*(ofsx >> 16);
for(c=0; c<pixsize; c++)
newrectf[c] = rf[c];
newrectf+= pixsize;
ofsx += stepx;
}
}
return outbuf;
}
static CompBuf *typecheck_compbuf(CompBuf *inbuf, int type)
{
if(inbuf && inbuf->type!=type && inbuf->rect_procedural==NULL) {
CompBuf *outbuf= alloc_compbuf(inbuf->x, inbuf->y, type, 1);
float *inrf= inbuf->rect;
float *outrf= outbuf->rect;
int x= inbuf->x*inbuf->y;
/* warning note: xof and yof are applied in pixelprocessor, but should be copied otherwise? */
outbuf->xof= inbuf->xof;
outbuf->yof= inbuf->yof;
if(type==CB_VAL) {
if(inbuf->type==CB_VEC2) {
for(; x>0; x--, outrf+= 1, inrf+= 2)
*outrf= 0.5f*(inrf[0]+inrf[1]);
}
else if(inbuf->type==CB_VEC3) {
for(; x>0; x--, outrf+= 1, inrf+= 3)
*outrf= 0.333333f*(inrf[0]+inrf[1]+inrf[2]);
}
else if(inbuf->type==CB_RGBA) {
for(; x>0; x--, outrf+= 1, inrf+= 4)
*outrf= inrf[0]*0.35f + inrf[1]*0.45f + inrf[2]*0.2f;
}
}
else if(type==CB_VEC2) {
if(inbuf->type==CB_VAL) {
for(; x>0; x--, outrf+= 2, inrf+= 1) {
outrf[0]= inrf[0];
outrf[1]= inrf[0];
}
}
else if(inbuf->type==CB_VEC3) {
for(; x>0; x--, outrf+= 2, inrf+= 3) {
outrf[0]= inrf[0];
outrf[1]= inrf[1];
}
}
else if(inbuf->type==CB_RGBA) {
for(; x>0; x--, outrf+= 2, inrf+= 4) {
outrf[0]= inrf[0];
outrf[1]= inrf[1];
}
}
}
else if(type==CB_VEC3) {
if(inbuf->type==CB_VAL) {
for(; x>0; x--, outrf+= 3, inrf+= 1) {
outrf[0]= inrf[0];
outrf[1]= inrf[0];
outrf[2]= inrf[0];
}
}
else if(inbuf->type==CB_VEC2) {
for(; x>0; x--, outrf+= 3, inrf+= 2) {
outrf[0]= inrf[0];
outrf[1]= inrf[1];
outrf[2]= 0.0f;
}
}
else if(inbuf->type==CB_RGBA) {
for(; x>0; x--, outrf+= 3, inrf+= 4) {
outrf[0]= inrf[0];
outrf[1]= inrf[1];
outrf[2]= inrf[2];
}
}
}
else if(type==CB_RGBA) {
if(inbuf->type==CB_VAL) {
for(; x>0; x--, outrf+= 4, inrf+= 1) {
outrf[0]= inrf[0];
outrf[1]= inrf[0];
outrf[2]= inrf[0];
outrf[3]= inrf[0];
}
}
else if(inbuf->type==CB_VEC2) {
for(; x>0; x--, outrf+= 4, inrf+= 2) {
outrf[0]= inrf[0];
outrf[1]= inrf[1];
outrf[2]= 0.0f;
outrf[3]= 1.0f;
}
}
else if(inbuf->type==CB_VEC3) {
for(; x>0; x--, outrf+= 4, inrf+= 3) {
outrf[0]= inrf[0];
outrf[1]= inrf[1];
outrf[2]= inrf[2];
outrf[3]= 1.0f;
}
}
}
return outbuf;
}
return inbuf;
}
float *compbuf_get_pixel(CompBuf *cbuf, float *rectf, int x, int y, int xrad, int yrad)
{
if(cbuf) {
if(cbuf->rect_procedural) {
cbuf->rect_procedural(cbuf, rectf, (float)x/(float)xrad, (float)y/(float)yrad);
return rectf;
}
else {
static float col[4]= {0.0f, 0.0f, 0.0f, 0.0f};
/* map coords */
x-= cbuf->xof;
y-= cbuf->yof;
if(y<-cbuf->yrad || y>= -cbuf->yrad+cbuf->y) return col;
if(x<-cbuf->xrad || x>= -cbuf->xrad+cbuf->x) return col;
return cbuf->rect + cbuf->type*( (cbuf->yrad+y)*cbuf->x + (cbuf->xrad+x) );
}
}
else return rectf;
}
/* **************************************************** */
/* Pixel-to-Pixel operation, 1 Image in, 1 out */
static void composit1_pixel_processor(bNode *node, CompBuf *out, CompBuf *src_buf, float *src_col,
void (*func)(bNode *, float *, float *),
int src_type)
{
CompBuf *src_use;
float *outfp=out->rect, *srcfp;
int xrad, yrad, x, y;
src_use= typecheck_compbuf(src_buf, src_type);
xrad= out->xrad;
yrad= out->yrad;
for(y= -yrad; y<-yrad+out->y; y++) {
for(x= -xrad; x<-xrad+out->x; x++, outfp+=out->type) {
srcfp= compbuf_get_pixel(src_use, src_col, x, y, xrad, yrad);
func(node, outfp, srcfp);
}
}
if(src_use!=src_buf)
free_compbuf(src_use);
}
/* Pixel-to-Pixel operation, 2 Images in, 1 out */
static void composit2_pixel_processor(bNode *node, CompBuf *out, CompBuf *src_buf, float *src_col,
CompBuf *fac_buf, float *fac, void (*func)(bNode *, float *, float *, float *),
int src_type, int fac_type)
{
CompBuf *src_use, *fac_use;
float *outfp=out->rect, *srcfp, *facfp;
int xrad, yrad, x, y;
src_use= typecheck_compbuf(src_buf, src_type);
fac_use= typecheck_compbuf(fac_buf, fac_type);
xrad= out->xrad;
yrad= out->yrad;
for(y= -yrad; y<-yrad+out->y; y++) {
for(x= -xrad; x<-xrad+out->x; x++, outfp+=out->type) {
srcfp= compbuf_get_pixel(src_use, src_col, x, y, xrad, yrad);
facfp= compbuf_get_pixel(fac_use, fac, x, y, xrad, yrad);
func(node, outfp, srcfp, facfp);
}
}
if(src_use!=src_buf)
free_compbuf(src_use);
if(fac_use!=fac_buf)
free_compbuf(fac_use);
}
/* Pixel-to-Pixel operation, 3 Images in, 1 out */
static void composit3_pixel_processor(bNode *node, CompBuf *out, CompBuf *src1_buf, float *src1_col, CompBuf *src2_buf, float *src2_col,
CompBuf *fac_buf, float *fac, void (*func)(bNode *, float *, float *, float *, float *),
int src1_type, int src2_type, int fac_type)
{
CompBuf *src1_use, *src2_use, *fac_use;
float *outfp=out->rect, *src1fp, *src2fp, *facfp;
int xrad, yrad, x, y;
src1_use= typecheck_compbuf(src1_buf, src1_type);
src2_use= typecheck_compbuf(src2_buf, src2_type);
fac_use= typecheck_compbuf(fac_buf, fac_type);
xrad= out->xrad;
yrad= out->yrad;
for(y= -yrad; y<-yrad+out->y; y++) {
for(x= -xrad; x<-xrad+out->x; x++, outfp+=out->type) {
src1fp= compbuf_get_pixel(src1_use, src1_col, x, y, xrad, yrad);
src2fp= compbuf_get_pixel(src2_use, src2_col, x, y, xrad, yrad);
facfp= compbuf_get_pixel(fac_use, fac, x, y, xrad, yrad);
func(node, outfp, src1fp, src2fp, facfp);
}
}
if(src1_use!=src1_buf)
free_compbuf(src1_use);
if(src2_use!=src2_buf)
free_compbuf(src2_use);
if(fac_use!=fac_buf)
free_compbuf(fac_use);
}
/* Pixel-to-Pixel operation, 4 Images in, 1 out */
static void composit4_pixel_processor(bNode *node, CompBuf *out, CompBuf *src1_buf, float *src1_col, CompBuf *fac1_buf, float *fac1,
CompBuf *src2_buf, float *src2_col, CompBuf *fac2_buf, float *fac2,
void (*func)(bNode *, float *, float *, float *, float *, float *),
int src1_type, int fac1_type, int src2_type, int fac2_type)
{
CompBuf *src1_use, *src2_use, *fac1_use, *fac2_use;
float *outfp=out->rect, *src1fp, *src2fp, *fac1fp, *fac2fp;
int xrad, yrad, x, y;
src1_use= typecheck_compbuf(src1_buf, src1_type);
src2_use= typecheck_compbuf(src2_buf, src2_type);
fac1_use= typecheck_compbuf(fac1_buf, fac1_type);
fac2_use= typecheck_compbuf(fac2_buf, fac2_type);
xrad= out->xrad;
yrad= out->yrad;
for(y= -yrad; y<-yrad+out->y; y++) {
for(x= -xrad; x<-xrad+out->x; x++, outfp+=out->type) {
src1fp= compbuf_get_pixel(src1_use, src1_col, x, y, xrad, yrad);
src2fp= compbuf_get_pixel(src2_use, src2_col, x, y, xrad, yrad);
fac1fp= compbuf_get_pixel(fac1_use, fac1, x, y, xrad, yrad);
fac2fp= compbuf_get_pixel(fac2_use, fac2, x, y, xrad, yrad);
func(node, outfp, src1fp, fac1fp, src2fp, fac2fp);
}
}
if(src1_use!=src1_buf)
free_compbuf(src1_use);
if(src2_use!=src2_buf)
free_compbuf(src2_use);
if(fac1_use!=fac1_buf)
free_compbuf(fac1_use);
if(fac2_use!=fac2_buf)
free_compbuf(fac2_use);
}
static CompBuf *valbuf_from_rgbabuf(CompBuf *cbuf, int channel)
{
CompBuf *valbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1);
float *valf, *rectf;
int tot;
/* warning note: xof and yof are applied in pixelprocessor, but should be copied otherwise? */
valbuf->xof= cbuf->xof;
valbuf->yof= cbuf->yof;
valf= valbuf->rect;
/* defaults to returning alpha channel */
if ((channel < CHAN_R) && (channel > CHAN_A)) channel = CHAN_A;
rectf= cbuf->rect + channel;
for(tot= cbuf->x*cbuf->y; tot>0; tot--, valf++, rectf+=4)
*valf= *rectf;
return valbuf;
}
static void generate_preview(bNode *node, CompBuf *stackbuf)
{
bNodePreview *preview= node->preview;
if(preview && stackbuf) {
CompBuf *cbuf, *stackbuf_use;
if(stackbuf->rect==NULL) return;
stackbuf_use= typecheck_compbuf(stackbuf, CB_RGBA);
if(stackbuf->x > stackbuf->y) {
preview->xsize= 140;
preview->ysize= (140*stackbuf->y)/stackbuf->x;
}
else {
preview->ysize= 140;
preview->xsize= (140*stackbuf->x)/stackbuf->y;
}
cbuf= scalefast_compbuf(stackbuf_use, preview->xsize, preview->ysize);
/* this ensures free-compbuf does the right stuff */
SWAP(float *, cbuf->rect, node->preview->rect);
free_compbuf(cbuf);
if(stackbuf_use!=stackbuf)
free_compbuf(stackbuf_use);
}
}
/* ******************************************************** */
/* ********* Composit Node type definitions ***************** */
/* ******************************************************** */
/* SocketType syntax:
socket type, max connections (0 is no limit), name, 4 values for default, 2 values for range */
/* Verification rule: If name changes, a saved socket and its links will be removed! Type changes are OK */
/* **************** VIEWER ******************** */
static bNodeSocketType cmp_node_viewer_in[]= {
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_copy_rgba(bNode *node, float *out, float *in)
{
QUATCOPY(out, in);
}
static void do_copy_rgb(bNode *node, float *out, float *in)
{
VECCOPY(out, in);
out[3]= 1.0f;
}
static void do_copy_value(bNode *node, float *out, float *in)
{
out[0]= in[0];
}
static void do_copy_a_rgba(bNode *node, float *out, float *in, float *fac)
{
VECCOPY(out, in);
out[3]= *fac;
}
static void node_composit_exec_viewer(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* image assigned to output */
/* stack order input sockets: col, alpha, z */
if(node->id && (node->flag & NODE_DO_OUTPUT)) { /* only one works on out */
RenderData *rd= data;
Image *ima= (Image *)node->id;
ImBuf *ibuf;
CompBuf *cbuf, *tbuf;
int rectx, recty;
BKE_image_user_calc_imanr(node->storage, rd->cfra, 0);
/* always returns for viewer image, but we check nevertheless */
ibuf= BKE_image_get_ibuf(ima, node->storage);
if(ibuf==NULL) {
printf("node_composit_exec_viewer error\n");
return;
}
/* free all in ibuf */
imb_freerectImBuf(ibuf);
imb_freerectfloatImBuf(ibuf);
IMB_freezbuffloatImBuf(ibuf);
/* get size */
tbuf= in[0]->data?in[0]->data:(in[1]->data?in[1]->data:in[2]->data);
if(tbuf==NULL) {
rectx= 320; recty= 256;
}
else {
rectx= tbuf->x;
recty= tbuf->y;
}
/* make ibuf, and connect to ima */
ibuf->x= rectx;
ibuf->y= recty;
imb_addrectfloatImBuf(ibuf);
ima->ok= IMA_OK_LOADED;
/* now we combine the input with ibuf */
cbuf= alloc_compbuf(rectx, recty, CB_RGBA, 0); /* no alloc*/
cbuf->rect= ibuf->rect_float;
/* when no alpha, we can simply copy */
if(in[1]->data==NULL) {
composit1_pixel_processor(node, cbuf, in[0]->data, in[0]->vec, do_copy_rgba, CB_RGBA);
}
else
composit2_pixel_processor(node, cbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec, do_copy_a_rgba, CB_RGBA, CB_VAL);
/* zbuf option */
if(in[2]->data) {
CompBuf *zbuf= alloc_compbuf(rectx, recty, CB_VAL, 1);
ibuf->zbuf_float= zbuf->rect;
ibuf->mall |= IB_zbuffloat;
composit1_pixel_processor(node, zbuf, in[2]->data, in[2]->vec, do_copy_value, CB_VAL);
/* free compbuf, but not the rect */
zbuf->malloc= 0;
free_compbuf(zbuf);
}
generate_preview(node, cbuf);
free_compbuf(cbuf);
}
else if(in[0]->data) {
generate_preview(node, in[0]->data);
}
}
static bNodeType cmp_node_viewer= {
/* type code */ CMP_NODE_VIEWER,
/* name */ "Viewer",
/* width+range */ 80, 60, 200,
/* class+opts */ NODE_CLASS_OUTPUT, NODE_PREVIEW,
/* input sock */ cmp_node_viewer_in,
/* output sock */ NULL,
/* storage */ "ImageUser",
/* execfunc */ node_composit_exec_viewer
};
/* **************** SPLIT VIEWER ******************** */
static bNodeSocketType cmp_node_splitviewer_in[]= {
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_copy_split_rgba(bNode *node, float *out, float *in1, float *in2, float *fac)
{
if(*fac==0.0f) {
QUATCOPY(out, in1);
}
else {
QUATCOPY(out, in2);
}
}
static void node_composit_exec_splitviewer(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* image assigned to output */
/* stack order input sockets: image image */
if(in[0]->data==NULL || in[1]->data==NULL)
return;
if(node->id && (node->flag & NODE_DO_OUTPUT)) { /* only one works on out */
Image *ima= (Image *)node->id;
RenderData *rd= data;
ImBuf *ibuf;
CompBuf *cbuf, *buf1, *buf2, *mask;
int x, y;
float offset;
buf1= typecheck_compbuf(in[0]->data, CB_RGBA);
buf2= typecheck_compbuf(in[1]->data, CB_RGBA);
BKE_image_user_calc_imanr(node->storage, rd->cfra, 0);
/* always returns for viewer image, but we check nevertheless */
ibuf= BKE_image_get_ibuf(ima, node->storage);
if(ibuf==NULL) {
printf("node_composit_exec_viewer error\n");
return;
}
/* free all in ibuf */
imb_freerectImBuf(ibuf);
imb_freerectfloatImBuf(ibuf);
IMB_freezbuffloatImBuf(ibuf);
/* make ibuf, and connect to ima */
ibuf->x= buf1->x;
ibuf->y= buf1->y;
imb_addrectfloatImBuf(ibuf);
ima->ok= IMA_OK_LOADED;
/* output buf */
cbuf= alloc_compbuf(buf1->x, buf1->y, CB_RGBA, 0); /* no alloc*/
cbuf->rect= ibuf->rect_float;
/* mask buf */
mask= alloc_compbuf(buf1->x, buf1->y, CB_VAL, 1);
/* Check which offset mode is selected and limit offset if needed */
if(node->custom2 == 0) {
offset = buf1->x / 100.0f * node->custom1;
CLAMP(offset, 0, buf1->x);
}
else {
offset = buf1->y / 100.0f * node->custom1;
CLAMP(offset, 0, buf1->y);
}
if(node->custom2 == 0) {
for(y=0; y<buf1->y; y++) {
float *fac= mask->rect + y*buf1->x;
for(x=offset; x>0; x--, fac++)
*fac= 1.0f;
}
}
else {
for(y=0; y<offset; y++) {
float *fac= mask->rect + y*buf1->x;
for(x=buf1->x; x>0; x--, fac++)
*fac= 1.0f;
}
}
composit3_pixel_processor(node, cbuf, buf1, in[0]->vec, buf2, in[1]->vec, mask, NULL, do_copy_split_rgba, CB_RGBA, CB_RGBA, CB_VAL);
generate_preview(node, cbuf);
free_compbuf(cbuf);
free_compbuf(mask);
if(in[0]->data != buf1)
free_compbuf(buf1);
if(in[1]->data != buf2)
free_compbuf(buf2);
}
}
static bNodeType cmp_node_splitviewer= {
/* type code */ CMP_NODE_SPLITVIEWER,
/* name */ "SplitViewer",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_OUTPUT, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ cmp_node_splitviewer_in,
/* output sock */ NULL,
/* storage */ "ImageUser",
/* execfunc */ node_composit_exec_splitviewer
};
/* **************** COMPOSITE ******************** */
static bNodeSocketType cmp_node_composite_in[]= {
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* applies to render pipeline */
static void node_composit_exec_composite(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* image assigned to output */
/* stack order input sockets: col, alpha, z */
if(node->flag & NODE_DO_OUTPUT) { /* only one works on out */
RenderData *rd= data;
if(rd->scemode & R_DOCOMP) {
RenderResult *rr= RE_GetResult(RE_GetRender(G.scene->id.name)); /* G.scene is WEAK! */
if(rr) {
CompBuf *outbuf, *zbuf=NULL;
if(rr->rectf)
MEM_freeN(rr->rectf);
outbuf= alloc_compbuf(rr->rectx, rr->recty, CB_RGBA, 1);
if(in[1]->data==NULL)
composit1_pixel_processor(node, outbuf, in[0]->data, in[0]->vec, do_copy_rgba, CB_RGBA);
else
composit2_pixel_processor(node, outbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec, do_copy_a_rgba, CB_RGBA, CB_VAL);
if(in[2]->data) {
if(rr->rectz)
MEM_freeN(rr->rectz);
zbuf= alloc_compbuf(rr->rectx, rr->recty, CB_VAL, 1);
composit1_pixel_processor(node, zbuf, in[2]->data, in[2]->vec, do_copy_value, CB_VAL);
rr->rectz= zbuf->rect;
zbuf->malloc= 0;
free_compbuf(zbuf);
}
generate_preview(node, outbuf);
/* we give outbuf to rr... */
rr->rectf= outbuf->rect;
outbuf->malloc= 0;
free_compbuf(outbuf);
/* signal for imageviewer to refresh (it converts to byte rects...) */
BKE_image_signal(BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result"), NULL, IMA_SIGNAL_FREE);
return;
}
}
}
if(in[0]->data)
generate_preview(node, in[0]->data);
}
static bNodeType cmp_node_composite= {
/* type code */ CMP_NODE_COMPOSITE,
/* name */ "Composite",
/* width+range */ 80, 60, 200,
/* class+opts */ NODE_CLASS_OUTPUT, NODE_PREVIEW,
/* input sock */ cmp_node_composite_in,
/* output sock */ NULL,
/* storage */ "",
/* execfunc */ node_composit_exec_composite
};
/* **************** OUTPUT FILE ******************** */
static bNodeSocketType cmp_node_output_file_in[]= {
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_output_file(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* image assigned to output */
/* stack order input sockets: col, alpha */
if(in[0]->data) {
RenderData *rd= data;
NodeImageFile *nif= node->storage;
if(nif->sfra!=nif->efra && (rd->cfra<nif->sfra || rd->cfra>nif->efra)) {
return; /* BAIL OUT RETURN */
}
else {
CompBuf *cbuf= typecheck_compbuf(in[0]->data, CB_RGBA);
ImBuf *ibuf= IMB_allocImBuf(cbuf->x, cbuf->y, 32, 0, 0);
char string[256];
ibuf->rect_float= cbuf->rect;
ibuf->dither= rd->dither_intensity;
if(in[1]->data) {
CompBuf *zbuf= in[1]->data;
if(zbuf->type==CB_VAL && zbuf->x==cbuf->x && zbuf->y==cbuf->y) {
nif->subimtype|= R_OPENEXR_ZBUF;
ibuf->zbuf_float= zbuf->rect;
}
}
BKE_makepicstring(string, nif->name, rd->cfra, nif->imtype);
if(0 == BKE_write_ibuf(ibuf, string, nif->imtype, nif->subimtype, nif->imtype==R_OPENEXR?nif->codec:nif->quality))
printf("Cannot save Node File Output to %s\n", string);
else
printf("Saved: %s\n", string);
IMB_freeImBuf(ibuf);
generate_preview(node, cbuf);
if(in[0]->data != cbuf)
free_compbuf(cbuf);
}
}
}
static bNodeType cmp_node_output_file= {
/* type code */ CMP_NODE_OUTPUT_FILE,
/* name */ "File Output",
/* width+range */ 140, 80, 300,
/* class+opts */ NODE_CLASS_OUTPUT, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ cmp_node_output_file_in,
/* output sock */ NULL,
/* storage */ "NodeImageFile",
/* execfunc */ node_composit_exec_output_file
};
/* **************** IMAGE (and RenderResult, multilayer image) ******************** */
/* output socket defines */
#define RRES_OUT_IMAGE 0
#define RRES_OUT_ALPHA 1
#define RRES_OUT_Z 2
#define RRES_OUT_NORMAL 3
#define RRES_OUT_UV 4
#define RRES_OUT_VEC 5
#define RRES_OUT_RGBA 6
#define RRES_OUT_DIFF 7
#define RRES_OUT_SPEC 8
#define RRES_OUT_SHADOW 9
#define RRES_OUT_AO 10
#define RRES_OUT_REFLECT 11
#define RRES_OUT_REFRACT 12
#define RRES_OUT_RADIO 13
#define RRES_OUT_INDEXOB 14
static bNodeSocketType cmp_node_rlayers_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "Z", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VECTOR, 0, "UV", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VECTOR, 0, "Speed", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Color", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Diffuse", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Specular", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Shadow", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "AO", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Reflect", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Refract", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 0, "Radio", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "IndexOB", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* note: this function is used for multilayer too, to ensure uniform
handling with BKE_image_get_ibuf() */
static CompBuf *node_composit_get_image(RenderData *rd, Image *ima, ImageUser *iuser)
{
ImBuf *ibuf;
CompBuf *stackbuf;
int type;
ibuf= BKE_image_get_ibuf(ima, iuser);
if(ibuf==NULL)
return NULL;
if(ibuf->rect_float==NULL)
IMB_float_from_rect(ibuf);
type= ibuf->channels;
if(rd->scemode & R_COMP_CROP) {
stackbuf= get_cropped_compbuf(&rd->disprect, ibuf->rect_float, ibuf->x, ibuf->y, type);
}
else {
/* we put imbuf copy on stack, cbuf knows rect is from other ibuf when freed! */
stackbuf= alloc_compbuf(ibuf->x, ibuf->y, type, 0);
stackbuf->rect= ibuf->rect_float;
}
return stackbuf;
}
static CompBuf *node_composit_get_zimage(bNode *node, RenderData *rd)
{
ImBuf *ibuf= BKE_image_get_ibuf((Image *)node->id, node->storage);
CompBuf *zbuf= NULL;
if(ibuf && ibuf->zbuf_float) {
if(rd->scemode & R_COMP_CROP) {
zbuf= get_cropped_compbuf(&rd->disprect, ibuf->zbuf_float, ibuf->x, ibuf->y, CB_VAL);
}
else {
zbuf= alloc_compbuf(ibuf->x, ibuf->y, CB_VAL, 0);
zbuf->rect= ibuf->zbuf_float;
}
}
return zbuf;
}
/* check if layer is available, returns pass buffer */
static CompBuf *compbuf_multilayer_get(RenderData *rd, RenderLayer *rl, Image *ima, ImageUser *iuser, int passtype)
{
RenderPass *rpass;
short index;
for(index=0, rpass= rl->passes.first; rpass; rpass= rpass->next, index++)
if(rpass->passtype==passtype)
break;
if(rpass) {
CompBuf *cbuf;
iuser->pass= index;
BKE_image_multilayer_index(ima->rr, iuser);
cbuf= node_composit_get_image(rd, ima, iuser);
return cbuf;
}
return NULL;
}
void outputs_multilayer_get(RenderData *rd, RenderLayer *rl, bNodeStack **out, Image *ima, ImageUser *iuser)
{
if(out[RRES_OUT_Z]->hasoutput)
out[RRES_OUT_Z]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_Z);
if(out[RRES_OUT_VEC]->hasoutput)
out[RRES_OUT_VEC]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_VECTOR);
if(out[RRES_OUT_NORMAL]->hasoutput)
out[RRES_OUT_NORMAL]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_NORMAL);
if(out[RRES_OUT_UV]->hasoutput)
out[RRES_OUT_UV]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_UV);
if(out[RRES_OUT_RGBA]->hasoutput)
out[RRES_OUT_RGBA]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_RGBA);
if(out[RRES_OUT_DIFF]->hasoutput)
out[RRES_OUT_DIFF]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_DIFFUSE);
if(out[RRES_OUT_SPEC]->hasoutput)
out[RRES_OUT_SPEC]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_SPEC);
if(out[RRES_OUT_SHADOW]->hasoutput)
out[RRES_OUT_SHADOW]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_SHADOW);
if(out[RRES_OUT_AO]->hasoutput)
out[RRES_OUT_AO]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_AO);
if(out[RRES_OUT_REFLECT]->hasoutput)
out[RRES_OUT_REFLECT]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_REFLECT);
if(out[RRES_OUT_REFRACT]->hasoutput)
out[RRES_OUT_REFRACT]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_REFRACT);
if(out[RRES_OUT_RADIO]->hasoutput)
out[RRES_OUT_RADIO]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_RADIO);
if(out[RRES_OUT_INDEXOB]->hasoutput)
out[RRES_OUT_INDEXOB]->data= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_INDEXOB);
}
static void node_composit_exec_image(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* image assigned to output */
/* stack order input sockets: col, alpha */
if(node->id) {
RenderData *rd= data;
Image *ima= (Image *)node->id;
ImageUser *iuser= (ImageUser *)node->storage;
CompBuf *stackbuf= NULL;
/* first set the right frame number in iuser */
BKE_image_user_calc_imanr(iuser, rd->cfra, 0);
/* force a load, we assume iuser index will be set OK anyway */
if(ima->type==IMA_TYPE_MULTILAYER)
BKE_image_get_ibuf(ima, iuser);
if(ima->type==IMA_TYPE_MULTILAYER && ima->rr) {
RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer);
if(rl) {
out[0]->data= stackbuf= compbuf_multilayer_get(rd, rl, ima, iuser, SCE_PASS_COMBINED);
/* go over all layers */
outputs_multilayer_get(rd, rl, out, ima, iuser);
}
}
else {
stackbuf= node_composit_get_image(rd, ima, iuser);
/* put image on stack */
out[0]->data= stackbuf;
if(out[2]->hasoutput)
out[2]->data= node_composit_get_zimage(node, rd);
}
/* alpha and preview for both types */
if(stackbuf) {
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
generate_preview(node, stackbuf);
}
}
}
/* uses node->storage to indicate animated image */
static bNodeType cmp_node_image= {
/* type code */ CMP_NODE_IMAGE,
/* name */ "Image",
/* width+range */ 120, 80, 300,
/* class+opts */ NODE_CLASS_INPUT, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ NULL,
/* output sock */ cmp_node_rlayers_out,
/* storage */ "ImageUser",
/* execfunc */ node_composit_exec_image
};
/* **************** RENDER RESULT ******************** */
static CompBuf *compbuf_from_pass(RenderData *rd, RenderLayer *rl, int rectx, int recty, int passcode)
{
float *fp= RE_RenderLayerGetPass(rl, passcode);
if(fp) {
CompBuf *buf;
int buftype= CB_VEC3;
if(ELEM(passcode, SCE_PASS_Z, SCE_PASS_INDEXOB))
buftype= CB_VAL;
else if(passcode==SCE_PASS_VECTOR)
buftype= CB_VEC4;
else if(ELEM(passcode, SCE_PASS_COMBINED, SCE_PASS_RGBA))
buftype= CB_RGBA;
if(rd->scemode & R_COMP_CROP)
buf= get_cropped_compbuf(&rd->disprect, fp, rectx, recty, buftype);
else {
buf= alloc_compbuf(rectx, recty, buftype, 0);
buf->rect= fp;
}
return buf;
}
return NULL;
}
void node_composit_rlayers_out(RenderData *rd, RenderLayer *rl, bNodeStack **out, int rectx, int recty)
{
if(out[RRES_OUT_Z]->hasoutput)
out[RRES_OUT_Z]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_Z);
if(out[RRES_OUT_VEC]->hasoutput)
out[RRES_OUT_VEC]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_VECTOR);
if(out[RRES_OUT_NORMAL]->hasoutput)
out[RRES_OUT_NORMAL]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_NORMAL);
if(out[RRES_OUT_UV]->hasoutput)
out[RRES_OUT_UV]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_UV);
if(out[RRES_OUT_RGBA]->hasoutput)
out[RRES_OUT_RGBA]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_RGBA);
if(out[RRES_OUT_DIFF]->hasoutput)
out[RRES_OUT_DIFF]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_DIFFUSE);
if(out[RRES_OUT_SPEC]->hasoutput)
out[RRES_OUT_SPEC]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_SPEC);
if(out[RRES_OUT_SHADOW]->hasoutput)
out[RRES_OUT_SHADOW]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_SHADOW);
if(out[RRES_OUT_AO]->hasoutput)
out[RRES_OUT_AO]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_AO);
if(out[RRES_OUT_REFLECT]->hasoutput)
out[RRES_OUT_REFLECT]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_REFLECT);
if(out[RRES_OUT_REFRACT]->hasoutput)
out[RRES_OUT_REFRACT]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_REFRACT);
if(out[RRES_OUT_RADIO]->hasoutput)
out[RRES_OUT_RADIO]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_RADIO);
if(out[RRES_OUT_INDEXOB]->hasoutput)
out[RRES_OUT_INDEXOB]->data= compbuf_from_pass(rd, rl, rectx, recty, SCE_PASS_INDEXOB);
}
static void node_composit_exec_rlayers(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
Scene *sce= node->id?(Scene *)node->id:G.scene; /* G.scene is WEAK! */
RenderData *rd= data;
RenderResult *rr;
rr= RE_GetResult(RE_GetRender(sce->id.name));
if(rr) {
SceneRenderLayer *srl= BLI_findlink(&sce->r.layers, node->custom1);
if(srl) {
RenderLayer *rl= RE_GetRenderLayer(rr, srl->name);
if(rl && rl->rectf) {
CompBuf *stackbuf;
/* we put render rect on stack, cbuf knows rect is from other ibuf when freed! */
if(rd->scemode & R_COMP_CROP)
stackbuf= get_cropped_compbuf(&rd->disprect, rl->rectf, rr->rectx, rr->recty, CB_RGBA);
else {
stackbuf= alloc_compbuf(rr->rectx, rr->recty, CB_RGBA, 0);
stackbuf->rect= rl->rectf;
}
if(stackbuf==NULL) {
printf("Error; Preview Panel in UV Window returns zero sized image\n");
}
else {
stackbuf->xof= rr->xof;
stackbuf->yof= rr->yof;
/* put on stack */
out[RRES_OUT_IMAGE]->data= stackbuf;
if(out[RRES_OUT_ALPHA]->hasoutput)
out[RRES_OUT_ALPHA]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
node_composit_rlayers_out(rd, rl, out, rr->rectx, rr->recty);
generate_preview(node, stackbuf);
}
}
}
}
}
/* custom1 = render layer in use */
/* custom2 = re-render tag */
static bNodeType cmp_node_rlayers= {
/* type code */ CMP_NODE_R_LAYERS,
/* name */ "Render Layers",
/* width+range */ 150, 100, 300,
/* class+opts */ NODE_CLASS_INPUT, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ NULL,
/* output sock */ cmp_node_rlayers_out,
/* storage */ "",
/* execfunc */ node_composit_exec_rlayers
};
/* **************** TEXTURE ******************** */
static bNodeSocketType cmp_node_texture_in[]= {
{ SOCK_VECTOR, 0, "Offset", 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f},
{ SOCK_VECTOR, 0, "Scale", 1.0f, 1.0f, 1.0f, 1.0f, -10.0f, 10.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_texture_out[]= {
{ SOCK_VALUE, 0, "Value", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA , 0, "Color", 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* called without rect allocated */
static void texture_procedural(CompBuf *cbuf, float *col, float xco, float yco)
{
bNode *node= cbuf->node;
bNodeSocket *sock= node->inputs.first;
TexResult texres= {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, NULL};
float vec[3], *size, nor[3]={0.0f, 0.0f, 0.0f};
int retval, type= cbuf->type;
size= sock->next->ns.vec;
vec[0]= size[0]*(xco + sock->ns.vec[0]);
vec[1]= size[1]*(yco + sock->ns.vec[1]);
vec[2]= size[2]*sock->ns.vec[2];
retval= multitex_ext((Tex *)node->id, vec, NULL, NULL, 0, &texres);
if(type==CB_VAL) {
if(texres.talpha)
col[0]= texres.ta;
else
col[0]= texres.tin;
}
else if(type==CB_RGBA) {
if(texres.talpha)
col[3]= texres.ta;
else
col[3]= texres.tin;
if((retval & TEX_RGB)) {
col[0]= texres.tr;
col[1]= texres.tg;
col[2]= texres.tb;
}
else col[0]= col[1]= col[2]= col[3];
}
else {
VECCOPY(col, nor);
}
}
/* texture node outputs get a small rect, to make sure all other nodes accept it */
/* only the pixel-processor nodes do something with it though */
static void node_composit_exec_texture(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* outputs: value, color, normal */
if(node->id) {
/* first make the preview image */
CompBuf *prevbuf= alloc_compbuf(140, 140, CB_RGBA, 1); /* alloc */
prevbuf->rect_procedural= texture_procedural;
prevbuf->node= node;
composit1_pixel_processor(node, prevbuf, prevbuf, out[0]->vec, do_copy_rgba, CB_RGBA);
generate_preview(node, prevbuf);
free_compbuf(prevbuf);
if(out[0]->hasoutput) {
CompBuf *stackbuf= alloc_compbuf(140, 140, CB_VAL, 1); /* alloc */
stackbuf->rect_procedural= texture_procedural;
stackbuf->node= node;
out[0]->data= stackbuf;
}
if(out[1]->hasoutput) {
CompBuf *stackbuf= alloc_compbuf(140, 140, CB_RGBA, 1); /* alloc */
stackbuf->rect_procedural= texture_procedural;
stackbuf->node= node;
out[1]->data= stackbuf;
}
}
}
static bNodeType cmp_node_texture= {
/* type code */ CMP_NODE_TEXTURE,
/* name */ "Texture",
/* width+range */ 120, 80, 240,
/* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS|NODE_PREVIEW,
/* input sock */ cmp_node_texture_in,
/* output sock */ cmp_node_texture_out,
/* storage */ "",
/* execfunc */ node_composit_exec_texture
};
/* **************** NORMAL ******************** */
static bNodeSocketType cmp_node_normal_in[]= {
{ SOCK_VECTOR, 1, "Normal", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_normal_out[]= {
{ SOCK_VECTOR, 0, "Normal", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f},
{ SOCK_VALUE, 0, "Dot", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_normal(bNode *node, float *out, float *in)
{
bNodeSocket *sock= node->outputs.first;
float *nor= sock->ns.vec;
/* render normals point inside... the widget points outside */
out[0]= -INPR(nor, in);
}
/* generates normal, does dot product */
static void node_composit_exec_normal(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
bNodeSocket *sock= node->outputs.first;
/* stack order input: normal */
/* stack order output: normal, value */
/* input no image? then only vector op */
if(in[0]->data==NULL) {
VECCOPY(out[0]->vec, sock->ns.vec);
/* render normals point inside... the widget points outside */
out[1]->vec[0]= -INPR(out[0]->vec, in[0]->vec);
}
else if(out[1]->hasoutput) {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1); /* allocs */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_normal, CB_VEC3);
out[1]->data= stackbuf;
}
}
static bNodeType cmp_node_normal= {
/* type code */ CMP_NODE_NORMAL,
/* name */ "Normal",
/* width+range */ 100, 60, 200,
/* class+opts */ NODE_CLASS_OP_VECTOR, NODE_OPTIONS,
/* input sock */ cmp_node_normal_in,
/* output sock */ cmp_node_normal_out,
/* storage */ "",
/* execfunc */ node_composit_exec_normal
};
/* **************** CURVE Time ******************** */
/* custom1 = sfra, custom2 = efra */
static bNodeSocketType cmp_node_time_out[]= {
{ SOCK_VALUE, 0, "Fac", 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_time(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order output: fac */
float fac= 0.0f;
if(node->custom1 < node->custom2)
fac= (G.scene->r.cfra - node->custom1)/(float)(node->custom2-node->custom1);
out[0]->vec[0]= curvemapping_evaluateF(node->storage, 0, fac);
}
static bNodeType cmp_node_time= {
/* type code */ CMP_NODE_TIME,
/* name */ "Time",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS,
/* input sock */ NULL,
/* output sock */ cmp_node_time_out,
/* storage */ "CurveMapping",
/* execfunc */ node_composit_exec_time
};
/* **************** CURVE VEC ******************** */
static bNodeSocketType cmp_node_curve_vec_in[]= {
{ SOCK_VECTOR, 1, "Vector", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_curve_vec_out[]= {
{ SOCK_VECTOR, 0, "Vector", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_curve_vec(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order input: vec */
/* stack order output: vec */
curvemapping_evaluate_premulRGBF(node->storage, out[0]->vec, in[0]->vec);
}
static bNodeType cmp_node_curve_vec= {
/* type code */ CMP_NODE_CURVE_VEC,
/* name */ "Vector Curves",
/* width+range */ 200, 140, 320,
/* class+opts */ NODE_CLASS_OP_VECTOR, NODE_OPTIONS,
/* input sock */ cmp_node_curve_vec_in,
/* output sock */ cmp_node_curve_vec_out,
/* storage */ "CurveMapping",
/* execfunc */ node_composit_exec_curve_vec
};
/* **************** CURVE RGB ******************** */
static bNodeSocketType cmp_node_curve_rgb_in[]= {
{ SOCK_VALUE, 1, "Fac", 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_curve_rgb_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f},
{ -1, 0, "" }
};
static void do_curves(bNode *node, float *out, float *in)
{
curvemapping_evaluate_premulRGBF(node->storage, out, in);
out[3]= in[3];
}
static void do_curves_fac(bNode *node, float *out, float *in, float *fac)
{
if(*fac>=1.0)
curvemapping_evaluate_premulRGBF(node->storage, out, in);
else if(*fac<=0.0) {
VECCOPY(out, in);
}
else {
float col[4], mfac= 1.0f-*fac;
curvemapping_evaluate_premulRGBF(node->storage, col, in);
out[0]= mfac*in[0] + *fac*col[0];
out[1]= mfac*in[1] + *fac*col[1];
out[2]= mfac*in[2] + *fac*col[2];
}
out[3]= in[3];
}
static void node_composit_exec_curve_rgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order input: fac, image */
/* stack order output: image */
if(out[0]->hasoutput==0)
return;
/* input no image? then only color operation */
if(in[1]->data==NULL) {
curvemapping_evaluateRGBF(node->storage, out[0]->vec, in[1]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[1]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
if(in[0]->data)
composit2_pixel_processor(node, stackbuf, in[1]->data, in[1]->vec, in[0]->data, in[0]->vec, do_curves_fac, CB_RGBA, CB_VAL);
else
composit1_pixel_processor(node, stackbuf, in[1]->data, in[1]->vec, do_curves, CB_RGBA);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_curve_rgb= {
/* type code */ CMP_NODE_CURVE_RGB,
/* name */ "RGB Curves",
/* width+range */ 200, 140, 320,
/* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS,
/* input sock */ cmp_node_curve_rgb_in,
/* output sock */ cmp_node_curve_rgb_out,
/* storage */ "CurveMapping",
/* execfunc */ node_composit_exec_curve_rgb
};
/* **************** VALUE ******************** */
static bNodeSocketType cmp_node_value_out[]= {
{ SOCK_VALUE, 0, "Value", 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_value(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
bNodeSocket *sock= node->outputs.first;
out[0]->vec[0]= sock->ns.vec[0];
}
static bNodeType cmp_node_value= {
/* type code */ CMP_NODE_VALUE,
/* name */ "Value",
/* width+range */ 80, 40, 120,
/* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS,
/* input sock */ NULL,
/* output sock */ cmp_node_value_out,
/* storage */ "",
/* execfunc */ node_composit_exec_value
};
/* **************** RGB ******************** */
static bNodeSocketType cmp_node_rgb_out[]= {
{ SOCK_RGBA, 0, "RGBA", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_rgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
bNodeSocket *sock= node->outputs.first;
VECCOPY(out[0]->vec, sock->ns.vec);
}
static bNodeType cmp_node_rgb= {
/* type code */ CMP_NODE_RGB,
/* name */ "RGB",
/* width+range */ 100, 60, 140,
/* class+opts */ NODE_CLASS_INPUT, NODE_OPTIONS,
/* input sock */ NULL,
/* output sock */ cmp_node_rgb_out,
/* storage */ "",
/* execfunc */ node_composit_exec_rgb
};
/* **************** Hue Saturation ******************** */
static bNodeSocketType cmp_node_hue_sat_in[]= {
{ SOCK_VALUE, 1, "Fac", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_hue_sat_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_hue_sat_fac(bNode *node, float *out, float *in, float *fac)
{
NodeHueSat *nhs= node->storage;
if(*fac!=0.0f && (nhs->hue!=0.5f || nhs->sat!=1.0 || nhs->val!=1.0)) {
float col[3], hsv[3], mfac= 1.0f - *fac;
rgb_to_hsv(in[0], in[1], in[2], hsv, hsv+1, hsv+2);
hsv[0]+= (nhs->hue - 0.5f);
if(hsv[0]>1.0) hsv[0]-=1.0; else if(hsv[0]<0.0) hsv[0]+= 1.0;
hsv[1]*= nhs->sat;
if(hsv[1]>1.0) hsv[1]= 1.0; else if(hsv[1]<0.0) hsv[1]= 0.0;
hsv[2]*= nhs->val;
if(hsv[2]>1.0) hsv[2]= 1.0; else if(hsv[2]<0.0) hsv[2]= 0.0;
hsv_to_rgb(hsv[0], hsv[1], hsv[2], col, col+1, col+2);
out[0]= mfac*in[0] + *fac*col[0];
out[1]= mfac*in[1] + *fac*col[1];
out[2]= mfac*in[2] + *fac*col[2];
out[3]= in[3];
}
else {
QUATCOPY(out, in);
}
}
static void node_composit_exec_hue_sat(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: Fac, Image */
/* stack order out: Image */
if(out[0]->hasoutput==0) return;
/* input no image? then only color operation */
if(in[1]->data==NULL) {
do_hue_sat_fac(node, out[0]->vec, in[1]->vec, in[0]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[1]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit2_pixel_processor(node, stackbuf, cbuf, in[1]->vec, in[0]->data, in[0]->vec, do_hue_sat_fac, CB_RGBA, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_hue_sat= {
/* type code */ CMP_NODE_HUE_SAT,
/* name */ "Hue Saturation Value",
/* width+range */ 150, 80, 250,
/* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS,
/* input sock */ cmp_node_hue_sat_in,
/* output sock */ cmp_node_hue_sat_out,
/* storage */ "NodeHueSat",
/* execfunc */ node_composit_exec_hue_sat
};
/* **************** MIX RGB ******************** */
static bNodeSocketType cmp_node_mix_rgb_in[]= {
{ SOCK_VALUE, 1, "Fac", 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 5.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_mix_rgb_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_mix_rgb(bNode *node, float *out, float *in1, float *in2, float *fac)
{
float col[3];
VECCOPY(col, in1);
if(node->custom2)
ramp_blend(node->custom1, col, col+1, col+2, in2[3]*fac[0], in2);
else
ramp_blend(node->custom1, col, col+1, col+2, fac[0], in2);
VECCOPY(out, col);
out[3]= in1[3];
}
static void node_composit_exec_mix_rgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: fac, Image, Image */
/* stack order out: Image */
float *fac= in[0]->vec;
if(out[0]->hasoutput==0) return;
/* input no image? then only color operation */
if(in[1]->data==NULL && in[2]->data==NULL) {
do_mix_rgb(node, out[0]->vec, in[1]->vec, in[2]->vec, fac);
}
else {
/* make output size of first available input image */
CompBuf *cbuf= in[1]->data?in[1]->data:in[2]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit3_pixel_processor(node, stackbuf, in[1]->data, in[1]->vec, in[2]->data, in[2]->vec, in[0]->data, fac, do_mix_rgb, CB_RGBA, CB_RGBA, CB_VAL);
out[0]->data= stackbuf;
}
}
/* custom1 = mix type */
static bNodeType cmp_node_mix_rgb= {
/* type code */ CMP_NODE_MIX_RGB,
/* name */ "Mix",
/* width+range */ 80, 60, 120,
/* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS,
/* input sock */ cmp_node_mix_rgb_in,
/* output sock */ cmp_node_mix_rgb_out,
/* storage */ "",
/* execfunc */ node_composit_exec_mix_rgb
};
/* **************** FILTER ******************** */
static bNodeSocketType cmp_node_filter_in[]= {
{ SOCK_VALUE, 1, "Fac", 1.0f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_filter_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_filter_edge(CompBuf *out, CompBuf *in, float *filter, float fac)
{
float *row1, *row2, *row3;
float *fp, f1, f2, mfac= 1.0f-fac;
int rowlen, x, y, c, pix= in->type;
rowlen= in->x;
for(y=0; y<in->y; y++) {
/* setup rows */
if(y==0) row1= in->rect;
else row1= in->rect + pix*(y-1)*rowlen;
row2= in->rect + y*pix*rowlen;
if(y==in->y-1) row3= row2;
else row3= row2 + pix*rowlen;
fp= out->rect + pix*y*rowlen;
if(pix==CB_RGBA) {
QUATCOPY(fp, row2);
fp+= pix;
for(x=2; x<rowlen; x++) {
for(c=0; c<3; c++) {
f1= filter[0]*row1[0] + filter[1]*row1[4] + filter[2]*row1[8] + filter[3]*row2[0] + filter[4]*row2[4] + filter[5]*row2[8] + filter[6]*row3[0] + filter[7]*row3[4] + filter[8]*row3[8];
f2= filter[0]*row1[0] + filter[3]*row1[4] + filter[6]*row1[8] + filter[1]*row2[0] + filter[4]*row2[4] + filter[7]*row2[8] + filter[2]*row3[0] + filter[5]*row3[4] + filter[8]*row3[8];
fp[0]= mfac*row2[4] + fac*sqrt(f1*f1 + f2*f2);
fp++; row1++; row2++; row3++;
}
fp[0]= row2[4];
/* no alpha... will clear it completely */
fp++; row1++; row2++; row3++;
}
QUATCOPY(fp, row2+4);
}
else if(pix==CB_VAL) {
for(x=2; x<rowlen; x++) {
f1= filter[0]*row1[0] + filter[1]*row1[1] + filter[2]*row1[2] + filter[3]*row2[0] + filter[4]*row2[1] + filter[5]*row2[2] + filter[6]*row3[0] + filter[7]*row3[1] + filter[8]*row3[2];
f2= filter[0]*row1[0] + filter[3]*row1[1] + filter[6]*row1[2] + filter[1]*row2[0] + filter[4]*row2[1] + filter[7]*row2[2] + filter[2]*row3[0] + filter[5]*row3[1] + filter[8]*row3[2];
fp[0]= mfac*row2[1] + fac*sqrt(f1*f1 + f2*f2);
fp++; row1++; row2++; row3++;
}
}
}
}
static void do_filter3(CompBuf *out, CompBuf *in, float *filter, float fac)
{
float *row1, *row2, *row3;
float *fp, mfac= 1.0f-fac;
int rowlen, x, y, c;
int pixlen= in->type;
rowlen= in->x;
for(y=0; y<in->y; y++) {
/* setup rows */
if(y==0) row1= in->rect;
else row1= in->rect + pixlen*(y-1)*rowlen;
row2= in->rect + y*pixlen*rowlen;
if(y==in->y-1) row3= row2;
else row3= row2 + pixlen*rowlen;
fp= out->rect + pixlen*(y)*rowlen;
if(pixlen==1) {
fp[0]= row2[0];
fp+= 1;
for(x=2; x<rowlen; x++) {
fp[0]= mfac*row2[1] + fac*(filter[0]*row1[0] + filter[1]*row1[1] + filter[2]*row1[2] + filter[3]*row2[0] + filter[4]*row2[1] + filter[5]*row2[2] + filter[6]*row3[0] + filter[7]*row3[1] + filter[8]*row3[2]);
fp++; row1++; row2++; row3++;
}
fp[0]= row2[1];
}
else if(pixlen==2) {
fp[0]= row2[0];
fp[1]= row2[1];
fp+= 2;
for(x=2; x<rowlen; x++) {
for(c=0; c<2; c++) {
fp[0]= mfac*row2[2] + fac*(filter[0]*row1[0] + filter[1]*row1[2] + filter[2]*row1[4] + filter[3]*row2[0] + filter[4]*row2[2] + filter[5]*row2[4] + filter[6]*row3[0] + filter[7]*row3[2] + filter[8]*row3[4]);
fp++; row1++; row2++; row3++;
}
}
fp[0]= row2[2];
fp[1]= row2[3];
}
else if(pixlen==3) {
VECCOPY(fp, row2);
fp+= 3;
for(x=2; x<rowlen; x++) {
for(c=0; c<3; c++) {
fp[0]= mfac*row2[3] + fac*(filter[0]*row1[0] + filter[1]*row1[3] + filter[2]*row1[6] + filter[3]*row2[0] + filter[4]*row2[3] + filter[5]*row2[6] + filter[6]*row3[0] + filter[7]*row3[3] + filter[8]*row3[6]);
fp++; row1++; row2++; row3++;
}
}
VECCOPY(fp, row2+3);
}
else {
QUATCOPY(fp, row2);
fp+= 4;
for(x=2; x<rowlen; x++) {
for(c=0; c<4; c++) {
fp[0]= mfac*row2[4] + fac*(filter[0]*row1[0] + filter[1]*row1[4] + filter[2]*row1[8] + filter[3]*row2[0] + filter[4]*row2[4] + filter[5]*row2[8] + filter[6]*row3[0] + filter[7]*row3[4] + filter[8]*row3[8]);
fp++; row1++; row2++; row3++;
}
}
QUATCOPY(fp, row2+4);
}
}
}
static void node_composit_exec_filter(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
static float soft[9]= {1/16.0f, 2/16.0f, 1/16.0f, 2/16.0f, 4/16.0f, 2/16.0f, 1/16.0f, 2/16.0f, 1/16.0f};
float sharp[9]= {-1,-1,-1,-1,9,-1,-1,-1,-1};
float laplace[9]= {-1/8.0f, -1/8.0f, -1/8.0f, -1/8.0f, 1.0f, -1/8.0f, -1/8.0f, -1/8.0f, -1/8.0f};
float sobel[9]= {1,2,1,0,0,0,-1,-2,-1};
float prewitt[9]= {1,1,1,0,0,0,-1,-1,-1};
float kirsch[9]= {5,5,5,-3,-3,-3,-2,-2,-2};
float shadow[9]= {1,2,1,0,1,0,-1,-2,-1};
if(out[0]->hasoutput==0) return;
/* stack order in: Image */
/* stack order out: Image */
if(in[1]->data) {
/* make output size of first available input image */
CompBuf *cbuf= in[1]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, cbuf->type, 1); /* allocs */
/* warning note: xof and yof are applied in pixelprocessor, but should be copied otherwise? */
stackbuf->xof= cbuf->xof;
stackbuf->yof= cbuf->yof;
switch(node->custom1) {
case CMP_FILT_SOFT:
do_filter3(stackbuf, cbuf, soft, in[0]->vec[0]);
break;
case CMP_FILT_SHARP:
do_filter3(stackbuf, cbuf, sharp, in[0]->vec[0]);
break;
case CMP_FILT_LAPLACE:
do_filter3(stackbuf, cbuf, laplace, in[0]->vec[0]);
break;
case CMP_FILT_SOBEL:
do_filter_edge(stackbuf, cbuf, sobel, in[0]->vec[0]);
break;
case CMP_FILT_PREWITT:
do_filter_edge(stackbuf, cbuf, prewitt, in[0]->vec[0]);
break;
case CMP_FILT_KIRSCH:
do_filter_edge(stackbuf, cbuf, kirsch, in[0]->vec[0]);
break;
case CMP_FILT_SHADOW:
do_filter3(stackbuf, cbuf, shadow, in[0]->vec[0]);
break;
}
out[0]->data= stackbuf;
}
}
/* custom1 = filter type */
static bNodeType cmp_node_filter= {
/* type code */ CMP_NODE_FILTER,
/* name */ "Filter",
/* width+range */ 80, 40, 120,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_filter_in,
/* output sock */ cmp_node_filter_out,
/* storage */ "",
/* execfunc */ node_composit_exec_filter
};
/* **************** VALTORGB ******************** */
static bNodeSocketType cmp_node_valtorgb_in[]= {
{ SOCK_VALUE, 1, "Fac", 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_valtorgb_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_colorband_composit(bNode *node, float *out, float *in)
{
do_colorband(node->storage, in[0], out);
}
static void node_composit_exec_valtorgb(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: fac */
/* stack order out: col, alpha */
if(out[0]->hasoutput==0 && out[1]->hasoutput==0)
return;
if(node->storage) {
/* input no image? then only color operation */
if(in[0]->data==NULL) {
do_colorband(node->storage, in[0]->vec[0], out[0]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_colorband_composit, CB_VAL);
out[0]->data= stackbuf;
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
}
}
}
static bNodeType cmp_node_valtorgb= {
/* type code */ CMP_NODE_VALTORGB,
/* name */ "ColorRamp",
/* width+range */ 240, 200, 300,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_valtorgb_in,
/* output sock */ cmp_node_valtorgb_out,
/* storage */ "ColorBand",
/* execfunc */ node_composit_exec_valtorgb
};
/* **************** RGBTOBW ******************** */
static bNodeSocketType cmp_node_rgbtobw_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_rgbtobw_out[]= {
{ SOCK_VALUE, 0, "Val", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_rgbtobw(bNode *node, float *out, float *in)
{
out[0]= in[0]*0.35f + in[1]*0.45f + in[2]*0.2f;
}
static void node_composit_exec_rgbtobw(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: bw */
/* stack order in: col */
if(out[0]->hasoutput==0)
return;
/* input no image? then only color operation */
if(in[0]->data==NULL) {
do_rgbtobw(node, out[0]->vec, in[0]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1); /* allocs */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_rgbtobw, CB_RGBA);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_rgbtobw= {
/* type code */ CMP_NODE_RGBTOBW,
/* name */ "RGB to BW",
/* width+range */ 80, 40, 120,
/* class+opts */ NODE_CLASS_CONVERTOR, 0,
/* input sock */ cmp_node_rgbtobw_in,
/* output sock */ cmp_node_rgbtobw_out,
/* storage */ "",
/* execfunc */ node_composit_exec_rgbtobw
};
/* **************** SEPARATE RGBA ******************** */
static bNodeSocketType cmp_node_seprgba_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_seprgba_out[]= {
{ SOCK_VALUE, 0, "R", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "G", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "B", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "A", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_seprgba(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: bw channels */
/* stack order in: col */
/* input no image? then only color operation */
if(in[0]->data==NULL) {
out[0]->vec[0] = in[0]->vec[0];
out[1]->vec[0] = in[0]->vec[1];
out[2]->vec[0] = in[0]->vec[2];
out[3]->vec[0] = in[0]->vec[3];
}
else {
/* make sure we get right rgba buffer */
CompBuf *cbuf= typecheck_compbuf(in[0]->data, CB_RGBA);
/* don't do any pixel processing, just copy the stack directly (faster, I presume) */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(cbuf, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(cbuf, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(cbuf, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(cbuf, CHAN_A);
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
}
}
static bNodeType cmp_node_seprgba= {
/* type code */ CMP_NODE_SEPRGBA,
/* name */ "Separate RGBA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, 0,
/* input sock */ cmp_node_seprgba_in,
/* output sock */ cmp_node_seprgba_out,
/* storage */ "",
/* execfunc */ node_composit_exec_seprgba
};
/* **************** SEPARATE HSVA ******************** */
static bNodeSocketType cmp_node_sephsva_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_sephsva_out[]= {
{ SOCK_VALUE, 0, "H", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "S", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "V", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "A", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_sephsva(bNode *node, float *out, float *in)
{
float h, s, v;
rgb_to_hsv(in[0], in[1], in[2], &h, &s, &v);
out[0]= h;
out[1]= s;
out[2]= v;
out[3]= in[3];
}
static void node_composit_exec_sephsva(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: bw channels */
/* stack order in: col */
/* input no image? then only color operation */
if(in[0]->data==NULL) {
float h, s, v;
rgb_to_hsv(in[0]->vec[0], in[0]->vec[1], in[0]->vec[2], &h, &s, &v);
out[0]->vec[0] = h;
out[1]->vec[0] = s;
out[2]->vec[0] = v;
out[3]->vec[0] = in[0]->vec[3];
}
else if ((out[0]->hasoutput) || (out[1]->hasoutput) || (out[2]->hasoutput) || (out[3]->hasoutput)) {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
/* convert the RGB stackbuf to an HSV representation */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_sephsva, CB_RGBA);
/* separate each of those channels */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(stackbuf, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(stackbuf, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
free_compbuf(stackbuf);
}
}
static bNodeType cmp_node_sephsva= {
/* type code */ CMP_NODE_SEPHSVA,
/* name */ "Separate HSVA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, 0,
/* input sock */ cmp_node_sephsva_in,
/* output sock */ cmp_node_sephsva_out,
/* storage */ "",
/* execfunc */ node_composit_exec_sephsva
};
/* **************** COMBINE RGBA ******************** */
static bNodeSocketType cmp_node_combrgba_in[]= {
{ SOCK_VALUE, 1, "R", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "G", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "B", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "A", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_combrgba_out[]= {
{ SOCK_RGBA, 0, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_combrgba(bNode *node, float *out, float *in1, float *in2, float *in3, float *in4)
{
out[0] = in1[0];
out[1] = in2[0];
out[2] = in3[0];
out[3] = in4[0];
}
static void node_composit_exec_combrgba(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: 1 rgba channels */
/* stack order in: 4 value channels */
/* input no image? then only color operation */
if((in[0]->data==NULL) && (in[1]->data==NULL) && (in[2]->data==NULL) && (in[3]->data==NULL)) {
out[0]->vec[0] = in[0]->vec[0];
out[0]->vec[1] = in[1]->vec[0];
out[0]->vec[2] = in[2]->vec[0];
out[0]->vec[3] = in[3]->vec[0];
}
else {
/* make output size of first available input image */
CompBuf *cbuf;
CompBuf *stackbuf;
/* allocate a CompBuf the size of the first available input */
if (in[0]->data) cbuf = in[0]->data;
else if (in[1]->data) cbuf = in[1]->data;
else if (in[2]->data) cbuf = in[2]->data;
else cbuf = in[3]->data;
stackbuf = alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit4_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec,
in[2]->data, in[2]->vec, in[3]->data, in[3]->vec,
do_combrgba, CB_VAL, CB_VAL, CB_VAL, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_combrgba= {
/* type code */ CMP_NODE_COMBRGBA,
/* name */ "Combine RGBA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_combrgba_in,
/* output sock */ cmp_node_combrgba_out,
/* storage */ "",
/* execfunc */ node_composit_exec_combrgba
};
/* **************** SET ALPHA ******************** */
static bNodeSocketType cmp_node_setalpha_in[]= {
{ SOCK_RGBA, 1, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Alpha", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_setalpha_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_setalpha(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: RGBA image */
/* stack order in: col, alpha */
/* input no image? then only color operation */
if(in[0]->data==NULL) {
out[0]->vec[0] = in[0]->vec[0];
out[0]->vec[1] = in[0]->vec[1];
out[0]->vec[2] = in[0]->vec[2];
out[0]->vec[3] = in[1]->vec[0];
}
else {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
if(in[1]->data==NULL && in[1]->vec[0]==1.0f) {
/* pass on image */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_copy_rgb, CB_RGBA);
}
else {
/* send an compbuf or a value to set as alpha - composit2_pixel_processor handles choosing the right one */
composit2_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec, do_copy_a_rgba, CB_RGBA, CB_VAL);
}
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_setalpha= {
/* type code */ CMP_NODE_SETALPHA,
/* name */ "Set Alpha",
/* width+range */ 120, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_setalpha_in,
/* output sock */ cmp_node_setalpha_out,
/* storage */ "",
/* execfunc */ node_composit_exec_setalpha
};
/* **************** ALPHAOVER ******************** */
static bNodeSocketType cmp_node_alphaover_in[]= {
{ SOCK_VALUE, 0, "Fac", 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_alphaover_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_alphaover_premul(bNode *node, float *out, float *src, float *over, float *fac)
{
if(over[3]<=0.0f) {
QUATCOPY(out, src);
}
else if(*fac==1.0f && over[3]>=1.0f) {
QUATCOPY(out, over);
}
else {
float mul= 1.0f - *fac*over[3];
out[0]= (mul*src[0]) + *fac*over[0];
out[1]= (mul*src[1]) + *fac*over[1];
out[2]= (mul*src[2]) + *fac*over[2];
out[3]= (mul*src[3]) + *fac*over[3];
}
}
/* result will be still premul, but the over part is premulled */
static void do_alphaover_key(bNode *node, float *out, float *src, float *over, float *fac)
{
if(over[3]<=0.0f) {
QUATCOPY(out, src);
}
else if(*fac==1.0f && over[3]>=1.0f) {
QUATCOPY(out, over);
}
else {
float premul= fac[0]*over[3];
float mul= 1.0f - premul;
out[0]= (mul*src[0]) + premul*over[0];
out[1]= (mul*src[1]) + premul*over[1];
out[2]= (mul*src[2]) + premul*over[2];
out[3]= (mul*src[3]) + fac[0]*over[3];
}
}
static void node_composit_exec_alphaover(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: col col */
/* stack order out: col */
if(out[0]->hasoutput==0)
return;
/* input no image? then only color operation */
if(in[1]->data==NULL) {
do_alphaover_premul(node, out[0]->vec, in[1]->vec, in[2]->vec, in[0]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[1]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
if(node->custom1)
composit3_pixel_processor(node, stackbuf, in[1]->data, in[1]->vec, in[2]->data, in[2]->vec, in[0]->data, in[0]->vec, do_alphaover_key, CB_RGBA, CB_RGBA, CB_VAL);
else
composit3_pixel_processor(node, stackbuf, in[1]->data, in[1]->vec, in[2]->data, in[2]->vec, in[0]->data, in[0]->vec, do_alphaover_premul, CB_RGBA, CB_RGBA, CB_VAL);
out[0]->data= stackbuf;
}
}
/* custom1: convert 'over' to premul */
static bNodeType cmp_node_alphaover= {
/* type code */ CMP_NODE_ALPHAOVER,
/* name */ "AlphaOver",
/* width+range */ 80, 40, 120,
/* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS,
/* input sock */ cmp_node_alphaover_in,
/* output sock */ cmp_node_alphaover_out,
/* storage */ "",
/* execfunc */ node_composit_exec_alphaover
};
/* **************** Z COMBINE ******************** */
/* lazy coder note: node->custom1 is abused to send signal */
static bNodeSocketType cmp_node_zcombine_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 10000.0f},
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 10000.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_zcombine_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 10000.0f},
{ -1, 0, "" }
};
static void do_zcombine_mask(bNode *node, float *out, float *z1, float *z2)
{
if(*z1 > *z2) {
*out= 1.0f;
if(node->custom1)
*z1= *z2;
}
}
static void do_zcombine_add(bNode *node, float *out, float *col1, float *col2, float *acol)
{
float alpha= *acol;
float malpha= 1.0f - alpha;
out[0]= malpha*col1[0] + alpha*col2[0];
out[1]= malpha*col1[1] + alpha*col2[1];
out[2]= malpha*col1[2] + alpha*col2[2];
out[3]= malpha*col1[3] + alpha*col2[3];
}
static void node_composit_exec_zcombine(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: col z col z */
/* stack order out: col z */
if(out[0]->hasoutput==0)
return;
/* no input image; do nothing now */
if(in[0]->data==NULL) {
return;
}
else {
/* make output size of first input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
CompBuf *zbuf, *mbuf;
float *fp;
int x;
char *aabuf;
if(out[1]->hasoutput) {
/* copy or make a buffer for for the first z value, here we write result in */
if(in[1]->data)
zbuf= dupalloc_compbuf(in[1]->data);
else {
float *zval;
int tot= cbuf->x*cbuf->y;
zbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1);
for(zval= zbuf->rect; tot; tot--, zval++)
*zval= in[1]->vec[0];
}
/* lazy coder hack */
node->custom1= 1;
}
else {
node->custom1= 0;
zbuf= in[1]->data;
}
/* make a mask based on comparison, optionally write zvalue */
mbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1);
composit2_pixel_processor(node, mbuf, zbuf, in[1]->vec, in[3]->data, in[3]->vec, do_zcombine_mask, CB_VAL, CB_VAL);
/* convert to char */
aabuf= MEM_mallocN(cbuf->x*cbuf->y, "aa buf");
fp= mbuf->rect;
for(x= cbuf->x*cbuf->y-1; x>=0; x--)
if(fp[x]==0.0f) aabuf[x]= 0;
else aabuf[x]= 255;
antialias_tagbuf(cbuf->x, cbuf->y, aabuf);
/* convert to float */
fp= mbuf->rect;
for(x= cbuf->x*cbuf->y-1; x>=0; x--)
if(aabuf[x]>1)
fp[x]= (1.0f/255.0f)*(float)aabuf[x];
composit3_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[2]->data, in[2]->vec, mbuf, NULL,
do_zcombine_add, CB_RGBA, CB_RGBA, CB_VAL);
/* free */
free_compbuf(mbuf);
MEM_freeN(aabuf);
out[0]->data= stackbuf;
if(node->custom1)
out[1]->data= zbuf;
}
}
static bNodeType cmp_node_zcombine= {
/* type code */ CMP_NODE_ZCOMBINE,
/* name */ "Z Combine",
/* width+range */ 80, 40, 120,
/* class+opts */ NODE_CLASS_OP_COLOR, NODE_OPTIONS,
/* input sock */ cmp_node_zcombine_in,
/* output sock */ cmp_node_zcombine_out,
/* storage */ "",
/* execfunc */ node_composit_exec_zcombine
};
/* **************** MAP VALUE ******************** */
static bNodeSocketType cmp_node_map_value_in[]= {
{ SOCK_VALUE, 1, "Value", 1.0f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_map_value_out[]= {
{ SOCK_VALUE, 0, "Value", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_map_value(bNode *node, float *out, float *src)
{
TexMapping *texmap= node->storage;
out[0]= (src[0] + texmap->loc[0])*texmap->size[0];
if(texmap->flag & TEXMAP_CLIP_MIN)
if(out[0]<texmap->min[0])
out[0]= texmap->min[0];
if(texmap->flag & TEXMAP_CLIP_MAX)
if(out[0]>texmap->max[0])
out[0]= texmap->max[0];
}
static void node_composit_exec_map_value(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: valbuf */
/* stack order out: valbuf */
if(out[0]->hasoutput==0) return;
/* input no image? then only value operation */
if(in[0]->data==NULL) {
do_map_value(node, out[0]->vec, in[0]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1); /* allocs */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_map_value, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_map_value= {
/* type code */ CMP_NODE_MAP_VALUE,
/* name */ "Map Value",
/* width+range */ 100, 60, 150,
/* class+opts */ NODE_CLASS_OP_VECTOR, NODE_OPTIONS,
/* input sock */ cmp_node_map_value_in,
/* output sock */ cmp_node_map_value_out,
/* storage */ "TexMapping",
/* execfunc */ node_composit_exec_map_value
};
/* **************** BLUR ******************** */
static bNodeSocketType cmp_node_blur_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Size", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_blur_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static float *make_gausstab(int filtertype, int rad)
{
float *gausstab, sum, val;
int i, n;
n = 2 * rad + 1;
gausstab = (float *) MEM_mallocN(n * sizeof(float), "gauss");
sum = 0.0f;
for (i = -rad; i <= rad; i++) {
val= RE_filter_value(filtertype, (float)i/(float)rad);
sum += val;
gausstab[i+rad] = val;
}
sum= 1.0f/sum;
for(i=0; i<n; i++)
gausstab[i]*= sum;
return gausstab;
}
static float *make_bloomtab(int rad)
{
float *bloomtab, val;
int i, n;
n = 2 * rad + 1;
bloomtab = (float *) MEM_mallocN(n * sizeof(float), "bloom");
for (i = -rad; i <= rad; i++) {
val = pow(1.0 - fabs((float)i)/((float)rad), 4.0);
bloomtab[i+rad] = val;
}
return bloomtab;
}
/* both input images of same type, either 4 or 1 channel */
static void blur_single_image(CompBuf *new, CompBuf *img, float scale, NodeBlurData *nbd)
{
CompBuf *work;
register float sum, val;
float rval, gval, bval, aval;
float *gausstab, *gausstabcent;
int rad, imgx= img->x, imgy= img->y;
int x, y, pix= img->type;
int i, bigstep;
float *src, *dest;
/* helper image */
work= alloc_compbuf(imgx, imgy, img->type, 1); /* allocs */
/* horizontal */
rad = scale*(float)nbd->sizex;
if(rad>imgx/2)
rad= imgx/2;
else if(rad<1)
rad= 1;
gausstab= make_gausstab(nbd->filtertype, rad);
gausstabcent= gausstab+rad;
for (y = 0; y < imgy; y++) {
float *srcd= img->rect + pix*(y*img->x);
dest = work->rect + pix*(y * img->x);
for (x = 0; x < imgx ; x++) {
int minr= x-rad<0?-x:-rad;
int maxr= x+rad>imgx?imgx-x:rad;
src= srcd + pix*(x+minr);
sum= gval = rval= bval= aval= 0.0f;
for (i= minr; i < maxr; i++) {
val= gausstabcent[i];
sum+= val;
rval += val * (*src++);
if(pix==4) {
gval += val * (*src++);
bval += val * (*src++);
aval += val * (*src++);
}
}
sum= 1.0f/sum;
*dest++ = rval*sum;
if(pix==4) {
*dest++ = gval*sum;
*dest++ = bval*sum;
*dest++ = aval*sum;
}
}
}
/* vertical */
MEM_freeN(gausstab);
rad = scale*(float)nbd->sizey;
if(rad>imgy/2)
rad= imgy/2;
else if(rad<1)
rad= 1;
gausstab= make_gausstab(nbd->filtertype, rad);
gausstabcent= gausstab+rad;
bigstep = pix*imgx;
for (x = 0; x < imgx; x++) {
float *srcd= work->rect + pix*x;
dest = new->rect + pix*x;
for (y = 0; y < imgy ; y++) {
int minr= y-rad<0?-y:-rad;
int maxr= y+rad>imgy?imgy-y:rad;
src= srcd + bigstep*(y+minr);
sum= gval = rval= bval= aval= 0.0f;
for (i= minr; i < maxr; i++) {
val= gausstabcent[i];
sum+= val;
rval += val * src[0];
if(pix==4) {
gval += val * src[1];
bval += val * src[2];
aval += val * src[3];
}
src += bigstep;
}
sum= 1.0f/sum;
dest[0] = rval*sum;
if(pix==4) {
dest[1] = gval*sum;
dest[2] = bval*sum;
dest[3] = aval*sum;
}
dest+= bigstep;
}
}
free_compbuf(work);
MEM_freeN(gausstab);
}
/* reference has to be mapped 0-1, and equal in size */
static void bloom_with_reference(CompBuf *new, CompBuf *img, CompBuf *ref, float fac, NodeBlurData *nbd)
{
CompBuf *wbuf;
register float val;
float radxf, radyf;
float **maintabs;
float *gausstabx, *gausstabcenty;
float *gausstaby, *gausstabcentx;
int radx, rady, imgx= img->x, imgy= img->y;
int x, y;
int i, j;
float *src, *dest, *wb;
wbuf= alloc_compbuf(imgx, imgy, CB_VAL, 1);
/* horizontal */
radx = (float)nbd->sizex;
if(radx>imgx/2)
radx= imgx/2;
else if(radx<1)
radx= 1;
/* vertical */
rady = (float)nbd->sizey;
if(rady>imgy/2)
rady= imgy/2;
else if(rady<1)
rady= 1;
x= MAX2(radx, rady);
maintabs= MEM_mallocN(x*sizeof(void *), "gauss array");
for(i= 0; i<x; i++)
maintabs[i]= make_bloomtab(i+1);
/* vars to store before we go */
// refd= ref->rect;
src= img->rect;
radxf= (float)radx;
radyf= (float)rady;
for (y = 0; y < imgy; y++) {
for (x = 0; x < imgx ; x++, src+=4) {//, refd++) {
// int refradx= (int)(refd[0]*radxf);
// int refrady= (int)(refd[0]*radyf);
int refradx= (int)(radxf*0.3f*src[3]*(src[0]+src[1]+src[2]));
int refrady= (int)(radyf*0.3f*src[3]*(src[0]+src[1]+src[2]));
if(refradx>radx) refradx= radx;
else if(refradx<1) refradx= 1;
if(refrady>rady) refrady= rady;
else if(refrady<1) refrady= 1;
if(refradx==1 && refrady==1) {
wb= wbuf->rect + ( y*imgx + x);
dest= new->rect + 4*( y*imgx + x);
wb[0]+= 1.0f;
dest[0] += src[0];
dest[1] += src[1];
dest[2] += src[2];
dest[3] += src[3];
}
else {
int minxr= x-refradx<0?-x:-refradx;
int maxxr= x+refradx>imgx?imgx-x:refradx;
int minyr= y-refrady<0?-y:-refrady;
int maxyr= y+refrady>imgy?imgy-y:refrady;
float *destd= new->rect + 4*( (y + minyr)*imgx + x + minxr);
float *wbufd= wbuf->rect + ( (y + minyr)*imgx + x + minxr);
gausstabx= maintabs[refradx-1];
gausstabcentx= gausstabx+refradx;
gausstaby= maintabs[refrady-1];
gausstabcenty= gausstaby+refrady;
for (i= minyr; i < maxyr; i++, destd+= 4*imgx, wbufd+= imgx) {
dest= destd;
wb= wbufd;
for (j= minxr; j < maxxr; j++, dest+=4, wb++) {
val= gausstabcenty[i]*gausstabcentx[j];
wb[0]+= val;
dest[0] += val * src[0];
dest[1] += val * src[1];
dest[2] += val * src[2];
dest[3] += val * src[3];
}
}
}
}
}
x= imgx*imgy;
dest= new->rect;
wb= wbuf->rect;
while(x--) {
val= 1.0f/wb[0];
dest[0]*= val;
dest[1]*= val;
dest[2]*= val;
dest[3]*= val;
wb++;
dest+= 4;
}
free_compbuf(wbuf);
x= MAX2(radx, rady);
for(i= 0; i<x; i++)
MEM_freeN(maintabs[i]);
MEM_freeN(maintabs);
}
/* only accepts RGBA buffers */
static void gamma_correct_compbuf(CompBuf *img, int inversed)
{
float *drect;
int x;
if(img->type!=CB_RGBA) return;
drect= img->rect;
if(inversed) {
for(x=img->x*img->y; x>0; x--, drect+=4) {
if(drect[0]>0.0f) drect[0]= sqrt(drect[0]); else drect[0]= 0.0f;
if(drect[1]>0.0f) drect[1]= sqrt(drect[1]); else drect[1]= 0.0f;
if(drect[2]>0.0f) drect[2]= sqrt(drect[2]); else drect[2]= 0.0f;
}
}
else {
for(x=img->x*img->y; x>0; x--, drect+=4) {
if(drect[0]>0.0f) drect[0]*= drect[0]; else drect[0]= 0.0f;
if(drect[1]>0.0f) drect[1]*= drect[1]; else drect[1]= 0.0f;
if(drect[2]>0.0f) drect[2]*= drect[2]; else drect[2]= 0.0f;
}
}
}
#if 0
static float hexagon_filter(float fi, float fj)
{
fi= fabs(fi);
fj= fabs(fj);
if(fj>0.33f) {
fj= (fj-0.33f)/0.66f;
if(fi+fj>1.0f)
return 0.0f;
else
return 1.0f;
}
else return 1.0f;
}
#endif
/* uses full filter, no horizontal/vertical optimize possible */
/* both images same type, either 1 or 4 channels */
static void bokeh_single_image(CompBuf *new, CompBuf *img, float fac, NodeBlurData *nbd)
{
register float val;
float radxf, radyf;
float *gausstab, *dgauss;
int radx, rady, imgx= img->x, imgy= img->y;
int x, y, pix= img->type;
int i, j, n;
float *src= NULL, *dest, *srcd= NULL;
/* horizontal */
radxf = fac*(float)nbd->sizex;
if(radxf>imgx/2.0f)
radxf= imgx/2.0f;
else if(radxf<1.0f)
radxf= 1.0f;
/* vertical */
radyf = fac*(float)nbd->sizey;
if(radyf>imgy/2.0f)
radyf= imgy/2.0f;
else if(radyf<1.0f)
radyf= 1.0f;
radx= ceil(radxf);
rady= ceil(radyf);
n = (2*radx+1)*(2*rady+1);
/* create a full filter image */
gausstab= MEM_mallocN(sizeof(float)*n, "filter tab");
dgauss= gausstab;
val= 0.0f;
for(j=-rady; j<=rady; j++) {
for(i=-radx; i<=radx; i++, dgauss++) {
float fj= (float)j/radyf;
float fi= (float)i/radxf;
float dist= sqrt(fj*fj + fi*fi);
//*dgauss= hexagon_filter(fi, fj);
*dgauss= RE_filter_value(nbd->filtertype, 2.0f*dist - 1.0f);
val+= *dgauss;
}
}
if(val!=0.0f) {
val= 1.0f/val;
for(j= n -1; j>=0; j--)
gausstab[j]*= val;
}
else gausstab[4]= 1.0f;
for (y = -rady+1; y < imgy+rady-1; y++) {
if(y<=0) srcd= img->rect;
else if(y<imgy) srcd+= pix*imgx;
else srcd= img->rect + pix*(imgy-1)*imgx;
for (x = -radx+1; x < imgx+radx-1 ; x++) {
int minxr= x-radx<0?-x:-radx;
int maxxr= x+radx>=imgx?imgx-x-1:radx;
int minyr= y-rady<0?-y:-rady;
int maxyr= y+rady>imgy-1?imgy-y-1:rady;
float *destd= new->rect + pix*( (y + minyr)*imgx + x + minxr);
float *dgausd= gausstab + (minyr+rady)*2*radx + minxr+radx;
if(x<=0) src= srcd;
else if(x<imgx) src+= pix;
else src= srcd + pix*(imgx-1);
for (i= minyr; i <=maxyr; i++, destd+= pix*imgx, dgausd+= 2*radx + 1) {
dest= destd;
dgauss= dgausd;
for (j= minxr; j <=maxxr; j++, dest+=pix, dgauss++) {
val= *dgauss;
if(val!=0.0f) {
dest[0] += val * src[0];
if(pix>1) {
dest[1] += val * src[1];
dest[2] += val * src[2];
dest[3] += val * src[3];
}
}
}
}
}
}
MEM_freeN(gausstab);
}
/* reference has to be mapped 0-1, and equal in size */
static void blur_with_reference(CompBuf *new, CompBuf *img, CompBuf *ref, NodeBlurData *nbd)
{
CompBuf *blurbuf, *ref_use;
register float sum, val;
float rval, gval, bval, aval, radxf, radyf;
float **maintabs;
float *gausstabx, *gausstabcenty;
float *gausstaby, *gausstabcentx;
int radx, rady, imgx= img->x, imgy= img->y;
int x, y, pix= img->type;
int i, j;
float *src, *dest, *refd, *blurd;
if(ref->x!=img->x && ref->y!=img->y)
return;
ref_use= typecheck_compbuf(ref, CB_VAL);
/* trick is; we blur the reference image... but only works with clipped values*/
blurbuf= alloc_compbuf(imgx, imgy, CB_VAL, 1);
blurd= blurbuf->rect;
refd= ref_use->rect;
for(x= imgx*imgy; x>0; x--, refd++, blurd++) {
if(refd[0]<0.0f) blurd[0]= 0.0f;
else if(refd[0]>1.0f) blurd[0]= 1.0f;
else blurd[0]= refd[0];
}
blur_single_image(blurbuf, blurbuf, 1.0f, nbd);
/* horizontal */
radx = (float)nbd->sizex;
if(radx>imgx/2)
radx= imgx/2;
else if(radx<1)
radx= 1;
/* vertical */
rady = (float)nbd->sizey;
if(rady>imgy/2)
rady= imgy/2;
else if(rady<1)
rady= 1;
x= MAX2(radx, rady);
maintabs= MEM_mallocN(x*sizeof(void *), "gauss array");
for(i= 0; i<x; i++)
maintabs[i]= make_gausstab(nbd->filtertype, i+1);
refd= blurbuf->rect;
dest= new->rect;
radxf= (float)radx;
radyf= (float)rady;
for (y = 0; y < imgy; y++) {
for (x = 0; x < imgx ; x++, dest+=pix, refd++) {
int refradx= (int)(refd[0]*radxf);
int refrady= (int)(refd[0]*radyf);
if(refradx>radx) refradx= radx;
else if(refradx<1) refradx= 1;
if(refrady>rady) refrady= rady;
else if(refrady<1) refrady= 1;
if(refradx==1 && refrady==1) {
src= img->rect + pix*( y*imgx + x);
if(pix==1)
dest[0]= src[0];
else
QUATCOPY(dest, src);
}
else {
int minxr= x-refradx<0?-x:-refradx;
int maxxr= x+refradx>imgx?imgx-x:refradx;
int minyr= y-refrady<0?-y:-refrady;
int maxyr= y+refrady>imgy?imgy-y:refrady;
float *srcd= img->rect + pix*( (y + minyr)*imgx + x + minxr);
gausstabx= maintabs[refradx-1];
gausstabcentx= gausstabx+refradx;
gausstaby= maintabs[refrady-1];
gausstabcenty= gausstaby+refrady;
sum= gval = rval= bval= aval= 0.0f;
for (i= minyr; i < maxyr; i++, srcd+= pix*imgx) {
src= srcd;
for (j= minxr; j < maxxr; j++, src+=pix) {
val= gausstabcenty[i]*gausstabcentx[j];
sum+= val;
rval += val * src[0];
if(pix>1) {
gval += val * src[1];
bval += val * src[2];
aval += val * src[3];
}
}
}
sum= 1.0f/sum;
dest[0] = rval*sum;
if(pix>1) {
dest[1] = gval*sum;
dest[2] = bval*sum;
dest[3] = aval*sum;
}
}
}
}
free_compbuf(blurbuf);
x= MAX2(radx, rady);
for(i= 0; i<x; i++)
MEM_freeN(maintabs[i]);
MEM_freeN(maintabs);
if(ref_use!=ref)
free_compbuf(ref_use);
}
static void node_composit_exec_blur(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_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 */
blur_with_reference(new, img, in[1]->data, node->storage);
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 */
if(nbd->gamma) {
gammabuf= dupalloc_compbuf(img);
gamma_correct_compbuf(gammabuf, 0);
}
else gammabuf= img;
if(nbd->bokeh)
bokeh_single_image(new, gammabuf, in[1]->vec[0], nbd);
else if(1)
blur_single_image(new, gammabuf, in[1]->vec[0], nbd);
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);
}
}
out[0]->data= new;
}
if(img!=in[0]->data)
free_compbuf(img);
}
static bNodeType cmp_node_blur= {
/* type code */ CMP_NODE_BLUR,
/* name */ "Blur",
/* width+range */ 120, 80, 200,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_blur_in,
/* output sock */ cmp_node_blur_out,
/* storage */ "NodeBlurData",
/* execfunc */ node_composit_exec_blur
};
/* ************ qdn: Defocus node ****************** */
static bNodeSocketType cmp_node_defocus_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_defocus_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
// line coefs for point sampling & scancon. data.
typedef struct BokehCoeffs {
float x0, y0, dx, dy;
float ls_x, ls_y;
float min_x, min_y, max_x, max_y;
} BokehCoeffs;
// returns array of BokehCoeffs
// returns length of array in 'len_bkh',
// radius squared of inscribed disk in 'inradsq', needed in getWeight() test,
// BKH[8] is the data returned for the bokeh shape & bkh_b[4] is it's 2d bound
static void makeBokeh(char bktype, char ro, int* len_bkh, float* inradsq, BokehCoeffs BKH[8], float bkh_b[4])
{
float x0, x1, y0, y1, dx, dy, iDxy, w = ro*M_PI/180.f;
float wi = (360.f/bktype)*M_PI/180.f;
int i, ov, nv;
// bktype must be at least 3 & <= 8
bktype = (bktype<3) ? 3 : ((bktype>8) ? 8 : bktype);
*len_bkh = bktype;
*inradsq = -1.f;
for (i=0; i<(*len_bkh); i++) {
x0 = cos(w);
y0 = sin(w);
w += wi;
x1 = cos(w);
y1 = sin(w);
if ((*inradsq)<0.f) {
// radius squared of inscribed disk
float idx=(x0+x1)*0.5f, idy=(y0+y1)*0.5f;
*inradsq = idx*idx + idy*idy;
}
BKH[i].x0 = x0;
BKH[i].y0 = y0;
dx = x1-x0, dy = y1-y0;
iDxy = 1.f / sqrt(dx*dx + dy*dy);
dx *= iDxy;
dy *= iDxy;
BKH[i].dx = dx;
BKH[i].dy = dy;
}
// precalc scanconversion data
// bokeh bound, not transformed, for scanconvert
bkh_b[0] = bkh_b[2] = 1e10f; // xmin/ymin
bkh_b[1] = bkh_b[3] = -1e10f; // xmax/ymax
ov = (*len_bkh) - 1;
for (nv=0; nv<(*len_bkh); nv++) {
bkh_b[0] = MIN2(bkh_b[0], BKH[nv].x0); // xmin
bkh_b[1] = MAX2(bkh_b[1], BKH[nv].x0); // xmax
bkh_b[2] = MIN2(bkh_b[2], BKH[nv].y0); // ymin
bkh_b[3] = MAX2(bkh_b[3], BKH[nv].y0); // ymax
BKH[nv].min_x = MIN2(BKH[ov].x0, BKH[nv].x0);
BKH[nv].max_x = MAX2(BKH[ov].x0, BKH[nv].x0);
BKH[nv].min_y = MIN2(BKH[ov].y0, BKH[nv].y0);
BKH[nv].max_y = MAX2(BKH[ov].y0, BKH[nv].y0);
dy = BKH[nv].y0 - BKH[ov].y0;
BKH[nv].ls_x = (BKH[nv].x0 - BKH[ov].x0) / ((dy==0.f) ? 1.f : dy);
BKH[nv].ls_y = (BKH[nv].ls_x==0.f) ? 1.f : (1.f/BKH[nv].ls_x);
ov = nv;
}
}
// test if u/v inside shape & returns weight value
static float getWeight(BokehCoeffs* BKH, int len_bkh, float u, float v, float rad, float inradsq)
{
BokehCoeffs* bc = BKH;
float cdist, irad = (rad==0.f) ? 1.f : (1.f/rad);
u *= irad;
v *= irad;
// early out test1: if point outside outer unit disk, it cannot be inside shape
cdist = u*u + v*v;
if (cdist>1.f) return 0.f;
// early out test2: if point inside or on inner disk, point must be inside shape
if (cdist<=inradsq) return 1.f;
while (len_bkh--) {
if ((bc->dy*(u - bc->x0) - bc->dx*(v - bc->y0)) > 0.f) return 0.f;
bc++;
}
return 1.f;
}
// QMC.seq. for sampling, A.Keller, EMS
static float RI_vdC(unsigned int bits, unsigned int r)
{
bits = ( bits << 16) | ( bits >> 16);
bits = ((bits & 0x00ff00ff) << 8) | ((bits & 0xff00ff00) >> 8);
bits = ((bits & 0x0f0f0f0f) << 4) | ((bits & 0xf0f0f0f0) >> 4);
bits = ((bits & 0x33333333) << 2) | ((bits & 0xcccccccc) >> 2);
bits = ((bits & 0x55555555) << 1) | ((bits & 0xaaaaaaaa) >> 1);
bits ^= r;
return (float)((double)bits / 4294967296.0);
}
// single channel IIR gaussian filtering
// much faster than anything else, constant time independent of width
// should extend to multichannel and make this a node, could be useful
static void IIR_gauss(CompBuf* buf, float sigma)
{
double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3];
float *X, *Y, *W;
int i, x, y, sz;
// single channel only for now
if (buf->type != CB_VAL) return;
// <0.5 not valid, though can have a possibly useful sort of sharpening effect
if (sigma < 0.5) return;
// see "Recursive Gabor Filtering" by Young/VanVliet
// all factors here in double.prec. Required, because for single.prec it seems to blow up if sigma > ~200
if (sigma >= 3.556)
q = 0.9804*(sigma - 3.556) + 2.5091;
else // sigma >= 0.5
q = (0.0561*sigma + 0.5784)*sigma - 0.2568;
q2 = q*q;
sc = (1.1668 + q)*(3.203729649 + (2.21566 + q)*q);
// no gabor filtering here, so no complex multiplies, just the regular coefs.
// all negated here, so as not to have to recalc Triggs/Sdika matrix
cf[1] = q*(5.788961737 + (6.76492 + 3.0*q)*q)/ sc;
cf[2] = -q2*(3.38246 + 3.0*q)/sc;
// 0 & 3 unchanged
cf[3] = q2*q/sc;
cf[0] = 1.0 - cf[1] - cf[2] - cf[3];
// Triggs/Sdika border corrections,
// it seems to work, not entirely sure if it is actually totally correct,
// Besides J.M.Geusebroek's anigauss.c (see http://www.science.uva.nl/~mark),
// found one other implementation by Cristoph Lampert,
// but neither seem to be quite the same, result seems to be ok sofar anyway.
// Extra scale factor here to not have to do it in filter,
// though maybe this had something to with the precision errors
sc = cf[0]/((1.0 + cf[1] - cf[2] + cf[3])*(1.0 - cf[1] - cf[2] - cf[3])*(1.0 + cf[2] + (cf[1] - cf[3])*cf[3]));
tsM[0] = sc*(-cf[3]*cf[1] + 1.0 - cf[3]*cf[3] - cf[2]);
tsM[1] = sc*((cf[3] + cf[1])*(cf[2] + cf[3]*cf[1]));
tsM[2] = sc*(cf[3]*(cf[1] + cf[3]*cf[2]));
tsM[3] = sc*(cf[1] + cf[3]*cf[2]);
tsM[4] = sc*(-(cf[2] - 1.0)*(cf[2] + cf[3]*cf[1]));
tsM[5] = sc*(-(cf[3]*cf[1] + cf[3]*cf[3] + cf[2] - 1.0)*cf[3]);
tsM[6] = sc*(cf[3]*cf[1] + cf[2] + cf[1]*cf[1] - cf[2]*cf[2]);
tsM[7] = sc*(cf[1]*cf[2] + cf[3]*cf[2]*cf[2] - cf[1]*cf[3]*cf[3] - cf[3]*cf[3]*cf[3] - cf[3]*cf[2] + cf[3]);
tsM[8] = sc*(cf[3]*(cf[1] + cf[3]*cf[2]));
#define YVV(L)\
{\
W[0] = cf[0]*X[0] + cf[1]*X[0] + cf[2]*X[0] + cf[3]*X[0];\
W[1] = cf[0]*X[1] + cf[1]*W[0] + cf[2]*X[0] + cf[3]*X[0];\
W[2] = cf[0]*X[2] + cf[1]*W[1] + cf[2]*W[0] + cf[3]*X[0];\
for (i=3; i<L; i++)\
W[i] = cf[0]*X[i] + cf[1]*W[i-1] + cf[2]*W[i-2] + cf[3]*W[i-3];\
tsu[0] = W[L-1] - X[L-1];\
tsu[1] = W[L-2] - X[L-1];\
tsu[2] = W[L-3] - X[L-1];\
tsv[0] = tsM[0]*tsu[0] + tsM[1]*tsu[1] + tsM[2]*tsu[2] + X[L-1];\
tsv[1] = tsM[3]*tsu[0] + tsM[4]*tsu[1] + tsM[5]*tsu[2] + X[L-1];\
tsv[2] = tsM[6]*tsu[0] + tsM[7]*tsu[1] + tsM[8]*tsu[2] + X[L-1];\
Y[L-1] = cf[0]*W[L-1] + cf[1]*tsv[0] + cf[2]*tsv[1] + cf[3]*tsv[2];\
Y[L-2] = cf[0]*W[L-2] + cf[1]*Y[L-1] + cf[2]*tsv[0] + cf[3]*tsv[1];\
Y[L-3] = cf[0]*W[L-3] + cf[1]*Y[L-2] + cf[2]*Y[L-1] + cf[3]*tsv[0];\
for (i=L-4; i>=0; i--)\
Y[i] = cf[0]*W[i] + cf[1]*Y[i+1] + cf[2]*Y[i+2] + cf[3]*Y[i+3];\
}
// intermediate buffers
sz = MAX2(buf->x, buf->y);
Y = MEM_callocN(sz*sizeof(float), "IIR_gauss Y buf");
W = MEM_callocN(sz*sizeof(float), "IIR_gauss W buf");
// H
for (y=0; y<buf->y; y++) {
X = &buf->rect[y*buf->x];
YVV(buf->x);
memcpy(X, Y, sizeof(float)*buf->x);
}
// V
X = MEM_callocN(buf->y*sizeof(float), "IIR_gauss X buf");
for (x=0; x<buf->x; x++) {
for (y=0; y<buf->y; y++)
X[y] = buf->rect[x + y*buf->x];
YVV(buf->y);
for (y=0; y<buf->y; y++)
buf->rect[x + y*buf->x] = Y[y];
}
MEM_freeN(X);
MEM_freeN(W);
MEM_freeN(Y);
#undef YVV
}
static void defocus_blur(CompBuf* new, CompBuf* img, CompBuf* zbuf, float inpval, NodeDefocus* nqd)
{
CompBuf *wts; // weights buffer
CompBuf *crad; // CoC radius buffer
BokehCoeffs BKH[8]; // bokeh shape data, here never > 8 pts.
float bkh_b[4] = {0}; // shape 2D bound
unsigned int p, px, p4, zp, cp, cp4;
float *ctcol, u, v, iZ, ct_crad, bcrad, lwt, wt=0, cR2=0;
float dof_sp, maxfgc, nmaxc, scf, bk_hn_theta=0, inradsq=0;
float cam_fdist=1, cam_invfdist=1, cam_lens=35;
int x, y, sx, sy, len_bkh=0;
float aspect, aperture;
int minsz;
// get some required params from the current scene camera
Object* camob = G.scene->camera;
if (camob->type==OB_CAMERA) {
Camera* cam = (Camera*)camob->data;
cam_lens = cam->lens;
cam_fdist = (cam->YF_dofdist==0.f) ? 1e10f : cam->YF_dofdist;
cam_invfdist = 1.f/cam_fdist;
}
// guess work here.. best match with raytraced result
minsz = MIN2(img->x, img->y);
dof_sp = (float)minsz / (16.f / cam_lens); // <- == aspect * MIN2(img->x, img->y) / tan(0.5f * fov);
// aperture
aspect = (img->x > img->y) ? (img->y / (float)img->x) : (img->x / (float)img->y);
aperture = 0.5f*(cam_lens / (aspect*32.f)) / nqd->fstop;
// if not disk, make bokeh coefficients and other needed data
if (nqd->bktype!=0) {
makeBokeh(nqd->bktype, nqd->rotation, &len_bkh, &inradsq, BKH, bkh_b);
bk_hn_theta = 0.5 * nqd->bktype * sin(2.0 * M_PI / nqd->bktype); // weight factor
}
// accumulated weights
wts = alloc_compbuf(img->x, img->y, CB_VAL, 1);
// CoC radius buffer
crad = alloc_compbuf(img->x, img->y, CB_VAL, 1);
// if 'no_zbuf' flag set (which is always set if input is not an image),
// values are instead interpreted directly as blur radius values
if (nqd->no_zbuf) {
// to prevent *reaaallly* big radius values and impossible calculation times,
// limit the maximum to half the image width or height, whichever is smaller
float maxr = 0.5f*(float)MIN2(img->x, img->y);
for (p=0; p<(unsigned int)(img->x*img->y); p++) {
crad->rect[p] = zbuf ? (zbuf->rect[p]*nqd->scale) : inpval;
crad->rect[p] = MIN2(crad->rect[p], maxr);
// if maxblur!=0, limit maximum
if (nqd->maxblur != 0.f) crad->rect[p] = MIN2(crad->rect[p], nqd->maxblur);
}
}
else {
// actual zbuffer.
// separate foreground from background CoC's
// then blur background and blend in again with foreground,
// improves the 'blurred foreground overlapping in-focus midground' sharp boundary problem.
// wts buffer here used for blendmask
maxfgc = 0.f; // maximum foreground CoC radius
for (y=0; y<img->y; y++) {
p = y * img->x;
for (x=0; x<img->x; x++) {
px = p + x;
iZ = (zbuf->rect[px]==0.f) ? 0.f : (1.f/zbuf->rect[px]);
crad->rect[px] = 0.5f*(aperture*(dof_sp*(cam_invfdist - iZ) - 1.f));
if (crad->rect[px] <= 0.f) {
wts->rect[px] = 1.f;
crad->rect[px] = -crad->rect[px];
if (crad->rect[px] > maxfgc) maxfgc = crad->rect[px];
}
else crad->rect[px] = wts->rect[px] = 0;
}
}
// fast blur...
IIR_gauss(crad, 2.f*maxfgc);
IIR_gauss(wts, 2.f*maxfgc);
// find new maximum to scale it back to original
// (could skip this, not strictly necessary, in general, difference is quite small, but just in case...)
nmaxc = 0;
for (p=0; p<(img->x*img->y); p++)
if (crad->rect[p] > nmaxc) nmaxc = crad->rect[p];
// rescale factor
scf = (nmaxc==0.f) ? 1.f: (maxfgc / nmaxc);
// and blend...
for (y=0; y<img->y; y++) {
p = y*img->x;
for (x=0; x<img->x; x++) {
px = p + x;
if (zbuf->rect[px]!=0.f) {
iZ = (zbuf->rect[px]==0.f) ? 0.f : (1.f/zbuf->rect[px]);
bcrad = 0.5f*fabs(aperture*(dof_sp*(cam_invfdist - iZ) - 1.f));
// scale crad back to original maximum and blend
crad->rect[px] = bcrad + wts->rect[px]*(scf*crad->rect[px] - bcrad);
if (crad->rect[px] < 0.01f) crad->rect[px] = 0.01f;
// if maxblur!=0, limit maximum
if (nqd->maxblur != 0.f) crad->rect[px] = MIN2(crad->rect[px], nqd->maxblur);
}
else crad->rect[px] = 0.f;
// clear weights for next part
wts->rect[px] = 0.f;
}
}
}
//------------------------------------------------------------------
// main loop
for (y=0; y<img->y; y++) {
// some sort of visual feedback would be nice, or at least this text in the renderwin header
// but for now just print some info in the console every 8 scanlines.
if (((y & 7)==0) || (y==(img->y-1))) {
printf("\rdefocus: Processing Line %d of %d ... ", y+1, img->y);
fflush(stdout);
}
zp = y * img->x;
for (x=0; x<img->x; x++) {
cp = zp + x;
cp4 = cp * img->type;
// Circle of Confusion radius for current pixel
cR2 = ct_crad = crad->rect[cp];
// skip if zero (border render)
if (ct_crad==0.f) continue;
cR2 *= cR2;
// pixel color
ctcol = &img->rect[cp4];
if (!nqd->preview) {
int xs, xe, ys, ye;
float lwt, wtcol[4] = {0}, aacol[4] = {0};
// shape weight
if (nqd->bktype==0) // disk
wt = 1.f/((float)M_PI*cR2);
else
wt = 1.f/(cR2*bk_hn_theta);
// weighted color
wtcol[0] = wt*ctcol[0];
if (new->type != CB_VAL) {
wtcol[1] = wt*ctcol[1];
wtcol[2] = wt*ctcol[2];
wtcol[3] = wt*ctcol[3];
}
// macro for background blur overlap test
// unfortunately, since this is done per pixel,
// it has a very significant negative impact on processing time...
// (eg. aa disk blur without test: 112 sec, vs with test: 176 sec...)
// iff center blur radius > threshold
// and if overlap pixel in focus, do nothing, else add color/weigbt
// (threshold constant is dependant on amount of blur)
#define TESTBG1(c, w) {\
if (ct_crad > nqd->bthresh) {\
if (crad->rect[p] > nqd->bthresh) {\
new->rect[p] += c[0];\
wts->rect[p] += w;\
}\
}\
else {\
new->rect[p] += c[0];\
wts->rect[p] += w;\
}\
}
#define TESTBG4(c, w) {\
if (ct_crad > nqd->bthresh) {\
if (crad->rect[p] > nqd->bthresh) {\
new->rect[p4] += c[0];\
new->rect[p4+1] += c[1];\
new->rect[p4+2] += c[2];\
new->rect[p4+3] += c[3];\
wts->rect[p] += w;\
}\
}\
else {\
new->rect[p4] += c[0];\
new->rect[p4+1] += c[1];\
new->rect[p4+2] += c[2];\
new->rect[p4+3] += c[3];\
wts->rect[p] += w;\
}\
}
if (nqd->bktype == 0) {
// Disk
int _x, i, j, di;
float Dj, T;
// AA pixel
#define AAPIX(a, b) {\
int _ny = b;\
if ((_ny >= 0) && (_ny < new->y)) {\
int _nx = a;\
if ((_nx >=0) && (_nx < new->x)) {\
p = _ny*new->x + _nx;\
if (new->type==CB_VAL) {\
TESTBG1(aacol, lwt);\
}\
else {\
p4 = p * new->type;\
TESTBG4(aacol, lwt);\
}\
}\
}\
}
// circle scanline
#define CSCAN(a, b) {\
int _ny = y + b;\
if ((_ny >= 0) && (_ny < new->y)) {\
xs = x - a + 1;\
if (xs < 0) xs = 0;\
xe = x + a;\
if (xe > new->x) xe = new->x;\
p = _ny*new->x + xs;\
if (new->type==CB_VAL) {\
for (_x=xs; _x<xe; _x++, p++) TESTBG1(wtcol, wt);\
}\
else {\
p4 = p * new->type;\
for (_x=xs; _x<xe; _x++, p++, p4+=new->type) TESTBG4(wtcol, wt);\
}\
}\
}
i = ceil(ct_crad);
j = 0;
T = 0;
while (i > j) {
Dj = sqrt(cR2 - j*j);
Dj -= floor(Dj);
di = 0;
if (Dj > T) { i--; di = 1; }
T = Dj;
aacol[0] = wtcol[0]*Dj;
if (new->type != CB_VAL) {
aacol[1] = wtcol[1]*Dj;
aacol[2] = wtcol[2]*Dj;
aacol[3] = wtcol[3]*Dj;
}
lwt = wt*Dj;
if (i!=j) {
// outer pixels
AAPIX(x+j, y+i);
AAPIX(x+j, y-i);
if (j) {
AAPIX(x-j, y+i); // BL
AAPIX(x-j, y-i); // TL
}
if (di) { // only when i changed, interior of outer section
CSCAN(j, i); // bottom
CSCAN(j, -i); // top
}
}
// lower mid section
AAPIX(x+i, y+j);
if (i) AAPIX(x-i, y+j);
CSCAN(i, j);
// upper mid section
if (j) {
AAPIX(x+i, y-j);
if (i) AAPIX(x-i, y-j);
CSCAN(i, -j);
}
j++;
}
#undef CSCAN
#undef AAPIX
}
else {
// n-agonal
int ov, nv;
float mind, maxd, lwt;
ys = MAX2((int)floor(bkh_b[2]*ct_crad + y), 0);
ye = MIN2((int)ceil(bkh_b[3]*ct_crad + y), new->y - 1);
for (sy=ys; sy<=ye; sy++) {
float fxs = 1e10f, fxe = -1e10f;
float yf = (sy - y)/ct_crad;
int found = 0;
ov = len_bkh - 1;
mind = maxd = 0;
for (nv=0; nv<len_bkh; nv++) {
if ((BKH[nv].max_y >= yf) && (BKH[nv].min_y <= yf)) {
float tx = BKH[ov].x0 + BKH[nv].ls_x*(yf - BKH[ov].y0);
if (tx < fxs) { fxs = tx; mind = BKH[nv].ls_x; }
if (tx > fxe) { fxe = tx; maxd = BKH[nv].ls_x; }
if (++found == 2) break;
}
ov = nv;
}
if (found) {
fxs = fxs*ct_crad + x;
fxe = fxe*ct_crad + x;
xs = (int)floor(fxs), xe = (int)ceil(fxe);
// AA hack for first and last x pixel, near vertical edges only
if (fabs(mind) <= 1.f) {
if ((xs >= 0) && (xs < new->x)) {
lwt = 1.f-(fxs - xs);
aacol[0] = wtcol[0]*lwt;
p = xs + sy*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
if (fabs(maxd) <= 1.f) {
if ((xe >= 0) && (xe < new->x)) {
lwt = 1.f-(xe - fxe);
aacol[0] = wtcol[0]*lwt;
p = xe + sy*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
xs = MAX2(xs+1, 0);
xe = MIN2(xe, new->x);
// remaining interior scanline
p = sy*new->x + xs;
if (new->type==CB_VAL) {
for (sx=xs; sx<xe; sx++, p++) TESTBG1(wtcol, wt);
}
else {
p4 = p * new->type;
for (sx=xs; sx<xe; sx++, p++, p4+=new->type) TESTBG4(wtcol, wt);
}
}
}
// now traverse in opposite direction, y scanlines,
// but this time only draw the near horizontal edges,
// applying same AA hack as above
xs = MAX2((int)floor(bkh_b[0]*ct_crad + x), 0);
xe = MIN2((int)ceil(bkh_b[1]*ct_crad + x), img->x - 1);
for (sx=xs; sx<=xe; sx++) {
float xf = (sx - x)/ct_crad;
float fys = 1e10f, fye = -1e10f;
int found = 0;
ov = len_bkh - 1;
mind = maxd = 0;
for (nv=0; nv<len_bkh; nv++) {
if ((BKH[nv].max_x >= xf) && (BKH[nv].min_x <= xf)) {
float ty = BKH[ov].y0 + BKH[nv].ls_y*(xf - BKH[ov].x0);
if (ty < fys) { fys = ty; mind = BKH[nv].ls_y; }
if (ty > fye) { fye = ty; maxd = BKH[nv].ls_y; }
if (++found == 2) break;
}
ov = nv;
}
if (found) {
fys = fys*ct_crad + y;
fye = fye*ct_crad + y;
// near horizontal edges only, line slope <= 1
if (fabs(mind) <= 1.f) {
int iys = (int)floor(fys);
if ((iys >= 0) && (iys < new->y)) {
lwt = 1.f - (fys - iys);
aacol[0] = wtcol[0]*lwt;
p = sx + iys*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
if (fabs(maxd) <= 1.f) {
int iye = ceil(fye);
if ((iye >= 0) && (iye < new->y)) {
lwt = 1.f - (iye - fye);
aacol[0] = wtcol[0]*lwt;
p = sx + iye*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
}
}
}
#undef TESTBG4
#undef TESTBG1
}
else {
// sampled, simple rejection sampling here, good enough
unsigned int maxsam, s, ui = BLI_rand()*BLI_rand();
float wcor, cpr = BLI_frand();
if (nqd->no_zbuf)
maxsam = nqd->samples; // no zbuffer input, use sample value directly
else {
// depth adaptive sampling hack, the more out of focus, the more samples taken, 16 minimum.
maxsam = (int)(0.5f + nqd->samples*(1.f-(float)exp(-fabs(zbuf->rect[cp] - cam_fdist))));
if (maxsam < 16) maxsam = 16;
}
wcor = 1.f/(float)maxsam;
for (s=0; s<maxsam; ++s) {
u = ct_crad*(2.f*RI_vdC(s, ui) - 1.f);
v = ct_crad*(2.f*(s + cpr)/(float)maxsam - 1.f);
// should use extra 0.5 offset here, but will cause gap around focal point...
sx = (int)(x + u), sy = (int)(y + v);
if ((sx<0) || (sx >= new->x) || (sy<0) || (sy >= new->y)) continue;
p = sx + sy*new->x;
p4 = p * new->type;
if (nqd->bktype==0) // Disk
lwt = ((u*u + v*v)<=cR2) ? wcor : 0.f;
else // AA not needed here
lwt = wcor * getWeight(BKH, len_bkh, u, v, ct_crad, inradsq);
// prevent background bleeding onto in-focus pixels, user-option
if (ct_crad > nqd->bthresh) { // if center blur > threshold
if (crad->rect[p] > nqd->bthresh) { // if overlap pixel in focus, do nothing, else add color/weigbt
new->rect[p4] += ctcol[0] * lwt;
if (new->type != CB_VAL) {
new->rect[p4+1] += ctcol[1] * lwt;
new->rect[p4+2] += ctcol[2] * lwt;
new->rect[p4+3] += ctcol[3] * lwt;
}
wts->rect[p] += lwt;
}
}
else {
new->rect[p4] += ctcol[0] * lwt;
if (new->type != CB_VAL) {
new->rect[p4+1] += ctcol[1] * lwt;
new->rect[p4+2] += ctcol[2] * lwt;
new->rect[p4+3] += ctcol[3] * lwt;
}
wts->rect[p] += lwt;
}
}
}
}
}
// finally, normalize
for (y=0; y<new->y; y++) {
p = y * new->x;
p4 = p * new->type;
for (x=0; x<new->x; x++) {
float dv = (wts->rect[p]==0.f) ? 1.f : (1.f/wts->rect[p]);
new->rect[p4] *= dv;
if (new->type!=CB_VAL) {
new->rect[p4+1] *= dv;
new->rect[p4+2] *= dv;
new->rect[p4+3] *= dv;
}
p++;
p4 += new->type;
}
}
free_compbuf(crad);
free_compbuf(wts);
printf("Done\n");
}
static void node_composit_exec_defocus(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *old, *zbuf_use = NULL, *img = in[0]->data, *zbuf = in[1]->data;
NodeDefocus* nqd = node->storage;
if ((img==NULL) || (out[0]->hasoutput==0)) return;
// if image not valid type or fstop==infinite (128), nothing to do, pass in to out
if (((img->type!=CB_RGBA) && (img->type!=CB_VAL)) || ((nqd->no_zbuf==0) && (nqd->fstop==128.f))) {
new = alloc_compbuf(img->x, img->y, img->type, 0);
new->rect = img->rect;
out[0]->data = new;
return;
}
if (zbuf!=NULL) {
// Zbuf input, check to make sure, single channel, same size
// doesn't have to be actual zbuffer, but must be value type
if ((zbuf->x != img->x) || (zbuf->y != img->y)) {
// could do a scale here instead...
printf("Z input must be same size as image !\n");
return;
}
zbuf_use = typecheck_compbuf(zbuf, CB_VAL);
}
else nqd->no_zbuf = 1; // no zbuffer input
// ok, process
old = img;
if (nqd->gamco) {
// gamma correct, blender func is simplified, fixed value & RGBA only, should make user param
old = dupalloc_compbuf(img);
gamma_correct_compbuf(old, 0);
}
new = alloc_compbuf(old->x, old->y, old->type, 1);
defocus_blur(new, old, zbuf_use, in[1]->vec[0]*nqd->scale, node->storage);
if (nqd->gamco) {
gamma_correct_compbuf(new, 1);
free_compbuf(old);
}
out[0]->data = new;
if (zbuf_use && (zbuf_use != zbuf)) free_compbuf(zbuf_use);
}
static bNodeType cmp_node_defocus = {
/* type code */ CMP_NODE_DEFOCUS,
/* name */ "Defocus",
/* width+range */ 150, 120, 200,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_defocus_in,
/* output sock */ cmp_node_defocus_out,
/* storage */ "NodeDefocus",
/* execfunc */ node_composit_exec_defocus
};
/* **************** VECTOR BLUR ******************** */
static bNodeSocketType cmp_node_vecblur_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Z", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VECTOR, 1, "Speed", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_vecblur_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_vecblur(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
NodeBlurData *nbd= node->storage;
CompBuf *new, *img= in[0]->data, *vecbuf= in[2]->data, *zbuf= in[1]->data;
if(img==NULL || vecbuf==NULL || zbuf==NULL || out[0]->hasoutput==0)
return;
if(vecbuf->x!=img->x || vecbuf->y!=img->y) {
printf("ERROR: cannot do different sized vecbuf yet\n");
return;
}
if(vecbuf->type!=CB_VEC4) {
printf("ERROR: input should be vecbuf\n");
return;
}
if(zbuf->type!=CB_VAL) {
printf("ERROR: input should be zbuf\n");
return;
}
if(zbuf->x!=img->x || zbuf->y!=img->y) {
printf("ERROR: cannot do different sized zbuf yet\n");
return;
}
/* allow the input image to be of another type */
img= typecheck_compbuf(in[0]->data, CB_RGBA);
new= dupalloc_compbuf(img);
/* call special zbuffer version */
RE_zbuf_accumulate_vecblur(nbd, img->x, img->y, new->rect, img->rect, vecbuf->rect, zbuf->rect);
out[0]->data= new;
if(img!=in[0]->data)
free_compbuf(img);
}
/* custom1: itterations, custom2: maxspeed (0 = nolimit) */
static bNodeType cmp_node_vecblur= {
/* type code */ CMP_NODE_VECBLUR,
/* name */ "Vector Blur",
/* width+range */ 120, 80, 200,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_vecblur_in,
/* output sock */ cmp_node_vecblur_out,
/* storage */ "NodeBlurData",
/* execfunc */ node_composit_exec_vecblur
};
/* **************** Translate ******************** */
static bNodeSocketType cmp_node_translate_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "X", 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{ SOCK_VALUE, 1, "Y", 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_translate_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_translate(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(in[0]->data) {
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= pass_on_compbuf(cbuf);
stackbuf->xof+= (int)floor(in[1]->vec[0]);
stackbuf->yof+= (int)floor(in[2]->vec[0]);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_translate= {
/* type code */ CMP_NODE_TRANSLATE,
/* name */ "Translate",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_translate_in,
/* output sock */ cmp_node_translate_out,
/* storage */ "",
/* execfunc */ node_composit_exec_translate
};
/* **************** Flip ******************** */
static bNodeSocketType cmp_node_flip_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_flip_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void node_composit_exec_flip(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(in[0]->data) {
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, cbuf->type, 1); /* note, this returns zero'd image */
int i, src_pix, src_width, src_height, srcydelt, outydelt, x, y;
float *srcfp, *outfp;
src_pix= cbuf->type;
src_width= cbuf->x;
src_height= cbuf->y;
srcfp= cbuf->rect;
outfp= stackbuf->rect;
srcydelt= src_width*src_pix;
outydelt= srcydelt;
if(node->custom1) { /*set up output pointer for y flip*/
outfp+= (src_height-1)*outydelt;
outydelt= -outydelt;
}
for(y=0; y<src_height; y++) {
if(node->custom1 == 1) { /* no x flip so just copy line*/
memcpy(outfp, srcfp, sizeof(float) * src_pix * src_width);
srcfp+=srcydelt;
}
else {
outfp += (src_width-1)*src_pix;
for(x=0; x<src_width; x++) {
for(i=0; i<src_pix; i++) {
outfp[i]= srcfp[i];
}
outfp -= src_pix;
srcfp += src_pix;
}
outfp += src_pix;
}
outfp += outydelt;
}
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_flip= {
/* type code */ CMP_NODE_FLIP,
/* name */ "Flip",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_flip_in,
/* output sock */ cmp_node_flip_out,
/* storage */ "",
/* execfunc */ node_composit_exec_flip
};
/* **************** Dilate/Erode ******************** */
static bNodeSocketType cmp_node_dilateerode_in[]= {
{ SOCK_VALUE, 1, "Mask", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_dilateerode_out[]= {
{ SOCK_VALUE, 0, "Mask", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void morpho_dilate(CompBuf *cbuf)
{
int x, y;
float *p, *rectf = cbuf->rect;
for (y=0; y < cbuf->y; y++) {
for (x=0; x < cbuf->x-1; x++) {
p = rectf + cbuf->x*y + x;
*p = MAX2(*p, *(p + 1));
}
}
for (y=0; y < cbuf->y; y++) {
for (x=cbuf->x-1; x >= 1; x--) {
p = rectf + cbuf->x*y + x;
*p = MAX2(*p, *(p - 1));
}
}
for (x=0; x < cbuf->x; x++) {
for (y=0; y < cbuf->y-1; y++) {
p = rectf + cbuf->x*y + x;
*p = MAX2(*p, *(p + cbuf->x));
}
}
for (x=0; x < cbuf->x; x++) {
for (y=cbuf->y-1; y >= 1; y--) {
p = rectf + cbuf->x*y + x;
*p = MAX2(*p, *(p - cbuf->x));
}
}
}
static void morpho_erode(CompBuf *cbuf)
{
int x, y;
float *p, *rectf = cbuf->rect;
for (y=0; y < cbuf->y; y++) {
for (x=0; x < cbuf->x-1; x++) {
p = rectf + cbuf->x*y + x;
*p = MIN2(*p, *(p + 1));
}
}
for (y=0; y < cbuf->y; y++) {
for (x=cbuf->x-1; x >= 1; x--) {
p = rectf + cbuf->x*y + x;
*p = MIN2(*p, *(p - 1));
}
}
for (x=0; x < cbuf->x; x++) {
for (y=0; y < cbuf->y-1; y++) {
p = rectf + cbuf->x*y + x;
*p = MIN2(*p, *(p + cbuf->x));
}
}
for (x=0; x < cbuf->x; x++) {
for (y=cbuf->y-1; y >= 1; y--) {
p = rectf + cbuf->x*y + x;
*p = MIN2(*p, *(p - cbuf->x));
}
}
}
static void node_composit_exec_dilateerode(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order in: mask */
/* stack order out: mask */
if(out[0]->hasoutput==0)
return;
/* input no image? then only color operation */
if(in[0]->data==NULL) {
out[0]->vec[0] = out[0]->vec[1] = out[0]->vec[2] = 0.0f;
out[0]->vec[3] = 0.0f;
}
else {
/* make output size of input image */
CompBuf *cbuf= typecheck_compbuf(in[0]->data, CB_VAL);
CompBuf *stackbuf= dupalloc_compbuf(cbuf);
short i;
if (node->custom2 > 0) { // positive, dilate
for (i = 0; i < node->custom2; i++)
morpho_dilate(stackbuf);
} else if (node->custom2 < 0) { // negative, erode
for (i = 0; i > node->custom2; i--)
morpho_erode(stackbuf);
}
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_dilateerode= {
/* type code */ CMP_NODE_DILATEERODE,
/* name */ "Dilate/Erode",
/* width+range */ 130, 100, 320,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_dilateerode_in,
/* output sock */ cmp_node_dilateerode_out,
/* storage */ "",
/* execfunc */ node_composit_exec_dilateerode
};
/* **************** SEPARATE YUVA ******************** */
static bNodeSocketType cmp_node_sepyuva_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_sepyuva_out[]= {
{ SOCK_VALUE, 0, "Y", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "U", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "V", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "A", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_sepyuva(bNode *node, float *out, float *in)
{
float y, u, v;
rgb_to_yuv(in[0], in[1], in[2], &y, &u, &v);
out[0]= y;
out[1]= u;
out[2]= v;
out[3]= in[3];
}
static void node_composit_exec_sepyuva(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: bw channels */
/* stack order in: col */
/* input no image? then only color operation */
if(in[0]->data==NULL) {
float y, u, v;
rgb_to_yuv(in[0]->vec[0], in[0]->vec[1], in[0]->vec[2], &y, &u, &v);
out[0]->vec[0] = y;
out[1]->vec[0] = u;
out[2]->vec[0] = v;
out[3]->vec[0] = in[0]->vec[3];
}
else if ((out[0]->hasoutput) || (out[1]->hasoutput) || (out[2]->hasoutput) || (out[3]->hasoutput)) {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
/* convert the RGB stackbuf to an YUV representation */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_sepyuva, CB_RGBA);
/* separate each of those channels */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(stackbuf, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(stackbuf, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
free_compbuf(stackbuf);
}
}
static bNodeType cmp_node_sepyuva= {
/* type code */ CMP_NODE_SEPYUVA,
/* name */ "Separate YUVA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, 0,
/* input sock */ cmp_node_sepyuva_in,
/* output sock */ cmp_node_sepyuva_out,
/* storage */ "",
/* execfunc */ node_composit_exec_sepyuva
};
/* **************** SEPARATE YCCA ******************** */
static bNodeSocketType cmp_node_sepycca_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_sepycca_out[]= {
{ SOCK_VALUE, 0, "Y", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "Cb", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "Cr", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 0, "A", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_sepycca(bNode *node, float *out, float *in)
{
float y, cb, cr;
rgb_to_ycc(in[0], in[1], in[2], &y, &cb, &cr);
/*divided by 255 to normalize for viewing in */
out[0]= y/255.0;
out[1]= cb/255.0;
out[2]= cr/255.0;
out[3]= in[3];
}
static void node_composit_exec_sepycca(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* input no image? then only color operation */
if(in[0]->data==NULL) {
float y, cb, cr;
rgb_to_ycc(in[0]->vec[0], in[0]->vec[1], in[0]->vec[2], &y, &cb, &cr);
/*divided by 255 to normalize for viewing in */
out[0]->vec[0] = y/255.0;
out[1]->vec[0] = cb/255.0;
out[2]->vec[0] = cr/255.0;
out[3]->vec[0] = in[0]->vec[3];
}
else if ((out[0]->hasoutput) || (out[1]->hasoutput) || (out[2]->hasoutput) || (out[3]->hasoutput)) {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
/* convert the RGB stackbuf to an HSV representation */
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_sepycca, CB_RGBA);
/* separate each of those channels */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(stackbuf, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(stackbuf, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
free_compbuf(stackbuf);
}
}
static bNodeType cmp_node_sepycca= {
/* type code */ CMP_NODE_SEPYCCA,
/* name */ "Separate YCbCrA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, 0,
/* input sock */ cmp_node_sepycca_in,
/* output sock */ cmp_node_sepycca_out,
/* storage */ "",
/* execfunc */ node_composit_exec_sepycca
};
/* ******************* channel Difference Matte ********************************* */
static bNodeSocketType cmp_node_diff_matte_in[]={
{SOCK_RGBA,1,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{SOCK_RGBA,1,"Key Color", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static bNodeSocketType cmp_node_diff_matte_out[]={
{SOCK_RGBA,0,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{SOCK_VALUE,0,"Matte",0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static void do_rgba_to_yuva(bNode *node, float *out, float *in)
{
rgb_to_yuv(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
static void do_rgba_to_hsva(bNode *node, float *out, float *in)
{
rgb_to_hsv(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
static void do_rgba_to_ycca(bNode *node, float *out, float *in)
{
rgb_to_ycc(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
static void do_yuva_to_rgba(bNode *node, float *out, float *in)
{
yuv_to_rgb(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
static void do_hsva_to_rgba(bNode *node, float *out, float *in)
{
hsv_to_rgb(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
static void do_ycca_to_rgba(bNode *node, float *out, float *in)
{
ycc_to_rgb(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
/* note, keyvals is passed on from caller as stack array */
/* might have been nicer as temp struct though... */
static void do_diff_matte(bNode *node, float *colorbuf, float *inbuf, float *keyvals)
{
NodeChroma *c= (NodeChroma *)node->storage;
float *keymin= keyvals;
float *keymax= keyvals+3;
float *key= keyvals+6;
float tolerance= keyvals[9];
float distance, alpha;
/*process the pixel if it is close to the key or already transparent*/
if(((colorbuf[0]>keymin[0] && colorbuf[0]<keymax[0]) &&
(colorbuf[1]>keymin[1] && colorbuf[1]<keymax[1]) &&
(colorbuf[2]>keymin[2] && colorbuf[2]<keymax[2])) || inbuf[3]<1.0f) {
/*true distance from key*/
distance= sqrt((colorbuf[0]-key[0])*(colorbuf[0]-key[0])+
(colorbuf[1]-key[1])*(colorbuf[1]-key[1])+
(colorbuf[2]-key[2])*(colorbuf[2]-key[2]));
/*is it less transparent than the prevous pixel*/
alpha= distance/tolerance;
if(alpha > inbuf[3]) alpha= inbuf[3];
if(alpha > c->fstrength) alpha= 0.0f;
/*clamp*/
if (alpha>1.0f) alpha=1.0f;
if (alpha<0.0f) alpha=0.0f;
/*premultiplied picture*/
colorbuf[3]= alpha;
}
else {
/*foreground object*/
colorbuf[3]= inbuf[3];
}
}
static void node_composit_exec_diff_matte(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/*
Losely based on the Sequencer chroma key plug-in, but enhanced to work in other color spaces and
uses a differnt difference function (suggested in forums of vfxtalk.com).
*/
CompBuf *workbuf;
CompBuf *inbuf;
NodeChroma *c;
float keyvals[10];
float *keymin= keyvals;
float *keymax= keyvals+3;
float *key= keyvals+6;
float *tolerance= keyvals+9;
float t[3];
/*is anything connected?*/
if(out[0]->hasoutput==0 && out[1]->hasoutput==0) return;
/*must have an image imput*/
if(in[0]->data==NULL) return;
inbuf=typecheck_compbuf(in[0]->data, CB_RGBA);
c=node->storage;
workbuf=dupalloc_compbuf(inbuf);
/*use the input color*/
key[0]= in[1]->vec[0];
key[1]= in[1]->vec[1];
key[2]= in[1]->vec[2];
/*get the tolerances from the UI*/
t[0]=c->t1;
t[1]=c->t2;
t[2]=c->t3;
/*convert to colorspace*/
switch(node->custom1) {
case 1: /*RGB*/
break;
case 2: /*HSV*/
/*convert the key (in place)*/
rgb_to_hsv(key[0], key[1], key[2], &key[0], &key[1], &key[2]);
composit1_pixel_processor(node, workbuf, inbuf, in[1]->vec, do_rgba_to_hsva, CB_RGBA);
break;
case 3: /*YUV*/
rgb_to_yuv(key[0], key[1], key[2], &key[0], &key[1], &key[2]);
composit1_pixel_processor(node, workbuf, inbuf, in[1]->vec, do_rgba_to_yuva, CB_RGBA);
break;
case 4: /*YCC*/
rgb_to_ycc(key[0], key[1], key[2], &key[0], &key[1], &key[2]);
composit1_pixel_processor(node, workbuf, inbuf, in[1]->vec, do_rgba_to_ycca, CB_RGBA);
/*account for ycc is on a 0..255 scale*/
t[0]= c->t1*255.0;
t[1]= c->t2*255.0;
t[2]= c->t3*255.0;
break;
default:
break;
}
/*find min/max tolerances*/
keymin[0]= key[0]-t[0];
keymin[1]= key[1]-t[1];
keymin[2]= key[2]-t[2];
keymax[0]= key[0]+t[0];
keymax[1]= key[1]+t[1];
keymax[2]= key[2]+t[2];
/*tolerance*/
*tolerance= sqrt((t[0])*(t[0])+
(t[1])*(t[1])+
(t[2])*(t[2]));
/* note, processor gets a keyvals array passed on as buffer constant */
composit2_pixel_processor(node, workbuf, workbuf, in[0]->vec, NULL, keyvals, do_diff_matte, CB_RGBA, CB_VAL);
/*convert back to RGB colorspace*/
switch(node->custom1) {
case 1: /*RGB*/
composit1_pixel_processor(node, workbuf, workbuf, in[1]->vec, do_copy_rgba, CB_RGBA);
break;
case 2: /*HSV*/
composit1_pixel_processor(node, workbuf, workbuf, in[1]->vec, do_hsva_to_rgba, CB_RGBA);
break;
case 3: /*YUV*/
composit1_pixel_processor(node, workbuf, workbuf, in[1]->vec, do_yuva_to_rgba, CB_RGBA);
break;
case 4: /*YCC*/
composit1_pixel_processor(node, workbuf, workbuf, in[1]->vec, do_ycca_to_rgba, CB_RGBA);
break;
default:
break;
}
out[0]->data=workbuf;
if(out[1]->hasoutput)
out[1]->data=valbuf_from_rgbabuf(workbuf, CHAN_A);
generate_preview(node, workbuf);
if(inbuf!=in[0]->data)
free_compbuf(inbuf);
}
static bNodeType cmp_node_diff_matte={
/* type code */ CMP_NODE_DIFF_MATTE,
/* name */ "Difference Key",
/* width+range */ 200, 80, 250,
/* class+opts */ NODE_CLASS_MATTE, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ cmp_node_diff_matte_in,
/* output sock */ cmp_node_diff_matte_out,
/* storage */ "NodeChroma",
/* execfunc */ node_composit_exec_diff_matte
};
/* ******************* Color Spill Supression ********************************* */
static bNodeSocketType cmp_node_color_spill_in[]={
{SOCK_RGBA,1,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static bNodeSocketType cmp_node_color_spill_out[]={
{SOCK_RGBA,0,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static void do_reduce_red(bNode *node, float* out, float *in)
{
NodeChroma *c;
c=node->storage;
if(in[0] > in[1] && in[0] > in[2]) {
out[0]=((in[1]+in[2])/2)*(1-c->t1);
}
}
static void do_reduce_green(bNode *node, float* out, float *in)
{
NodeChroma *c;
c=node->storage;
if(in[1] > in[0] && in[1] > in[2]) {
out[1]=((in[0]+in[2])/2)*(1-c->t1);
}
}
static void do_reduce_blue(bNode *node, float* out, float *in)
{
NodeChroma *c;
c=node->storage;
if(in[2] > in[1] && in[2] > in[1]) {
out[2]=((in[1]+in[0])/2)*(1-c->t1);
}
}
static void node_composit_exec_color_spill(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/*
Originally based on the information from the book "The Art and Science of Digital Composition" and
discussions from vfxtalk.com.*/
CompBuf *cbuf;
CompBuf *rgbbuf;
if(out[0]->hasoutput==0 || in[0]->hasinput==0) return;
if(in[0]->data==NULL) return;
cbuf=typecheck_compbuf(in[0]->data, CB_RGBA);
rgbbuf=dupalloc_compbuf(cbuf);
switch(node->custom1)
{
case 1: /*red spill*/
{
composit1_pixel_processor(node, rgbbuf, cbuf, in[1]->vec, do_reduce_red, CB_RGBA);
break;
}
case 2: /*green spill*/
{
composit1_pixel_processor(node, rgbbuf, cbuf, in[1]->vec, do_reduce_green, CB_RGBA);
break;
}
case 3: /*blue spill*/
{
composit1_pixel_processor(node, rgbbuf, cbuf, in[1]->vec, do_reduce_blue, CB_RGBA);
break;
}
default:
break;
}
out[0]->data=rgbbuf;
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
}
static bNodeType cmp_node_color_spill={
/* type code */ CMP_NODE_COLOR_SPILL,
/* name */ "Color Spill",
/* width+range */ 140, 80, 200,
/* class+opts */ NODE_CLASS_MATTE, NODE_OPTIONS,
/* input sock */ cmp_node_color_spill_in,
/* output sock */ cmp_node_color_spill_out,
/* storage */ "NodeChroma",
/* execfunc */ node_composit_exec_color_spill
};
/* ******************* Chroma Key ********************************************************** */
static bNodeSocketType cmp_node_chroma_in[]={
{SOCK_RGBA,1,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{SOCK_RGBA,1,"Key Color", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static bNodeSocketType cmp_node_chroma_out[]={
{SOCK_RGBA,0,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{SOCK_VALUE,0,"Matte",0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static void do_rgba_to_ycca_normalized(bNode *node, float *out, float *in)
{
/*normalize to the range -1.0 to 1.0) */
rgb_to_ycc(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[0]=((out[0])-16)/255.0;
out[1]=((out[1])-128)/255.0;
out[2]=((out[2])-128)/255.0;
out[3]=in[3];
}
static void do_normalized_ycca_to_rgba(bNode *node, float *out, float *in)
{
/*un-normalize the normalize from above */
in[0]=(in[0]*255.0)+16;
in[1]=(in[1]*255.0)+128;
in[2]=(in[2]*255.0)+128;
ycc_to_rgb(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
static void do_chroma_key(bNode *node, float *out, float *in)
{
NodeChroma *c;
float x, z, alpha;
float theta, beta, angle;
float kfg, newY, newCb, newCr;
c=node->storage;
/* Algorithm from book "Video Demistified" */
/* find theta, the angle that the color space should be rotated based on key*/
theta=atan2f(c->key[2],c->key[1]);
/*rotate the cb and cr into x/z space */
x=in[1]*cosf(theta)+in[2]*sinf(theta);
z=in[2]*cosf(theta)-in[1]*sinf(theta);
/*if within the acceptance angle */
angle=c->t1*M_PI/180.0; /* convert to radians */
/* if kfg is <0 then the pixel is outside of the key color */
kfg=x-(fabsf(z)/tan(angle/2.0));
if(kfg>0.0) { /* found a pixel that is within key color */
newY=in[0]-(1-c->t3)*kfg;
newCb=in[1]-kfg*cosf(theta);
newCr=in[2]-kfg*sinf(theta);
alpha=(kfg+c->fsize)*(c->fstrength);
beta=atan2f(newCr,newCb);
beta=beta*180.0/M_PI; /* convert to degrees for compare*/
/* if beta is within the clippin angle */
if(fabsf(beta)<(c->t2/2.0)) {
newCb=0.0;
newCr=0.0;
alpha=0.0;
}
out[0]=newY;
out[1]=newCb;
out[2]=newCr;
/* don't make something that was more transparent less transparent */
if (alpha<in[3]) {
out[3]=alpha;
}
else {
out[3]=in[3];
}
}
else { /*pixel is outside key color */
out[0]=in[0];
out[1]=in[1];
out[2]=in[2];
out[3]=in[3]; /* make pixel just as transparent as it was before */
}
}
static void node_composit_exec_chroma(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *cbuf;
CompBuf *chromabuf;
NodeChroma *c;
if(out[0]->hasoutput==0 || in[0]->hasinput==0) return;
if(in[0]->data==NULL) return;
cbuf= typecheck_compbuf(in[0]->data, CB_RGBA);
chromabuf= dupalloc_compbuf(cbuf);
c=node->storage;
/*convert rgbbuf to normalized chroma space*/
composit1_pixel_processor(node, chromabuf, cbuf, in[0]->vec, do_rgba_to_ycca_normalized, CB_RGBA);
/*convert key to normalized chroma color space */
do_rgba_to_ycca_normalized(node, c->key, in[1]->vec);
/*per pixel chroma key*/
composit1_pixel_processor(node, chromabuf, chromabuf, in[0]->vec, do_chroma_key, CB_RGBA);
/*convert back*/
composit1_pixel_processor(node, chromabuf, chromabuf, in[0]->vec, do_normalized_ycca_to_rgba, CB_RGBA);
out[0]->data= chromabuf;
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(chromabuf, CHAN_A);
generate_preview(node, chromabuf);
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
};
static bNodeType cmp_node_chroma={
/* type code */ CMP_NODE_CHROMA,
/* name */ "Chroma Key",
/* width+range */ 200, 80, 300,
/* class+opts */ NODE_CLASS_MATTE, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ cmp_node_chroma_in,
/* output sock */ cmp_node_chroma_out,
/* storage */ "NodeChroma",
/* execfunc */ node_composit_exec_chroma
};
/* ******************* Channel Matte Node ********************************* */
static bNodeSocketType cmp_node_channel_matte_in[]={
{SOCK_RGBA,1,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static bNodeSocketType cmp_node_channel_matte_out[]={
{SOCK_RGBA,0,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{SOCK_VALUE,0,"Matte",0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static void do_channel_matte(bNode *node, float *out, float *in)
{
NodeChroma *c=(NodeChroma *)node->storage;
float alpha=0.0;
/* Alpha=G-MAX(R, B) */
switch(node->custom2)
{
case 1:
{
alpha=in[0]-MAX2(in[1],in[2]);
break;
}
case 2:
{
alpha=in[1]-MAX2(in[0],in[2]);
break;
}
case 3:
{
alpha=in[2]-MAX2(in[0],in[1]);
break;
}
default:
break;
}
/*flip because 0.0 is transparent, not 1.0*/
alpha=1-alpha;
/* test range*/
if(alpha>c->t1) {
alpha=in[3]; /*whatever it was prior */
}
else if(alpha<c->t2){
alpha=0.0;
}
else {/*blend */
alpha=(alpha-c->t2)/(c->t1-c->t2);
}
/* don't make something that was more transparent less transparent */
if (alpha<in[3]) {
out[3]=alpha;
}
else {
out[3]=in[3];
}
}
static void node_composit_exec_channel_matte(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *cbuf;
CompBuf *outbuf;
if(in[0]->hasinput==0) return;
if(in[0]->data==NULL) return;
if(out[0]->hasoutput==0 && out[1]->hasoutput==0) return;
cbuf=typecheck_compbuf(in[0]->data, CB_RGBA);
outbuf=dupalloc_compbuf(cbuf);
/*convert to colorspace*/
switch(node->custom1) {
case 1: /*RGB */
break;
case 2: /*HSV*/
composit1_pixel_processor(node, outbuf, cbuf, in[1]->vec, do_rgba_to_hsva, CB_RGBA);
break;
case 3: /*YUV*/
composit1_pixel_processor(node, outbuf, cbuf, in[1]->vec, do_rgba_to_yuva, CB_RGBA);
break;
case 4: /*YCC*/
composit1_pixel_processor(node, outbuf, cbuf, in[1]->vec, do_rgba_to_ycca, CB_RGBA);
break;
default:
break;
}
/*use the selected channel information to do the key */
composit1_pixel_processor(node, outbuf, outbuf, in[1]->vec, do_channel_matte, CB_RGBA);
/*convert back to RGB colorspace in place*/
switch(node->custom1) {
case 1: /*RGB*/
break;
case 2: /*HSV*/
composit1_pixel_processor(node, outbuf, outbuf, in[1]->vec, do_hsva_to_rgba, CB_RGBA);
break;
case 3: /*YUV*/
composit1_pixel_processor(node, outbuf, outbuf, in[1]->vec, do_yuva_to_rgba, CB_RGBA);
break;
case 4: /*YCC*/
composit1_pixel_processor(node, outbuf, outbuf, in[1]->vec, do_ycca_to_rgba, CB_RGBA);
break;
default:
break;
}
generate_preview(node, outbuf);
out[0]->data=outbuf;
if(out[1]->hasoutput)
out[1]->data=valbuf_from_rgbabuf(outbuf, CHAN_A);
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
}
static bNodeType cmp_node_channel_matte={
/* type code */ CMP_NODE_CHANNEL_MATTE,
/* name */ "Channel Key",
/* width+range */ 200, 80, 250,
/* class+opts */ NODE_CLASS_MATTE, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ cmp_node_channel_matte_in,
/* output sock */ cmp_node_channel_matte_out,
/* storage */ "NodeChroma",
/* execfunc */ node_composit_exec_channel_matte
};
/* ******************* Luma Matte Node ********************************* */
static bNodeSocketType cmp_node_luma_matte_in[]={
{SOCK_RGBA,1,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static bNodeSocketType cmp_node_luma_matte_out[]={
{SOCK_RGBA,0,"Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{SOCK_VALUE,0,"Matte",0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{-1,0,""}
};
static void do_luma_matte(bNode *node, float *out, float *in)
{
NodeChroma *c=(NodeChroma *)node->storage;
float alpha;
alpha=0.0;
/* test range*/
if(in[0]>c->t1) {
alpha=1.0;
}
else if(in[1]<c->t2){
alpha=0.0;
}
else {/*blend */
alpha=(in[0]-c->t2)/(c->t1-c->t2);
}
/* don't make something that was more transparent less transparent */
if (alpha<in[3]) {
out[3]=alpha;
}
else {
out[3]=in[3];
}
}
static void node_composit_exec_luma_matte(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *cbuf;
CompBuf *outbuf;
if(in[0]->hasinput==0) return;
if(in[0]->data==NULL) return;
if(out[0]->hasoutput==0 && out[1]->hasoutput==0) return;
cbuf=typecheck_compbuf(in[0]->data, CB_RGBA);
outbuf=dupalloc_compbuf(cbuf);
composit1_pixel_processor(node, outbuf, cbuf, in[1]->vec, do_rgba_to_yuva, CB_RGBA);
composit1_pixel_processor(node, outbuf, outbuf, in[1]->vec, do_luma_matte, CB_RGBA);
composit1_pixel_processor(node, outbuf, outbuf, in[1]->vec, do_yuva_to_rgba, CB_RGBA);
generate_preview(node, outbuf);
out[0]->data=outbuf;
if (out[1]->hasoutput)
out[1]->data=valbuf_from_rgbabuf(outbuf, CHAN_A);
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
}
static bNodeType cmp_node_luma_matte={
/* type code */ CMP_NODE_LUMA_MATTE,
/* name */ "Luminance Key",
/* width+range */ 200, 80, 250,
/* class+opts */ NODE_CLASS_MATTE, NODE_PREVIEW|NODE_OPTIONS,
/* input sock */ cmp_node_luma_matte_in,
/* output sock */ cmp_node_luma_matte_out,
/* storage */ "NodeChroma",
/* execfunc */ node_composit_exec_luma_matte
};
/* **************** COMBINE YCCA ******************** */
static bNodeSocketType cmp_node_combycca_in[]= {
{ SOCK_VALUE, 1, "Y", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Cb", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Cr", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "A", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_combycca_out[]= {
{ SOCK_RGBA, 0, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_comb_ycca(bNode *node, float *out, float *in1, float *in2, float *in3, float *in4)
{
float r,g,b;
float y, cb, cr;
/*need to un-normalize the data*/
y=in1[0]*255;
cb=in2[0]*255;
cr=in3[0]*255;
ycc_to_rgb(y,cb,cr, &r, &g, &b);
out[0] = r;
out[1] = g;
out[2] = b;
out[3] = in4[0];
}
static void node_composit_exec_combycca(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: 1 ycca channels */
/* stack order in: 4 value channels */
/* input no image? then only color operation */
if((in[0]->data==NULL) && (in[1]->data==NULL) && (in[2]->data==NULL) && (in[3]->data==NULL)) {
out[0]->vec[0] = in[0]->vec[0];
out[0]->vec[1] = in[1]->vec[0];
out[0]->vec[2] = in[2]->vec[0];
out[0]->vec[3] = in[3]->vec[0];
}
else {
/* make output size of first available input image */
CompBuf *cbuf;
CompBuf *stackbuf;
/* allocate a CompBuf the size of the first available input */
if (in[0]->data) cbuf = in[0]->data;
else if (in[1]->data) cbuf = in[1]->data;
else if (in[2]->data) cbuf = in[2]->data;
else cbuf = in[3]->data;
stackbuf = alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit4_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec,
in[2]->data, in[2]->vec, in[3]->data, in[3]->vec,
do_comb_ycca, CB_VAL, CB_VAL, CB_VAL, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_combycca= {
/* type code */ CMP_NODE_COMBYCCA,
/* name */ "Combine YCbCrA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_combycca_in,
/* output sock */ cmp_node_combycca_out,
/* storage */ "",
/* execfunc */ node_composit_exec_combycca
};
/* **************** COMBINE YUVA ******************** */
static bNodeSocketType cmp_node_combyuva_in[]= {
{ SOCK_VALUE, 1, "Y", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "U", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "V", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "A", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_combyuva_out[]= {
{ SOCK_RGBA, 0, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_comb_yuva(bNode *node, float *out, float *in1, float *in2, float *in3, float *in4)
{
float r,g,b;
yuv_to_rgb(in1[0], in2[0], in3[0], &r, &g, &b);
out[0] = r;
out[1] = g;
out[2] = b;
out[3] = in4[0];
}
static void node_composit_exec_combyuva(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: 1 rgba channels */
/* stack order in: 4 value channels */
/* input no image? then only color operation */
if((in[0]->data==NULL) && (in[1]->data==NULL) && (in[2]->data==NULL) && (in[3]->data==NULL)) {
out[0]->vec[0] = in[0]->vec[0];
out[0]->vec[1] = in[1]->vec[0];
out[0]->vec[2] = in[2]->vec[0];
out[0]->vec[3] = in[3]->vec[0];
}
else {
/* make output size of first available input image */
CompBuf *cbuf;
CompBuf *stackbuf;
/* allocate a CompBuf the size of the first available input */
if (in[0]->data) cbuf = in[0]->data;
else if (in[1]->data) cbuf = in[1]->data;
else if (in[2]->data) cbuf = in[2]->data;
else cbuf = in[3]->data;
stackbuf = alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit4_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec,
in[2]->data, in[2]->vec, in[3]->data, in[3]->vec,
do_comb_yuva, CB_VAL, CB_VAL, CB_VAL, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_combyuva= {
/* type code */ CMP_NODE_COMBYUVA,
/* name */ "Combine YUVA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_combyuva_in,
/* output sock */ cmp_node_combyuva_out,
/* storage */ "",
/* execfunc */ node_composit_exec_combyuva
};
/* **************** COMBINE HSVA ******************** */
static bNodeSocketType cmp_node_combhsva_in[]= {
{ SOCK_VALUE, 1, "H", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "S", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "V", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "A", 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_combhsva_out[]= {
{ SOCK_RGBA, 0, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_comb_hsva(bNode *node, float *out, float *in1, float *in2, float *in3, float *in4)
{
float r,g,b;
hsv_to_rgb(in1[0], in2[0], in3[0], &r, &g, &b);
out[0] = r;
out[1] = g;
out[2] = b;
out[3] = in4[0];
}
static void node_composit_exec_combhsva(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: 1 rgba channels */
/* stack order in: 4 value channels */
/* input no image? then only color operation */
if((in[0]->data==NULL) && (in[1]->data==NULL) && (in[2]->data==NULL) && (in[3]->data==NULL)) {
out[0]->vec[0] = in[0]->vec[0];
out[0]->vec[1] = in[1]->vec[0];
out[0]->vec[2] = in[2]->vec[0];
out[0]->vec[3] = in[3]->vec[0];
}
else {
/* make output size of first available input image */
CompBuf *cbuf;
CompBuf *stackbuf;
/* allocate a CompBuf the size of the first available input */
if (in[0]->data) cbuf = in[0]->data;
else if (in[1]->data) cbuf = in[1]->data;
else if (in[2]->data) cbuf = in[2]->data;
else cbuf = in[3]->data;
stackbuf = alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
composit4_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec,
in[2]->data, in[2]->vec, in[3]->data, in[3]->vec,
do_comb_hsva, CB_VAL, CB_VAL, CB_VAL, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_combhsva= {
/* type code */ CMP_NODE_COMBHSVA,
/* name */ "Combine HSVA",
/* width+range */ 80, 40, 140,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_combhsva_in,
/* output sock */ cmp_node_combhsva_out,
/* storage */ "",
/* execfunc */ node_composit_exec_combhsva
};
/* **************** Rotate ******************** */
static bNodeSocketType cmp_node_rotate_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "Degr", 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_rotate_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* function assumes out to be zero'ed, only does RGBA */
static void bilinear_interpolation_rotate(CompBuf *in, float *out, float u, float v)
{
float *row1, *row2, *row3, *row4, a, b;
float a_b, ma_b, a_mb, ma_mb;
float empty[4]= {0.0f, 0.0f, 0.0f, 0.0f};
int y1, y2, x1, x2;
x1= (int)floor(u);
x2= (int)ceil(u);
y1= (int)floor(v);
y2= (int)ceil(v);
/* sample area entirely outside image? */
if(x2<0 || x1>in->x-1 || y2<0 || y1>in->y-1)
return;
/* sample including outside of edges of image */
if(x1<0 || y1<0) row1= empty;
else row1= in->rect + in->x * y1 * in->type + in->type*x1;
if(x1<0 || y2>in->y-1) row2= empty;
else row2= in->rect + in->x * y2 * in->type + in->type*x1;
if(x2>in->x-1 || y1<0) row3= empty;
else row3= in->rect + in->x * y1 * in->type + in->type*x2;
if(x2>in->x-1 || y2>in->y-1) row4= empty;
else row4= in->rect + in->x * y2 * in->type + in->type*x2;
a= u-floor(u);
b= v-floor(v);
a_b= a*b; ma_b= (1.0f-a)*b; a_mb= a*(1.0f-b); ma_mb= (1.0f-a)*(1.0f-b);
out[0]= ma_mb*row1[0] + a_mb*row3[0] + ma_b*row2[0]+ a_b*row4[0];
out[1]= ma_mb*row1[1] + a_mb*row3[1] + ma_b*row2[1]+ a_b*row4[1];
out[2]= ma_mb*row1[2] + a_mb*row3[2] + ma_b*row2[2]+ a_b*row4[2];
out[3]= ma_mb*row1[3] + a_mb*row3[3] + ma_b*row2[3]+ a_b*row4[3];
}
/* only supports RGBA nodes now */
static void node_composit_exec_rotate(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(out[0]->hasoutput==0)
return;
if(in[0]->data) {
CompBuf *cbuf= typecheck_compbuf(in[0]->data, CB_RGBA);
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* note, this returns zero'd image */
float *ofp, rad, u, v, s, c, centx, centy, miny, maxy, minx, maxx;
int x, y, yo;
rad= (M_PI*in[1]->vec[0])/180.0f;
s= sin(rad);
c= cos(rad);
centx= cbuf->x/2;
centy= cbuf->y/2;
minx= -centx;
maxx= -centx + (float)cbuf->x;
miny= -centy;
maxy= -centy + (float)cbuf->y;
for(y=miny; y<maxy; y++) {
yo= y+(int)centy;
ofp= stackbuf->rect + 4*yo*stackbuf->x;
for(x=minx; x<maxx; x++, ofp+=4) {
u= c*x + y*s + centx;
v= -s*x + c*y + centy;
bilinear_interpolation_rotate(cbuf, ofp, u, v);
}
}
/* rotate offset vector too, but why negative rad, ehh?? Has to be replaced with [3][3] matrix once (ton) */
s= sin(-rad);
c= cos(-rad);
centx= (float)cbuf->xof; centy= (float)cbuf->yof;
stackbuf->xof= (int)( c*centx + s*centy);
stackbuf->yof= (int)(-s*centx + c*centy);
/* pass on output and free */
out[0]->data= stackbuf;
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
}
}
static bNodeType cmp_node_rotate= {
/* type code */ CMP_NODE_ROTATE,
/* name */ "Rotate",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_rotate_in,
/* output sock */ cmp_node_rotate_out,
/* storage */ "",
/* execfunc */ node_composit_exec_rotate
};
/* **************** Scale ******************** */
#define CMP_SCALE_MAX 12000
static bNodeSocketType cmp_node_scale_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "X", 1.0f, 0.0f, 0.0f, 0.0f, 0.0001f, CMP_SCALE_MAX},
{ SOCK_VALUE, 1, "Y", 1.0f, 0.0f, 0.0f, 0.0f, 0.0001f, CMP_SCALE_MAX},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_scale_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* only supports RGBA nodes now */
/* node->custom1 stores if input values are absolute or relative scale */
static void node_composit_exec_scale(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(out[0]->hasoutput==0)
return;
if(in[0]->data) {
CompBuf *stackbuf, *cbuf= typecheck_compbuf(in[0]->data, CB_RGBA);
ImBuf *ibuf;
int newx, newy;
if(node->custom1==CMP_SCALE_RELATIVE) {
newx= MAX2((int)(in[1]->vec[0]*cbuf->x), 1);
newy= MAX2((int)(in[2]->vec[0]*cbuf->y), 1);
}
else { /* CMP_SCALE_ABSOLUTE */
newx= (int)in[1]->vec[0];
newy= (int)in[2]->vec[0];
}
newx= MIN2(newx, CMP_SCALE_MAX);
newy= MIN2(newy, CMP_SCALE_MAX);
ibuf= IMB_allocImBuf(cbuf->x, cbuf->y, 32, 0, 0);
if(ibuf) {
ibuf->rect_float= cbuf->rect;
IMB_scaleImBuf(ibuf, newx, newy);
if(ibuf->rect_float == cbuf->rect) {
/* no scaling happened. */
stackbuf= pass_on_compbuf(in[0]->data);
}
else {
stackbuf= alloc_compbuf(newx, newy, CB_RGBA, 0);
stackbuf->rect= ibuf->rect_float;
stackbuf->malloc= 1;
}
ibuf->rect_float= NULL;
ibuf->mall &= ~IB_rectfloat;
IMB_freeImBuf(ibuf);
/* also do the translation vector */
stackbuf->xof = (int)(((float)newx/(float)cbuf->x) * (float)cbuf->xof);
stackbuf->yof = (int)(((float)newy/(float)cbuf->y) * (float)cbuf->yof);
}
else {
stackbuf= dupalloc_compbuf(cbuf);
printf("Scaling to %dx%d failed\n", newx, newy);
}
out[0]->data= stackbuf;
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
}
}
static bNodeType cmp_node_scale= {
/* type code */ CMP_NODE_SCALE,
/* name */ "Scale",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_scale_in,
/* output sock */ cmp_node_scale_out,
/* storage */ "",
/* execfunc */ node_composit_exec_scale
};
/* **************** Map UV ******************** */
static bNodeSocketType cmp_node_mapuv_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VECTOR, 1, "UV", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_mapuv_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* foreach UV, use these values to read in cbuf and write to stackbuf */
/* stackbuf should be zeroed */
static void do_mapuv(CompBuf *stackbuf, CompBuf *cbuf, CompBuf *uvbuf, float threshold)
{
ImBuf *ibuf;
float *out= stackbuf->rect, *uv, *uvnext, *uvprev;
float dx, dy, alpha;
int x, y, sx, sy, row= 3*stackbuf->x;
/* ibuf needed for sampling */
ibuf= IMB_allocImBuf(cbuf->x, cbuf->y, 32, 0, 0);
ibuf->rect_float= cbuf->rect;
/* vars for efficient looping */
uv= uvbuf->rect;
uvnext= uv+row;
uvprev= uv-row;
sx= stackbuf->x;
sy= stackbuf->y;
for(y=0; y<sy; y++) {
for(x=0; x<sx; x++, out+=4, uv+=3, uvnext+=3, uvprev+=3) {
if(x>0 && x<sx-1 && y>0 && y<sy-1) {
if(uv[2]!=0.0f) {
/* adaptive sampling, red (U) channel */
dx= 0.5f*(fabs(uv[0]-uv[-3]) + fabs(uv[0]-uv[3]));
dx+= 0.25f*(fabs(uv[0]-uvprev[-3]) + fabs(uv[0]-uvnext[-3]));
dx+= 0.25f*(fabs(uv[0]-uvprev[+3]) + fabs(uv[0]-uvnext[+3]));
/* adaptive sampling, green (V) channel */
dy= 0.5f*(fabs(uv[1]-uv[-row+1]) + fabs(uv[1]-uv[row+1]));
dy+= 0.25f*(fabs(uv[1]-uvprev[+1-3]) + fabs(uv[1]-uvnext[+1-3]));
dy+= 0.25f*(fabs(uv[1]-uvprev[+1+3]) + fabs(uv[1]-uvnext[+1+3]));
/* UV to alpha threshold */
alpha= 1.0f - threshold*(dx+dy);
if(alpha<0.0f) alpha= 0.0f;
else alpha*= uv[2];
/* should use mipmap */
if(dx > 0.20f) dx= 0.20f;
if(dy > 0.20f) dy= 0.20f;
ibuf_sample(ibuf, uv[0], uv[1], dx, dy, out);
/* premul */
if(alpha<1.0f) {
out[0]*= alpha;
out[1]*= alpha;
out[2]*= alpha;
out[3]*= alpha;
}
}
}
}
}
IMB_freeImBuf(ibuf);
}
static void node_composit_exec_mapuv(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(out[0]->hasoutput==0)
return;
if(in[0]->data && in[1]->data) {
CompBuf *cbuf= in[0]->data;
CompBuf *uvbuf= in[1]->data;
CompBuf *stackbuf;
cbuf= typecheck_compbuf(cbuf, CB_RGBA);
uvbuf= typecheck_compbuf(uvbuf, CB_VEC3);
stackbuf= alloc_compbuf(uvbuf->x, uvbuf->y, CB_RGBA, 1); /* allocs */;
do_mapuv(stackbuf, cbuf, uvbuf, 0.05f*(float)node->custom1);
out[0]->data= stackbuf;
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
if(uvbuf!=in[1]->data)
free_compbuf(uvbuf);
}
}
static bNodeType cmp_node_mapuv= {
/* type code */ CMP_NODE_MAP_UV,
/* name */ "Map UV",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_mapuv_in,
/* output sock */ cmp_node_mapuv_out,
/* storage */ "",
/* execfunc */ node_composit_exec_mapuv
};
/* **************** ID Mask ******************** */
static bNodeSocketType cmp_node_idmask_in[]= {
{ SOCK_VALUE, 1, "ID value", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_idmask_out[]= {
{ SOCK_VALUE, 0, "Alpha", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
/* stackbuf should be zeroed */
static void do_idmask(CompBuf *stackbuf, CompBuf *cbuf, float idnr)
{
float *rect;
int x;
char *abuf= MEM_mapallocN(cbuf->x*cbuf->y, "anti ali buf");
rect= cbuf->rect;
for(x= cbuf->x*cbuf->y - 1; x>=0; x--)
if(rect[x]==idnr)
abuf[x]= 255;
antialias_tagbuf(cbuf->x, cbuf->y, abuf);
rect= stackbuf->rect;
for(x= cbuf->x*cbuf->y - 1; x>=0; x--)
if(abuf[x]>1)
rect[x]= (1.0f/255.0f)*(float)abuf[x];
MEM_freeN(abuf);
}
static void node_composit_exec_idmask(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(out[0]->hasoutput==0)
return;
if(in[0]->data) {
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf;
if(cbuf->type!=CB_VAL)
return;
stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1); /* allocs */;
do_idmask(stackbuf, cbuf, (float)node->custom1);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_idmask= {
/* type code */ CMP_NODE_ID_MASK,
/* name */ "ID Mask",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_idmask_in,
/* output sock */ cmp_node_idmask_out,
/* storage */ "",
/* execfunc */ node_composit_exec_idmask
};
/* **************** Displace ******************** */
static bNodeSocketType cmp_node_displace_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ SOCK_VECTOR, 1, "Vector", 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
{ SOCK_VALUE, 1, "X Scale", 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{ SOCK_VALUE, 1, "Y Scale", 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_displace_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_displace(CompBuf *stackbuf, CompBuf *cbuf, CompBuf *vecbuf, float *veccol, float *xscale, float *yscale)
{
ImBuf *ibuf;
int x, y, sx, sy;
float dx=0.0, dy=0.0;
float dspx, dspy;
float uv[2];
float *out= stackbuf->rect, *vec=vecbuf->rect, *in= cbuf->rect;
float *vp, *vpnext, *vpprev;
int row = 3*vecbuf->x;
/* ibuf needed for sampling */
ibuf= IMB_allocImBuf(cbuf->x, cbuf->y, 32, 0, 0);
ibuf->rect_float= cbuf->rect;
vec = vecbuf->rect;
sx= stackbuf->x;
sy= stackbuf->y;
for(y=0; y<sy; y++) {
for(x= 0; x< sx; x++, out+=4, in+=4, vec+=3) {
/* the x-xrad stuff is a bit weird, but i seem to need it otherwise
* my returned pixels are offset weirdly */
vp = compbuf_get_pixel(vecbuf, veccol, x-vecbuf->xrad, y-vecbuf->yrad, vecbuf->xrad, vecbuf->yrad);
/* find the new displaced co-ords, also correcting for translate offset */
dspx = x - (*xscale * vp[0]);
dspy = y - (*yscale * vp[1]);
/* convert image space to 0.0-1.0 UV space for sampling, correcting for translate offset */
uv[0] = dspx / (float)sx;
uv[1] = dspy / (float)sy;
if(x>0 && x< vecbuf->x-1 && y>0 && y< vecbuf->y-1) {
vpnext = vp+row;
vpprev = vp-row;
/* adaptive sampling, X channel */
dx= 0.5f*(fabs(vp[0]-vp[-3]) + fabs(vp[0]-vp[3]));
dx+= 0.25f*(fabs(vp[0]-vpprev[-3]) + fabs(vp[0]-vpnext[-3]));
dx+= 0.25f*(fabs(vp[0]-vpprev[+3]) + fabs(vp[0]-vpnext[+3]));
/* adaptive sampling, Y channel */
dy= 0.5f*(fabs(vp[1]-vp[-row+1]) + fabs(vp[1]-vp[row+1]));
dy+= 0.25f*(fabs(vp[1]-vpprev[+1-3]) + fabs(vp[1]-vpnext[+1-3]));
dy+= 0.25f*(fabs(vp[1]-vpprev[+1+3]) + fabs(vp[1]-vpnext[+1+3]));
/* scaled down to prevent blurriness */
/* 8: magic number, provides a good level of sharpness without getting too aliased */
dx /= 8;
dy /= 8;
}
/* should use mipmap */
if(dx > 0.006f) dx= 0.006f;
if(dy > 0.006f) dy= 0.006f;
if ((vp[0]> 0.0) && (dx < 0.004)) dx = 0.004;
if ((vp[1]> 0.0) && (dy < 0.004)) dy = 0.004;
ibuf_sample(ibuf, uv[0], uv[1], dx, dy, out);
}
}
IMB_freeImBuf(ibuf);
}
static void node_composit_exec_displace(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
if(out[0]->hasoutput==0)
return;
if(in[0]->data && in[1]->data) {
CompBuf *cbuf= in[0]->data;
CompBuf *vecbuf= in[1]->data;
CompBuf *stackbuf;
cbuf= typecheck_compbuf(cbuf, CB_RGBA);
vecbuf= typecheck_compbuf(vecbuf, CB_VEC3);
stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); /* allocs */
do_displace(stackbuf, cbuf, vecbuf, in[1]->vec, in[2]->vec, in[3]->vec);
out[0]->data= stackbuf;
if(cbuf!=in[0]->data)
free_compbuf(cbuf);
if(vecbuf!=in[1]->data)
free_compbuf(vecbuf);
}
}
static bNodeType cmp_node_displace= {
/* type code */ CMP_NODE_DISPLACE,
/* name */ "Displace",
/* width+range */ 140, 100, 320,
/* class+opts */ NODE_CLASS_DISTORT, NODE_OPTIONS,
/* input sock */ cmp_node_displace_in,
/* output sock */ cmp_node_displace_out,
/* storage */ "",
/* execfunc */ node_composit_exec_displace
};
/* **************** SCALAR MATH ******************** */
static bNodeSocketType cmp_node_math_in[]= {
{ SOCK_VALUE, 1, "Value", 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f},
{ SOCK_VALUE, 1, "Value", 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_math_out[]= {
{ SOCK_VALUE, 0, "Value", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static void do_math(bNode *node, float *out, float *in, float *in2)
{
switch(node->custom1)
{
case 0: /* Add */
out[0]= in[0] + in2[0];
break;
case 1: /* Subtract */
out[0]= in[0] - in2[0];
break;
case 2: /* Multiply */
out[0]= in[0] * in2[0];
break;
case 3: /* Divide */
{
if(in[1]==0) /* We don't want to divide by zero. */
out[0]= 0.0;
else
out[0]= in[0] / in2[0];
}
break;
case 4: /* Sine */
out[0]= sin(in[0]);
break;
case 5: /* Cosine */
out[0]= cos(in[0]);
break;
case 6: /* Tangent */
out[0]= tan(in[0]);
break;
case 7: /* Arc-Sine */
{
/* Can't do the impossible... */
if(in[0] <= 1 && in[0] >= -1 )
out[0]= asin(in[0]);
else
out[0]= 0.0;
}
break;
case 8: /* Arc-Cosine */
{
/* Can't do the impossible... */
if( in[0] <= 1 && in[0] >= -1 )
out[0]= acos(in[0]);
else
out[0]= 0.0;
}
break;
case 9: /* Arc-Tangent */
out[0]= atan(in[0]);
break;
case 10: /* Power */
{
/* Don't want any imaginary numbers... */
if( in[0] >= 0 )
out[0]= pow(in[0], in2[0]);
else
out[0]= 0.0;
}
break;
case 11: /* Logarithm */
{
/* Don't want any imaginary numbers... */
if( in[0] > 0 && in2[0] > 0 )
out[0]= log(in[0]) / log(in2[0]);
else
out[0]= 0.0;
}
break;
case 12: /* Minimum */
{
if( in[0] < in2[0] )
out[0]= in2[0];
else
out[0]= in[0];
}
break;
case 13: /* Maximum */
{
if( in[0] > in2[0] )
out[0]= in2[0];
else
out[0]= in[0];
}
break;
case 14: /* Round */
{
out[0]= (int)(in[0] + 0.5f);
}
break;
}
}
static void node_composit_exec_math(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
/* stack order out: bw */
/* stack order in: col */
if(out[0]->hasoutput==0)
return;
/* input no image? then only color operation */
if(in[0]->data==NULL) {
do_math(node, out[0]->vec, in[0]->vec, in[1]->vec);
}
else {
/* make output size of input image */
CompBuf *cbuf= in[0]->data;
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1); /* allocs */
composit2_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec, do_math, CB_VAL, CB_VAL);
out[0]->data= stackbuf;
}
}
static bNodeType cmp_node_math= {
/* type code */ CMP_NODE_MATH,
/* name */ "Math",
/* width+range */ 120, 110, 160,
/* class+opts */ NODE_CLASS_CONVERTOR, NODE_OPTIONS,
/* input sock */ cmp_node_math_in,
/* output sock */ cmp_node_math_out,
/* storage */ "",
/* execfunc */ node_composit_exec_math
};
/* ****************** types array for all shaders ****************** */
bNodeType *node_all_composit[]= {
&node_group_typeinfo,
&cmp_node_rlayers,
&cmp_node_image,
&cmp_node_texture,
&cmp_node_value,
&cmp_node_rgb,
&cmp_node_time,
&cmp_node_composite,
&cmp_node_viewer,
&cmp_node_splitviewer,
&cmp_node_output_file,
&cmp_node_curve_rgb,
&cmp_node_mix_rgb,
&cmp_node_hue_sat,
&cmp_node_alphaover,
&cmp_node_zcombine,
&cmp_node_normal,
&cmp_node_curve_vec,
&cmp_node_map_value,
&cmp_node_filter,
&cmp_node_blur,
&cmp_node_vecblur,
&cmp_node_dilateerode,
&cmp_node_defocus,
&cmp_node_valtorgb,
&cmp_node_rgbtobw,
&cmp_node_setalpha,
&cmp_node_idmask,
&cmp_node_math,
&cmp_node_seprgba,
&cmp_node_combrgba,
&cmp_node_sephsva,
&cmp_node_combhsva,
&cmp_node_sepyuva,
&cmp_node_combyuva,
&cmp_node_sepycca,
&cmp_node_combycca,
&cmp_node_diff_matte,
&cmp_node_chroma,
&cmp_node_channel_matte,
&cmp_node_color_spill,
&cmp_node_luma_matte,
&cmp_node_translate,
&cmp_node_rotate,
&cmp_node_scale,
&cmp_node_flip,
&cmp_node_displace,
&cmp_node_mapuv,
NULL
};
/* ******************* parse ************ */
/* clumsy checking... should do dynamic outputs once */
static void force_hidden_passes(bNode *node, int passflag)
{
bNodeSocket *sock;
for(sock= node->outputs.first; sock; sock= sock->next)
sock->flag &= ~SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_Z);
if(!(passflag & SCE_PASS_Z)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_NORMAL);
if(!(passflag & SCE_PASS_NORMAL)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_VEC);
if(!(passflag & SCE_PASS_VECTOR)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_UV);
if(!(passflag & SCE_PASS_UV)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_RGBA);
if(!(passflag & SCE_PASS_RGBA)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_DIFF);
if(!(passflag & SCE_PASS_DIFFUSE)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_SPEC);
if(!(passflag & SCE_PASS_SPEC)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_SHADOW);
if(!(passflag & SCE_PASS_SHADOW)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_AO);
if(!(passflag & SCE_PASS_AO)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_REFLECT);
if(!(passflag & SCE_PASS_REFLECT)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_REFRACT);
if(!(passflag & SCE_PASS_REFRACT)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_RADIO);
if(!(passflag & SCE_PASS_RADIO)) sock->flag |= SOCK_UNAVAIL;
sock= BLI_findlink(&node->outputs, RRES_OUT_INDEXOB);
if(!(passflag & SCE_PASS_INDEXOB)) sock->flag |= SOCK_UNAVAIL;
}
/* based on rules, force sockets hidden always */
void ntreeCompositForceHidden(bNodeTree *ntree)
{
bNode *node;
if(ntree==NULL) return;
for(node= ntree->nodes.first; node; node= node->next) {
if( node->type==CMP_NODE_R_LAYERS) {
Scene *sce= node->id?(Scene *)node->id:G.scene; /* G.scene is WEAK! */
SceneRenderLayer *srl= BLI_findlink(&sce->r.layers, node->custom1);
if(srl)
force_hidden_passes(node, srl->passflag);
}
else if( node->type==CMP_NODE_IMAGE) {
Image *ima= (Image *)node->id;
if(ima) {
if(ima->rr) {
ImageUser *iuser= node->storage;
RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer);
if(rl)
force_hidden_passes(node, rl->passflag);
else
force_hidden_passes(node, 0);
}
else if(ima->type!=IMA_TYPE_MULTILAYER) { /* if ->rr not yet read we keep inputs */
force_hidden_passes(node, RRES_OUT_Z);
}
}
else
force_hidden_passes(node, 0);
}
}
}
/* called from render pipeline, to tag render input and output */
/* need to do all scenes, to prevent errors when you re-render 1 scene */
void ntreeCompositTagRender(Scene *curscene)
{
Scene *sce;
for(sce= G.main->scene.first; sce; sce= sce->id.next) {
if(sce->nodetree) {
bNode *node;
for(node= sce->nodetree->nodes.first; node; node= node->next) {
if(node->id==(ID *)curscene || node->type==CMP_NODE_COMPOSITE)
NodeTagChanged(sce->nodetree, node);
}
}
}
}
/* tags nodes that have animation capabilities */
void ntreeCompositTagAnimated(bNodeTree *ntree)
{
bNode *node;
if(ntree==NULL) return;
for(node= ntree->nodes.first; node; node= node->next) {
if(node->type==CMP_NODE_IMAGE) {
Image *ima= (Image *)node->id;
if(ima && ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE))
NodeTagChanged(ntree, node);
}
else if(node->type==CMP_NODE_TIME)
NodeTagChanged(ntree, node);
}
}
/* called from image window preview */
void ntreeCompositTagGenerators(bNodeTree *ntree)
{
bNode *node;
if(ntree==NULL) return;
for(node= ntree->nodes.first; node; node= node->next) {
if( ELEM(node->type, CMP_NODE_R_LAYERS, CMP_NODE_IMAGE))
NodeTagChanged(ntree, node);
}
}