/** * $Id:$ * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * The contents of this file may be used under the terms of either the GNU * General Public License Version 2 or later (the "GPL", see * http://www.gnu.org/licenses/gpl.html ), or the Blender License 1.0 or * later (the "BL", see http://www.blender.org/BL/ ) which has to be * bought from the Blender Foundation to become active, in which case the * above mentioned GPL option does not apply. * * The Original Code is Copyright (C) 2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** */ /* * pixelblending.c * * Functions to blend pixels with or without alpha, in various formats * nzc - June 2000 * * Version: $Id: pixelblending.c,v 1.3 2000/09/11 13:13:00 nzc Exp $ */ /* global includes */ #include "blender.h" #include "render.h" /* local includes */ #include "vanillaRenderPipe_types.h" /* own includes */ #include "pixelblending_types.h" #include "pixelblending.h" /* externals */ /* ------------------------------------------------------------------------- */ /* Debug/behaviour defines */ /* if defined: alpha blending with floats clips colour, as with shorts */ /* #define RE_FLOAT_COLOUR_CLIPPING */ /* if defined: alpha values are clipped */ /* For now, we just keep alpha clipping. We run into thresholding and */ /* blending difficulties otherwise. Be careful here. */ #define RE_ALPHA_CLIPPING /* One of these three MUST be set. */ /* if defined: do 'expensive' gamma correction, ie. without lookup table. */ /* #define RE_PRIMITIVE_GAMMA_CORRECTION */ /* if defined: lookup-table and interpolation gamma correction. */ #define RE_INTERPOLATED_GAMMA_CORRECTION /* if defined: no gamma correction. */ /* #define RE_NO_GAMMA_CORRECTION */ /* These indicate the status of the gamma lookup table --------------------- */ static float gamma_range_table[RE_GAMMA_TABLE_SIZE + 1]; static float gamfactor_table[RE_GAMMA_TABLE_SIZE]; static float inv_gamma_range_table[RE_GAMMA_TABLE_SIZE + 1]; static float inv_gamfactor_table[RE_GAMMA_TABLE_SIZE]; static float colour_domain_table[RE_GAMMA_TABLE_SIZE + 1]; static float colour_step; static float inv_colour_step; static float valid_gamma; static float valid_inv_gamma; static int gamma_table_initialised = 0; /* functions --------------------------------------------------------------- */ void addAddSampColF(float *sampvec, float *source, int mask, int osaNr, uchar addfac) { int a; for(a=0; a < osaNr; a++) { if(mask & (1< RE_FULL_COLOUR_FLOAT) retval--; sampvec+= 4; } return retval; } /* end of int addToSampColF(float, float, int, int) */ /* ------------------------------------------------------------------------- */ int addToSampCol(ushort *sampcol, ushort *shortcol, int mask, int osaNr) { int a, retval = osaNr; for(a=0; a < osaNr; a++) { if(mask & (1<0xFFF0) retval--; sampcol+= 4; } return retval; } /* end of int addToSampCol(ushort, uhost, int, int) */ /* ------------------------------------------------------------------------- */ int addtosampcol(ushort *sampcol, ushort *shortcol, int mask) { int a, retval = R.osa; for(a=0; a < R.osa; a++) { if(mask & (1<0xFFF0) retval--; sampcol+= 4; } return retval; } /* end of int addtosampcol(ushort *sampcol, ushort *shortcol, int mask) */ /* ------------------------------------------------------------------------- */ void addAlphaOverShort(ushort *doel, ushort *bron) /* vult bron over doel in met alpha van bron */ { uint c; uint mul; if( doel[3]==0 || bron[3]>=0xFFF0) { /* is getest, scheelt veel */ *((uint *)doel)= *((uint *)bron); *((uint *)(doel+2))= *((uint *)(bron+2)); return; } mul= 0xFFFF-bron[3]; c= ((mul*doel[0])>>16)+bron[0]; if(c>=0xFFF0) doel[0]=0xFFF0; else doel[0]= c; c= ((mul*doel[1])>>16)+bron[1]; if(c>=0xFFF0) doel[1]=0xFFF0; else doel[1]= c; c= ((mul*doel[2])>>16)+bron[2]; if(c>=0xFFF0) doel[2]=0xFFF0; else doel[2]= c; c= ((mul*doel[3])>>16)+bron[3]; if(c>=0xFFF0) doel[3]=0xFFF0; else doel[3]= c; } /* end of void addAlphaOverShort(ushort *doel, ushort *bron) */ /* ------------------------------------------------------------------------- */ void addAlphaUnderShort(ushort *doel, ushort *bron) /* vult bron onder doel in met alpha van doel */ { register uint c; register uint mul; if(doel[3]>=0xFFF0) return; if( doel[3]==0 ) { /* is getest, scheelt veel */ *((uint *)doel)= *((uint *)bron); *((uint *)(doel+2))= *((uint *)(bron+2)); return; } mul= 0xFFFF-doel[3]; c= ((mul*bron[0])>>16)+doel[0]; if(c>=0xFFF0) doel[0]=0xFFF0; else doel[0]= c; c= ((mul*bron[1])>>16)+doel[1]; if(c>=0xFFF0) doel[1]=0xFFF0; else doel[1]= c; c= ((mul*bron[2])>>16)+doel[2]; if(c>=0xFFF0) doel[2]=0xFFF0; else doel[2]= c; c= ((mul*bron[3])>>16)+doel[3]; if(c>=0xFFF0) doel[3]=0xFFF0; else doel[3]= c; } /* end of void addAlphaUnderShort(ushort *doel, ushort *bron) */ /* ------------------------------------------------------------------------- */ void addAlphaOverFloat(float *dest, float *source) { /* d = s + (1-alpha_s)d*/ float c; float mul; /* I may want to disable this clipping */ #ifdef RE_FLOAT_COLOUR_CLIPPING if( /* (-RE_FULL_COLOUR_FLOAT < source[3]) */ /* && */ (source[3] > RE_FULL_COLOUR_FLOAT) ) { /* is getest, scheelt veel */ dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest[3] = source[3]; return; } #endif mul= 1.0 - source[3]; c= (mul*dest[0]) + source[0]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[0] = RE_UNITY_COLOUR_FLOAT; else #endif dest[0]= c; c= (mul*dest[1]) + source[1]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[1] = RE_UNITY_COLOUR_FLOAT; else #endif dest[1]= c; c= (mul*dest[2]) + source[2]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[2] = RE_UNITY_COLOUR_FLOAT; else #endif dest[2]= c; c= (mul*dest[3]) + source[3]; #ifdef RE_ALPHA_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[3] = RE_UNITY_COLOUR_FLOAT; else #endif dest[3]= c; } /* end of void addAlphaOverFloat(float *doel, float *bron) */ /* ------------------------------------------------------------------------- */ void addAlphaUnderFloat(float *dest, float *source) { float c; float mul; /* I may want to disable this clipping */ #ifdef RE_FLOAT_COLOUR_CLIPPING if( dest[3] >= RE_FULL_COLOUR_FLOAT) return; #endif if( (-RE_EMPTY_COLOUR_FLOAT < dest[3]) && (dest[3] < RE_EMPTY_COLOUR_FLOAT) ) { /* is getest, scheelt veel */ dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest[3] = source[3]; return; } mul= 1.0 - dest[3]; c= (mul*source[0]) + dest[0]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[0] = RE_UNITY_COLOUR_FLOAT; else #endif dest[0]= c; c= (mul*source[1]) + dest[1]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[1] = RE_UNITY_COLOUR_FLOAT; else #endif dest[1]= c; c= (mul*source[2]) + dest[2]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[2] = RE_UNITY_COLOUR_FLOAT; else #endif dest[2]= c; c= (mul*source[3]) + dest[3]; #ifdef RE_ALPHA_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[3] = RE_UNITY_COLOUR_FLOAT; else #endif dest[3]= c; } /* end of void addAlphaUnderFloat(float *doel, float *bron) */ /* ------------------------------------------------------------------------- */ void cpShortColV2CharColV(ushort *source, uchar *dest) { dest[0] = source[0]>>8; dest[1] = source[1]>>8; dest[2] = source[2]>>8; dest[3] = source[3]>>8; } /* end of void cpShortColV2CharColV(ushort *source, uchar *dest) */ /* ------------------------------------------------------------------------- */ void cpCharColV2ShortColV(uchar *source, ushort *dest) { dest[0] = source[0]<<8; dest[1] = source[1]<<8; dest[2] = source[2]<<8; dest[3] = source[3]<<8; } /* end of void cpShortColV2CharColV(uchar *source, ushort *dest) */ /* ------------------------------------------------------------------------- */ void cpIntColV2CharColV(uint *source, uchar *dest) { dest[0] = source[0]>>24; dest[1] = source[1]>>24; dest[2] = source[2]>>24; dest[3] = source[3]>>24; } /* end of void cpIntColV2CharColV(uint *source, uchar *dest) */ /* ------------------------------------------------------------------------- */ void cpCharColV2FloatColV(uchar *source, float *dest) { dest[0] = source[0]/255.0; dest[1] = source[1]/255.0; dest[2] = source[2]/255.0; dest[3] = source[3]/255.0; } /* end of void cpCharColV2FloatColV(char *source, float *dest) */ /* ------------------------------------------------------------------------- */ void cpShortColV2FloatColV(ushort *source, float *dest) { dest[0] = source[0]/65535.0; dest[1] = source[1]/65535.0; dest[2] = source[2]/65535.0; dest[3] = source[3]/65535.0; } /* end of void cpShortColV2FloatColV(char *source, float *dest) */ /* ------------------------------------------------------------------------- */ void cpFloatColV2CharColV(float* source, uchar *dest) { /* can't this be done more efficient? hope the conversions are correct... */ if (source[0] < 0.0) dest[0] = 0; else if (source[0] > 1.0) dest[0] = 255; else dest[0] = (uchar) (source[0] * 255.0); if (source[1] < 0.0) dest[1] = 0; else if (source[1] > 1.0) dest[1] = 255; else dest[1] = (uchar) (source[1] * 255.0); if (source[2] < 0.0) dest[2] = 0; else if (source[2] > 1.0) dest[2] = 255; else dest[2] = (uchar) (source[2] * 255.0); if (source[3] < 0.0) dest[3] = 0; else if (source[3] > 1.0) dest[3] = 255; else dest[3] = (uchar) (source[3] * 255.0); } /* end of void cpFloatColV2CharColV(float* source, uchar *dest) */ /* ------------------------------------------------------------------------- */ void cpShortColV(ushort *source, ushort *dest) { dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest[3] = source[3]; } /* end of void cpShortColV(ushort *source, ushort *dest) */ /* ------------------------------------------------------------------------- */ void cpFloatColV(float *source, float *dest) { dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest[3] = source[3]; } /* end of void cpFloatColV(float *source, float *dest) */ /* ------------------------------------------------------------------------- */ void cpCharColV(uchar *source, uchar *dest) { dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest[3] = source[3]; } /* end of void cpCharColV(uchar *source, uchar *dest) */ /* ------------------------------------------------------------------------- */ void addalphaAddfacFloat(float *dest, float *source, uchar addfac) /* doel= bron over doel */ { float m; /* weiging factor of destination */ float c; /* intermediate colour */ /* 1. copy source straight away if dest has zero alpha */ /* 2. copy dest straight away if dest has full alpha */ /* I am not sure whether (2) is correct. It seems to */ /* me that this should not happen if float colours */ /* aren't clipped at 1.0 . */ /* I'll keep the code, but disabled.... */ if ( (dest[3] < RE_EMPTY_COLOUR_FLOAT) /* || source[3] > RE_FULL_COLOUR_FLOAT */ ) { dest[0] = source[0]; dest[1] = source[1]; dest[2] = source[2]; dest[3] = source[3]; return; } /* Addfac is a number between 0 and 1: rescale */ /* final target is to diminish the influence of dest when addfac rises */ m = 1.0 - ( source[3] * ((255.0 - addfac) / 255.0)); /* blend colours*/ c= (m * dest[0]) + source[0]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[0] = RE_FULL_COLOUR_FLOAT; else #endif dest[0]= c; c= (m * dest[1]) + source[1]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[1] = RE_FULL_COLOUR_FLOAT; else #endif dest[1]= c; c= (m * dest[2]) + source[2]; #ifdef RE_FLOAT_COLOUR_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[2] = RE_FULL_COLOUR_FLOAT; else #endif dest[2]= c; c= dest[3] + source[3]; #ifdef RE_ALPHA_CLIPPING if(c >= RE_FULL_COLOUR_FLOAT) dest[3] = RE_FULL_COLOUR_FLOAT; else #endif dest[3]= c; } /* end of void addalphaAddfacFloat(ushort *doel, ushort *bron, uchar addfac_help) */ /* ------------------------------------------------------------------------- */ void addalphaAddfacShort(ushort *doel, ushort *bron, uchar addfac) /* doel= bron over doel */ { float m; /* weiging factor of destination */ float c; /* intermediate colour */ /* 1. copy bron straight away if doel has zero alpha */ if( doel[3] == 0) { *((uint *)doel) = *((uint *)bron); *((uint *)(doel+2)) = *((uint *)(bron+2)); return; } /* Addfac is a number between 0 and 1: rescale */ /* final target is to diminish the influence of dest when addfac rises */ m = 1.0 - ( bron[3] * ((255.0 - addfac) / 255.0)); /* blend colours*/ c = (m * doel[0]) + bron[0]; if( c > 65535.0 ) doel[0]=65535; else doel[0] = ffloor(c); c = (m * doel[1]) + bron[1]; if( c > 65535.0 ) doel[1]=65535; else doel[1] = ffloor(c); c = (m * doel[2]) + bron[2]; if( c > 65535.0 ) doel[2]=65535; else doel[2] = ffloor(c); c = doel[3] + bron[3]; if(c > 65535.0) doel[3] = 65535; else doel[3]= ffloor(c); } /* end of void addalphaAddfacShort(ushort *doel, ushort *bron, uchar addfac_help) */ /* ------------------------------------------------------------------------- */ void addHaloToHaloShort(ushort *d, ushort *s) { /* float m; */ /* weiging factor of destination */ float c[4]; /* intermediate colour */ float rescale = 1.0; /* 1. copy straight away if has zero alpha */ if( d[3] == 0) { *((uint *) d) = *((uint *) s); *((uint *)(d + 2)) = *((uint *)(s + 2)); return; } /* 2. halo blending */ /* no blending, just add */ c[0] = s[0] + d[0]; c[1] = s[1] + d[1]; c[2] = s[2] + d[2]; c[3] = s[3] + d[3]; /* One thing that may happen is that this pixel is over-saturated with light - */ /* i.e. too much light comes out, and the pixel is clipped. Currently, this */ /* leads to artifacts such as overproportional undersampling of background */ /* colours. */ /* Compensating for over-saturation: */ /* - increase alpha */ /* - increase alpha and rescale colours */ /* let's try alpha increase and clipping */ /* calculate how much rescaling we need */ if( c[0] > 65535.0 ) { rescale *= c[0] /65535.0; d[0] = 65535; } else d[0] = ffloor(c[0]); if( c[1] > 65535.0 ) { rescale *= c[1] /65535.0; d[1] = 65535; } else d[1] = ffloor(c[1]); if( c[2] > 65535.0 ) { rescale *= c[2] /65535.0; d[2] = 65535; } else d[2] = ffloor(c[2]); /* a bit too hefty I think */ c[3] *= rescale; if( c[3] > 65535.0 ) d[3] = 65535; else d[3]= ffloor(c[3]); } /* end of void addHaloToHaloShort(ushort *dest, ushort *source, char addfac) */ /* ------------------------------------------------------------------------- */ void sampleShortColV2ShortColV(ushort *sample, ushort *dest, int osaNr) { uint intcol[4] = {0}; ushort *scol = sample; int a = 0; for(a=0; a < osaNr; a++, scol+=4) { intcol[0]+= scol[0]; intcol[1]+= scol[1]; intcol[2]+= scol[2]; intcol[3]+= scol[3]; } /* Now normalise the integrated colour. It is guaranteed */ /* to be correctly bounded. */ dest[0]= intcol[0]/osaNr; dest[1]= intcol[1]/osaNr; dest[2]= intcol[2]/osaNr; dest[3]= intcol[3]/osaNr; } /* end of void sampleShortColVToShortColV(ushort *sample, ushort *dest) */ /* ------------------------------------------------------------------------- */ void sampleFloatColV2FloatColV(float *sample, float *dest, int osaNr) { float intcol[4] = {0}; float *scol = sample; int a = 0; #ifdef RE_PRIMITIVE_GAMMA_CORRECTION /* Needs to become something with lookup tables.*/ /* Go to intensities, and integrate those. Don't touch alpha */ float invgamma = 1.0 / RE_DEFAULT_GAMMA; for(a=0; a < osaNr; a++, scol+=4) { intcol[0] += powf(scol[0], RE_DEFAULT_GAMMA); intcol[1] += powf(scol[1], RE_DEFAULT_GAMMA); intcol[2] += powf(scol[2], RE_DEFAULT_GAMMA); intcol[3] += scol[3]; } /* renormalise */ intcol[0] /= osaNr; intcol[1] /= osaNr; intcol[2] /= osaNr; intcol[3] /= osaNr; /* back to pixel values */ dest[0] = powf(intcol[0], invgamma); dest[1] = powf(intcol[1], invgamma); dest[2] = powf(intcol[2], invgamma); dest[3] = intcol[3]; return; #endif #ifdef RE_INTERPOLATED_GAMMA_CORRECTION /* use a LUT and interpolation to do the gamma correction */ for(a=0; a < osaNr; a++, scol+=4) { intcol[0] += gammaCorrect(scol[0]); intcol[1] += gammaCorrect(scol[1]); intcol[2] += gammaCorrect(scol[2]); intcol[3] += scol[3]; } /* renormalise */ intcol[0] /= osaNr; intcol[1] /= osaNr; intcol[2] /= osaNr; intcol[3] /= osaNr; /* back to pixel values */ dest[0] = invGammaCorrect(intcol[0]); dest[1] = invGammaCorrect(intcol[1]); dest[2] = invGammaCorrect(intcol[2]); dest[3] = intcol[3]; return; #endif #ifdef RE_NO_GAMMA_CORRECTION /* Each colour should be weighted by alpha? We need gamma correction */ /* badly */ for(a=0; a < osaNr; a++, scol+=4) { intcol[0] += scol[0]; intcol[1] += scol[1]; intcol[2] += scol[2]; intcol[3] += scol[3]; } dest[0]= intcol[0]/osaNr; dest[1]= intcol[1]/osaNr; dest[2]= intcol[2]/osaNr; dest[3]= intcol[3]/osaNr; #endif } /* end of void sampleFloatColVToFloatColV(ushort *sample, ushort *dest) */ /* ------------------------------------------------------------------------- */ float gammaCorrect(float c) { int i; float res = 0.0; i = ffloor(c * inv_colour_step); /* Clip to range [0,1]: outside, just do the complete calculation. */ /* We may have some performance problems here. Stretching up the LUT */ /* may help solve that, by exchanging LUT size for the interpolation. */ if (i < 0) res = powf(c, valid_gamma); else if (i >= RE_GAMMA_TABLE_SIZE ) res = powf(c, valid_gamma); else res = gamma_range_table[i] + ( (c - colour_domain_table[i]) * gamfactor_table[i]); return res; } /* end of float gammaCorrect(float col) */ /* ------------------------------------------------------------------------- */ float invGammaCorrect(float col) { int i; float res = 0.0; i = ffloor(col*inv_colour_step); if (i < 0) res = powf(col, valid_inv_gamma); else if (i >= RE_GAMMA_TABLE_SIZE) res = powf(col, valid_inv_gamma); else res = inv_gamma_range_table[i] + ( (col - colour_domain_table[i]) * inv_gamfactor_table[i]); return res; } /* end of float invGammaCorrect(float col) */ /* ------------------------------------------------------------------------- */ void makeGammaTables(float gamma) { /* we need two tables: one forward, one backward */ int i; valid_gamma = gamma; valid_inv_gamma = 1.0 / gamma; colour_step = 1.0 / RE_GAMMA_TABLE_SIZE; inv_colour_step = (float) RE_GAMMA_TABLE_SIZE; /* We could squeeze out the two range tables to gain some memory. */ for (i = 0; i < RE_GAMMA_TABLE_SIZE; i++) { colour_domain_table[i] = i * colour_step; gamma_range_table[i] = powf(colour_domain_table[i], valid_gamma); inv_gamma_range_table[i] = powf(colour_domain_table[i], valid_inv_gamma); } /* The end of the table should match 1.0 carefully. In order to avoid */ /* rounding errors, we just set this explicitly. The last segment may */ /* have a different lenght than the other segments, but our */ /* interpolation is insensitive to that. */ colour_domain_table[RE_GAMMA_TABLE_SIZE] = 1.0; gamma_range_table[RE_GAMMA_TABLE_SIZE] = 1.0; inv_gamma_range_table[RE_GAMMA_TABLE_SIZE] = 1.0; /* To speed up calculations, we make these calc factor tables. They are */ /* multiplication factors used in scaling the interpolation. */ for (i = 0; i < RE_GAMMA_TABLE_SIZE; i++ ) { gamfactor_table[i] = inv_colour_step * (gamma_range_table[i + 1] - gamma_range_table[i]) ; inv_gamfactor_table[i] = inv_colour_step * (inv_gamma_range_table[i + 1] - inv_gamma_range_table[i]) ; } gamma_table_initialised = 1; } /* end of void makeGammaTables(float gamma) */ /* ------------------------------------------------------------------------- */ int gammaTableIsInitialised(void) { return gamma_table_initialised; } /* ------------------------------------------------------------------------- */ /* eof pixelblending.c */