4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 * The Original Code is Copyright (C) 2005 Blender Foundation.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL/BL DUAL LICENSE BLOCK *****
39 #include "MEM_guardedalloc.h"
41 #include "DNA_color_types.h"
42 #include "DNA_curve_types.h"
44 #include "BKE_colortools.h"
45 #include "BKE_curve.h"
47 #include "BKE_utildefines.h"
49 #include "BLI_blenlib.h"
52 #include "IMB_imbuf.h"
53 #include "IMB_imbuf_types.h"
56 void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int UNUSED(w))
61 unsigned char *rc= rectc;
63 for(y=y1; y<y2; y++) {
64 for(x=x1; x<x2; x++, rf+=4, rc+=4) {
65 srgb[0]= linearrgb_to_srgb(rf[0]);
66 srgb[1]= linearrgb_to_srgb(rf[1]);
67 srgb[2]= linearrgb_to_srgb(rf[2]);
69 rc[0]= FTOCHAR(srgb[0]);
70 rc[1]= FTOCHAR(srgb[1]);
71 rc[2]= FTOCHAR(srgb[2]);
72 rc[3]= FTOCHAR(rf[3]);
77 void floatbuf_to_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int UNUSED(w))
81 unsigned char *rc= rectc;
83 for(y=y1; y<y2; y++) {
84 for(x=x1; x<x2; x++, rf+=4, rc+=4) {
85 rc[0]= FTOCHAR(rf[0]);
86 rc[1]= FTOCHAR(rf[1]);
87 rc[2]= FTOCHAR(rf[2]);
88 rc[3]= FTOCHAR(rf[3]);
94 /* ********************************* color curve ********************* */
96 /* ***************** operations on full struct ************* */
98 CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
102 float clipminx, clipminy, clipmaxx, clipmaxy;
104 cumap= MEM_callocN(sizeof(CurveMapping), "new curvemap");
105 cumap->flag= CUMA_DO_CLIP;
106 if(tot==4) cumap->cur= 3; /* rhms, hack for 'col' curve? */
108 clipminx = MIN2(minx, maxx);
109 clipminy = MIN2(miny, maxy);
110 clipmaxx = MAX2(minx, maxx);
111 clipmaxy = MAX2(miny, maxy);
113 BLI_init_rctf(&cumap->curr, clipminx, clipmaxx, clipminy, clipmaxy);
114 cumap->clipr= cumap->curr;
116 cumap->white[0]= cumap->white[1]= cumap->white[2]= 1.0f;
117 cumap->bwmul[0]= cumap->bwmul[1]= cumap->bwmul[2]= 1.0f;
119 for(a=0; a<tot; a++) {
120 cumap->cm[a].flag= CUMA_EXTEND_EXTRAPOLATE;
121 cumap->cm[a].totpoint= 2;
122 cumap->cm[a].curve= MEM_callocN(2*sizeof(CurveMapPoint), "curve points");
124 cumap->cm[a].curve[0].x= minx;
125 cumap->cm[a].curve[0].y= miny;
126 cumap->cm[a].curve[1].x= maxx;
127 cumap->cm[a].curve[1].y= maxy;
130 cumap->changed_timestamp = 0;
135 void curvemapping_free(CurveMapping *cumap)
140 for(a=0; a<CM_TOT; a++) {
141 if(cumap->cm[a].curve) MEM_freeN(cumap->cm[a].curve);
142 if(cumap->cm[a].table) MEM_freeN(cumap->cm[a].table);
143 if(cumap->cm[a].premultable) MEM_freeN(cumap->cm[a].premultable);
149 CurveMapping *curvemapping_copy(CurveMapping *cumap)
154 CurveMapping *cumapn= MEM_dupallocN(cumap);
155 for(a=0; a<CM_TOT; a++) {
156 if(cumap->cm[a].curve)
157 cumapn->cm[a].curve= MEM_dupallocN(cumap->cm[a].curve);
158 if(cumap->cm[a].table)
159 cumapn->cm[a].table= MEM_dupallocN(cumap->cm[a].table);
160 if(cumap->cm[a].premultable)
161 cumapn->cm[a].premultable= MEM_dupallocN(cumap->cm[a].premultable);
168 void curvemapping_set_black_white(CurveMapping *cumap, float *black, float *white)
173 VECCOPY(cumap->white, white);
175 VECCOPY(cumap->black, black);
178 if(cumap->white[a]==cumap->black[a])
179 cumap->bwmul[a]= 0.0f;
181 cumap->bwmul[a]= 1.0f/(cumap->white[a] - cumap->black[a]);
185 /* ***************** operations on single curve ************* */
186 /* ********** NOTE: requires curvemapping_changed() call after ******** */
188 /* removes with flag set */
189 void curvemap_remove(CurveMap *cuma, int flag)
191 CurveMapPoint *cmp= MEM_mallocN((cuma->totpoint)*sizeof(CurveMapPoint), "curve points");
194 /* well, lets keep the two outer points! */
195 cmp[0]= cuma->curve[0];
196 for(a=1, b=1; a<cuma->totpoint-1; a++) {
197 if(!(cuma->curve[a].flag & flag)) {
198 cmp[b]= cuma->curve[a];
203 cmp[b]= cuma->curve[a];
205 MEM_freeN(cuma->curve);
207 cuma->totpoint -= removed;
210 void curvemap_insert(CurveMap *cuma, float x, float y)
212 CurveMapPoint *cmp= MEM_callocN((cuma->totpoint+1)*sizeof(CurveMapPoint), "curve points");
213 int a, b, foundloc= 0;
215 /* insert fragments of the old one and the new point to the new curve */
217 for(a=0, b=0; a<cuma->totpoint; a++) {
218 if((x < cuma->curve[a].x) && !foundloc) {
221 cmp[a].flag= CUMA_SELECT;
225 cmp[a].x= cuma->curve[b].x;
226 cmp[a].y= cuma->curve[b].y;
227 cmp[a].flag= cuma->curve[b].flag;
228 cmp[a].flag &= ~CUMA_SELECT; /* make sure old points don't remain selected */
229 cmp[a].shorty= cuma->curve[b].shorty;
234 /* free old curve and replace it with new one */
235 MEM_freeN(cuma->curve);
239 void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset, int slope)
242 MEM_freeN(cuma->curve);
245 case CURVE_PRESET_LINE: cuma->totpoint= 2; break;
246 case CURVE_PRESET_SHARP: cuma->totpoint= 4; break;
247 case CURVE_PRESET_SMOOTH: cuma->totpoint= 4; break;
248 case CURVE_PRESET_MAX: cuma->totpoint= 2; break;
249 case CURVE_PRESET_MID9: cuma->totpoint= 9; break;
250 case CURVE_PRESET_ROUND: cuma->totpoint= 4; break;
251 case CURVE_PRESET_ROOT: cuma->totpoint= 4; break;
254 cuma->curve= MEM_callocN(cuma->totpoint*sizeof(CurveMapPoint), "curve points");
257 case CURVE_PRESET_LINE:
258 cuma->curve[0].x= clipr->xmin;
259 cuma->curve[0].y= clipr->ymax;
260 cuma->curve[0].flag= 0;
261 cuma->curve[1].x= clipr->xmax;
262 cuma->curve[1].y= clipr->ymin;
263 cuma->curve[1].flag= 0;
265 case CURVE_PRESET_SHARP:
268 cuma->curve[1].x= 0.25;
269 cuma->curve[1].y= 0.50;
270 cuma->curve[2].x= 0.75;
271 cuma->curve[2].y= 0.04;
275 case CURVE_PRESET_SMOOTH:
278 cuma->curve[1].x= 0.25;
279 cuma->curve[1].y= 0.94;
280 cuma->curve[2].x= 0.75;
281 cuma->curve[2].y= 0.06;
285 case CURVE_PRESET_MAX:
291 case CURVE_PRESET_MID9:
294 for (i=0; i < cuma->totpoint; i++)
296 cuma->curve[i].x= i / ((float)cuma->totpoint-1);
297 cuma->curve[i].y= 0.5;
301 case CURVE_PRESET_ROUND:
304 cuma->curve[1].x= 0.5;
305 cuma->curve[1].y= 0.90;
306 cuma->curve[2].x= 0.86;
307 cuma->curve[2].y= 0.5;
311 case CURVE_PRESET_ROOT:
314 cuma->curve[1].x= 0.25;
315 cuma->curve[1].y= 0.95;
316 cuma->curve[2].x= 0.75;
317 cuma->curve[2].y= 0.44;
323 /* mirror curve in x direction to have positive slope
324 * rather than default negative slope */
325 if (slope == CURVEMAP_SLOPE_POSITIVE) {
326 int i, last=cuma->totpoint-1;
327 CurveMapPoint *newpoints= MEM_dupallocN(cuma->curve);
329 for (i=0; i<cuma->totpoint; i++) {
330 newpoints[i].y = cuma->curve[last-i].y;
333 MEM_freeN(cuma->curve);
334 cuma->curve = newpoints;
338 MEM_freeN(cuma->table);
343 /* if type==1: vector, else auto */
344 void curvemap_sethandle(CurveMap *cuma, int type)
348 for(a=0; a<cuma->totpoint; a++) {
349 if(cuma->curve[a].flag & CUMA_SELECT) {
350 if(type) cuma->curve[a].flag |= CUMA_VECTOR;
351 else cuma->curve[a].flag &= ~CUMA_VECTOR;
356 /* *********************** Making the tables and display ************** */
358 /* reduced copy of garbled calchandleNurb() code in curve.c */
359 static void calchandle_curvemap(BezTriple *bezt, BezTriple *prev, BezTriple *next, int UNUSED(mode))
361 float *p1,*p2,*p3,pt[3];
362 float dx1,dy1, dx,dy, vx,vy, len,len1,len2;
364 if(bezt->h1==0 && bezt->h2==0) return;
370 pt[0]= 2*p2[0]- p3[0];
371 pt[1]= 2*p2[1]- p3[1];
374 else p1= prev->vec[1];
378 pt[0]= 2*p2[0]- p1[0];
379 pt[1]= 2*p2[1]- p1[1];
382 else p3= next->vec[1];
387 len1= (float)sqrt(dx*dx+dy*dy);
392 len2= (float)sqrt(dx1*dx1+dy1*dy1);
394 if(len1==0.0f) len1=1.0f;
395 if(len2==0.0f) len2=1.0f;
397 if(bezt->h1==HD_AUTO || bezt->h2==HD_AUTO) { /* auto */
398 vx= dx1/len2 + dx/len1;
399 vy= dy1/len2 + dy/len1;
401 len= 2.5614f*(float)sqrt(vx*vx + vy*vy);
404 if(bezt->h1==HD_AUTO) {
406 *(p2-3)= *p2-vx*len1;
407 *(p2-2)= *(p2+1)-vy*len1;
409 if(bezt->h2==HD_AUTO) {
411 *(p2+3)= *p2+vx*len2;
412 *(p2+4)= *(p2+1)+vy*len2;
417 if(bezt->h1==HD_VECT) { /* vector */
423 if(bezt->h2==HD_VECT) {
427 *(p2+4)= *(p2+1)+dy1;
432 X is presumed to be outside first or last */
433 static float curvemap_calc_extend(CurveMap *cuma, float x, float *first, float *last)
436 if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0) {
441 if(cuma->ext_in[0]==0.0f)
442 return first[1] + cuma->ext_in[1]*10000.0f;
444 return first[1] + cuma->ext_in[1]*(x - first[0])/cuma->ext_in[0];
447 else if(x >= last[0]) {
448 if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0) {
453 if(cuma->ext_out[0]==0.0f)
454 return last[1] - cuma->ext_out[1]*10000.0f;
456 return last[1] + cuma->ext_out[1]*(x - last[0])/cuma->ext_out[0];
462 /* only creates a table for a single channel in CurveMapping */
463 static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
465 CurveMapPoint *cmp= cuma->curve;
467 float *fp, *allpoints, *lastpoint, curf, range;
470 if(cuma->curve==NULL) return;
472 /* default rect also is table range */
473 cuma->mintable= clipr->xmin;
474 cuma->maxtable= clipr->xmax;
476 /* hrmf... we now rely on blender ipo beziers, these are more advanced */
477 bezt= MEM_callocN(cuma->totpoint*sizeof(BezTriple), "beztarr");
479 for(a=0; a<cuma->totpoint; a++) {
480 cuma->mintable= MIN2(cuma->mintable, cmp[a].x);
481 cuma->maxtable= MAX2(cuma->maxtable, cmp[a].x);
482 bezt[a].vec[1][0]= cmp[a].x;
483 bezt[a].vec[1][1]= cmp[a].y;
484 if(cmp[a].flag & CUMA_VECTOR)
485 bezt[a].h1= bezt[a].h2= HD_VECT;
487 bezt[a].h1= bezt[a].h2= HD_AUTO;
490 for(a=0; a<cuma->totpoint; a++) {
492 calchandle_curvemap(bezt, NULL, bezt+1, 0);
493 else if(a==cuma->totpoint-1)
494 calchandle_curvemap(bezt+a, bezt+a-1, NULL, 0);
496 calchandle_curvemap(bezt+a, bezt+a-1, bezt+a+1, 0);
499 /* first and last handle need correction, instead of pointing to center of next/prev,
500 we let it point to the closest handle */
501 if(cuma->totpoint>2) {
502 float hlen, nlen, vec[3];
504 if(bezt[0].h2==HD_AUTO) {
506 hlen= len_v3v3(bezt[0].vec[1], bezt[0].vec[2]); /* original handle length */
507 /* clip handle point */
508 VECCOPY(vec, bezt[1].vec[0]);
509 if(vec[0] < bezt[0].vec[1][0])
510 vec[0]= bezt[0].vec[1][0];
512 sub_v3_v3(vec, bezt[0].vec[1]);
514 if(nlen>FLT_EPSILON) {
515 mul_v3_fl(vec, hlen/nlen);
516 add_v3_v3v3(bezt[0].vec[2], vec, bezt[0].vec[1]);
517 sub_v3_v3v3(bezt[0].vec[0], bezt[0].vec[1], vec);
521 if(bezt[a].h2==HD_AUTO) {
523 hlen= len_v3v3(bezt[a].vec[1], bezt[a].vec[0]); /* original handle length */
524 /* clip handle point */
525 VECCOPY(vec, bezt[a-1].vec[2]);
526 if(vec[0] > bezt[a].vec[1][0])
527 vec[0]= bezt[a].vec[1][0];
529 sub_v3_v3(vec, bezt[a].vec[1]);
531 if(nlen>FLT_EPSILON) {
532 mul_v3_fl(vec, hlen/nlen);
533 add_v3_v3v3(bezt[a].vec[0], vec, bezt[a].vec[1]);
534 sub_v3_v3v3(bezt[a].vec[2], bezt[a].vec[1], vec);
538 /* make the bezier curve */
540 MEM_freeN(cuma->table);
541 totpoint= (cuma->totpoint-1)*CM_RESOL;
542 fp= allpoints= MEM_callocN(totpoint*2*sizeof(float), "table");
544 for(a=0; a<cuma->totpoint-1; a++, fp += 2*CM_RESOL) {
545 correct_bezpart(bezt[a].vec[1], bezt[a].vec[2], bezt[a+1].vec[0], bezt[a+1].vec[1]);
546 forward_diff_bezier(bezt[a].vec[1][0], bezt[a].vec[2][0], bezt[a+1].vec[0][0], bezt[a+1].vec[1][0], fp, CM_RESOL-1, 2*sizeof(float));
547 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*sizeof(float));
550 /* store first and last handle for extrapolation, unit length */
551 cuma->ext_in[0]= bezt[0].vec[0][0] - bezt[0].vec[1][0];
552 cuma->ext_in[1]= bezt[0].vec[0][1] - bezt[0].vec[1][1];
553 range= sqrt(cuma->ext_in[0]*cuma->ext_in[0] + cuma->ext_in[1]*cuma->ext_in[1]);
554 cuma->ext_in[0]/= range;
555 cuma->ext_in[1]/= range;
558 cuma->ext_out[0]= bezt[a].vec[1][0] - bezt[a].vec[2][0];
559 cuma->ext_out[1]= bezt[a].vec[1][1] - bezt[a].vec[2][1];
560 range= sqrt(cuma->ext_out[0]*cuma->ext_out[0] + cuma->ext_out[1]*cuma->ext_out[1]);
561 cuma->ext_out[0]/= range;
562 cuma->ext_out[1]/= range;
567 range= CM_TABLEDIV*(cuma->maxtable - cuma->mintable);
568 cuma->range= 1.0f/range;
570 /* now make a table with CM_TABLE equal x distances */
572 lastpoint= allpoints + 2*(totpoint-1);
573 cmp= MEM_callocN((CM_TABLE+1)*sizeof(CurveMapPoint), "dist table");
575 for(a=0; a<=CM_TABLE; a++) {
576 curf= cuma->mintable + range*(float)a;
579 /* get the first x coordinate larger than curf */
580 while(curf >= fp[0] && fp!=lastpoint) {
583 if(fp==allpoints || (curf >= fp[0] && fp==lastpoint))
584 cmp[a].y= curvemap_calc_extend(cuma, curf, allpoints, lastpoint);
586 float fac1= fp[0] - fp[-2];
587 float fac2= fp[0] - curf;
588 if(fac1 > FLT_EPSILON)
592 cmp[a].y= fac1*fp[-1] + (1.0f-fac1)*fp[1];
596 MEM_freeN(allpoints);
600 /* call when you do images etc, needs restore too. also verifies tables */
601 /* it uses a flag to prevent premul or free to happen twice */
602 void curvemapping_premultiply(CurveMapping *cumap, int restore)
607 if(cumap->flag & CUMA_PREMULLED) {
609 MEM_freeN(cumap->cm[a].table);
610 cumap->cm[a].table= cumap->cm[a].premultable;
611 cumap->cm[a].premultable= NULL;
614 cumap->flag &= ~CUMA_PREMULLED;
618 if((cumap->flag & CUMA_PREMULLED)==0) {
619 /* verify and copy */
621 if(cumap->cm[a].table==NULL)
622 curvemap_make_table(cumap->cm+a, &cumap->clipr);
623 cumap->cm[a].premultable= cumap->cm[a].table;
624 cumap->cm[a].table= MEM_mallocN((CM_TABLE+1)*sizeof(CurveMapPoint), "premul table");
625 memcpy(cumap->cm[a].table, cumap->cm[a].premultable, (CM_TABLE+1)*sizeof(CurveMapPoint));
628 if(cumap->cm[3].table==NULL)
629 curvemap_make_table(cumap->cm+3, &cumap->clipr);
634 for(b=0; b<=CM_TABLE; b++) {
635 cumap->cm[a].table[b].y= curvemap_evaluateF(cumap->cm+3, cumap->cm[a].table[b].y);
639 cumap->flag |= CUMA_PREMULLED;
644 static int sort_curvepoints(const void *a1, const void *a2)
646 const struct CurveMapPoint *x1=a1, *x2=a2;
648 if( x1->x > x2->x ) return 1;
649 else if( x1->x < x2->x) return -1;
653 /* ************************ more CurveMapping calls *************** */
655 /* note; only does current curvemap! */
656 void curvemapping_changed(CurveMapping *cumap, int rem_doubles)
658 CurveMap *cuma= cumap->cm+cumap->cur;
659 CurveMapPoint *cmp= cuma->curve;
660 rctf *clipr= &cumap->clipr;
661 float thresh= 0.01f*(clipr->xmax - clipr->xmin);
662 float dx= 0.0f, dy= 0.0f;
665 cumap->changed_timestamp++;
667 /* clamp with clip */
668 if(cumap->flag & CUMA_DO_CLIP) {
669 for(a=0; a<cuma->totpoint; a++) {
670 if(cmp[a].flag & CUMA_SELECT) {
671 if(cmp[a].x < clipr->xmin)
672 dx= MIN2(dx, cmp[a].x - clipr->xmin);
673 else if(cmp[a].x > clipr->xmax)
674 dx= MAX2(dx, cmp[a].x - clipr->xmax);
675 if(cmp[a].y < clipr->ymin)
676 dy= MIN2(dy, cmp[a].y - clipr->ymin);
677 else if(cmp[a].y > clipr->ymax)
678 dy= MAX2(dy, cmp[a].y - clipr->ymax);
681 for(a=0; a<cuma->totpoint; a++) {
682 if(cmp[a].flag & CUMA_SELECT) {
690 qsort(cmp, cuma->totpoint, sizeof(CurveMapPoint), sort_curvepoints);
692 /* remove doubles, threshold set on 1% of default range */
693 if(rem_doubles && cuma->totpoint>2) {
694 for(a=0; a<cuma->totpoint-1; a++) {
695 dx= cmp[a].x - cmp[a+1].x;
696 dy= cmp[a].y - cmp[a+1].y;
697 if( sqrt(dx*dx + dy*dy) < thresh ) {
700 if(cmp[a+1].flag & CUMA_SELECT)
701 cmp[a].flag |= CUMA_SELECT;
705 if(cmp[a].flag & CUMA_SELECT)
706 cmp[a+1].flag |= CUMA_SELECT;
708 break; /* we assume 1 deletion per edit is ok */
711 if(a != cuma->totpoint-1)
712 curvemap_remove(cuma, 2);
714 curvemap_make_table(cuma, clipr);
717 /* table should be verified */
718 float curvemap_evaluateF(CurveMap *cuma, float value)
724 fi= (value-cuma->mintable)*cuma->range;
727 /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */
728 if(fi<0.0f || fi>CM_TABLE)
729 return curvemap_calc_extend(cuma, value, &cuma->table[0].x, &cuma->table[CM_TABLE].x);
731 if(i<0) return cuma->table[0].y;
732 if(i>=CM_TABLE) return cuma->table[CM_TABLE].y;
735 return (1.0f-fi)*cuma->table[i].y + (fi)*cuma->table[i+1].y;
739 /* works with curve 'cur' */
740 float curvemapping_evaluateF(CurveMapping *cumap, int cur, float value)
742 CurveMap *cuma= cumap->cm+cur;
744 /* allocate or bail out */
745 if(cuma->table==NULL) {
746 curvemap_make_table(cuma, &cumap->clipr);
747 if(cuma->table==NULL)
750 return curvemap_evaluateF(cuma, value);
754 void curvemapping_evaluate3F(CurveMapping *cumap, float *vecout, const float *vecin)
756 vecout[0]= curvemapping_evaluateF(cumap, 0, vecin[0]);
757 vecout[1]= curvemapping_evaluateF(cumap, 1, vecin[1]);
758 vecout[2]= curvemapping_evaluateF(cumap, 2, vecin[2]);
761 /* RGB case, no black/white points, no premult */
762 void curvemapping_evaluateRGBF(CurveMapping *cumap, float *vecout, const float *vecin)
764 vecout[0]= curvemapping_evaluateF(cumap, 0, curvemapping_evaluateF(cumap, 3, vecin[0]));
765 vecout[1]= curvemapping_evaluateF(cumap, 1, curvemapping_evaluateF(cumap, 3, vecin[1]));
766 vecout[2]= curvemapping_evaluateF(cumap, 2, curvemapping_evaluateF(cumap, 3, vecin[2]));
770 /* RGB with black/white points and premult. tables are checked */
771 void curvemapping_evaluate_premulRGBF(CurveMapping *cumap, float *vecout, const float *vecin)
775 fac= (vecin[0] - cumap->black[0])*cumap->bwmul[0];
776 vecout[0]= curvemap_evaluateF(cumap->cm, fac);
778 fac= (vecin[1] - cumap->black[1])*cumap->bwmul[1];
779 vecout[1]= curvemap_evaluateF(cumap->cm+1, fac);
781 fac= (vecin[2] - cumap->black[2])*cumap->bwmul[2];
782 vecout[2]= curvemap_evaluateF(cumap->cm+2, fac);
787 /* basic error handler, if we dont do this blender will exit */
788 static int ErrorReportingFunction(int ErrorCode, const char *ErrorText)
790 fprintf(stderr, "%s:%d\n", ErrorText, ErrorCode);
795 void colorcorrection_do_ibuf(ImBuf *ibuf, const char *profile)
798 if (ibuf->crect == NULL)
800 cmsHPROFILE proofingProfile;
802 /* TODO, move to initialization area of code */
803 //cmsSetLogErrorHandler(ErrorReportingFunction);
804 cmsSetErrorHandler(ErrorReportingFunction);
806 /* will return NULL if the file isn't fount */
807 proofingProfile = cmsOpenProfileFromFile(profile, "r");
809 cmsErrorAction(LCMS_ERROR_SHOW);
811 if(proofingProfile) {
812 cmsHPROFILE imageProfile;
813 cmsHTRANSFORM hTransform;
815 ibuf->crect = MEM_mallocN(ibuf->x*ibuf->y*sizeof(int), "imbuf crect");
817 imageProfile = cmsCreate_sRGBProfile();
820 hTransform = cmsCreateProofingTransform(imageProfile, TYPE_RGBA_8, imageProfile, TYPE_RGBA_8,
822 INTENT_ABSOLUTE_COLORIMETRIC,
823 INTENT_ABSOLUTE_COLORIMETRIC,
824 cmsFLAGS_SOFTPROOFING);
826 cmsDoTransform(hTransform, ibuf->rect, ibuf->crect, ibuf->x * ibuf->y);
828 cmsDeleteTransform(hTransform);
829 cmsCloseProfile(imageProfile);
830 cmsCloseProfile(proofingProfile);
840 /* only used for image editor curves */
841 void curvemapping_do_ibuf(CurveMapping *cumap, ImBuf *ibuf)
852 if(ibuf->rect_float==NULL)
853 IMB_float_from_rect(ibuf);
854 else if(ibuf->rect==NULL)
855 imb_addrectImBuf(ibuf);
857 if (!ibuf->rect || !ibuf->rect_float)
860 /* work on a temp buffer, so can color manage afterwards.
861 * No worse off memory wise than comp nodes */
862 tmpbuf = IMB_dupImBuf(ibuf);
864 curvemapping_premultiply(cumap, 0);
866 pix_in= ibuf->rect_float;
867 pix_out= tmpbuf->rect_float;
870 stride= ibuf->channels;
872 for(pixel= ibuf->x*ibuf->y; pixel>0; pixel--, pix_in+=stride, pix_out+=stride) {
874 col[0]= curvemap_evaluateF(cumap->cm, *pix_in);
876 pix_out[1]= pix_out[2]= pix_out[3]= pix_out[0]= col[0];
879 curvemapping_evaluate_premulRGBF(cumap, col, pix_in);
884 pix_out[3]= pix_in[3];
890 IMB_rect_from_float(tmpbuf);
891 SWAP(unsigned int *, tmpbuf->rect, ibuf->rect);
892 IMB_freeImBuf(tmpbuf);
894 curvemapping_premultiply(cumap, 1);
897 int curvemapping_RGBA_does_something(CurveMapping *cumap)
901 if(cumap->black[0]!=0.0f) return 1;
902 if(cumap->black[1]!=0.0f) return 1;
903 if(cumap->black[2]!=0.0f) return 1;
904 if(cumap->white[0]!=1.0f) return 1;
905 if(cumap->white[1]!=1.0f) return 1;
906 if(cumap->white[2]!=1.0f) return 1;
908 for(a=0; a<CM_TOT; a++) {
909 if(cumap->cm[a].curve) {
910 if(cumap->cm[a].totpoint!=2) return 1;
912 if(cumap->cm[a].curve[0].x != 0.0f) return 1;
913 if(cumap->cm[a].curve[0].y != 0.0f) return 1;
914 if(cumap->cm[a].curve[1].x != 1.0f) return 1;
915 if(cumap->cm[a].curve[1].y != 1.0f) return 1;
921 void curvemapping_initialize(CurveMapping *cumap)
925 if(cumap==NULL) return;
927 for(a=0; a<CM_TOT; a++) {
928 if(cumap->cm[a].table==NULL)
929 curvemap_make_table(cumap->cm+a, &cumap->clipr);
933 void curvemapping_table_RGBA(CurveMapping *cumap, float **array, int *size)
938 *array = MEM_callocN(sizeof(float)*(*size)*4, "CurveMapping");
939 curvemapping_initialize(cumap);
941 for(a=0; a<*size; a++) {
942 if(cumap->cm[0].table)
943 (*array)[a*4+0]= cumap->cm[0].table[a].y;
944 if(cumap->cm[1].table)
945 (*array)[a*4+1]= cumap->cm[1].table[a].y;
946 if(cumap->cm[2].table)
947 (*array)[a*4+2]= cumap->cm[2].table[a].y;
948 if(cumap->cm[3].table)
949 (*array)[a*4+3]= cumap->cm[3].table[a].y;
953 /* ***************** Histogram **************** */
955 #define INV_255 (1.f/255.f)
957 DO_INLINE int get_bin_float(float f)
959 int bin= (int)(f*255);
961 /* note: clamp integer instead of float to avoid problems with NaN */
964 //return (int) (((f + 0.25) / 1.5) * 255);
969 DO_INLINE void save_sample_line(Scopes *scopes, const int idx, const float fx, float *rgb, float *ycc)
974 rgb_to_yuv(rgb[0], rgb[1], rgb[2], &yuv[0], &yuv[1], &yuv[2]);
975 scopes->vecscope[idx + 0] = yuv[1];
976 scopes->vecscope[idx + 1] = yuv[2];
979 switch (scopes->wavefrm_mode) {
980 case SCOPES_WAVEFRM_RGB:
981 scopes->waveform_1[idx + 0] = fx;
982 scopes->waveform_1[idx + 1] = rgb[0];
983 scopes->waveform_2[idx + 0] = fx;
984 scopes->waveform_2[idx + 1] = rgb[1];
985 scopes->waveform_3[idx + 0] = fx;
986 scopes->waveform_3[idx + 1] = rgb[2];
988 case SCOPES_WAVEFRM_LUMA:
989 scopes->waveform_1[idx + 0] = fx;
990 scopes->waveform_1[idx + 1] = ycc[0];
992 case SCOPES_WAVEFRM_YCC_JPEG:
993 case SCOPES_WAVEFRM_YCC_709:
994 case SCOPES_WAVEFRM_YCC_601:
995 scopes->waveform_1[idx + 0] = fx;
996 scopes->waveform_1[idx + 1] = ycc[0];
997 scopes->waveform_2[idx + 0] = fx;
998 scopes->waveform_2[idx + 1] = ycc[1];
999 scopes->waveform_3[idx + 0] = fx;
1000 scopes->waveform_3[idx + 1] = ycc[2];
1005 void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management)
1011 unsigned char *rc=NULL;
1012 unsigned int *bin_r, *bin_g, *bin_b, *bin_lum;
1013 int savedlines, saveline;
1014 float rgb[3], ycc[3], luma;
1017 if (scopes->ok == 1 ) return;
1019 if (scopes->hist.ymax == 0.f) scopes->hist.ymax = 1.f;
1022 if (!(ELEM(ibuf->channels, 3, 4))) return;
1023 scopes->hist.channels = 3;
1024 scopes->hist.x_resolution = 256;
1026 switch (scopes->wavefrm_mode) {
1027 case SCOPES_WAVEFRM_RGB:
1030 case SCOPES_WAVEFRM_LUMA:
1031 case SCOPES_WAVEFRM_YCC_JPEG:
1032 ycc_mode = BLI_YCC_JFIF_0_255;
1034 case SCOPES_WAVEFRM_YCC_601:
1035 ycc_mode = BLI_YCC_ITU_BT601;
1037 case SCOPES_WAVEFRM_YCC_709:
1038 ycc_mode = BLI_YCC_ITU_BT709;
1042 /* temp table to count pix value for histo */
1043 bin_r = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1044 bin_g = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1045 bin_b = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1046 bin_lum = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1048 /* convert to number of lines with logarithmic scale */
1049 scopes->sample_lines = (scopes->accuracy*0.01) * (scopes->accuracy*0.01) * ibuf->y;
1051 if (scopes->sample_full)
1052 scopes->sample_lines = ibuf->y;
1054 /* scan the image */
1056 for (c=0; c<3; c++) {
1057 scopes->minmax[c][0]=25500.0f;
1058 scopes->minmax[c][1]=-25500.0f;
1061 scopes->waveform_tot = ibuf->x*scopes->sample_lines;
1063 if (scopes->waveform_1)
1064 MEM_freeN(scopes->waveform_1);
1065 if (scopes->waveform_2)
1066 MEM_freeN(scopes->waveform_2);
1067 if (scopes->waveform_3)
1068 MEM_freeN(scopes->waveform_3);
1069 if (scopes->vecscope)
1070 MEM_freeN(scopes->vecscope);
1072 scopes->waveform_1= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 1");
1073 scopes->waveform_2= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 2");
1074 scopes->waveform_3= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 3");
1075 scopes->vecscope= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "vectorscope point channel");
1077 if (ibuf->rect_float)
1078 rf = ibuf->rect_float;
1079 else if (ibuf->rect)
1080 rc = (unsigned char *)ibuf->rect;
1082 for (y = 0; y < ibuf->y; y++) {
1083 if (savedlines<scopes->sample_lines && y>=((savedlines)*ibuf->y)/(scopes->sample_lines+1)) {
1086 for (x = 0; x < ibuf->x; x++) {
1088 if (ibuf->rect_float) {
1089 if (use_color_management)
1090 linearrgb_to_srgb_v3_v3(rgb, rf);
1092 copy_v3_v3(rgb, rf);
1094 else if (ibuf->rect) {
1096 rgb[c] = rc[c] * INV_255;
1099 /* we still need luma for histogram */
1100 luma = 0.299*rgb[0] + 0.587*rgb[1] + 0.114 * rgb[2];
1102 /* check for min max */
1103 if(ycc_mode == -1 ) {
1104 for (c=0; c<3; c++) {
1105 if (rgb[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = rgb[c];
1106 if (rgb[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = rgb[c];
1110 rgb_to_ycc(rgb[0],rgb[1],rgb[2],&ycc[0],&ycc[1],&ycc[2], ycc_mode);
1111 for (c=0; c<3; c++) {
1113 if (ycc[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = ycc[c];
1114 if (ycc[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = ycc[c];
1117 /* increment count for histo*/
1118 bin_r[ get_bin_float(rgb[0]) ] += 1;
1119 bin_g[ get_bin_float(rgb[1]) ] += 1;
1120 bin_b[ get_bin_float(rgb[2]) ] += 1;
1121 bin_lum[ get_bin_float(luma) ] += 1;
1123 /* save sample if needed */
1125 const float fx = (float)x / (float)ibuf->x;
1126 const int idx = 2*(ibuf->x*savedlines+x);
1127 save_sample_line(scopes, idx, fx, rgb, ycc);
1130 rf+= ibuf->channels;
1131 rc+= ibuf->channels;
1137 /* convert hist data to float (proportional to max count) */
1140 for (x=0; x<256; x++) {
1147 if (bin_lum[x] > nl)
1150 div = 1.f/(double)n;
1151 divl = 1.f/(double)nl;
1152 for (x=0; x<256; x++) {
1153 scopes->hist.data_r[x] = bin_r[x] * div;
1154 scopes->hist.data_g[x] = bin_g[x] * div;
1155 scopes->hist.data_b[x] = bin_b[x] * div;
1156 scopes->hist.data_luma[x] = bin_lum[x] * divl;
1166 void scopes_free(Scopes *scopes)
1168 if (scopes->waveform_1) {
1169 MEM_freeN(scopes->waveform_1);
1170 scopes->waveform_1 = NULL;
1172 if (scopes->waveform_2) {
1173 MEM_freeN(scopes->waveform_2);
1174 scopes->waveform_2 = NULL;
1176 if (scopes->waveform_3) {
1177 MEM_freeN(scopes->waveform_3);
1178 scopes->waveform_3 = NULL;
1180 if (scopes->vecscope) {
1181 MEM_freeN(scopes->vecscope);
1182 scopes->vecscope = NULL;
1186 void scopes_new(Scopes *scopes)
1188 scopes->accuracy=30.0;
1189 scopes->hist.mode=HISTO_MODE_RGB;
1190 scopes->wavefrm_alpha=0.3;
1191 scopes->vecscope_alpha=0.3;
1192 scopes->wavefrm_height= 100;
1193 scopes->vecscope_height= 100;
1194 scopes->hist.height= 100;
1196 scopes->waveform_1 = NULL;
1197 scopes->waveform_2 = NULL;
1198 scopes->waveform_3 = NULL;
1199 scopes->vecscope = NULL;