New Curves Widget option: curves can get extrapolated extension.

Especially for Compositing it was annoying that colors always got clipped
in the 0.0-1.0 range. For this reason, extrapolated Curves now is the
default. Old saved files still have horizontal extrapolation.

Set the option with 'Tools' menu (wrench icon). This is a setting per
curve, so you might need to set all 4 curves for an RGBA curves widget.
This commit is contained in:
Ton Roosendaal 2006-10-27 20:27:13 +00:00
parent a7d3a58ba9
commit 129cab4137
4 changed files with 93 additions and 15 deletions

@ -73,6 +73,7 @@ CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, floa
cumap->bwmul[0]= cumap->bwmul[1]= cumap->bwmul[2]= 1.0f;
for(a=0; a<tot; a++) {
cumap->cm[a].flag= CUMA_EXTEND_EXTRAPOLATE;
cumap->cm[a].totpoint= 2;
cumap->cm[a].curve= MEM_callocN(2*sizeof(CurveMapPoint), "curve points");
@ -278,6 +279,37 @@ static void calchandle_curvemap(BezTriple *bezt, BezTriple *prev, BezTriple *nex
}
}
/* in X, out Y.
X is presumed to be outside first or last */
static float curvemap_calc_extend(CurveMap *cuma, float x, float *first, float *last)
{
if(x <= first[0]) {
if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0) {
/* no extrapolate */
return first[1];
}
else {
if(cuma->ext_in[0]==0.0f)
return first[1] + cuma->ext_in[1]*10000.0f;
else
return first[1] + cuma->ext_in[1]*(x - first[0])/cuma->ext_in[0];
}
}
else if(x >= last[0]) {
if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0) {
/* no extrapolate */
return last[1];
}
else {
if(cuma->ext_out[0]==0.0f)
return last[1] - cuma->ext_out[1]*10000.0f;
else
return last[1] + cuma->ext_out[1]*(x - last[0])/cuma->ext_out[0];
}
}
return 0.0f;
}
/* only creates a table for a single channel in CurveMapping */
static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
{
@ -333,6 +365,7 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
if(nlen>FLT_EPSILON) {
VecMulf(vec, hlen/nlen);
VecAddf(bezt[0].vec[2], vec, bezt[0].vec[1]);
VecSubf(bezt[0].vec[0], bezt[0].vec[1], vec);
}
}
a= cuma->totpoint-1;
@ -349,6 +382,7 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
if(nlen>FLT_EPSILON) {
VecMulf(vec, hlen/nlen);
VecAddf(bezt[a].vec[0], vec, bezt[a].vec[1]);
VecSubf(bezt[a].vec[2], bezt[a].vec[1], vec);
}
}
}
@ -364,8 +398,23 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
forward_diff_bezier(bezt[a].vec[1][1], bezt[a].vec[2][1], bezt[a+1].vec[0][1], bezt[a+1].vec[1][1], fp+1, CM_RESOL-1, 2);
}
MEM_freeN(bezt);
/* store first and last handle for extrapolation, unit length */
cuma->ext_in[0]= bezt[0].vec[0][0] - bezt[0].vec[1][0];
cuma->ext_in[1]= bezt[0].vec[0][1] - bezt[0].vec[1][1];
range= sqrt(cuma->ext_in[0]*cuma->ext_in[0] + cuma->ext_in[1]*cuma->ext_in[1]);
cuma->ext_in[0]/= range;
cuma->ext_in[1]/= range;
a= cuma->totpoint-1;
cuma->ext_out[0]= bezt[a].vec[1][0] - bezt[a].vec[2][0];
cuma->ext_out[1]= bezt[a].vec[1][1] - bezt[a].vec[2][1];
range= sqrt(cuma->ext_out[0]*cuma->ext_out[0] + cuma->ext_out[1]*cuma->ext_out[1]);
cuma->ext_out[0]/= range;
cuma->ext_out[1]/= range;
/* cleanup */
MEM_freeN(bezt);
range= CM_TABLEDIV*(cuma->maxtable - cuma->mintable);
cuma->range= 1.0f/range;
@ -373,10 +422,8 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
fp= allpoints;
lastpoint= allpoints + 2*(totpoint-1);
cmp= MEM_callocN((CM_TABLE+1)*sizeof(CurveMapPoint), "dist table");
cmp[0].x= cuma->mintable;
cmp[0].y= allpoints[1];
for(a=1; a<CM_TABLE; a++) {
for(a=0; a<=CM_TABLE; a++) {
curf= cuma->mintable + range*(float)a;
cmp[a].x= curf;
@ -384,8 +431,8 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
while(curf >= fp[0] && fp!=lastpoint) {
fp+=2;
}
if(curf >= fp[0] && fp==lastpoint)
cmp[a].y= fp[1];
if(fp==allpoints || (curf >= fp[0] && fp==lastpoint))
cmp[a].y= curvemap_calc_extend(cuma, curf, allpoints, lastpoint);
else {
float fac1= fp[0] - fp[-2];
float fac2= fp[0] - curf;
@ -396,8 +443,6 @@ static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
cmp[a].y= fac1*fp[-1] + (1.0f-fac1)*fp[1];
}
}
cmp[CM_TABLE].x= cuma->maxtable;
cmp[CM_TABLE].y= allpoints[2*totpoint-1];
MEM_freeN(allpoints);
cuma->table= cmp;
@ -518,11 +563,16 @@ float curvemap_evaluateF(CurveMap *cuma, float value)
/* index in table */
fi= (value-cuma->mintable)*cuma->range;
i= (int)fi;
if(i<0) return cuma->table[0].y;
if(i>=CM_TABLE) return cuma->table[CM_TABLE].y;
fi= fi-(float)i;
return (1.0f-fi)*cuma->table[i].y + (fi)*cuma->table[i+1].y;
if(fi<0.0f || fi>cuma->range)
return curvemap_calc_extend(cuma, value, &cuma->table[0].x, &cuma->table[CM_TABLE].x);
else {
if(i<0) return cuma->table[0].y;
if(i>=CM_TABLE) return cuma->table[CM_TABLE].y;
fi= fi-(float)i;
return (1.0f-fi)*cuma->table[i].y + (fi)*cuma->table[i+1].y;
}
}
/* works with curve 'cur' */

