6104 lines
168 KiB
C
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);
|
|
}
|
|
}
|