they work ok in testing here and get done what I need, any checks or fixes are welcome. * Separate RGBA: Separates an input RGBA image into its R, G, B and A channels * Separate HSVA: Separates an input RGBA image into H, S, V and A channels * Set Alpha: Takes an input RGBA image and an alpha value channel and combines them into a single RGBA image channel. You can also set the alpha for the entire image with the number field when there's no input alpha channel. TODO: Allow input alpha channel with no input image, in order to output a solid colour, with alpha.
2628 lines
68 KiB
C
2628 lines
68 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 "DNA_ID.h"
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_node_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_material.h"
|
|
#include "BKE_texture.h"
|
|
#include "BKE_utildefines.h"
|
|
|
|
#include "BLI_arithb.h"
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_threads.h"
|
|
|
|
/* NOTE: no imbuf calls allowed in composit, we need threadsafe malloc! */
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
#include "RE_pipeline.h"
|
|
|
|
/* *************************** operations support *************************** */
|
|
|
|
/* general signal that's in output sockets, and goes over the wires */
|
|
typedef struct CompBuf {
|
|
float *rect;
|
|
int x, y;
|
|
short type, malloc;
|
|
rcti disprect; /* cropped part of image */
|
|
int xof, yof; /* relative to center of target image */
|
|
} 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_callocT(sizeof(CompBuf), "compbuf");
|
|
|
|
cbuf->x= sizex;
|
|
cbuf->y= sizey;
|
|
cbuf->type= type;
|
|
if(alloc) {
|
|
if(cbuf->type==CB_RGBA)
|
|
cbuf->rect= MEM_mapallocT(4*sizeof(float)*sizex*sizey, "compbuf RGBA rect");
|
|
else if(cbuf->type==CB_VEC3)
|
|
cbuf->rect= MEM_mapallocT(3*sizeof(float)*sizex*sizey, "compbuf Vector3 rect");
|
|
else if(cbuf->type==CB_VEC2)
|
|
cbuf->rect= MEM_mapallocT(2*sizeof(float)*sizex*sizey, "compbuf Vector2 rect");
|
|
else
|
|
cbuf->rect= MEM_mapallocT(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);
|
|
return dupbuf;
|
|
}
|
|
|
|
void free_compbuf(CompBuf *cbuf)
|
|
{
|
|
if(cbuf->malloc && cbuf->rect)
|
|
MEM_freeT(cbuf->rect);
|
|
|
|
MEM_freeT(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, type*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;
|
|
}
|
|
|
|
|
|
|
|
/* **************************************************** */
|
|
|
|
#if 0
|
|
/* on first call, disprect should be initialized to 'out', then you can call this on all 'src' images */
|
|
static void get_overlap_rct(CompBuf *out, CompBuf *src, rcti *disprect)
|
|
{
|
|
rcti rect;
|
|
/* output center is considered (0,0) */
|
|
|
|
if(src==NULL) return;
|
|
|
|
/* translate src into output space */
|
|
rect= src->disprect;
|
|
BLI_translate_rcti(&rect, out->xof-src->xof, out->xof-src->xof);
|
|
/* intersect rect with current disprect */
|
|
|
|
BLI_isect_rcti(&rect, disprect, disprect);
|
|
}
|
|
|
|
static void get_scanline_rcti(CompBuf *out, rcti *disprect, CompBuf *src, rcti *srcrect)
|
|
{
|
|
int xof, yof;
|
|
|
|
/* translate src into output space */
|
|
xof= out->xof-src->xof;
|
|
yof= out->xof-src->xof;
|
|
|
|
srcrect->xmin= disprect->xmin + xof;
|
|
srcrect->ymin= disprect->ymin + yof;
|
|
srcrect->xmax= disprect->xmax + xof;
|
|
srcrect->ymax= disprect->ymax + yof;
|
|
}
|
|
#endif
|
|
|
|
/* 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 *))
|
|
{
|
|
float *outfp, *srcfp, *out_data, *src_data;
|
|
int outx, outy;
|
|
int srcx, srcy;
|
|
int out_pix, out_stride, src_stride, src_pix, x, y;
|
|
|
|
outx= out->x;
|
|
outy= out->y;
|
|
out_pix= out->type;
|
|
out_stride= out->x;
|
|
out_data= out->rect;
|
|
|
|
/* handle case when input is constant color */
|
|
if(src_buf==NULL) {
|
|
srcx= outx; srcy= outy;
|
|
src_stride= 0;
|
|
src_pix= 0;
|
|
src_data= src_col;
|
|
}
|
|
else {
|
|
srcx= src_buf->x;
|
|
srcy= src_buf->y;
|
|
src_stride= srcx;
|
|
src_pix= src_buf->type;
|
|
src_data= src_buf->rect;
|
|
}
|
|
|
|
outx= MIN2(outx, srcx);
|
|
outy= MIN2(outy, srcy);
|
|
|
|
for(y=0; y<outy; y++) {
|
|
/* set scanlines on right location */
|
|
srcfp= src_data + src_pix*y*src_stride;
|
|
outfp= out_data + out_pix*y*out_stride;
|
|
|
|
for(x=0; x<outx; x++) {
|
|
func(node, outfp, srcfp);
|
|
srcfp += src_pix;
|
|
outfp += out_pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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 *))
|
|
{
|
|
float *outfp, *srcfp, *src_data, *facfp, *fac_data;
|
|
int outx= out->x, outy= out->y;
|
|
int srcx, srcy, facx, facy;
|
|
int out_pix, src_stride, src_pix, fac_stride, fac_pix, x, y;
|
|
|
|
out_pix= out->type;
|
|
|
|
/* handle case when input is constant color */
|
|
if(src_buf==NULL) {
|
|
srcx= outx; srcy= outy;
|
|
src_stride= 0;
|
|
src_pix= 0;
|
|
src_data= src_col;
|
|
}
|
|
else {
|
|
srcx= src_buf->x;
|
|
srcy= src_buf->y;
|
|
src_stride= srcx;
|
|
src_pix= src_buf->type;
|
|
src_data= src_buf->rect;
|
|
}
|
|
|
|
/* factor buf or constant? */
|
|
if(fac_buf==NULL) {
|
|
facx= outx; facy= outy;
|
|
fac_stride= 0;
|
|
fac_pix= 0;
|
|
fac_data= fac;
|
|
}
|
|
else {
|
|
facx= fac_buf->x;
|
|
facy= fac_buf->y;
|
|
fac_stride= facx;
|
|
fac_pix= fac_buf->type;
|
|
fac_data= fac_buf->rect;
|
|
}
|
|
|
|
if(fac_data==NULL) {
|
|
printf("fac buffer error, node %s\n", node->name);
|
|
return;
|
|
}
|
|
|
|
facx= MIN2(facx, srcx);
|
|
facy= MIN2(facy, srcy);
|
|
|
|
#if 0
|
|
if(src_buf) {
|
|
rcti disprect;
|
|
|
|
disprect= out->disprect;
|
|
get_overlap_rct(out, src_buf, &disprect);
|
|
printf("%s\n", node->name);
|
|
printf("union %d %d %d %d\n", disprect.xmin,disprect.ymin,disprect.xmax,disprect.ymax);
|
|
}
|
|
/* new approach */
|
|
outfp= out->rect_float + src.ymin*outx + ;
|
|
for(y=src.ymin; y<src.ymax; y++) {
|
|
|
|
/* all operators available */
|
|
if(y>=disp.ymin && y<disp.ymax) {
|
|
srcfp= src_data + (src_stride*(y+scrc.ymin) + src.xmin);
|
|
facfp= fac_data + (fac_stride*(y+fac.ymin) + fac.xmin);
|
|
|
|
for(x= src.xmin; x<src.xmax; x++) {
|
|
if(x>=disp.xmin && x<disp.xmax) {
|
|
|
|
srcfp+= src_pix;
|
|
facfp+= fac_pix;
|
|
}
|
|
else {
|
|
/* copy src1 */
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* copy src1 */
|
|
srcfp= src_data + (src_stride*(y+scrc.ymin) + src.xmin);
|
|
|
|
QUATCOPY(outfp, srcfp);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
outfp= out->rect;
|
|
for(y=0; y<outy; y++) {
|
|
/* set source scanline on right location */
|
|
srcfp= src_data + src_pix*y*src_stride;
|
|
facfp= fac_data + fac_pix*y*fac_stride;
|
|
|
|
for(x=0; x<outx; x++, outfp+=out_pix) {
|
|
if(x<facx && y<facy)
|
|
func(node, outfp, srcfp, facfp);
|
|
srcfp += src_pix;
|
|
facfp += fac_pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 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))
|
|
{
|
|
float *outfp, *src1fp, *src2fp, *facfp, *src1_data, *src2_data, *fac_data;
|
|
int outx= out->x, outy= out->y;
|
|
int src1x, src1y, src2x, src2y, facx, facy;
|
|
int src1_stride, src1_pix, src2_stride, src2_pix, fac_stride, fac_pix, x, y;
|
|
|
|
/* handle case when input has constant color */
|
|
if(src1_buf==NULL) {
|
|
src1x= outx; src1y= outy;
|
|
src1_stride= 0;
|
|
src1_pix= 0;
|
|
src1_data= src1_col;
|
|
}
|
|
else {
|
|
src1x= src1_buf->x;
|
|
src1y= src1_buf->y;
|
|
src1_stride= src1x;
|
|
src1_pix= src1_buf->type;
|
|
src1_data= src1_buf->rect;
|
|
}
|
|
|
|
if(src2_buf==NULL) {
|
|
src2x= outx; src2y= outy;
|
|
src2_stride= 0;
|
|
src2_pix= 0;
|
|
src2_data= src2_col;
|
|
}
|
|
else {
|
|
src2x= src2_buf->x;
|
|
src2y= src2_buf->y;
|
|
src2_stride= src2x;
|
|
src2_pix= src2_buf->type;
|
|
src2_data= src2_buf->rect;
|
|
}
|
|
|
|
/* factor buf or constant? */
|
|
if(fac_buf==NULL) {
|
|
facx= outx; facy= outy;
|
|
fac_stride= 0;
|
|
fac_pix= 0;
|
|
fac_data= &fac;
|
|
}
|
|
else {
|
|
facx= fac_buf->x;
|
|
facy= fac_buf->y;
|
|
fac_stride= facx;
|
|
fac_pix= 1;
|
|
fac_data= fac_buf->rect;
|
|
}
|
|
|
|
facx= MIN3(facx, src1x, src2x);
|
|
facy= MIN3(facy, src1y, src2y);
|
|
|
|
outfp= out->rect;
|
|
for(y=0; y<outy; y++) {
|
|
|
|
/* set source scanlines on right location */
|
|
src1fp= src1_data + src1_pix*y*src1_stride;
|
|
src2fp= src2_data + src2_pix*y*src2_stride;
|
|
facfp= fac_data + y*fac_stride;
|
|
|
|
for(x=0; x<outx; x++, outfp+=4) {
|
|
if(x<facx && y<facy)
|
|
func(node, outfp, src1fp, src2fp, *facfp);
|
|
src1fp+= src1_pix;
|
|
src2fp+= src2_pix;
|
|
facfp+= fac_pix;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ok to delete this and use the generalised version below? */
|
|
static CompBuf *alphabuf_from_rgbabuf(CompBuf *cbuf)
|
|
{
|
|
CompBuf *valbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_VAL, 1);
|
|
float *valf, *rectf;
|
|
int tot;
|
|
|
|
valf= valbuf->rect;
|
|
rectf= cbuf->rect + 3;
|
|
for(tot= cbuf->x*cbuf->y; tot>0; tot--, valf++, rectf+=4)
|
|
*valf= *rectf;
|
|
|
|
return valbuf;
|
|
}
|
|
|
|
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;
|
|
|
|
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;
|
|
|
|
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, preview->xsize, preview->ysize);
|
|
|
|
/* this ensures free-compbuf does the right stuff */
|
|
SWAP(float *, cbuf->rect, node->preview->rect);
|
|
|
|
free_compbuf(cbuf);
|
|
}
|
|
}
|
|
|
|
/* ******************************************************** */
|
|
/* ********* 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_rg(bNode *node, float *out, float *in)
|
|
{
|
|
out[0]= in[0];
|
|
out[1]= in[1];
|
|
out[2]= 0.0f;
|
|
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 */
|
|
Image *ima= (Image *)node->id;
|
|
CompBuf *cbuf, *inbuf= in[0]->data;
|
|
int rectx, recty;
|
|
|
|
if(inbuf==NULL) {
|
|
rectx= 320; recty= 256;
|
|
}
|
|
else {
|
|
rectx= inbuf->x;
|
|
recty= inbuf->y;
|
|
}
|
|
|
|
/* full copy of imbuf, but threadsafe... */
|
|
if(ima->ibuf==NULL) {
|
|
ima->ibuf = MEM_callocT(sizeof(struct ImBuf), "ImBuf_struct");
|
|
ima->ibuf->depth= 32;
|
|
ima->ibuf->ftype= TGA;
|
|
}
|
|
|
|
/* cleanup of composit image */
|
|
if(ima->ibuf->rect) {
|
|
MEM_freeT(ima->ibuf->rect);
|
|
ima->ibuf->rect= NULL;
|
|
ima->ibuf->mall &= ~IB_rect;
|
|
}
|
|
if(ima->ibuf->zbuf_float) {
|
|
MEM_freeT(ima->ibuf->zbuf_float);
|
|
ima->ibuf->zbuf_float= NULL;
|
|
ima->ibuf->mall &= ~IB_zbuffloat;
|
|
}
|
|
if(ima->ibuf->rect_float)
|
|
MEM_freeT(ima->ibuf->rect_float);
|
|
|
|
ima->ibuf->x= rectx;
|
|
ima->ibuf->y= recty;
|
|
ima->ibuf->mall |= IB_rectfloat;
|
|
ima->ibuf->rect_float= MEM_mallocT(4*rectx*recty*sizeof(float), "viewer rect");
|
|
|
|
/* now we combine the input with ibuf */
|
|
cbuf= alloc_compbuf(rectx, recty, CB_RGBA, 0); // no alloc
|
|
cbuf->rect= ima->ibuf->rect_float;
|
|
|
|
/* when no alpha, we can simply copy */
|
|
if(in[1]->data==NULL) {
|
|
void (*func)(bNode *, float *, float *);
|
|
|
|
if(inbuf==NULL || inbuf->type==CB_RGBA) func= do_copy_rgba;
|
|
else if(inbuf->type==CB_VEC3) func= do_copy_rgb;
|
|
else if(inbuf->type==CB_VEC2) func= do_copy_rg;
|
|
else func= do_copy_value;
|
|
|
|
composit1_pixel_processor(node, cbuf, inbuf, in[0]->vec, func);
|
|
}
|
|
else
|
|
composit2_pixel_processor(node, cbuf, inbuf, in[0]->vec, in[1]->data, in[1]->vec, do_copy_a_rgba);
|
|
|
|
if(in[2]->data) {
|
|
CompBuf *zbuf= alloc_compbuf(rectx, recty, CB_VAL, 1);
|
|
ima->ibuf->zbuf_float= zbuf->rect;
|
|
ima->ibuf->mall |= IB_zbuffloat;
|
|
|
|
composit1_pixel_processor(node, zbuf, in[2]->data, in[2]->vec, do_copy_value);
|
|
|
|
/* free compbuf, but not the rect */
|
|
zbuf->malloc= 0;
|
|
free_compbuf(zbuf);
|
|
}
|
|
|
|
generate_preview(node, cbuf);
|
|
free_compbuf(cbuf);
|
|
|
|
} /* lets make only previews when not done yet, so activating doesnt update */
|
|
else if(in[0]->data && node->preview && node->preview->rect==NULL) {
|
|
CompBuf *cbuf, *inbuf= in[0]->data;
|
|
|
|
if(inbuf->type!=CB_RGBA) {
|
|
void (*func)(bNode *, float *, float *);
|
|
|
|
if(inbuf->type==CB_VEC3) func= do_copy_rgb;
|
|
else if(inbuf->type==CB_VEC2) func= do_copy_rg;
|
|
else func= do_copy_value;
|
|
|
|
cbuf= alloc_compbuf(inbuf->x, inbuf->y, CB_RGBA, 1);
|
|
composit1_pixel_processor(node, cbuf, inbuf, in[0]->vec, func);
|
|
generate_preview(node, cbuf);
|
|
free_compbuf(cbuf);
|
|
}
|
|
else
|
|
generate_preview(node, inbuf);
|
|
}
|
|
}
|
|
|
|
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 */ "",
|
|
/* execfunc */ node_composit_exec_viewer
|
|
|
|
};
|
|
|
|
/* **************** 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("Render"));
|
|
if(rr) {
|
|
CompBuf *outbuf, *zbuf=NULL;
|
|
|
|
if(rr->rectf)
|
|
MEM_freeT(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);
|
|
else
|
|
composit2_pixel_processor(node, outbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec, do_copy_a_rgba);
|
|
|
|
if(in[2]->data) {
|
|
if(rr->rectz)
|
|
MEM_freeT(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);
|
|
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);
|
|
|
|
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, "Alpha", 1.0f, 0.0f, 0.0f, 0.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(node->id && (node->flag & NODE_DO_OUTPUT)) { /* only one works on out */
|
|
}
|
|
else if(in[0]->data)
|
|
generate_preview(node, in[0]->data);
|
|
}
|
|
|
|
static bNodeType cmp_node_output_file= {
|
|
/* type code */ CMP_NODE_OUTPUT_FILE,
|
|
/* name */ "File Output",
|
|
/* width+range */ 80, 60, 200,
|
|
/* class+opts */ NODE_CLASS_FILE, NODE_PREVIEW,
|
|
/* input sock */ cmp_node_output_file_in,
|
|
/* output sock */ NULL,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_output_file
|
|
|
|
};
|
|
|
|
/* **************** IMAGE ******************** */
|
|
static bNodeSocketType cmp_node_image_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},
|
|
{ -1, 0, "" }
|
|
};
|
|
|
|
static int calcimanr(int cfra, NodeImageAnim *nia)
|
|
{
|
|
|
|
if(nia->frames==0) return nia->nr;
|
|
|
|
cfra= cfra - nia->sfra;
|
|
|
|
/* cyclic */
|
|
if(nia->cyclic)
|
|
cfra= (cfra % nia->frames);
|
|
else if(cfra>=nia->frames)
|
|
cfra= nia->frames-1;
|
|
else if(cfra<0)
|
|
cfra= 0;
|
|
|
|
cfra+= nia->nr;
|
|
|
|
if(cfra<1) cfra= 1;
|
|
|
|
return cfra;
|
|
}
|
|
|
|
|
|
static void animated_image(bNode *node, int cfra)
|
|
{
|
|
Image *ima;
|
|
NodeImageAnim *nia;
|
|
int imanr;
|
|
unsigned short numlen;
|
|
char name[FILE_MAXDIR+FILE_MAXFILE], head[FILE_MAXDIR+FILE_MAXFILE], tail[FILE_MAXDIR+FILE_MAXFILE];
|
|
|
|
ima= (Image *)node->id;
|
|
nia= node->storage;
|
|
|
|
if(nia && nia->frames && ima && ima->name) { /* frames */
|
|
strcpy(name, ima->name);
|
|
|
|
imanr= calcimanr(cfra, nia);
|
|
if(imanr!=ima->lastframe) {
|
|
ima->lastframe= imanr;
|
|
|
|
BLI_stringdec(name, head, tail, &numlen);
|
|
BLI_stringenc(name, head, tail, numlen, imanr);
|
|
|
|
ima= add_image(name);
|
|
|
|
if(ima) {
|
|
ima->flag |= IMA_FROMANIM;
|
|
if(node->id) node->id->us--;
|
|
node->id= (ID *)ima;
|
|
|
|
ima->ok= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static float *float_from_byte_rect(int rectx, int recty, char *rect)
|
|
{
|
|
/* quick method to convert byte to floatbuf */
|
|
float *rect_float= MEM_mallocT(4*sizeof(float)*rectx*recty, "float rect");
|
|
float *tof = rect_float;
|
|
int i;
|
|
|
|
for (i = rectx*recty; i > 0; i--) {
|
|
tof[0] = ((float)rect[0])*(1.0f/255.0f);
|
|
tof[1] = ((float)rect[1])*(1.0f/255.0f);
|
|
tof[2] = ((float)rect[2])*(1.0f/255.0f);
|
|
tof[3] = ((float)rect[3])*(1.0f/255.0f);
|
|
rect += 4;
|
|
tof += 4;
|
|
}
|
|
return rect_float;
|
|
}
|
|
|
|
|
|
|
|
static CompBuf *node_composit_get_image(bNode *node, RenderData *rd)
|
|
{
|
|
Image *ima;
|
|
CompBuf *stackbuf;
|
|
|
|
/* animated image? */
|
|
if(node->storage)
|
|
animated_image(node, rd->cfra);
|
|
|
|
ima= (Image *)node->id;
|
|
|
|
/* test if image is OK */
|
|
if(ima->ok==0) return NULL;
|
|
|
|
if(ima->ibuf==NULL) {
|
|
BLI_lock_thread();
|
|
load_image(ima, IB_rect, G.sce, rd->cfra); /* G.sce is current .blend path */
|
|
BLI_unlock_thread();
|
|
if(ima->ibuf==NULL) {
|
|
ima->ok= 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
if(ima->ibuf->rect_float==NULL) {
|
|
/* can't use imbuf module, we need secure malloc */
|
|
ima->ibuf->rect_float= float_from_byte_rect(ima->ibuf->x, ima->ibuf->y, (char *)ima->ibuf->rect);
|
|
ima->ibuf->mall |= IB_rectfloat;
|
|
}
|
|
|
|
if(rd->scemode & R_COMP_CROP) {
|
|
stackbuf= get_cropped_compbuf(&rd->disprect, ima->ibuf->rect_float, ima->ibuf->x, ima->ibuf->y, CB_RGBA);
|
|
}
|
|
else {
|
|
/* we put imbuf copy on stack, cbuf knows rect is from other ibuf when freed! */
|
|
stackbuf= alloc_compbuf(ima->ibuf->x, ima->ibuf->y, CB_RGBA, 0);
|
|
stackbuf->rect= ima->ibuf->rect_float;
|
|
}
|
|
|
|
return stackbuf;
|
|
}
|
|
|
|
static CompBuf *node_composit_get_zimage(bNode *node, RenderData *rd)
|
|
{
|
|
Image *ima= (Image *)node->id;
|
|
CompBuf *zbuf= NULL;
|
|
|
|
if(ima->ibuf && ima->ibuf->zbuf_float) {
|
|
if(rd->scemode & R_COMP_CROP) {
|
|
zbuf= get_cropped_compbuf(&rd->disprect, ima->ibuf->zbuf_float, ima->ibuf->x, ima->ibuf->y, CB_VAL);
|
|
}
|
|
else {
|
|
zbuf= alloc_compbuf(ima->ibuf->x, ima->ibuf->y, CB_VAL, 0);
|
|
zbuf->rect= ima->ibuf->zbuf_float;
|
|
}
|
|
}
|
|
return zbuf;
|
|
}
|
|
|
|
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) {
|
|
CompBuf *stackbuf= node_composit_get_image(node, data);
|
|
|
|
/* put ibuf on stack */
|
|
out[0]->data= stackbuf;
|
|
|
|
if(stackbuf) {
|
|
if(out[1]->hasoutput)
|
|
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
|
|
|
|
if(out[2]->hasoutput)
|
|
out[2]->data= node_composit_get_zimage(node, data);
|
|
|
|
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_GENERATOR, NODE_PREVIEW|NODE_OPTIONS,
|
|
/* input sock */ NULL,
|
|
/* output sock */ cmp_node_image_out,
|
|
/* storage */ "NodeImageAnim",
|
|
/* execfunc */ node_composit_exec_image
|
|
|
|
};
|
|
|
|
/* **************** RENDER RESULT ******************** */
|
|
|
|
/* output socket defines */
|
|
#define RRES_OUT_IMAGE 0
|
|
#define RRES_OUT_ALPHA 1
|
|
#define RRES_OUT_Z 2
|
|
#define RRES_OUT_NOR 3
|
|
#define RRES_OUT_VEC 4
|
|
#define RRES_OUT_COL 5
|
|
#define RRES_OUT_DIFF 6
|
|
#define RRES_OUT_SPEC 7
|
|
#define RRES_OUT_SHAD 8
|
|
#define RRES_OUT_AO 9
|
|
#define RRES_OUT_RAY 10
|
|
|
|
static bNodeSocketType cmp_node_rresult_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, "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, "Ray", 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
|
|
|
|
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(passcode==SCE_PASS_Z)
|
|
buftype= CB_VAL;
|
|
else if(passcode==SCE_PASS_VECTOR)
|
|
buftype= CB_VEC4;
|
|
else if(passcode==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;
|
|
}
|
|
|
|
static void node_composit_exec_rresult(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
RenderData *rd= data;
|
|
RenderResult *rr;
|
|
|
|
if(node->id && node->id!=&G.scene->id)
|
|
rr= RE_GetResult(RE_GetRender(node->id->name+2));
|
|
else
|
|
rr= RE_GetResult(RE_GetRender("Render"));
|
|
|
|
if(rr) {
|
|
RenderLayer *rl= BLI_findlink(&rr->layers, node->custom1);
|
|
if(rl) {
|
|
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;
|
|
}
|
|
|
|
/* 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);
|
|
if(out[RRES_OUT_Z]->hasoutput)
|
|
out[RRES_OUT_Z]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_Z);
|
|
if(out[RRES_OUT_VEC]->hasoutput)
|
|
out[RRES_OUT_VEC]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_VECTOR);
|
|
if(out[RRES_OUT_NOR]->hasoutput)
|
|
out[RRES_OUT_NOR]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_NORMAL);
|
|
/*
|
|
if(out[RRES_OUT_COL]->hasoutput)
|
|
out[RRES_OUT_COL]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_RGBA);
|
|
if(out[RRES_OUT_DIFF]->hasoutput)
|
|
out[RRES_OUT_DIFF]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_DIFFUSE);
|
|
if(out[RRES_OUT_SPEC]->hasoutput)
|
|
out[RRES_OUT_SPEC]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_SPEC);
|
|
if(out[RRES_OUT_SHAD]->hasoutput)
|
|
out[RRES_OUT_SHAD]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_SHADOW);
|
|
if(out[RRES_OUT_AO]->hasoutput)
|
|
out[RRES_OUT_AO]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_AO);
|
|
if(out[RRES_OUT_RAY]->hasoutput)
|
|
out[RRES_OUT_RAY]->data= compbuf_from_pass(rd, rl, rr->rectx, rr->recty, SCE_PASS_RAY);
|
|
*/
|
|
generate_preview(node, stackbuf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* custom1 = render layer in use */
|
|
static bNodeType cmp_node_rresult= {
|
|
/* type code */ CMP_NODE_R_RESULT,
|
|
/* name */ "Render Result",
|
|
/* width+range */ 120, 80, 300,
|
|
/* class+opts */ NODE_CLASS_GENERATOR, NODE_PREVIEW|NODE_OPTIONS,
|
|
/* input sock */ NULL,
|
|
/* output sock */ cmp_node_rresult_out,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_rresult
|
|
|
|
};
|
|
|
|
/* **************** 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, NULL, do_normal);
|
|
|
|
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_OPERATOR, NODE_OPTIONS,
|
|
/* input sock */ cmp_node_normal_in,
|
|
/* output sock */ cmp_node_normal_out,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_normal
|
|
|
|
};
|
|
|
|
/* **************** CURVE Time ******************** */
|
|
|
|
/* custom1 = sfra, custom2 = efra */
|
|
static bNodeSocketType cmp_node_time_out[]= {
|
|
{ SOCK_VALUE, 0, "Fac", 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
|
|
static void node_composit_exec_time(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
/* stack order output: fac */
|
|
float fac= 0.0f;
|
|
|
|
if(node->custom1 < node->custom2)
|
|
fac= (G.scene->r.cfra - node->custom1)/(float)(node->custom2-node->custom1);
|
|
|
|
out[0]->vec[0]= curvemapping_evaluateF(node->storage, 0, fac);
|
|
}
|
|
|
|
static bNodeType cmp_node_time= {
|
|
/* type code */ CMP_NODE_TIME,
|
|
/* name */ "Time",
|
|
/* width+range */ 140, 100, 320,
|
|
/* class+opts */ NODE_CLASS_GENERATOR, 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_OPERATOR, 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_evaluateRGBF(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_evaluateRGBF(node->storage, out, in);
|
|
else if(*fac<=0.0) {
|
|
VECCOPY(out, in);
|
|
}
|
|
else {
|
|
float col[4], mfac= 1.0f-*fac;
|
|
curvemapping_evaluateRGBF(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: vec */
|
|
/* stack order output: vec */
|
|
|
|
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);
|
|
else
|
|
composit1_pixel_processor(node, stackbuf, in[1]->data, NULL, do_curves);
|
|
|
|
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_OPERATOR, 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_GENERATOR, 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_GENERATOR, NODE_OPTIONS,
|
|
/* input sock */ NULL,
|
|
/* output sock */ cmp_node_rgb_out,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_rgb
|
|
|
|
};
|
|
|
|
/* **************** MIX RGB ******************** */
|
|
static bNodeSocketType cmp_node_mix_rgb_in[]= {
|
|
{ SOCK_VALUE, 1, "Fac", 0.5f, 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},
|
|
{ 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);
|
|
ramp_blend(node->custom1, col, col+1, col+2, fac, 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[0];
|
|
|
|
CLAMP(fac, 0.0f, 1.0f);
|
|
|
|
/* 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);
|
|
|
|
out[0]->data= stackbuf;
|
|
}
|
|
}
|
|
|
|
/* custom1 = mix type */
|
|
static bNodeType cmp_node_mix_rgb= {
|
|
/* type code */ CMP_NODE_MIX_RGB,
|
|
/* name */ "Mix",
|
|
/* width+range */ 80, 40, 120,
|
|
/* class+opts */ NODE_CLASS_OPERATOR, 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;
|
|
|
|
rowlen= in->x;
|
|
|
|
if(in->type==CB_RGBA) {
|
|
|
|
for(y=2; y<in->y; y++) {
|
|
/* setup rows */
|
|
row1= in->rect + 4*(y-2)*rowlen;
|
|
row2= row1 + 4*rowlen;
|
|
row3= row2 + 4*rowlen;
|
|
fp= out->rect + 4*(y-1)*rowlen + 4;
|
|
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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=2; y<in->y; y++) {
|
|
/* setup rows */
|
|
row1= in->rect + pixlen*(y-2)*rowlen;
|
|
row2= row1 + pixlen*rowlen;
|
|
row3= row2 + pixlen*rowlen;
|
|
|
|
fp= out->rect + pixlen*(y-1)*rowlen;
|
|
QUATCOPY(fp, row2);
|
|
fp+= pixlen;
|
|
|
|
for(x=2; x<rowlen; x++) {
|
|
for(c=0; c<pixlen; 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++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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};
|
|
|
|
static void node_composit_exec_filter(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
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};
|
|
|
|
/* 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, CB_RGBA, 1); // allocs
|
|
|
|
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_OPERATOR, 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(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, NULL, do_colorband_composit);
|
|
|
|
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_OPERATOR, 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 */
|
|
|
|
/* input no image? then only color operation */
|
|
if(in[0]->data==NULL) {
|
|
do_rgbtobw(node, out[0], in[0]);
|
|
}
|
|
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, NULL, do_rgbtobw);
|
|
|
|
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_OPERATOR, 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 output size of input image */
|
|
CompBuf *cbuf= in[0]->data;
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
|
|
static bNodeType cmp_node_seprgba= {
|
|
/* type code */ CMP_NODE_SEPRGBA,
|
|
/* name */ "Separate RGBA",
|
|
/* width+range */ 80, 40, 140,
|
|
/* class+opts */ NODE_CLASS_OPERATOR, 0,
|
|
/* input sock */ cmp_node_seprgba_in,
|
|
/* output sock */ cmp_node_seprgba_out,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_seprgba
|
|
|
|
};
|
|
|
|
/* **************** SEPARATE HSVA ******************** */
|
|
static bNodeSocketType cmp_node_sephsva_in[]= {
|
|
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
static bNodeSocketType cmp_node_sephsva_out[]= {
|
|
{ SOCK_VALUE, 0, "H", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{ SOCK_VALUE, 0, "S", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{ SOCK_VALUE, 0, "V", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{ SOCK_VALUE, 0, "A", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{ -1, 0, "" }
|
|
};
|
|
|
|
static void do_sephsva(bNode *node, float *out, float *in)
|
|
{
|
|
float h, s, v;
|
|
|
|
rgb_to_hsv(in[0], in[1], in[2], &h, &s, &v);
|
|
|
|
out[0]= h;
|
|
out[1]= s;
|
|
out[2]= v;
|
|
out[3]= in[3];
|
|
}
|
|
|
|
static void node_composit_exec_sephsva(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
|
|
{
|
|
/* stack order out: bw channels */
|
|
/* stack order in: col */
|
|
|
|
/* input no image? then only color operation */
|
|
if(in[0]->data==NULL) {
|
|
float h, s, v;
|
|
|
|
rgb_to_hsv(in[0]->vec[0], in[0]->vec[1], in[0]->vec[2], &h, &s, &v);
|
|
|
|
out[0]->vec[0] = h;
|
|
out[1]->vec[0] = s;
|
|
out[2]->vec[0] = v;
|
|
out[3]->vec[0] = in[0]->vec[3];
|
|
}
|
|
else if ((out[0]->hasoutput) || (out[1]->hasoutput) || (out[2]->hasoutput) || (out[3]->hasoutput)) {
|
|
/* make output size of input image */
|
|
CompBuf *cbuf= in[0]->data;
|
|
|
|
CompBuf *stackbuf= alloc_compbuf(cbuf->x, cbuf->y, CB_RGBA, 1); // allocs
|
|
|
|
/* convert the RGB stackbuf to an HSV representation */
|
|
composit1_pixel_processor(node, stackbuf, in[0]->data, NULL, do_sephsva);
|
|
|
|
/* separate each of those channels */
|
|
if(out[0]->hasoutput)
|
|
out[0]->data= valbuf_from_rgbabuf(stackbuf, CHAN_R);
|
|
if(out[1]->hasoutput)
|
|
out[1]->data= valbuf_from_rgbabuf(stackbuf, CHAN_G);
|
|
if(out[2]->hasoutput)
|
|
out[2]->data= valbuf_from_rgbabuf(stackbuf, CHAN_B);
|
|
if(out[3]->hasoutput)
|
|
out[3]->data= valbuf_from_rgbabuf(stackbuf, CHAN_A);
|
|
|
|
free_compbuf(stackbuf);
|
|
}
|
|
}
|
|
|
|
static bNodeType cmp_node_sephsva= {
|
|
/* type code */ CMP_NODE_SEPHSVA,
|
|
/* name */ "Separate HSVA",
|
|
/* width+range */ 80, 40, 140,
|
|
/* class+opts */ NODE_CLASS_OPERATOR, 0,
|
|
/* input sock */ cmp_node_sephsva_in,
|
|
/* output sock */ cmp_node_sephsva_out,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_sephsva
|
|
|
|
};
|
|
|
|
/* **************** 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]->vec[0]==1.0f) {
|
|
/* pass on image */
|
|
composit1_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, do_copy_rgb);
|
|
}
|
|
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);
|
|
}
|
|
|
|
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_OPERATOR, 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_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(bNode *node, float *out, float *src, float *over)
|
|
{
|
|
|
|
if(over[3]<=0.0f) {
|
|
QUATCOPY(out, src);
|
|
}
|
|
else if(over[3]>=1.0f) {
|
|
QUATCOPY(out, over);
|
|
}
|
|
else {
|
|
float mul= 1.0f - over[3];
|
|
|
|
/* handle case where backdrop has no alpha, but still color */
|
|
if(src[0]==0.0f) {
|
|
out[0]= over[0];
|
|
out[1]= (mul*src[1]) + over[1];
|
|
out[2]= (mul*src[2]) + over[2];
|
|
out[3]= (mul*src[3]) + over[3];
|
|
}
|
|
else {
|
|
out[0]= (mul*src[0]) + over[0];
|
|
out[1]= (mul*src[1]) + over[1];
|
|
out[2]= (mul*src[2]) + over[2];
|
|
out[3]= (mul*src[3]) + 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 */
|
|
|
|
/* input no image? then only color operation */
|
|
if(in[0]->data==NULL) {
|
|
do_alphaover(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_RGBA, 1); // allocs
|
|
|
|
composit2_pixel_processor(node, stackbuf, in[0]->data, in[0]->vec, in[1]->data, in[1]->vec, do_alphaover);
|
|
|
|
out[0]->data= stackbuf;
|
|
}
|
|
}
|
|
|
|
static bNodeType cmp_node_alphaover= {
|
|
/* type code */ CMP_NODE_ALPHAOVER,
|
|
/* name */ "AlphaOver",
|
|
/* width+range */ 80, 40, 120,
|
|
/* class+opts */ NODE_CLASS_OPERATOR, 0,
|
|
/* input sock */ cmp_node_alphaover_in,
|
|
/* output sock */ cmp_node_alphaover_out,
|
|
/* storage */ "",
|
|
/* execfunc */ node_composit_exec_alphaover
|
|
|
|
};
|
|
|
|
/* **************** 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: col col */
|
|
/* stack order out: col */
|
|
|
|
/* 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);
|
|
|
|
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_OPERATOR, 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_mallocT(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_mallocT(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;
|
|
}
|
|
|
|
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_freeT(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_freeT(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);
|
|
// memset(wbuf->rect, sizeof(float)*imgx*imgy, 0);
|
|
|
|
/* 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_mallocT(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;
|
|
|
|
// memset(new->rect, 4*imgx*imgy, 0);
|
|
|
|
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_freeT(maintabs[i]);
|
|
MEM_freeT(maintabs);
|
|
|
|
}
|
|
|
|
static void gamma_correct_compbuf(CompBuf *img, int inversed)
|
|
{
|
|
float *drect;
|
|
int x;
|
|
|
|
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 */
|
|
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;
|
|
int i, j;
|
|
float *src, *dest, *srcd;
|
|
|
|
/* horizontal */
|
|
radx = fac*(float)nbd->sizex;
|
|
if(radx>imgx/2)
|
|
radx= imgx/2;
|
|
else if(radx<1)
|
|
radx= 1;
|
|
|
|
/* vertical */
|
|
rady = fac*(float)nbd->sizey;
|
|
if(rady>imgy/2)
|
|
rady= imgy/2;
|
|
else if(rady<1)
|
|
rady= 1;
|
|
|
|
radxf= (float)radx;
|
|
radyf= (float)rady;
|
|
|
|
/* create a full filter image */
|
|
gausstab= MEM_mallocT(sizeof(float)*radx*rady*4, "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;
|
|
}
|
|
}
|
|
val= 1.0f/val;
|
|
for(j= 4*radx*rady -1; j>=0; j--)
|
|
gausstab[j]*= val;
|
|
|
|
// memset(new->rect, 4*imgx*imgy, 0);
|
|
|
|
for (y = -rady+1; y < imgy+rady-1; y++) {
|
|
|
|
if(y<=0) srcd= img->rect;
|
|
else if(y<imgy) srcd+= 4*imgx;
|
|
else srcd= img->rect + 4*(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:radx;
|
|
int minyr= y-rady<0?-y:-rady;
|
|
int maxyr= y+rady>imgy?imgy-y:rady;
|
|
|
|
float *destd= new->rect + 4*( (y + minyr)*imgx + x + minxr);
|
|
float *dgausd= gausstab + (minyr+rady)*2*radx + minxr+radx;
|
|
|
|
if(x<=0) src= srcd;
|
|
else if(x<imgx) src+= 4;
|
|
else src= srcd + 4*(imgx-1);
|
|
|
|
for (i= minyr; i < maxyr; i++, destd+= 4*imgx, dgausd+= 2*radx) {
|
|
dest= destd;
|
|
dgauss= dgausd;
|
|
for (j= minxr; j < maxxr; j++, dest+=4, dgauss++) {
|
|
val= *dgauss;
|
|
if(val!=0.0f) {
|
|
dest[0] += val * src[0];
|
|
dest[1] += val * src[1];
|
|
dest[2] += val * src[2];
|
|
dest[3] += val * src[3];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MEM_freeT(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;
|
|
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;
|
|
int i, j;
|
|
float *src, *dest, *refd, *blurd;
|
|
|
|
/* 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->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_mallocT(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+=4, 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 + 4*( y*imgx + x);
|
|
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 + 4*( (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+= 4*imgx) {
|
|
src= srcd;
|
|
for (j= minxr; j < maxxr; j++, src+=4) {
|
|
|
|
val= gausstabcenty[i]*gausstabcentx[j];
|
|
sum+= val;
|
|
rval += val * src[0];
|
|
gval += val * src[1];
|
|
bval += val * src[2];
|
|
aval += val * src[3];
|
|
}
|
|
}
|
|
sum= 1.0f/sum;
|
|
dest[0] = rval*sum;
|
|
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_freeT(maintabs[i]);
|
|
MEM_freeT(maintabs);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 fac input, we do it different */
|
|
if(in[1]->data) {
|
|
|
|
/* test fitness if reference */
|
|
|
|
/* make output size of input image */
|
|
new= alloc_compbuf(img->x, img->y, CB_RGBA, 1); // allocs
|
|
|
|
blur_with_reference(new, img, in[1]->data, node->storage);
|
|
|
|
out[0]->data= new;
|
|
}
|
|
else {
|
|
if(in[1]->vec[0]==0.0f) {
|
|
/* pass on image */
|
|
new= alloc_compbuf(img->x, img->y, img->type, 0);
|
|
new->rect= img->rect;
|
|
}
|
|
else {
|
|
NodeBlurData *nbd= node->storage;
|
|
CompBuf *gammabuf;
|
|
|
|
/* make output size of input image */
|
|
new= alloc_compbuf(img->x, img->y, img->type, 1); // allocs
|
|
|
|
if(nbd->gamma) {
|
|
gammabuf= dupalloc_compbuf(img);
|
|
gamma_correct_compbuf(gammabuf, 0);
|
|
}
|
|
else gammabuf= img;
|
|
|
|
if(nbd->bokeh)
|
|
bokeh_single_image(new, gammabuf, in[1]->vec[0], nbd);
|
|
else if(1)
|
|
blur_single_image(new, gammabuf, in[1]->vec[0], nbd);
|
|
else /* bloom experimental... */
|
|
bloom_with_reference(new, gammabuf, NULL, in[1]->vec[0], nbd);
|
|
|
|
if(nbd->gamma) {
|
|
gamma_correct_compbuf(new, 1);
|
|
free_compbuf(gammabuf);
|
|
}
|
|
}
|
|
out[0]->data= new;
|
|
}
|
|
}
|
|
|
|
|
|
static bNodeType cmp_node_blur= {
|
|
/* type code */ CMP_NODE_BLUR,
|
|
/* name */ "Blur",
|
|
/* width+range */ 120, 80, 200,
|
|
/* class+opts */ NODE_CLASS_OPERATOR, NODE_OPTIONS,
|
|
/* input sock */ cmp_node_blur_in,
|
|
/* output sock */ cmp_node_blur_out,
|
|
/* storage */ "NodeBlurData",
|
|
/* execfunc */ node_composit_exec_blur
|
|
|
|
};
|
|
|
|
/* **************** 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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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_OPERATOR, NODE_OPTIONS,
|
|
/* input sock */ cmp_node_vecblur_in,
|
|
/* output sock */ cmp_node_vecblur_out,
|
|
/* storage */ "NodeBlurData",
|
|
/* execfunc */ node_composit_exec_vecblur
|
|
|
|
};
|
|
|
|
|
|
/* ****************** types array for all shaders ****************** */
|
|
|
|
bNodeType *node_all_composit[]= {
|
|
&node_group_typeinfo,
|
|
&cmp_node_viewer,
|
|
&cmp_node_composite,
|
|
&cmp_node_output_file,
|
|
&cmp_node_value,
|
|
&cmp_node_rgb,
|
|
&cmp_node_mix_rgb,
|
|
&cmp_node_filter,
|
|
&cmp_node_valtorgb,
|
|
&cmp_node_rgbtobw,
|
|
&cmp_node_normal,
|
|
&cmp_node_curve_vec,
|
|
&cmp_node_curve_rgb,
|
|
&cmp_node_time,
|
|
&cmp_node_image,
|
|
&cmp_node_rresult,
|
|
&cmp_node_alphaover,
|
|
&cmp_node_blur,
|
|
&cmp_node_vecblur,
|
|
&cmp_node_map_value,
|
|
&cmp_node_seprgba,
|
|
&cmp_node_sephsva,
|
|
&cmp_node_setalpha,
|
|
NULL
|
|
};
|
|
|
|
/* ******************* parse ************ */
|
|
|
|
|
|
/* called from render pipeline, to tag render input and output */
|
|
void ntreeCompositTagRender(bNodeTree *ntree)
|
|
{
|
|
bNode *node;
|
|
|
|
if(ntree==NULL) return;
|
|
|
|
for(node= ntree->nodes.first; node; node= node->next) {
|
|
if( ELEM(node->type, CMP_NODE_R_RESULT, CMP_NODE_COMPOSITE))
|
|
NodeTagChanged(ntree, 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) {
|
|
/* no actual test yet... */
|
|
if(node->storage)
|
|
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_RESULT, CMP_NODE_IMAGE))
|
|
NodeTagChanged(ntree, node);
|
|
}
|
|
}
|
|
|
|
|