static void tonemap(NodeTonemap* ntm, CompBuf* dst, CompBuf* src) { int x, y; float dr, dg, db, al, igm = (ntm->gamma==0.f) ? 1 : (1.f / ntm->gamma); float auto_key, Lav, Cav[3] = {0, 0, 0}; al = avgLogLum(src, &auto_key, &Lav, Cav); al = (al == 0.f) ? 0.f : (ntm->key / al); if (ntm->type == 1) { // Reinhard/Devlin photoreceptor const float f = exp((double)-ntm->f); const float m = (ntm->m > 0.f) ? ntm->m : (0.3f + 0.7f*pow((double)auto_key, 1.4)); const float ic = 1.f - ntm->c, ia = 1.f - ntm->a; if (ntm->m == 0.f) printf("tonemap node, M: %g\n", m); for (y=0; y<src->y; ++y) { fRGB* sp = (fRGB*)&src->rect[y*src->x*src->type]; fRGB* dp = (fRGB*)&dst->rect[y*src->x*src->type]; for (x=0; x<src->x; ++x) { const float L = 0.212671f*sp[x][0] + 0.71516f*sp[x][1] + 0.072169f*sp[x][2]; float I_l = sp[x][0] + ic*(L - sp[x][0]); float I_g = Cav[0] + ic*(Lav - Cav[0]); float I_a = I_l + ia*(I_g - I_l); dp[x][0] /= (dp[x][0] + pow((double)f*I_a, (double)m)); I_l = sp[x][1] + ic*(L - sp[x][1]); I_g = Cav[1] + ic*(Lav - Cav[1]); I_a = I_l + ia*(I_g - I_l); dp[x][1] /= (dp[x][1] + pow((double)f*I_a,(double)m)); I_l = sp[x][2] + ic*(L - sp[x][2]); I_g = Cav[2] + ic*(Lav - Cav[2]); I_a = I_l + ia*(I_g - I_l); dp[x][2] /= (dp[x][2] + pow((double)f*I_a, (double)m)); } } return; } // Reinhard simple photographic tm (simplest, not using whitepoint var) for (y=0; y<src->y; y++) { fRGB* sp = (fRGB*)&src->rect[y*src->x*src->type]; fRGB* dp = (fRGB*)&dst->rect[y*src->x*src->type]; for (x=0; x<src->x; x++) { fRGB_copy(dp[x], sp[x]); fRGB_mult(dp[x], al); dr = dp[x][0] + ntm->offset; dg = dp[x][1] + ntm->offset; db = dp[x][2] + ntm->offset; dp[x][0] /= ((dr == 0.f) ? 1.f : dr); dp[x][1] /= ((dg == 0.f) ? 1.f : dg); dp[x][2] /= ((db == 0.f) ? 1.f : db); if (igm != 0.f) { dp[x][0] = pow((double)MAX2(dp[x][0], 0.), igm); dp[x][1] = pow((double)MAX2(dp[x][1], 0.), igm); dp[x][2] = pow((double)MAX2(dp[x][2], 0.), igm); } } } }
// 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); } } } }
static float avgLogLum(CompBuf *src, float* auto_key, float* Lav, float* Cav) { float lsum = 0; int p = src->x*src->y; fRGB* bc = (fRGB*)src->rect; float avl, maxl = -1e10f, minl = 1e10f; const float sc = 1.f/(src->x*src->y); *Lav = 0.f; while (p--) { float L = 0.212671f*bc[0][0] + 0.71516f*bc[0][1] + 0.072169f*bc[0][2]; *Lav += L; fRGB_add(Cav, bc[0]); lsum += (float)log((double)MAX2(L, 0.0) + 1e-5); maxl = (L > maxl) ? L : maxl; minl = (L < minl) ? L : minl; bc++; } *Lav *= sc; fRGB_mult(Cav, sc); maxl = log((double)maxl + 1e-5); minl = log((double)minl + 1e-5f); avl = lsum*sc; *auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.f; return exp((double)avl); }
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 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); }
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); }