summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTill Theato <[email protected]>2011-01-06 20:00:38 (GMT)
committer Till Theato <[email protected]>2011-01-13 19:29:33 (GMT)
commit4369fc13f5f50cb40e422b8a2868c78682857182 (patch)
tree67c4b7bb0b451585bb99b536b630d965902b8c3b
parentc6c4d43d6ebce85a83d7ee9be50d15c65fd19aff (diff)
curves: - increased precision for hue when using the Bezier spline because it now depends on the channel and creates a lut of variable length (for now hue = 360, other channels = 255) - speed improvements for hue, r, g, b, a - code cleanup
-rw-r--r--src/filter/curves/curves.c235
1 files changed, 129 insertions, 106 deletions
diff --git a/src/filter/curves/curves.c b/src/filter/curves/curves.c
index 140010b..96c68d1 100644
--- a/src/filter/curves/curves.c
+++ b/src/filter/curves/curves.c
@@ -50,12 +50,7 @@ typedef struct position
double y;
} position;
-typedef struct bspline_point
-{
- position handle1;
- position point;
- position handle2;
-} bspline_point;
+typedef position bspline_point[3]; // [0] = handle1, [1] = point, [2] = handle2
typedef struct curves_instance
@@ -70,8 +65,7 @@ typedef struct curves_instance
double formula;
char *bspline;
- int bsplineMap[256];
- double bsplineLumaMap[256];
+ double *bsplineMap;
} curves_instance_t;
@@ -80,7 +74,7 @@ typedef struct curves_instance
// slightly modified
// r,g,b values are from 0 to 255
-// h = [0,6], s = [0,1], v = [0,1]
+// h = [0,360], s = [0,1], v = [0,1]
// if s == 0, then h = -1 (undefined)
void RGBtoHSV(double r, double g, double b, double *h, double *s, double *v)
{
@@ -106,14 +100,13 @@ void RGBtoHSV(double r, double g, double b, double *h, double *s, double *v)
else
*h = 4 + (r - g) / delta; // between magenta & cyan
- //*h *= 60; // degrees
+ *h *= 60; // degrees
if (*h < 0)
- *h += 6;
- //*h += 360;
+ *h += 360;
}
// r,g,b values are from 0 to 1
-// h = [0,6], s = [0,1], v = [0,1]
+// h = [0,360], s = [0,1], v = [0,1]
void HSVtoRGB(double *r, double *g, double *b, double h, double s, double v)
{
if (s == 0) {
@@ -122,7 +115,7 @@ void HSVtoRGB(double *r, double *g, double *b, double h, double s, double v)
return;
}
- //h /= 60; // sector 0 to 5
+ h /= 60; // sector 0 to 5
int i = (int)h;
double f = h - i; // factorial part of h
double p = v * (1 - s);
@@ -259,6 +252,7 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
inst->pointNumber = 2;
inst->formula = 1;
inst->bspline = calloc(1, sizeof(char));
+ inst->bsplineMap = malloc(sizeof(double));
inst->points[0] = 0;
inst->points[1] = 0;
inst->points[2] = 1;
@@ -275,6 +269,7 @@ f0r_instance_t f0r_construct(unsigned int width, unsigned int height)
void f0r_destruct(f0r_instance_t instance)
{
free(((curves_instance_t*)instance)->bspline);
+ free(((curves_instance_t*)instance)->bsplineMap);
free(instance);
}
@@ -293,12 +288,25 @@ void f0r_set_param_value(f0r_instance_t instance,
tmp = *((f0r_param_double *)param);
if (tmp >= 1) {
// legacy support
- if (tmp == 3)
- inst->channel = CHANNEL_LUMA;
- else
- inst->channel = (enum CHANNELS)((int)tmp);
+ if (tmp == 3) {
+ if (inst->channel != CHANNEL_LUMA) {
+ inst->channel = CHANNEL_LUMA;
+ if (strlen(inst->bspline))
+ updateBsplineMap(instance);
+ }
+ } else {
+ if ((int)inst->channel != (int)tmp) {
+ inst->channel = (enum CHANNELS)((int)tmp);
+ if (strlen(inst->bspline))
+ updateBsplineMap(instance);
+ }
+ }
} else {
- inst->channel = (enum CHANNELS)(tmp * 10);
+ if ((int)inst->channel != (int)(tmp * 10)) {
+ inst->channel = (enum CHANNELS)(tmp * 10);
+ if (strlen(inst->bspline))
+ updateBsplineMap(instance);
+ }
}
break;
case 1:
@@ -515,7 +523,12 @@ void swap(double *points, int i, int j) {
position pointOnBezier(double t, position points[4])
{
position pos;
- // coefficients from Bernstein basis polynomial of degree 3
+
+ /*
+ * Calculating a point on the bezier curve using the coefficients from Bernstein basis polynomial of degree 3.
+ * Using the De Casteljau algorithm would be slightly faster when calculating a lot of values
+ * but the difference is far from noticable here since we update the spline only when the parameter changes
+ */
double c1 = (1-t) * (1-t) * (1-t);
double c2 = 3 * t * (1-t) * (1-t);
double c3 = 3 * t * t * (1-t);
@@ -526,7 +539,7 @@ position pointOnBezier(double t, position points[4])
}
/**
- * Splits given string into sub-strings on given delimiter.
+ * Splits given string into sub-strings at given delimiter.
* \param string input string
* \param delimiter delimiter
* \param tokens pointer to array of strings, will be filled with sub-strings
@@ -555,10 +568,19 @@ void updateBsplineMap(f0r_instance_t instance)
assert(instance);
curves_instance_t* inst = (curves_instance_t*)instance;
+ int range = inst->channel == CHANNEL_HUE ? 361 : 256;
+ free(inst->bsplineMap);
+ inst->bsplineMap = malloc(range * sizeof(double));
// fill with default values, in case the spline does not cover the whole range
- for(int i = 0; i < 256; ++i) {
- inst->bsplineMap[i] = i;
- inst->bsplineLumaMap[i] = (i == 0 ? i : i / 255.0);
+ if (inst->channel == CHANNEL_HUE) {
+ for(int i = 0; i < 361; ++i)
+ inst->bsplineMap[i] = i;
+ } else if (inst->channel == CHANNEL_LUMA || inst->channel == CHANNEL_SATURATION) {
+ for(int i = 0; i < 256; ++i)
+ inst->bsplineMap[i] = inst->channel == CHANNEL_LUMA ? 1 : i / 255.;
+ } else {
+ for(int i = 0; i < 256; ++i)
+ inst->bsplineMap[i] = i;
}
/*
@@ -572,38 +594,19 @@ void updateBsplineMap(f0r_instance_t instance)
for (int i = 0; i < count; ++i) {
char **positionsStr = calloc(1, sizeof(char *));
int positionsNum = tokenise(pointStr[i], "#", &positionsStr);
-
- if (positionsNum != 3) {
- for(int j = 0; j < positionsNum; ++j)
- free(positionsStr[j]);
- free(positionsStr);
- continue;
- }
-
- position positions[3];
- for (int j = 0; j < positionsNum; ++j) {
- char **coords = calloc(1, sizeof(char *));
- int coordsNum = tokenise(positionsStr[j], ";", &coords);
-
- if (coordsNum != 2) {
+ if (positionsNum == 3) { // h1, p, h2
+ for (int j = 0; j < positionsNum; ++j) {
+ char **coords = calloc(1, sizeof(char *));
+ int coordsNum = tokenise(positionsStr[j], ";", &coords);
+ if (coordsNum == 2) { // x, y
+ points[i][j].x = atof(coords[0]);
+ points[i][j].y = atof(coords[1]);
+ }
for (int k = 0; k < coordsNum; ++k)
free(coords[k]);
free(coords);
- continue;
}
-
- positions[j].x = atof(coords[0]) * 255;
- positions[j].y = atof(coords[1]) * 255;
-
- free(coords[0]);
- free(coords[1]);
- free(coords);
}
-
- points[i].handle1 = positions[0];
- points[i].point = positions[1];
- points[i].handle2 = positions[2];
-
for(int j = 0; j < positionsNum; ++j)
free(positionsStr[j]);
free(positionsStr);
@@ -617,13 +620,13 @@ void updateBsplineMap(f0r_instance_t instance)
* Actual work: calculate curves between points and fill map
*/
position p[4];
- double t, step, diff, diff2;
+ double t, step, diff, y;
int pn, c, k;
for (int i = 0; i < count - 1; ++i) {
- p[0] = points[i].point;
- p[1] = points[i].handle2;
- p[2] = points[i+1].handle1;
- p[3] = points[i+1].point;
+ p[0] = points[i][1];
+ p[1] = points[i][2];
+ p[2] = points[i+1][0];
+ p[3] = points[i+1][1];
// make sure points are in correct order
if (p[0].x > p[3].x)
@@ -636,9 +639,8 @@ void updateBsplineMap(f0r_instance_t instance)
t = 0;
pn = 0;
// number of points calculated for this curve
- // x range * 3 should give enough points
- // TODO: more tests on whether *3 is really sufficient
- c = (int)((p[3].x - p[0].x) * 3);
+ // x range * 10 should give enough points
+ c = (int)((p[3].x - p[0].x) * range * 10);
if (c == 0) {
// points have same x value -> will result in a jump in the curve
// calculate anyways, in case we only have these two points (with more points this x value will be calculated three times)
@@ -654,17 +656,28 @@ void updateBsplineMap(f0r_instance_t instance)
// Fill the map in range this curve provides
k = 0;
// connection points will be written twice (but therefore no special case for the last point is required)
- for (int j = (int)p[0].x; j <= (int)p[3].x; ++j) {
- diff = k > 0 ? j - curve[k-1].x : -1;
- diff2 = j - curve[k].x;
- // Find point closest to the one needed (integers 0-255)
- while (fabs(diff2) <= fabs(diff) && ++k < pn) {
- diff = diff2;
- diff2 = j - curve[k].x;
+ for (int j = (int)(p[0].x * (range-1)); j <= (int)(p[3].x * (range-1)); ++j) {
+ if (k > 0)
+ --k;
+ diff = fabs(j / ((double)(range-1)) - curve[k].x);
+ y = curve[k].y;
+ // Find point closest to the one needed (integers 0 - range)
+ while (++k < pn) {
+ if (fabs(j / ((double)(range-1)) - curve[k].x) > diff)
+ break;
+ diff = fabs(j / ((double)(range-1)) - curve[k].x);
+ y = curve[k++].y;
}
- inst->bsplineMap[j] = CLAMP0255(ROUND(curve[k-1].y));
- inst->bsplineLumaMap[j] = curve[k-1].y / 255.0 / (j == 0 ? 1 : (j / 255.0));
- }
+
+ if (inst->channel == CHANNEL_HUE)
+ inst->bsplineMap[j] = CLAMP(y * 360, 0, 360);
+ else if (inst->channel == CHANNEL_LUMA)
+ inst->bsplineMap[j] = y / (j == 0 ? 1 : j / 255.);
+ else if (inst->channel == CHANNEL_SATURATION)
+ inst->bsplineMap[j] = CLAMP(y, 0, 1);
+ else
+ inst->bsplineMap[j] = CLAMP0255(ROUND(y * 255));
+ }
}
}
@@ -675,14 +688,14 @@ void f0r_update(f0r_instance_t instance, double time,
assert(instance);
curves_instance_t* inst = (curves_instance_t*)instance;
unsigned int len = inst->width * inst->height;
-
+
unsigned char* dst = (unsigned char*)outframe;
const unsigned char* src = (unsigned char*)inframe;
int i = 0;
- int map[256];
+ int mapI[256];
double mapLuma[256];
- double *map01 = NULL;
+ double *map = NULL;
float *mapCurves = NULL;
int scale = inst->height / 2;
double *points = NULL;
@@ -703,7 +716,7 @@ void f0r_update(f0r_instance_t instance, double time,
for(i = 0; i < 256; i++) {
double v = i / 255.;
double w = spline(v, points, (size_t)inst->pointNumber, coeffs);
- map[i] = CLAMP(w, 0, 1) * 255;
+ mapI[i] = CLAMP(w, 0, 1) * 255;
mapLuma[i] = i == 0?w:w / v;
}
//building map for drawing curve
@@ -713,9 +726,26 @@ void f0r_update(f0r_instance_t instance, double time,
mapCurves[i] = spline((float)i / scale, points, (size_t)inst->pointNumber, coeffs) * scale;
}
free(coeffs);
+
+ if (inst->channel == CHANNEL_HUE || inst->channel == CHANNEL_SATURATION) {
+ map = malloc(361*sizeof(double));
+ if (CHANNEL_SATURATION)
+ for (i = 0; i < 256; ++i)
+ map[i] = mapI[i] / 255.;
+ else
+ for (i = 0; i < 361; ++i)
+ map[i] = mapI[(int)(i / 360. * 255)] / 360. * 255;
+ }
} else {
- memcpy(map, inst->bsplineMap, 256*sizeof(int));
- memcpy(mapLuma, inst->bsplineLumaMap, 256*sizeof(double));
+ map = malloc(361*sizeof(double));
+ memcpy(map, inst->bsplineMap, (inst->channel == CHANNEL_HUE ? 361 : 256)*sizeof(double));
+ if (inst->channel != CHANNEL_SATURATION && inst->channel != CHANNEL_HUE) {
+ if (inst->channel == CHANNEL_LUMA)
+ memcpy(mapLuma, map, 256*sizeof(double));
+ else
+ for (i = 0; i < 256; ++i)
+ mapI[i] = (int)map[i];
+ }
}
int r, g, b, luma;
@@ -725,42 +755,41 @@ void f0r_update(f0r_instance_t instance, double time,
switch ((int)inst->channel) {
case CHANNEL_RGB:
while (len--) {
- *dst++ = map[*src++]; // r
- *dst++ = map[*src++]; // g
- *dst++ = map[*src++]; // b
+ *dst++ = mapI[*src++]; // r
+ *dst++ = mapI[*src++]; // g
+ *dst++ = mapI[*src++]; // b
*dst++ = *src++; // a
}
break;
case CHANNEL_RED:
+ memcpy(outframe, inframe, len*sizeof(uint32_t));
while (len--) {
- *dst++ = map[*src++]; // r
- *dst++ = *src++; // g
- *dst++ = *src++; // b
- *dst++ = *src++; // a
+ *dst = mapI[*dst];
+ dst += 4;
}
break;
case CHANNEL_GREEN:
+ memcpy(outframe, inframe, len*sizeof(uint32_t));
+ dst += 1;
while (len--) {
- *dst++ = *src++; // r
- *dst++ = map[*src++]; // g
- *dst++ = *src++; // b
- *dst++ = *src++; // a
+ *dst = mapI[*dst];
+ dst += 4;
}
break;
case CHANNEL_BLUE:
+ memcpy(outframe, inframe, len*sizeof(uint32_t));
+ dst += 2;
while (len--) {
- *dst++ = *src++; // r
- *dst++ = *src++; // g
- *dst++ = map[*src++]; // b
- *dst++ = *src++; // a
+ *dst = mapI[*dst];
+ dst += 4;
}
break;
case CHANNEL_ALPHA:
+ memcpy(outframe, inframe, len*sizeof(uint32_t));
+ dst += 3;
while (len--) {
- *dst++ = *src++; // r
- *dst++ = *src++; // g
- *dst++ = *src++; // b
- *dst++ = map[*src++]; // a
+ *dst = mapI[*dst];
+ dst += 4;
}
break;
case CHANNEL_LUMA:
@@ -777,8 +806,7 @@ void f0r_update(f0r_instance_t instance, double time,
r = *src++;
g = *src++;
b = *src++;
- // should we maybe use ROUND here?
- luma = (int)(factorR * r + factorG * g + factorB * b);
+ luma = ROUND(factorR * r + factorG * g + factorB * b);
lumaValue = mapLuma[luma];
if (luma == 0) {
*dst++ = lumaValue;
@@ -793,17 +821,13 @@ void f0r_update(f0r_instance_t instance, double time,
}
break;
case CHANNEL_HUE:
- map01 = malloc(256 * sizeof(double));
- for (i = 0; i < 256; ++i) {
- map01[i] = inst->bsplineMap[i] / 255.;
- }
while (len--) {
rf = *src++;
gf = *src++;
bf = *src++;
RGBtoHSV(rf, gf, bf, &hue, &sat, &val);
if (hue != -1) {
- HSVtoRGB(&rf, &gf, &bf, map01[(int)(hue / 6. * 255)] * 6, sat, val);
+ HSVtoRGB(&rf, &gf, &bf, map[(int)hue], sat, val);
*dst++ = rf * 255;
*dst++ = gf * 255;
*dst++ = bf * 255;
@@ -816,16 +840,12 @@ void f0r_update(f0r_instance_t instance, double time,
}
break;
case CHANNEL_SATURATION:
- map01 = malloc(256 * sizeof(double));
- for (i = 0; i < 256; ++i) {
- map01[i] = inst->bsplineMap[i] / 255.;
- }
while (len--) {
rf = *src++;
gf = *src++;
bf = *src++;
RGBtoHSV(rf, gf, bf, &hue, &sat, &val);
- HSVtoRGB(&rf, &gf, &bf, hue, map01[(int)(sat * 255)], val);
+ HSVtoRGB(&rf, &gf, &bf, hue, map[(int)(sat * 255)], val);
*dst++ = rf * 255;
*dst++ = gf * 255;
*dst++ = bf * 255;
@@ -833,6 +853,9 @@ void f0r_update(f0r_instance_t instance, double time,
}
}
+ if (map)
+ free(map);
+
if (inst->drawCurves && !strlen(inst->bspline)) {
unsigned char color[] = {0, 0, 0};
if (inst->channel == CHANNEL_RED || inst->channel == CHANNEL_GREEN || inst->channel == CHANNEL_BLUE)