@ -53,11 +53,15 @@ typedef struct CurveMap {
float range; /* quick multiply value for reading table */
float mintable, maxtable; /* the x-axis range for the table */
float ext_in[2], ext_out[2]; /* for extrapolated curves, the direction vector */
CurveMapPoint *curve; /* actual curve */
CurveMapPoint *table; /* display and evaluate table */
CurveMapPoint *premultable; /* for RGB curves, premulled table */
} CurveMap;
/* cuma->flag */
#define CUMA_EXTEND_EXTRAPOLATE 1
typedef struct CurveMapping {
int flag, cur; /* cur; for buttons, to show active curve */
@ -68,7 +72,7 @@ typedef struct CurveMapping {
float bwmul[3], padf; /* black/white point multiply value, for speed */
} CurveMapping;
/* cumap->flag */
/* cumapping->flag */
#define CUMA_DO_CLIP 1
#define CUMA_PREMULLED 2

@ -403,6 +403,14 @@ static void curvemap_tools_dofunc(void *cumap_v, int event)
curvemap_sethandle(cuma, 0);
curvemapping_changed(cumap, 0);
break;
case 4: /* extend horiz */
cuma->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
curvemapping_changed(cumap, 0);
break;
case 5: /* extend extrapolate */
cuma->flag |= CUMA_EXTEND_EXTRAPOLATE;
curvemapping_changed(cumap, 0);
break;
}
addqueue(curarea->win, REDRAW, 1);
}
@ -418,6 +426,8 @@ static uiBlock *curvemap_tools_func(void *cumap_v)
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Reset View", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 1, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Vector Handle", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 2, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Auto Handle", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 3, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Horizontal", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 4, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Extend Extrapolated", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 5, "");
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Reset Curve", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 0, 0, "");
uiBlockSetDirection(block, UI_RIGHT);

@ -2187,13 +2187,27 @@ static void ui_draw_but_CURVE(uiBut *but)
curvemapping_changed(cumap, 0); /* 0 = no remove doubles */
cmp= cuma->table;
glVertex2f(but->x1, but->y1 + zoomy*(cmp[0].y-offsy)); /* first point */
/* first point */
if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0)
glVertex2f(but->x1, but->y1 + zoomy*(cmp[0].y-offsy));
else {
fx= but->x1 + zoomx*(cmp[0].x-offsx + cuma->ext_in[0]);
fy= but->y1 + zoomy*(cmp[0].y-offsy + cuma->ext_in[1]);
glVertex2f(fx, fy);
}
for(a=0; a<=CM_TABLE; a++) {
fx= but->x1 + zoomx*(cmp[a].x-offsx);
fy= but->y1 + zoomy*(cmp[a].y-offsy);
glVertex2f(fx, fy);
}
glVertex2f(but->x2, but->y1 + zoomy*(cmp[CM_TABLE].y-offsy)); /* last point */
/* last point */
if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0)
glVertex2f(but->x2, but->y1 + zoomy*(cmp[CM_TABLE].y-offsy));
else {
fx= but->x1 + zoomx*(cmp[CM_TABLE].x-offsx - cuma->ext_out[0]);
fy= but->y1 + zoomy*(cmp[CM_TABLE].y-offsy - cuma->ext_out[1]);
glVertex2f(fx, fy);
}
glEnd();
/* the points, use aspect to make them visible on edges */