diff options
Diffstat (limited to 'source/blender/blenkernel/intern/colorband.c')
-rw-r--r-- | source/blender/blenkernel/intern/colorband.c | 396 |
1 files changed, 396 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/colorband.c b/source/blender/blenkernel/intern/colorband.c new file mode 100644 index 00000000000..b1f6431019d --- /dev/null +++ b/source/blender/blenkernel/intern/colorband.c @@ -0,0 +1,396 @@ +/* + * ***** 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/colorband.c + * \ingroup bke + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_math_color.h" + +#include "DNA_key_types.h" +#include "DNA_texture_types.h" + +#include "BKE_colorband.h" +#include "BKE_material.h" +#include "BKE_key.h" + +void init_colorband(ColorBand *coba, bool rangetype) +{ + int a; + + coba->data[0].pos = 0.0; + coba->data[1].pos = 1.0; + + if (rangetype == 0) { + coba->data[0].r = 0.0; + coba->data[0].g = 0.0; + coba->data[0].b = 0.0; + coba->data[0].a = 0.0; + + coba->data[1].r = 1.0; + coba->data[1].g = 1.0; + coba->data[1].b = 1.0; + coba->data[1].a = 1.0; + } + else { + coba->data[0].r = 0.0; + coba->data[0].g = 0.0; + coba->data[0].b = 0.0; + coba->data[0].a = 1.0; + + coba->data[1].r = 1.0; + coba->data[1].g = 1.0; + coba->data[1].b = 1.0; + coba->data[1].a = 1.0; + } + + for (a = 2; a < MAXCOLORBAND; a++) { + coba->data[a].r = 0.5; + coba->data[a].g = 0.5; + coba->data[a].b = 0.5; + coba->data[a].a = 1.0; + coba->data[a].pos = 0.5; + } + + coba->tot = 2; + coba->color_mode = COLBAND_BLEND_RGB; +} + +ColorBand *add_colorband(bool rangetype) +{ + ColorBand *coba; + + coba = MEM_callocN(sizeof(ColorBand), "colorband"); + init_colorband(coba, rangetype); + + return coba; +} + +/* ------------------------------------------------------------------------- */ + +static float colorband_hue_interp( + const int ipotype_hue, + const float mfac, const float fac, + float h1, float h2) +{ + float h_interp; + int mode = 0; + +#define HUE_INTERP(h_a, h_b) ((mfac * (h_a)) + (fac * (h_b))) +#define HUE_MOD(h) (((h) < 1.0f) ? (h) : (h) - 1.0f) + + h1 = HUE_MOD(h1); + h2 = HUE_MOD(h2); + + BLI_assert(h1 >= 0.0f && h1 < 1.0f); + BLI_assert(h2 >= 0.0f && h2 < 1.0f); + + switch (ipotype_hue) { + case COLBAND_HUE_NEAR: + { + if ((h1 < h2) && (h2 - h1) > +0.5f) mode = 1; + else if ((h1 > h2) && (h2 - h1) < -0.5f) mode = 2; + else mode = 0; + break; + } + case COLBAND_HUE_FAR: + { + if ((h1 < h2) && (h2 - h1) < +0.5f) mode = 1; + else if ((h1 > h2) && (h2 - h1) > -0.5f) mode = 2; + else mode = 0; + break; + } + case COLBAND_HUE_CCW: + { + if (h1 > h2) mode = 2; + else mode = 0; + break; + } + case COLBAND_HUE_CW: + { + if (h1 < h2) mode = 1; + else mode = 0; + break; + } + } + + switch (mode) { + case 0: + h_interp = HUE_INTERP(h1, h2); + break; + case 1: + h_interp = HUE_INTERP(h1 + 1.0f, h2); + h_interp = HUE_MOD(h_interp); + break; + case 2: + h_interp = HUE_INTERP(h1, h2 + 1.0f); + h_interp = HUE_MOD(h_interp); + break; + } + + BLI_assert(h_interp >= 0.0f && h_interp < 1.0f); + +#undef HUE_INTERP +#undef HUE_MOD + + return h_interp; +} + +bool do_colorband(const ColorBand *coba, float in, float out[4]) +{ + const CBData *cbd1, *cbd2, *cbd0, *cbd3; + float fac; + int ipotype; + int a; + + if (coba == NULL || coba->tot == 0) return false; + + cbd1 = coba->data; + + ipotype = (coba->color_mode == COLBAND_BLEND_RGB) ? coba->ipotype : COLBAND_INTERP_LINEAR; + + if (coba->tot == 1) { + out[0] = cbd1->r; + out[1] = cbd1->g; + out[2] = cbd1->b; + out[3] = cbd1->a; + } + else if ((in <= cbd1->pos) && ELEM(ipotype, COLBAND_INTERP_LINEAR, COLBAND_INTERP_EASE)) { + out[0] = cbd1->r; + out[1] = cbd1->g; + out[2] = cbd1->b; + out[3] = cbd1->a; + } + else { + CBData left, right; + + /* we're looking for first pos > in */ + for (a = 0; a < coba->tot; a++, cbd1++) { + if (cbd1->pos > in) { + break; + } + } + + if (a == coba->tot) { + cbd2 = cbd1 - 1; + right = *cbd2; + right.pos = 1.0f; + cbd1 = &right; + } + else if (a == 0) { + left = *cbd1; + left.pos = 0.0f; + cbd2 = &left; + } + else { + cbd2 = cbd1 - 1; + } + + if ((in >= cbd1->pos) && ELEM(ipotype, COLBAND_INTERP_LINEAR, COLBAND_INTERP_EASE)) { + out[0] = cbd1->r; + out[1] = cbd1->g; + out[2] = cbd1->b; + out[3] = cbd1->a; + } + else { + + if (cbd2->pos != cbd1->pos) { + fac = (in - cbd1->pos) / (cbd2->pos - cbd1->pos); + } + else { + /* was setting to 0.0 in 2.56 & previous, but this + * is incorrect for the last element, see [#26732] */ + fac = (a != coba->tot) ? 0.0f : 1.0f; + } + + if (ipotype == COLBAND_INTERP_CONSTANT) { + /* constant */ + out[0] = cbd2->r; + out[1] = cbd2->g; + out[2] = cbd2->b; + out[3] = cbd2->a; + } + else if (ipotype >= COLBAND_INTERP_B_SPLINE) { + /* ipo from right to left: 3 2 1 0 */ + float t[4]; + + if (a >= coba->tot - 1) cbd0 = cbd1; + else cbd0 = cbd1 + 1; + if (a < 2) cbd3 = cbd2; + else cbd3 = cbd2 - 1; + + CLAMP(fac, 0.0f, 1.0f); + + if (ipotype == COLBAND_INTERP_CARDINAL) { + key_curve_position_weights(fac, t, KEY_CARDINAL); + } + else { + key_curve_position_weights(fac, t, KEY_BSPLINE); + } + + out[0] = t[3] * cbd3->r + t[2] * cbd2->r + t[1] * cbd1->r + t[0] * cbd0->r; + out[1] = t[3] * cbd3->g + t[2] * cbd2->g + t[1] * cbd1->g + t[0] * cbd0->g; + out[2] = t[3] * cbd3->b + t[2] * cbd2->b + t[1] * cbd1->b + t[0] * cbd0->b; + out[3] = t[3] * cbd3->a + t[2] * cbd2->a + t[1] * cbd1->a + t[0] * cbd0->a; + CLAMP(out[0], 0.0f, 1.0f); + CLAMP(out[1], 0.0f, 1.0f); + CLAMP(out[2], 0.0f, 1.0f); + CLAMP(out[3], 0.0f, 1.0f); + } + else { + float mfac; + + if (ipotype == COLBAND_INTERP_EASE) { + mfac = fac * fac; + fac = 3.0f * mfac - 2.0f * mfac * fac; + } + + mfac = 1.0f - fac; + + if (UNLIKELY(coba->color_mode == COLBAND_BLEND_HSV)) { + float col1[3], col2[3]; + + rgb_to_hsv_v(&cbd1->r, col1); + rgb_to_hsv_v(&cbd2->r, col2); + + out[0] = colorband_hue_interp(coba->ipotype_hue, mfac, fac, col1[0], col2[0]); + out[1] = mfac * col1[1] + fac * col2[1]; + out[2] = mfac * col1[2] + fac * col2[2]; + out[3] = mfac * cbd1->a + fac * cbd2->a; + + hsv_to_rgb_v(out, out); + } + else if (UNLIKELY(coba->color_mode == COLBAND_BLEND_HSL)) { + float col1[3], col2[3]; + + rgb_to_hsl_v(&cbd1->r, col1); + rgb_to_hsl_v(&cbd2->r, col2); + + out[0] = colorband_hue_interp(coba->ipotype_hue, mfac, fac, col1[0], col2[0]); + out[1] = mfac * col1[1] + fac * col2[1]; + out[2] = mfac * col1[2] + fac * col2[2]; + out[3] = mfac * cbd1->a + fac * cbd2->a; + + hsl_to_rgb_v(out, out); + } + else { + /* COLBAND_BLEND_RGB */ + out[0] = mfac * cbd1->r + fac * cbd2->r; + out[1] = mfac * cbd1->g + fac * cbd2->g; + out[2] = mfac * cbd1->b + fac * cbd2->b; + out[3] = mfac * cbd1->a + fac * cbd2->a; + } + } + } + } + return true; /* OK */ +} + +void colorband_table_RGBA(ColorBand *coba, float **array, int *size) +{ + int a; + + *size = CM_TABLE + 1; + *array = MEM_callocN(sizeof(float) * (*size) * 4, "ColorBand"); + + for (a = 0; a < *size; a++) + do_colorband(coba, (float)a / (float)CM_TABLE, &(*array)[a * 4]); +} + +static int vergcband(const void *a1, const void *a2) +{ + const CBData *x1 = a1, *x2 = a2; + + if (x1->pos > x2->pos) return 1; + else if (x1->pos < x2->pos) return -1; + return 0; +} + +void colorband_update_sort(ColorBand *coba) +{ + int a; + + if (coba->tot < 2) + return; + + for (a = 0; a < coba->tot; a++) + coba->data[a].cur = a; + + qsort(coba->data, coba->tot, sizeof(CBData), vergcband); + + for (a = 0; a < coba->tot; a++) { + if (coba->data[a].cur == coba->cur) { + coba->cur = a; + break; + } + } +} + +CBData *colorband_element_add(struct ColorBand *coba, float position) +{ + if (coba->tot == MAXCOLORBAND) { + return NULL; + } + else { + CBData *xnew; + + xnew = &coba->data[coba->tot]; + xnew->pos = position; + + if (coba->tot != 0) { + do_colorband(coba, position, &xnew->r); + } + else { + zero_v4(&xnew->r); + } + } + + coba->tot++; + coba->cur = coba->tot - 1; + + colorband_update_sort(coba); + + return coba->data + coba->cur; +} + +int colorband_element_remove(struct ColorBand *coba, int index) +{ + int a; + + if (coba->tot < 2) + return 0; + + if (index < 0 || index >= coba->tot) + return 0; + + coba->tot--; + for (a = index; a < coba->tot; a++) { + coba->data[a] = coba->data[a + 1]; + } + if (coba->cur) coba->cur--; + return 1; +} |