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
2007-02-15 12:01:41 +00:00

6104 lines
168 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]= 1.0f;
}
}
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, 1, "Offset", 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f},
{ SOCK_VECTOR, 1, "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);
fac= curvemapping_evaluateF(node->storage, 0, fac);
out[0]->vec[0]= CLAMPIS(fac, 0.0f, 1.0f);
}
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= dupalloc_compbuf(in[1]->data);
CompBuf *stackbuf=typecheck_compbuf(cbuf,CB_RGBA);
composit2_pixel_processor(node, stackbuf, stackbuf, in[1]->vec, in[0]->data, in[0]->vec, do_hue_sat_fac, CB_RGBA, CB_VAL);
out[0]->data= stackbuf;
/* get rid of intermediary cbuf if it's extra */
if(stackbuf!=cbuf)
free_compbuf(cbuf);
}
}
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)) {
/* create new buffer so input buffer doesn't get corrupted */
CompBuf *cbuf= dupalloc_compbuf(in[0]->data);
CompBuf *cbuf2= typecheck_compbuf(cbuf, CB_RGBA);
/* convert the RGB stackbuf to an HSV representation */
composit1_pixel_processor(node, cbuf2, cbuf2, in[0]->vec, do_sephsva, CB_RGBA);
/* separate each of those channels */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(cbuf2, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(cbuf2, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(cbuf2, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(cbuf2, CHAN_A);
/*not used anymore */
if(cbuf2!=cbuf)
free_compbuf(cbuf2);
free_compbuf(cbuf);
}
}
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, 1, "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, 0, "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 */
/* accept image offsets from other nodes */
new->xof = img->xof;
new->yof = img->yof;
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 */
/* accept image offsets from other nodes */
new->xof = img->xof;
new->yof = img->yof;
if(nbd->gamma) {
gammabuf= dupalloc_compbuf(img);
gamma_correct_compbuf(gammabuf, 0);
}
else gammabuf= img;
if(nbd->bokeh)
bokeh_single_image(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 && 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;
// bug #5921, limit minimum
crad->rect[p] = MAX2(1e-5f, crad->rect[p]);
crad->rect[p] = MIN2(crad->rect[p], maxr);
// if maxblur!=0, limit maximum
if (nqd->maxblur != 0.f) crad->rect[p] = MIN2(crad->rect[p], nqd->maxblur);
}
}
else {
// actual zbuffer.
// separate foreground from background CoC's
// then blur background and blend in again with foreground,
// improves the 'blurred foreground overlapping in-focus midground' sharp boundary problem.
// wts buffer here used for blendmask
maxfgc = 0.f; // maximum foreground CoC radius
for (y=0; y<img->y; y++) {
p = y * img->x;
for (x=0; x<img->x; x++) {
px = p + x;
iZ = (zbuf->rect[px]==0.f) ? 0.f : (1.f/zbuf->rect[px]);
crad->rect[px] = 0.5f*(aperture*(dof_sp*(cam_invfdist - iZ) - 1.f));
if (crad->rect[px] <= 0.f) {
wts->rect[px] = 1.f;
crad->rect[px] = -crad->rect[px];
if (crad->rect[px] > maxfgc) maxfgc = crad->rect[px];
}
else crad->rect[px] = wts->rect[px] = 0;
}
}
// fast blur...
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) {
// related to bug #5921, forgot output image when skipping 0 radius values
new->rect[cp4] = img->rect[cp4];
if (new->type != CB_VAL) {
new->rect[cp4+1] = img->rect[cp4+1];
new->rect[cp4+2] = img->rect[cp4+2];
new->rect[cp4+3] = img->rect[cp4+3];
}
continue;
}
cR2 *= cR2;
// pixel color
ctcol = &img->rect[cp4];
if (!nqd->preview) {
int xs, xe, ys, ye;
float lwt, wtcol[4] = {0}, aacol[4] = {0};
// shape weight
if (nqd->bktype==0) // disk
wt = 1.f/((float)M_PI*cR2);
else
wt = 1.f/(cR2*bk_hn_theta);
// weighted color
wtcol[0] = wt*ctcol[0];
if (new->type != CB_VAL) {
wtcol[1] = wt*ctcol[1];
wtcol[2] = wt*ctcol[2];
wtcol[3] = wt*ctcol[3];
}
// macro for background blur overlap test
// unfortunately, since this is done per pixel,
// it has a very significant negative impact on processing time...
// (eg. aa disk blur without test: 112 sec, vs with test: 176 sec...)
// iff center blur radius > threshold
// and if overlap pixel in focus, do nothing, else add color/weigbt
// (threshold constant is dependant on amount of blur)
#define TESTBG1(c, w) {\
if (ct_crad > nqd->bthresh) {\
if (crad->rect[p] > nqd->bthresh) {\
new->rect[p] += c[0];\
wts->rect[p] += w;\
}\
}\
else {\
new->rect[p] += c[0];\
wts->rect[p] += w;\
}\
}
#define TESTBG4(c, w) {\
if (ct_crad > nqd->bthresh) {\
if (crad->rect[p] > nqd->bthresh) {\
new->rect[p4] += c[0];\
new->rect[p4+1] += c[1];\
new->rect[p4+2] += c[2];\
new->rect[p4+3] += c[3];\
wts->rect[p] += w;\
}\
}\
else {\
new->rect[p4] += c[0];\
new->rect[p4+1] += c[1];\
new->rect[p4+2] += c[2];\
new->rect[p4+3] += c[3];\
wts->rect[p] += w;\
}\
}
if (nqd->bktype == 0) {
// Disk
int _x, i, j, di;
float Dj, T;
// AA pixel
#define AAPIX(a, b) {\
int _ny = b;\
if ((_ny >= 0) && (_ny < new->y)) {\
int _nx = a;\
if ((_nx >=0) && (_nx < new->x)) {\
p = _ny*new->x + _nx;\
if (new->type==CB_VAL) {\
TESTBG1(aacol, lwt);\
}\
else {\
p4 = p * new->type;\
TESTBG4(aacol, lwt);\
}\
}\
}\
}
// circle scanline
#define CSCAN(a, b) {\
int _ny = y + b;\
if ((_ny >= 0) && (_ny < new->y)) {\
xs = x - a + 1;\
if (xs < 0) xs = 0;\
xe = x + a;\
if (xe > new->x) xe = new->x;\
p = _ny*new->x + xs;\
if (new->type==CB_VAL) {\
for (_x=xs; _x<xe; _x++, p++) TESTBG1(wtcol, wt);\
}\
else {\
p4 = p * new->type;\
for (_x=xs; _x<xe; _x++, p++, p4+=new->type) TESTBG4(wtcol, wt);\
}\
}\
}
i = ceil(ct_crad);
j = 0;
T = 0;
while (i > j) {
Dj = sqrt(cR2 - j*j);
Dj -= floor(Dj);
di = 0;
if (Dj > T) { i--; di = 1; }
T = Dj;
aacol[0] = wtcol[0]*Dj;
if (new->type != CB_VAL) {
aacol[1] = wtcol[1]*Dj;
aacol[2] = wtcol[2]*Dj;
aacol[3] = wtcol[3]*Dj;
}
lwt = wt*Dj;
if (i!=j) {
// outer pixels
AAPIX(x+j, y+i);
AAPIX(x+j, y-i);
if (j) {
AAPIX(x-j, y+i); // BL
AAPIX(x-j, y-i); // TL
}
if (di) { // only when i changed, interior of outer section
CSCAN(j, i); // bottom
CSCAN(j, -i); // top
}
}
// lower mid section
AAPIX(x+i, y+j);
if (i) AAPIX(x-i, y+j);
CSCAN(i, j);
// upper mid section
if (j) {
AAPIX(x+i, y-j);
if (i) AAPIX(x-i, y-j);
CSCAN(i, -j);
}
j++;
}
#undef CSCAN
#undef AAPIX
}
else {
// n-agonal
int ov, nv;
float mind, maxd, lwt;
ys = MAX2((int)floor(bkh_b[2]*ct_crad + y), 0);
ye = MIN2((int)ceil(bkh_b[3]*ct_crad + y), new->y - 1);
for (sy=ys; sy<=ye; sy++) {
float fxs = 1e10f, fxe = -1e10f;
float yf = (sy - y)/ct_crad;
int found = 0;
ov = len_bkh - 1;
mind = maxd = 0;
for (nv=0; nv<len_bkh; nv++) {
if ((BKH[nv].max_y >= yf) && (BKH[nv].min_y <= yf)) {
float tx = BKH[ov].x0 + BKH[nv].ls_x*(yf - BKH[ov].y0);
if (tx < fxs) { fxs = tx; mind = BKH[nv].ls_x; }
if (tx > fxe) { fxe = tx; maxd = BKH[nv].ls_x; }
if (++found == 2) break;
}
ov = nv;
}
if (found) {
fxs = fxs*ct_crad + x;
fxe = fxe*ct_crad + x;
xs = (int)floor(fxs), xe = (int)ceil(fxe);
// AA hack for first and last x pixel, near vertical edges only
if (fabs(mind) <= 1.f) {
if ((xs >= 0) && (xs < new->x)) {
lwt = 1.f-(fxs - xs);
aacol[0] = wtcol[0]*lwt;
p = xs + sy*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
if (fabs(maxd) <= 1.f) {
if ((xe >= 0) && (xe < new->x)) {
lwt = 1.f-(xe - fxe);
aacol[0] = wtcol[0]*lwt;
p = xe + sy*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
xs = MAX2(xs+1, 0);
xe = MIN2(xe, new->x);
// remaining interior scanline
p = sy*new->x + xs;
if (new->type==CB_VAL) {
for (sx=xs; sx<xe; sx++, p++) TESTBG1(wtcol, wt);
}
else {
p4 = p * new->type;
for (sx=xs; sx<xe; sx++, p++, p4+=new->type) TESTBG4(wtcol, wt);
}
}
}
// now traverse in opposite direction, y scanlines,
// but this time only draw the near horizontal edges,
// applying same AA hack as above
xs = MAX2((int)floor(bkh_b[0]*ct_crad + x), 0);
xe = MIN2((int)ceil(bkh_b[1]*ct_crad + x), img->x - 1);
for (sx=xs; sx<=xe; sx++) {
float xf = (sx - x)/ct_crad;
float fys = 1e10f, fye = -1e10f;
int found = 0;
ov = len_bkh - 1;
mind = maxd = 0;
for (nv=0; nv<len_bkh; nv++) {
if ((BKH[nv].max_x >= xf) && (BKH[nv].min_x <= xf)) {
float ty = BKH[ov].y0 + BKH[nv].ls_y*(xf - BKH[ov].x0);
if (ty < fys) { fys = ty; mind = BKH[nv].ls_y; }
if (ty > fye) { fye = ty; maxd = BKH[nv].ls_y; }
if (++found == 2) break;
}
ov = nv;
}
if (found) {
fys = fys*ct_crad + y;
fye = fye*ct_crad + y;
// near horizontal edges only, line slope <= 1
if (fabs(mind) <= 1.f) {
int iys = (int)floor(fys);
if ((iys >= 0) && (iys < new->y)) {
lwt = 1.f - (fys - iys);
aacol[0] = wtcol[0]*lwt;
p = sx + iys*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
if (fabs(maxd) <= 1.f) {
int iye = ceil(fye);
if ((iye >= 0) && (iye < new->y)) {
lwt = 1.f - (iye - fye);
aacol[0] = wtcol[0]*lwt;
p = sx + iye*new->x;
if (new->type==CB_VAL) {
lwt *= wt;
TESTBG1(aacol, lwt);
}
else {
p4 = p * new->type;
aacol[1] = wtcol[1]*lwt;
aacol[2] = wtcol[2]*lwt;
aacol[3] = wtcol[3]*lwt;
lwt *= wt;
TESTBG4(aacol, lwt);
}
}
}
}
}
}
#undef TESTBG4
#undef TESTBG1
}
else {
// sampled, simple rejection sampling here, good enough
unsigned int maxsam, s, ui = BLI_rand()*BLI_rand();
float wcor, cpr = BLI_frand();
if (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);
sx = (int)(x + u + 0.5f), sy = (int)(y + v + 0.5f);
if ((sx<0) || (sx >= new->x) || (sy<0) || (sy >= new->y)) continue;
p = sx + sy*new->x;
p4 = p * new->type;
if (nqd->bktype==0) // Disk
lwt = ((u*u + v*v)<=cR2) ? wcor : 0.f;
else // AA not needed here
lwt = wcor * getWeight(BKH, len_bkh, u, v, ct_crad, inradsq);
// prevent background bleeding onto in-focus pixels, user-option
if (ct_crad > nqd->bthresh) { // if center blur > threshold
if (crad->rect[p] > nqd->bthresh) { // if overlap pixel in focus, do nothing, else add color/weigbt
new->rect[p4] += ctcol[0] * lwt;
if (new->type != CB_VAL) {
new->rect[p4+1] += ctcol[1] * lwt;
new->rect[p4+2] += ctcol[2] * lwt;
new->rect[p4+3] += ctcol[3] * lwt;
}
wts->rect[p] += lwt;
}
}
else {
new->rect[p4] += ctcol[0] * lwt;
if (new->type != CB_VAL) {
new->rect[p4+1] += ctcol[1] * lwt;
new->rect[p4+2] += ctcol[2] * lwt;
new->rect[p4+3] += ctcol[3] * lwt;
}
wts->rect[p] += lwt;
}
}
}
}
}
// finally, normalize
for (y=0; y<new->y; y++) {
p = y * new->x;
p4 = p * new->type;
for (x=0; x<new->x; x++) {
float dv = (wts->rect[p]==0.f) ? 1.f : (1.f/wts->rect[p]);
new->rect[p4] *= dv;
if (new->type!=CB_VAL) {
new->rect[p4+1] *= dv;
new->rect[p4+2] *= dv;
new->rect[p4+3] *= dv;
}
p++;
p4 += new->type;
}
}
free_compbuf(crad);
free_compbuf(wts);
printf("Done\n");
}
static void node_composit_exec_defocus(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *old, *zbuf_use = NULL, *img = in[0]->data, *zbuf = in[1]->data;
NodeDefocus* nqd = node->storage;
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 copy of buffer so input image doesn't get corrupted */
CompBuf *cbuf= dupalloc_compbuf(in[0]->data);
CompBuf *cbuf2=typecheck_compbuf(cbuf, CB_RGBA);
/* convert the RGB stackbuf to an YUV representation */
composit1_pixel_processor(node, cbuf2, cbuf2, in[0]->vec, do_sepyuva, CB_RGBA);
/* separate each of those channels */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(cbuf2, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(cbuf2, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(cbuf2, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(cbuf2, CHAN_A);
/*not used anymore */
if(cbuf2!=cbuf)
free_compbuf(cbuf2);
free_compbuf(cbuf);
}
}
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 copy of buffer so input buffer doesn't get corrupted */
CompBuf *cbuf= dupalloc_compbuf(in[0]->data);
CompBuf *cbuf2=typecheck_compbuf(cbuf, CB_RGBA);
/* convert the RGB stackbuf to an HSV representation */
composit1_pixel_processor(node, cbuf2, cbuf2, in[0]->vec, do_sepycca, CB_RGBA);
/* separate each of those channels */
if(out[0]->hasoutput)
out[0]->data= valbuf_from_rgbabuf(cbuf2, CHAN_R);
if(out[1]->hasoutput)
out[1]->data= valbuf_from_rgbabuf(cbuf2, CHAN_G);
if(out[2]->hasoutput)
out[2]->data= valbuf_from_rgbabuf(cbuf2, CHAN_B);
if(out[3]->hasoutput)
out[3]->data= valbuf_from_rgbabuf(cbuf2, CHAN_A);
/*not used anymore */
if(cbuf2!=cbuf)
free_compbuf(cbuf2);
free_compbuf(cbuf);
}
}
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_ycca_to_rgba_normalized(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=atan2(c->key[2],c->key[1]);
/*rotate the cb and cr into x/z space */
x=in[1]*cos(theta)+in[2]*sin(theta);
z=in[2]*cos(theta)-in[1]*sin(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-(fabs(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=atan2(newCr,newCb);
beta=beta*180.0/M_PI; /* convert to degrees for compare*/
/* if beta is within the clippin angle */
if(fabs(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(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);
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_ycca_to_rgba_normalized, 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_normalized_rgba_to_ycca2(bNode *node, float *out, float *in)
{
/*normalize to the range 0.0 to 1.0) */
rgb_to_ycc(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[0]=(out[0])/255.0;
out[1]=(out[1])/255.0;
out[2]=(out[2])/255.0;
out[3]=in[3];
}
static void do_normalized_ycca_to_rgba2(bNode *node, float *out, float *in)
{
/*un-normalize the normalize from above */
in[0]=in[0]*255.0;
in[1]=in[1]*255.0;
in[2]=in[2]*255.0;
ycc_to_rgb(in[0],in[1],in[2], &out[0], &out[1], &out[2]);
out[3]=in[3];
}
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_normalized_rgba_to_ycca2, 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_normalized_ycca_to_rgba2, 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]= in[0];
else
out[0]= in2[0];
}
break;
case 13: /* Maximum */
{
if( in[0] > in2[0] )
out[0]= in[0];
else
out[0]= in2[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);
}
}