This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/nodes/intern/CMP_nodes/CMP_glare.c
Matt Ebb e26b5aa9c4 * fix for [#8085] Glare node crashes on inputs with < 4 colour channels
I had this assigned to Alfredo for a while, but he hasn't replied to the tracker at all, so I suspect he's not around. I'll commit this now to prevent crashes.

Some of the code in the glare node assumed that all buffers will be 4 channel RGBA, when in fact it was possible to give it a VEC3, such as a spec pass with no alpha, which would crash it. This fix just duplicates the input to a new temporary RGBA buffer to work on, if it's not already RGBA.
2008-02-13 13:36:35 +00:00

503 lines
14 KiB
C

/**
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2006 Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): Alfredo de Greef (eeshlo)
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "../CMP_util.h"
static bNodeSocketType cmp_node_glare_in[]= {
{ SOCK_RGBA, 1, "Image", 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
static bNodeSocketType cmp_node_glare_out[]= {
{ SOCK_RGBA, 0, "Image", 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
{ -1, 0, "" }
};
// mix two images, src buffer does not have to be same size,
static void mixImages(CompBuf *dst, CompBuf *src, float mix)
{
int x, y;
fRGB c1, c2, *dcolp, *scolp;
const float mf = 2.f - 2.f*fabsf(mix - 0.5f);
if ((dst->x == src->x) && (dst->y == src->y)) {
for (y=0; y<dst->y; y++) {
dcolp = (fRGB*)&dst->rect[y*dst->x*dst->type];
scolp = (fRGB*)&src->rect[y*dst->x*dst->type];
for (x=0; x<dst->x; x++) {
fRGB_copy(c1, dcolp[x]);
fRGB_copy(c2, scolp[x]);
c1[0] += mix*(c2[0] - c1[0]);
c1[1] += mix*(c2[1] - c1[1]);
c1[2] += mix*(c2[2] - c1[2]);
if (c1[0] < 0.f) c1[0] = 0.f;
if (c1[1] < 0.f) c1[1] = 0.f;
if (c1[2] < 0.f) c1[2] = 0.f;
fRGB_mult(c1, mf);
fRGB_copy(dcolp[x], c1);
}
}
}
else {
float xr = src->x / (float)dst->x;
float yr = src->y / (float)dst->y;
for (y=0; y<dst->y; y++) {
dcolp = (fRGB*)&dst->rect[y*dst->x*dst->type];
for (x=0; x<dst->x; x++) {
fRGB_copy(c1, dcolp[x]);
qd_getPixelLerp(src, (x + 0.5f)*xr - 0.5f, (y + 0.5f)*yr - 0.5f, c2);
c1[0] += mix*(c2[0] - c1[0]);
c1[1] += mix*(c2[1] - c1[1]);
c1[2] += mix*(c2[2] - c1[2]);
if (c1[0] < 0.f) c1[0] = 0.f;
if (c1[1] < 0.f) c1[1] = 0.f;
if (c1[2] < 0.f) c1[2] = 0.f;
fRGB_mult(c1, mf);
fRGB_copy(dcolp[x], c1);
}
}
}
}
// adds src to dst image, must be of same size
static void addImage(CompBuf* dst, CompBuf* src, float scale)
{
if ((dst->x == src->x) && (dst->y == src->y)) {
int p = dst->x*dst->y*dst->type;
float *dcol = dst->rect, *scol = src->rect;
while (p--) *dcol++ += *scol++ * scale;
}
}
// returns possibly downscaled copy of all pixels above threshold
static CompBuf* BTP(CompBuf* src, float threshold, int scaledown)
{
int x, y;
CompBuf* bsrc = qd_downScaledCopy(src, scaledown);
float* cr = bsrc->rect;
for (y=0; y<bsrc->y; ++y)
for (x=0; x<bsrc->x; ++x, cr+=4) {
if ((0.212671f*cr[0] + 0.71516f*cr[1] + 0.072169f*cr[2]) >= threshold) {
cr[0] -= threshold, cr[1] -= threshold, cr[2] -= threshold;
cr[0] = MAX2(cr[0], 0.f);
cr[1] = MAX2(cr[1], 0.f);
cr[2] = MAX2(cr[2], 0.f);
}
else cr[0] = cr[1] = cr[2] = 0.f;
}
return bsrc;
}
//--------------------------------------------------------------------------------------------
// simple 4-point star filter
static void star4(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
int x, y, i, xm, xp, ym, yp;
float c[4] = {0,0,0,0}, tc[4] = {0,0,0,0};
CompBuf *tbuf1, *tbuf2, *tsrc;
const float f1 = 1.f - ndg->fade, f2 = (1.f - f1)*0.5f;
//const float t3 = ndg->threshold*3.f;
const float sc = (float)(1 << ndg->quality);
const float isc = 1.f/sc;
tsrc = BTP(src, ndg->threshold, (int)sc);
tbuf1 = dupalloc_compbuf(tsrc);
tbuf2 = dupalloc_compbuf(tsrc);
for (i=0; i<ndg->iter; i++) {
// (x || x-1, y-1) to (x || x+1, y+1)
// F
for (y=0; y<tbuf1->y; y++) {
ym = y - i;
yp = y + i;
for (x=0; x<tbuf1->x; x++) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf1, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf1, (ndg->angle ? xm : x), ym, tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf1, (ndg->angle ? xp : x), yp, tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf1, x, y, c);
}
}
// B
for (y=tbuf1->y-1; y>=0; y--) {
ym = y - i;
yp = y + i;
for (x=tbuf1->x-1; x>=0; x--) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf1, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf1, (ndg->angle ? xm : x), ym, tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf1, (ndg->angle ? xp : x), yp, tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf1, x, y, c);
}
}
// (x-1, y || y+1) to (x+1, y || y-1)
// F
for (y=0; y<tbuf2->y; y++) {
ym = y - i;
yp = y + i;
for (x=0; x<tbuf2->x; x++) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf2, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf2, xm, (ndg->angle ? yp : y), tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf2, xp, (ndg->angle ? ym : y), tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf2, x, y, c);
}
}
// B
for (y=tbuf2->y-1; y>=0; y--) {
ym = y - i;
yp = y + i;
for (x=tbuf2->x-1; x>=0; x--) {
xm = x - i;
xp = x + i;
qd_getPixel(tbuf2, x, y, c);
fRGB_mult(c, f1);
qd_getPixel(tbuf2, xm, (ndg->angle ? yp : y), tc);
fRGB_madd(c, tc, f2);
qd_getPixel(tbuf2, xp, (ndg->angle ? ym : y), tc);
fRGB_madd(c, tc, f2);
qd_setPixel(tbuf2, x, y, c);
}
}
}
for (y=0; y<tbuf1->y; ++y)
for (x=0; x<tbuf1->x; ++x) {
unsigned int p = (x + y*tbuf1->x)*tbuf1->type;
tbuf1->rect[p] += tbuf2->rect[p];
tbuf1->rect[p+1] += tbuf2->rect[p+1];
tbuf1->rect[p+2] += tbuf2->rect[p+2];
}
for (y=0; y<dst->y; ++y) {
const float m = 0.5f + 0.5f*ndg->mix;
for (x=0; x<dst->x; ++x) {
unsigned int p = (x + y*dst->x)*dst->type;
qd_getPixelLerp(tbuf1, x*isc, y*isc, tc);
dst->rect[p] = src->rect[p] + m*(tc[0] - src->rect[p]);
dst->rect[p+1] = src->rect[p+1] + m*(tc[1] - src->rect[p+1]);
dst->rect[p+2] = src->rect[p+2] + m*(tc[2] - src->rect[p+2]);
}
}
free_compbuf(tbuf1);
free_compbuf(tbuf2);
free_compbuf(tsrc);
}
//--------------------------------------------------------------------------------------------
// streak filter
static void streaks(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
CompBuf *bsrc, *tsrc, *tdst, *sbuf;
int x, y, n;
unsigned int nump=0;
fRGB c1, c2, c3, c4;
float a, ang = 360.f/(float)ndg->angle;
bsrc = BTP(src, ndg->threshold, 1 << ndg->quality);
tsrc = dupalloc_compbuf(bsrc); // sample from buffer
tdst = alloc_compbuf(tsrc->x, tsrc->y, tsrc->type, 1); // sample to buffer
sbuf = alloc_compbuf(tsrc->x, tsrc->y, tsrc->type, 1); // streak sum buffer
for (a=0.f; a<360.f; a+=ang) {
const float an = (a + (float)ndg->angle_ofs)*(float)M_PI/180.f;
const float vx = cos((double)an), vy = sin((double)an);
for (n=0; n<ndg->iter; ++n) {
const float p4 = pow(4.0, (double)n);
const float vxp = vx*p4, vyp = vy*p4;
const float wt = pow((double)ndg->fade, (double)p4);
const float cmo = 1.f - pow((double)ndg->colmod, (double)n+1); // colormodulation amount relative to current pass
float* tdstcol = tdst->rect;
for (y=0; y<tsrc->y; ++y) {
for (x=0; x<tsrc->x; ++x, tdstcol+=4) {
// first pass no offset, always same for every pass, exact copy,
// otherwise results in uneven brightness, only need once
if (n==0) qd_getPixel(tsrc, x, y, c1); else c1[0]=c1[1]=c1[2]=0;
qd_getPixelLerp(tsrc, x + vxp, y + vyp, c2);
qd_getPixelLerp(tsrc, x + vxp*2.f, y + vyp*2.f, c3);
qd_getPixelLerp(tsrc, x + vxp*3.f, y + vyp*3.f, c4);
// modulate color to look vaguely similar to a color spectrum
fRGB_rgbmult(c2, 1.f, cmo, cmo);
fRGB_rgbmult(c3, cmo, cmo, 1.f);
fRGB_rgbmult(c4, cmo, 1.f, cmo);
tdstcol[0] = 0.5f*(tdstcol[0] + c1[0] + wt*(c2[0] + wt*(c3[0] + wt*c4[0])));
tdstcol[1] = 0.5f*(tdstcol[1] + c1[1] + wt*(c2[1] + wt*(c3[1] + wt*c4[1])));
tdstcol[2] = 0.5f*(tdstcol[2] + c1[2] + wt*(c2[2] + wt*(c3[2] + wt*c4[2])));
}
}
memcpy(tsrc->rect, tdst->rect, sizeof(float)*tdst->x*tdst->y*tdst->type);
}
addImage(sbuf, tsrc, 1.f/(float)(6 - ndg->iter));
memset(tdst->rect, 0, tdst->x*tdst->y*tdst->type*sizeof(float));
memcpy(tsrc->rect, bsrc->rect, bsrc->x*bsrc->y*bsrc->type*sizeof(float));
nump++;
}
mixImages(dst, sbuf, 0.5f + 0.5f*ndg->mix);
free_compbuf(tsrc);
free_compbuf(tdst);
free_compbuf(sbuf);
free_compbuf(bsrc);
}
//--------------------------------------------------------------------------------------------
// Ghosts (lensflare)
static float smoothMask(float x, float y)
{
float t;
x = 2.f*x - 1.f, y = 2.f*y - 1.f;
if ((t = 1.f - sqrtf(x*x + y*y)) <= 0.f) return 0.f;
return t;
}
static void ghosts(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
// colormodulation and scale factors (cm & scalef) for 16 passes max: 64
int x, y, n, p, np;
fRGB c, tc, cm[64];
float sc, isc, u, v, sm, s, t, ofs, scalef[64];
CompBuf *tbuf1, *tbuf2, *gbuf;
const float cmo = 1.f - ndg->colmod;
const int qt = 1 << ndg->quality;
const float s1 = 4.f/(float)qt, s2 = 2.f*s1;
gbuf = BTP(src, ndg->threshold, qt);
tbuf1 = dupalloc_compbuf(gbuf);
IIR_gauss(tbuf1, s1, 0, 3);
IIR_gauss(tbuf1, s1, 1, 3);
IIR_gauss(tbuf1, s1, 2, 3);
tbuf2 = dupalloc_compbuf(tbuf1);
IIR_gauss(tbuf2, s2, 0, 3);
IIR_gauss(tbuf2, s2, 1, 3);
IIR_gauss(tbuf2, s2, 2, 3);
if (ndg->iter & 1) ofs = 0.5f; else ofs = 0.f;
for (x=0; x<(ndg->iter*4); x++) {
y = x & 3;
cm[x][0] = cm[x][1] = cm[x][2] = 1;
if (y==1) fRGB_rgbmult(cm[x], 1.f, cmo, cmo);
if (y==2) fRGB_rgbmult(cm[x], cmo, cmo, 1.f);
if (y==3) fRGB_rgbmult(cm[x], cmo, 1.f, cmo);
scalef[x] = 2.1f*(1.f-(x+ofs)/(float)(ndg->iter*4));
if (x & 1) scalef[x] = -0.99f/scalef[x];
}
sc = 2.13;
isc = -0.97;
for (y=0; y<gbuf->y; y++) {
v = (float)(y+0.5f) / (float)gbuf->y;
for (x=0; x<gbuf->x; x++) {
u = (float)(x+0.5f) / (float)gbuf->x;
s = (u-0.5f)*sc + 0.5f, t = (v-0.5f)*sc + 0.5f;
qd_getPixelLerp(tbuf1, s*gbuf->x, t*gbuf->y, c);
sm = smoothMask(s, t);
fRGB_mult(c, sm);
s = (u-0.5f)*isc + 0.5f, t = (v-0.5f)*isc + 0.5f;
qd_getPixelLerp(tbuf2, s*gbuf->x - 0.5f, t*gbuf->y - 0.5f, tc);
sm = smoothMask(s, t);
fRGB_madd(c, tc, sm);
qd_setPixel(gbuf, x, y, c);
}
}
memset(tbuf1->rect, 0, tbuf1->x*tbuf1->y*tbuf1->type*sizeof(float));
for (n=1; n<ndg->iter; n++) {
for (y=0; y<gbuf->y; y++) {
v = (float)(y+0.5f) / (float)gbuf->y;
for (x=0; x<gbuf->x; x++) {
u = (float)(x+0.5f) / (float)gbuf->x;
tc[0] = tc[1] = tc[2] = 0.f;
for (p=0;p<4;p++) {
np = (n<<2) + p;
s = (u-0.5f)*scalef[np] + 0.5f;
t = (v-0.5f)*scalef[np] + 0.5f;
qd_getPixelLerp(gbuf, s*gbuf->x - 0.5f, t*gbuf->y - 0.5f, c);
fRGB_colormult(c, cm[np]);
sm = smoothMask(s, t)*0.25f;
fRGB_madd(tc, c, sm);
}
p = (x + y*tbuf1->x)*tbuf1->type;
tbuf1->rect[p] += tc[0];
tbuf1->rect[p+1] += tc[1];
tbuf1->rect[p+2] += tc[2];
}
}
memcpy(gbuf->rect, tbuf1->rect, tbuf1->x*tbuf1->y*tbuf1->type*sizeof(float));
}
free_compbuf(tbuf1);
free_compbuf(tbuf2);
mixImages(dst, gbuf, 0.5f + 0.5f*ndg->mix);
free_compbuf(gbuf);
}
//--------------------------------------------------------------------------------------------
// Fog glow (convolution with kernel of exponential falloff)
static void fglow(NodeGlare* ndg, CompBuf* dst, CompBuf* src)
{
int x, y;
float scale, u, v, r, w, d;
fRGB fcol;
CompBuf *tsrc, *ckrn;
unsigned int sz = 1 << ndg->size;
const float cs_r = 1.f, cs_g = 1.f, cs_b = 1.f;
// temp. src image
tsrc = BTP(src, ndg->threshold, 1 << ndg->quality);
// make the convolution kernel
ckrn = alloc_compbuf(sz, sz, CB_RGBA, 1);
scale = 0.25f*sqrtf(sz*sz);
for (y=0; y<sz; ++y) {
v = 2.f*(y / (float)sz) - 1.f;
for (x=0; x<sz; ++x) {
u = 2.f*(x / (float)sz) - 1.f;
r = (u*u + v*v)*scale;
d = -sqrtf(sqrtf(sqrtf(r)))*9.f;
fcol[0] = expf(d*cs_r), fcol[1] = expf(d*cs_g), fcol[2] = expf(d*cs_b);
// linear window good enough here, visual result counts, not scientific analysis
//w = (1.f-fabs(u))*(1.f-fabs(v));
// actually, Hanning window is ok, cos^2 for some reason is slower
w = (0.5f + 0.5f*cos((double)u*M_PI))*(0.5f + 0.5f*cos((double)v*M_PI));
fRGB_mult(fcol, w);
qd_setPixel(ckrn, x, y, fcol);
}
}
convolve(tsrc, tsrc, ckrn);
free_compbuf(ckrn);
mixImages(dst, tsrc, 0.5f + 0.5f*ndg->mix);
free_compbuf(tsrc);
}
//--------------------------------------------------------------------------------------------
static void node_composit_exec_glare(void *data, bNode *node, bNodeStack **in, bNodeStack **out)
{
CompBuf *new, *src, *img = in[0]->data;
NodeGlare* ndg = node->storage;
if ((img == NULL) || (out[0]->hasoutput == 0)) return;
if (img->type != CB_RGBA) {
new = typecheck_compbuf(img, CB_RGBA);
src = typecheck_compbuf(img, CB_RGBA);
} else {
new = dupalloc_compbuf(img);
src = dupalloc_compbuf(img);
}
{
int x, y;
for (y=0; y<new->y; ++y) {
fRGB* col = (fRGB*)&new->rect[y*new->x*new->type];
for (x=0; x<new->x; ++x) {
col[x][0] = MAX2(col[x][0], 0.f);
col[x][1] = MAX2(col[x][1], 0.f);
col[x][2] = MAX2(col[x][2], 0.f);
}
}
}
switch (ndg->type) {
case 0:
star4(ndg, new, src);
break;
case 1:
fglow(ndg, new, src);
break;
case 3:
ghosts(ndg, new, src);
break;
case 2:
default:
streaks(ndg, new, src);
}
free_compbuf(src);
out[0]->data = new;
}
static void node_composit_init_glare(bNode* node)
{
NodeGlare *ndg = MEM_callocN(sizeof(NodeGlare), "node glare data");
ndg->quality = 1;
ndg->type = 2;
ndg->iter = 3;
ndg->colmod = 0.25;
ndg->mix = 0;
ndg->threshold = 1;
ndg->angle = 4;
ndg->angle_ofs = 0;
ndg->fade = 0.9;
ndg->size = 8;
node->storage = ndg;
}
bNodeType cmp_node_glare = {
/* *next,*prev */ NULL, NULL,
/* type code */ CMP_NODE_GLARE,
/* name */ "Glare",
/* width+range */ 150, 120, 200,
/* class+opts */ NODE_CLASS_OP_FILTER, NODE_OPTIONS,
/* input sock */ cmp_node_glare_in,
/* output sock */ cmp_node_glare_out,
/* storage */ "NodeGlare",
/* execfunc */ node_composit_exec_glare,
/* butfunc */ NULL,
/* initfunc */ node_composit_init_glare,
/* freestoragefunc */ node_free_standard_storage,
/* copystoragefunc */ node_copy_standard_storage,
/* id */ NULL
};