doxygen: blender/blenkernel tagged.
[blender.git] / source / blender / blenkernel / intern / colortools.c
1 /* 
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
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. 
10  *
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.
15  *
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.
19  *
20  * The Original Code is Copyright (C) 2005 Blender Foundation.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL/BL DUAL LICENSE BLOCK *****
28  */
29
30 /** \file blender/blenkernel/intern/colortools.c
31  *  \ingroup bke
32  */
33
34
35 #include <string.h>
36 #include <math.h>
37 #include <stdlib.h>
38 #include <float.h>
39
40 #ifdef WITH_LCMS
41 #include <lcms.h>
42 #endif
43
44 #include "MEM_guardedalloc.h"
45
46 #include "DNA_color_types.h"
47 #include "DNA_curve_types.h"
48
49 #include "BLI_blenlib.h"
50 #include "BLI_math.h"
51 #include "BLI_utildefines.h"
52
53 #include "BKE_colortools.h"
54 #include "BKE_curve.h"
55 #include "BKE_fcurve.h"
56
57
58 #include "IMB_imbuf.h"
59 #include "IMB_imbuf_types.h"
60
61
62 void floatbuf_to_srgb_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int UNUSED(w))
63 {
64         int x, y;
65         float *rf= rectf;
66         float srgb[3];
67         unsigned char *rc= rectc;
68         
69         for(y=y1; y<y2; y++) {
70                 for(x=x1; x<x2; x++, rf+=4, rc+=4) {
71                         srgb[0]= linearrgb_to_srgb(rf[0]);
72                         srgb[1]= linearrgb_to_srgb(rf[1]);
73                         srgb[2]= linearrgb_to_srgb(rf[2]);
74
75                         rc[0]= FTOCHAR(srgb[0]);
76                         rc[1]= FTOCHAR(srgb[1]);
77                         rc[2]= FTOCHAR(srgb[2]);
78                         rc[3]= FTOCHAR(rf[3]);
79                 }
80         }
81 }
82
83 void floatbuf_to_byte(float *rectf, unsigned char *rectc, int x1, int x2, int y1, int y2, int UNUSED(w))
84 {
85         int x, y;
86         float *rf= rectf;
87         unsigned char *rc= rectc;
88         
89         for(y=y1; y<y2; y++) {
90                 for(x=x1; x<x2; x++, rf+=4, rc+=4) {
91                         rc[0]= FTOCHAR(rf[0]);
92                         rc[1]= FTOCHAR(rf[1]);
93                         rc[2]= FTOCHAR(rf[2]);
94                         rc[3]= FTOCHAR(rf[3]);
95                 }
96         }
97 }
98
99
100 /* ********************************* color curve ********************* */
101
102 /* ***************** operations on full struct ************* */
103
104 CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
105 {
106         CurveMapping *cumap;
107         int a;
108         float clipminx, clipminy, clipmaxx, clipmaxy;
109         
110         cumap= MEM_callocN(sizeof(CurveMapping), "new curvemap");
111         cumap->flag= CUMA_DO_CLIP;
112         if(tot==4) cumap->cur= 3;               /* rhms, hack for 'col' curve? */
113         
114         clipminx = MIN2(minx, maxx);
115         clipminy = MIN2(miny, maxy);
116         clipmaxx = MAX2(minx, maxx);
117         clipmaxy = MAX2(miny, maxy);
118         
119         BLI_init_rctf(&cumap->curr, clipminx, clipmaxx, clipminy, clipmaxy);
120         cumap->clipr= cumap->curr;
121         
122         cumap->white[0]= cumap->white[1]= cumap->white[2]= 1.0f;
123         cumap->bwmul[0]= cumap->bwmul[1]= cumap->bwmul[2]= 1.0f;
124         
125         for(a=0; a<tot; a++) {
126                 cumap->cm[a].flag= CUMA_EXTEND_EXTRAPOLATE;
127                 cumap->cm[a].totpoint= 2;
128                 cumap->cm[a].curve= MEM_callocN(2*sizeof(CurveMapPoint), "curve points");
129                 
130                 cumap->cm[a].curve[0].x= minx;
131                 cumap->cm[a].curve[0].y= miny;
132                 cumap->cm[a].curve[1].x= maxx;
133                 cumap->cm[a].curve[1].y= maxy;
134         }       
135
136         cumap->changed_timestamp = 0;
137
138         return cumap;
139 }
140
141 void curvemapping_free(CurveMapping *cumap)
142 {
143         int a;
144         
145         if(cumap) {
146                 for(a=0; a<CM_TOT; a++) {
147                         if(cumap->cm[a].curve) MEM_freeN(cumap->cm[a].curve);
148                         if(cumap->cm[a].table) MEM_freeN(cumap->cm[a].table);
149                         if(cumap->cm[a].premultable) MEM_freeN(cumap->cm[a].premultable);
150                 }
151                 MEM_freeN(cumap);
152         }
153 }
154
155 CurveMapping *curvemapping_copy(CurveMapping *cumap)
156 {
157         int a;
158         
159         if(cumap) {
160                 CurveMapping *cumapn= MEM_dupallocN(cumap);
161                 for(a=0; a<CM_TOT; a++) {
162                         if(cumap->cm[a].curve) 
163                                 cumapn->cm[a].curve= MEM_dupallocN(cumap->cm[a].curve);
164                         if(cumap->cm[a].table) 
165                                 cumapn->cm[a].table= MEM_dupallocN(cumap->cm[a].table);
166                         if(cumap->cm[a].premultable) 
167                                 cumapn->cm[a].premultable= MEM_dupallocN(cumap->cm[a].premultable);
168                 }
169                 return cumapn;
170         }
171         return NULL;
172 }
173
174 void curvemapping_set_black_white(CurveMapping *cumap, float *black, float *white)
175 {
176         int a;
177         
178         if(white)
179                 VECCOPY(cumap->white, white);
180         if(black)
181                 VECCOPY(cumap->black, black);
182         
183         for(a=0; a<3; a++) {
184                 if(cumap->white[a]==cumap->black[a])
185                         cumap->bwmul[a]= 0.0f;
186                 else
187                         cumap->bwmul[a]= 1.0f/(cumap->white[a] - cumap->black[a]);
188         }       
189 }
190
191 /* ***************** operations on single curve ************* */
192 /* ********** NOTE: requires curvemapping_changed() call after ******** */
193
194 /* removes with flag set */
195 void curvemap_remove(CurveMap *cuma, int flag)
196 {
197         CurveMapPoint *cmp= MEM_mallocN((cuma->totpoint)*sizeof(CurveMapPoint), "curve points");
198         int a, b, removed=0;
199         
200         /* well, lets keep the two outer points! */
201         cmp[0]= cuma->curve[0];
202         for(a=1, b=1; a<cuma->totpoint-1; a++) {
203                 if(!(cuma->curve[a].flag & flag)) {
204                         cmp[b]= cuma->curve[a];
205                         b++;
206                 }
207                 else removed++;
208         }
209         cmp[b]= cuma->curve[a];
210         
211         MEM_freeN(cuma->curve);
212         cuma->curve= cmp;
213         cuma->totpoint -= removed;
214 }
215
216 void curvemap_insert(CurveMap *cuma, float x, float y)
217 {
218         CurveMapPoint *cmp= MEM_callocN((cuma->totpoint+1)*sizeof(CurveMapPoint), "curve points");
219         int a, b, foundloc= 0;
220                 
221         /* insert fragments of the old one and the new point to the new curve */
222         cuma->totpoint++;
223         for(a=0, b=0; a<cuma->totpoint; a++) {
224                 if((x < cuma->curve[a].x) && !foundloc) {
225                         cmp[a].x= x;
226                         cmp[a].y= y;
227                         cmp[a].flag= CUMA_SELECT;
228                         foundloc= 1;
229                 }
230                 else {
231                         cmp[a].x= cuma->curve[b].x;
232                         cmp[a].y= cuma->curve[b].y;
233                         cmp[a].flag= cuma->curve[b].flag;
234                         cmp[a].flag &= ~CUMA_SELECT; /* make sure old points don't remain selected */
235                         cmp[a].shorty= cuma->curve[b].shorty;
236                         b++;
237                 }
238         }
239
240         /* free old curve and replace it with new one */
241         MEM_freeN(cuma->curve);
242         cuma->curve= cmp;
243 }
244
245 void curvemap_reset(CurveMap *cuma, rctf *clipr, int preset, int slope)
246 {
247         if(cuma->curve)
248                 MEM_freeN(cuma->curve);
249
250         switch(preset) {
251                 case CURVE_PRESET_LINE: cuma->totpoint= 2; break;
252                 case CURVE_PRESET_SHARP: cuma->totpoint= 4; break;
253                 case CURVE_PRESET_SMOOTH: cuma->totpoint= 4; break;
254                 case CURVE_PRESET_MAX: cuma->totpoint= 2; break;
255                 case CURVE_PRESET_MID9: cuma->totpoint= 9; break;
256                 case CURVE_PRESET_ROUND: cuma->totpoint= 4; break;
257                 case CURVE_PRESET_ROOT: cuma->totpoint= 4; break;
258         }
259
260         cuma->curve= MEM_callocN(cuma->totpoint*sizeof(CurveMapPoint), "curve points");
261
262         switch(preset) {
263                 case CURVE_PRESET_LINE:
264                         cuma->curve[0].x= clipr->xmin;
265                         cuma->curve[0].y= clipr->ymax;
266                         cuma->curve[0].flag= 0;
267                         cuma->curve[1].x= clipr->xmax;
268                         cuma->curve[1].y= clipr->ymin;
269                         cuma->curve[1].flag= 0;
270                         break;
271                 case CURVE_PRESET_SHARP:
272                         cuma->curve[0].x= 0;
273                         cuma->curve[0].y= 1;
274                         cuma->curve[1].x= 0.25;
275                         cuma->curve[1].y= 0.50;
276                         cuma->curve[2].x= 0.75;
277                         cuma->curve[2].y= 0.04;
278                         cuma->curve[3].x= 1;
279                         cuma->curve[3].y= 0;
280                         break;
281                 case CURVE_PRESET_SMOOTH:
282                         cuma->curve[0].x= 0;
283                         cuma->curve[0].y= 1;
284                         cuma->curve[1].x= 0.25;
285                         cuma->curve[1].y= 0.94;
286                         cuma->curve[2].x= 0.75;
287                         cuma->curve[2].y= 0.06;
288                         cuma->curve[3].x= 1;
289                         cuma->curve[3].y= 0;
290                         break;
291                 case CURVE_PRESET_MAX:
292                         cuma->curve[0].x= 0;
293                         cuma->curve[0].y= 1;
294                         cuma->curve[1].x= 1;
295                         cuma->curve[1].y= 1;
296                         break;
297                 case CURVE_PRESET_MID9:
298                         {
299                                 int i;
300                                 for (i=0; i < cuma->totpoint; i++)
301                                 {
302                                         cuma->curve[i].x= i / ((float)cuma->totpoint-1);
303                                         cuma->curve[i].y= 0.5;
304                                 }
305                         }
306                         break;
307                 case CURVE_PRESET_ROUND:
308                         cuma->curve[0].x= 0;
309                         cuma->curve[0].y= 1;
310                         cuma->curve[1].x= 0.5;
311                         cuma->curve[1].y= 0.90;
312                         cuma->curve[2].x= 0.86;
313                         cuma->curve[2].y= 0.5;
314                         cuma->curve[3].x= 1;
315                         cuma->curve[3].y= 0;
316                         break;
317                 case CURVE_PRESET_ROOT:
318                         cuma->curve[0].x= 0;
319                         cuma->curve[0].y= 1;
320                         cuma->curve[1].x= 0.25;
321                         cuma->curve[1].y= 0.95;
322                         cuma->curve[2].x= 0.75;
323                         cuma->curve[2].y= 0.44;
324                         cuma->curve[3].x= 1;
325                         cuma->curve[3].y= 0;
326                         break;
327         }
328
329         /* mirror curve in x direction to have positive slope
330          * rather than default negative slope */
331         if (slope == CURVEMAP_SLOPE_POSITIVE) {
332                 int i, last=cuma->totpoint-1;
333                 CurveMapPoint *newpoints= MEM_dupallocN(cuma->curve);
334                 
335                 for (i=0; i<cuma->totpoint; i++) {
336                         newpoints[i].y = cuma->curve[last-i].y;
337                 }
338                 
339                 MEM_freeN(cuma->curve);
340                 cuma->curve = newpoints;
341         }
342         
343         if(cuma->table) {
344                 MEM_freeN(cuma->table);
345                 cuma->table= NULL;
346         }
347 }
348
349 /* if type==1: vector, else auto */
350 void curvemap_sethandle(CurveMap *cuma, int type)
351 {
352         int a;
353         
354         for(a=0; a<cuma->totpoint; a++) {
355                 if(cuma->curve[a].flag & CUMA_SELECT) {
356                         if(type) cuma->curve[a].flag |= CUMA_VECTOR;
357                         else cuma->curve[a].flag &= ~CUMA_VECTOR;
358                 }
359         }
360 }
361
362 /* *********************** Making the tables and display ************** */
363
364 /* reduced copy of garbled calchandleNurb() code in curve.c */
365 static void calchandle_curvemap(BezTriple *bezt, BezTriple *prev, BezTriple *next, int UNUSED(mode))
366 {
367         float *p1,*p2,*p3,pt[3];
368         float dx1,dy1, dx,dy, vx,vy, len,len1,len2;
369         
370         if(bezt->h1==0 && bezt->h2==0) return;
371         
372         p2= bezt->vec[1];
373         
374         if(prev==NULL) {
375                 p3= next->vec[1];
376                 pt[0]= 2*p2[0]- p3[0];
377                 pt[1]= 2*p2[1]- p3[1];
378                 p1= pt;
379         }
380         else p1= prev->vec[1];
381         
382         if(next==NULL) {
383                 p1= prev->vec[1];
384                 pt[0]= 2*p2[0]- p1[0];
385                 pt[1]= 2*p2[1]- p1[1];
386                 p3= pt;
387         }
388         else p3= next->vec[1];
389         
390         dx= p2[0]- p1[0];
391         dy= p2[1]- p1[1];
392
393         len1= (float)sqrt(dx*dx+dy*dy);
394         
395         dx1= p3[0]- p2[0];
396         dy1= p3[1]- p2[1];
397
398         len2= (float)sqrt(dx1*dx1+dy1*dy1);
399         
400         if(len1==0.0f) len1=1.0f;
401         if(len2==0.0f) len2=1.0f;
402         
403         if(bezt->h1==HD_AUTO || bezt->h2==HD_AUTO) {    /* auto */
404                 vx= dx1/len2 + dx/len1;
405                 vy= dy1/len2 + dy/len1;
406                 
407                 len= 2.5614f*(float)sqrt(vx*vx + vy*vy);
408                 if(len!=0.0f) {
409                         
410                         if(bezt->h1==HD_AUTO) {
411                                 len1/=len;
412                                 *(p2-3)= *p2-vx*len1;
413                                 *(p2-2)= *(p2+1)-vy*len1;
414                         }
415                         if(bezt->h2==HD_AUTO) {
416                                 len2/=len;
417                                 *(p2+3)= *p2+vx*len2;
418                                 *(p2+4)= *(p2+1)+vy*len2;
419                         }
420                 }
421         }
422
423         if(bezt->h1==HD_VECT) { /* vector */
424                 dx/=3.0; 
425                 dy/=3.0; 
426                 *(p2-3)= *p2-dx;
427                 *(p2-2)= *(p2+1)-dy;
428         }
429         if(bezt->h2==HD_VECT) {
430                 dx1/=3.0; 
431                 dy1/=3.0; 
432                 *(p2+3)= *p2+dx1;
433                 *(p2+4)= *(p2+1)+dy1;
434         }
435 }
436
437 /* in X, out Y. 
438    X is presumed to be outside first or last */
439 static float curvemap_calc_extend(CurveMap *cuma, float x, float *first, float *last)
440 {
441         if(x <= first[0]) {
442                 if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0) {
443                         /* no extrapolate */
444                         return first[1];
445                 }
446                 else {
447                         if(cuma->ext_in[0]==0.0f)
448                                 return first[1] + cuma->ext_in[1]*10000.0f;
449                         else
450                                 return first[1] + cuma->ext_in[1]*(x - first[0])/cuma->ext_in[0];
451                 }
452         }
453         else if(x >= last[0]) {
454                 if((cuma->flag & CUMA_EXTEND_EXTRAPOLATE)==0) {
455                         /* no extrapolate */
456                         return last[1];
457                 }
458                 else {
459                         if(cuma->ext_out[0]==0.0f)
460                                 return last[1] - cuma->ext_out[1]*10000.0f;
461                         else
462                                 return last[1] + cuma->ext_out[1]*(x - last[0])/cuma->ext_out[0];
463                 }
464         }
465         return 0.0f;
466 }
467
468 /* only creates a table for a single channel in CurveMapping */
469 static void curvemap_make_table(CurveMap *cuma, rctf *clipr)
470 {
471         CurveMapPoint *cmp= cuma->curve;
472         BezTriple *bezt;
473         float *fp, *allpoints, *lastpoint, curf, range;
474         int a, totpoint;
475         
476         if(cuma->curve==NULL) return;
477         
478         /* default rect also is table range */
479         cuma->mintable= clipr->xmin;
480         cuma->maxtable= clipr->xmax;
481         
482         /* hrmf... we now rely on blender ipo beziers, these are more advanced */
483         bezt= MEM_callocN(cuma->totpoint*sizeof(BezTriple), "beztarr");
484         
485         for(a=0; a<cuma->totpoint; a++) {
486                 cuma->mintable= MIN2(cuma->mintable, cmp[a].x);
487                 cuma->maxtable= MAX2(cuma->maxtable, cmp[a].x);
488                 bezt[a].vec[1][0]= cmp[a].x;
489                 bezt[a].vec[1][1]= cmp[a].y;
490                 if(cmp[a].flag & CUMA_VECTOR)
491                         bezt[a].h1= bezt[a].h2= HD_VECT;
492                 else
493                         bezt[a].h1= bezt[a].h2= HD_AUTO;
494         }
495         
496         for(a=0; a<cuma->totpoint; a++) {
497                 if(a==0)
498                         calchandle_curvemap(bezt, NULL, bezt+1, 0);
499                 else if(a==cuma->totpoint-1)
500                         calchandle_curvemap(bezt+a, bezt+a-1, NULL, 0);
501                 else
502                         calchandle_curvemap(bezt+a, bezt+a-1, bezt+a+1, 0);
503         }
504         
505         /* first and last handle need correction, instead of pointing to center of next/prev, 
506                 we let it point to the closest handle */
507         if(cuma->totpoint>2) {
508                 float hlen, nlen, vec[3];
509                 
510                 if(bezt[0].h2==HD_AUTO) {
511                         
512                         hlen= len_v3v3(bezt[0].vec[1], bezt[0].vec[2]); /* original handle length */
513                         /* clip handle point */
514                         VECCOPY(vec, bezt[1].vec[0]);
515                         if(vec[0] < bezt[0].vec[1][0])
516                                 vec[0]= bezt[0].vec[1][0];
517                         
518                         sub_v3_v3(vec, bezt[0].vec[1]);
519                         nlen= len_v3(vec);
520                         if(nlen>FLT_EPSILON) {
521                                 mul_v3_fl(vec, hlen/nlen);
522                                 add_v3_v3v3(bezt[0].vec[2], vec, bezt[0].vec[1]);
523                                 sub_v3_v3v3(bezt[0].vec[0], bezt[0].vec[1], vec);
524                         }
525                 }
526                 a= cuma->totpoint-1;
527                 if(bezt[a].h2==HD_AUTO) {
528                         
529                         hlen= len_v3v3(bezt[a].vec[1], bezt[a].vec[0]); /* original handle length */
530                         /* clip handle point */
531                         VECCOPY(vec, bezt[a-1].vec[2]);
532                         if(vec[0] > bezt[a].vec[1][0])
533                                 vec[0]= bezt[a].vec[1][0];
534                         
535                         sub_v3_v3(vec, bezt[a].vec[1]);
536                         nlen= len_v3(vec);
537                         if(nlen>FLT_EPSILON) {
538                                 mul_v3_fl(vec, hlen/nlen);
539                                 add_v3_v3v3(bezt[a].vec[0], vec, bezt[a].vec[1]);
540                                 sub_v3_v3v3(bezt[a].vec[2], bezt[a].vec[1], vec);
541                         }
542                 }
543         }       
544         /* make the bezier curve */
545         if(cuma->table)
546                 MEM_freeN(cuma->table);
547         totpoint= (cuma->totpoint-1)*CM_RESOL;
548         fp= allpoints= MEM_callocN(totpoint*2*sizeof(float), "table");
549         
550         for(a=0; a<cuma->totpoint-1; a++, fp += 2*CM_RESOL) {
551                 correct_bezpart(bezt[a].vec[1], bezt[a].vec[2], bezt[a+1].vec[0], bezt[a+1].vec[1]);
552                 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));   
553                 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));
554         }
555         
556         /* store first and last handle for extrapolation, unit length */
557         cuma->ext_in[0]= bezt[0].vec[0][0] - bezt[0].vec[1][0];
558         cuma->ext_in[1]= bezt[0].vec[0][1] - bezt[0].vec[1][1];
559         range= sqrt(cuma->ext_in[0]*cuma->ext_in[0] + cuma->ext_in[1]*cuma->ext_in[1]);
560         cuma->ext_in[0]/= range;
561         cuma->ext_in[1]/= range;
562         
563         a= cuma->totpoint-1;
564         cuma->ext_out[0]= bezt[a].vec[1][0] - bezt[a].vec[2][0];
565         cuma->ext_out[1]= bezt[a].vec[1][1] - bezt[a].vec[2][1];
566         range= sqrt(cuma->ext_out[0]*cuma->ext_out[0] + cuma->ext_out[1]*cuma->ext_out[1]);
567         cuma->ext_out[0]/= range;
568         cuma->ext_out[1]/= range;
569         
570         /* cleanup */
571         MEM_freeN(bezt);
572
573         range= CM_TABLEDIV*(cuma->maxtable - cuma->mintable);
574         cuma->range= 1.0f/range;
575         
576         /* now make a table with CM_TABLE equal x distances */
577         fp= allpoints;
578         lastpoint= allpoints + 2*(totpoint-1);
579         cmp= MEM_callocN((CM_TABLE+1)*sizeof(CurveMapPoint), "dist table");
580         
581         for(a=0; a<=CM_TABLE; a++) {
582                 curf= cuma->mintable + range*(float)a;
583                 cmp[a].x= curf;
584                 
585                 /* get the first x coordinate larger than curf */
586                 while(curf >= fp[0] && fp!=lastpoint) {
587                         fp+=2;
588                 }
589                 if(fp==allpoints || (curf >= fp[0] && fp==lastpoint))
590                         cmp[a].y= curvemap_calc_extend(cuma, curf, allpoints, lastpoint);
591                 else {
592                         float fac1= fp[0] - fp[-2];
593                         float fac2= fp[0] - curf;
594                         if(fac1 > FLT_EPSILON)
595                                 fac1= fac2/fac1;
596                         else
597                                 fac1= 0.0f;
598                         cmp[a].y= fac1*fp[-1] + (1.0f-fac1)*fp[1];
599                 }
600         }
601         
602         MEM_freeN(allpoints);
603         cuma->table= cmp;
604 }
605
606 /* call when you do images etc, needs restore too. also verifies tables */
607 /* it uses a flag to prevent premul or free to happen twice */
608 void curvemapping_premultiply(CurveMapping *cumap, int restore)
609 {
610         int a;
611         
612         if(restore) {
613                 if(cumap->flag & CUMA_PREMULLED) {
614                         for(a=0; a<3; a++) {
615                                 MEM_freeN(cumap->cm[a].table);
616                                 cumap->cm[a].table= cumap->cm[a].premultable;
617                                 cumap->cm[a].premultable= NULL;
618                         }
619                         
620                         cumap->flag &= ~CUMA_PREMULLED;
621                 }
622         }
623         else {
624                 if((cumap->flag & CUMA_PREMULLED)==0) {
625                         /* verify and copy */
626                         for(a=0; a<3; a++) {
627                                 if(cumap->cm[a].table==NULL)
628                                         curvemap_make_table(cumap->cm+a, &cumap->clipr);
629                                 cumap->cm[a].premultable= cumap->cm[a].table;
630                                 cumap->cm[a].table= MEM_mallocN((CM_TABLE+1)*sizeof(CurveMapPoint), "premul table");
631                                 memcpy(cumap->cm[a].table, cumap->cm[a].premultable, (CM_TABLE+1)*sizeof(CurveMapPoint));
632                         }
633                         
634                         if(cumap->cm[3].table==NULL)
635                                 curvemap_make_table(cumap->cm+3, &cumap->clipr);
636                 
637                         /* premul */
638                         for(a=0; a<3; a++) {
639                                 int b;
640                                 for(b=0; b<=CM_TABLE; b++) {
641                                         cumap->cm[a].table[b].y= curvemap_evaluateF(cumap->cm+3, cumap->cm[a].table[b].y);
642                                 }
643                         }
644                         
645                         cumap->flag |= CUMA_PREMULLED;
646                 }
647         }
648 }
649
650 static int sort_curvepoints(const void *a1, const void *a2)
651 {
652         const struct CurveMapPoint *x1=a1, *x2=a2;
653         
654         if( x1->x > x2->x ) return 1;
655         else if( x1->x < x2->x) return -1;
656         return 0;
657 }
658
659 /* ************************ more CurveMapping calls *************** */
660
661 /* note; only does current curvemap! */
662 void curvemapping_changed(CurveMapping *cumap, int rem_doubles)
663 {
664         CurveMap *cuma= cumap->cm+cumap->cur;
665         CurveMapPoint *cmp= cuma->curve;
666         rctf *clipr= &cumap->clipr;
667         float thresh= 0.01f*(clipr->xmax - clipr->xmin);
668         float dx= 0.0f, dy= 0.0f;
669         int a;
670
671         cumap->changed_timestamp++;
672
673         /* clamp with clip */
674         if(cumap->flag & CUMA_DO_CLIP) {
675                 for(a=0; a<cuma->totpoint; a++) {
676                         if(cmp[a].flag & CUMA_SELECT) {
677                                 if(cmp[a].x < clipr->xmin)
678                                         dx= MIN2(dx, cmp[a].x - clipr->xmin);
679                                 else if(cmp[a].x > clipr->xmax)
680                                         dx= MAX2(dx, cmp[a].x - clipr->xmax);
681                                 if(cmp[a].y < clipr->ymin)
682                                         dy= MIN2(dy, cmp[a].y - clipr->ymin);
683                                 else if(cmp[a].y > clipr->ymax)
684                                         dy= MAX2(dy, cmp[a].y - clipr->ymax);
685                         }
686                 }
687                 for(a=0; a<cuma->totpoint; a++) {
688                         if(cmp[a].flag & CUMA_SELECT) {
689                                 cmp[a].x -= dx;
690                                 cmp[a].y -= dy;
691                         }
692                 }
693         }
694         
695         
696         qsort(cmp, cuma->totpoint, sizeof(CurveMapPoint), sort_curvepoints);
697         
698         /* remove doubles, threshold set on 1% of default range */
699         if(rem_doubles && cuma->totpoint>2) {
700                 for(a=0; a<cuma->totpoint-1; a++) {
701                         dx= cmp[a].x - cmp[a+1].x;
702                         dy= cmp[a].y - cmp[a+1].y;
703                         if( sqrt(dx*dx + dy*dy) < thresh ) {
704                                 if(a==0) {
705                                         cmp[a+1].flag|= 2;
706                                         if(cmp[a+1].flag & CUMA_SELECT)
707                                                 cmp[a].flag |= CUMA_SELECT;
708                                 }
709                                 else {
710                                         cmp[a].flag|= 2;
711                                         if(cmp[a].flag & CUMA_SELECT)
712                                                 cmp[a+1].flag |= CUMA_SELECT;
713                                 }
714                                 break;  /* we assume 1 deletion per edit is ok */
715                         }
716                 }
717                 if(a != cuma->totpoint-1)
718                         curvemap_remove(cuma, 2);
719         }       
720         curvemap_make_table(cuma, clipr);
721 }
722
723 /* table should be verified */
724 float curvemap_evaluateF(CurveMap *cuma, float value)
725 {
726         float fi;
727         int i;
728
729         /* index in table */
730         fi= (value-cuma->mintable)*cuma->range;
731         i= (int)fi;
732         
733         /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */
734         if(fi<0.0f || fi>CM_TABLE)
735                 return curvemap_calc_extend(cuma, value, &cuma->table[0].x, &cuma->table[CM_TABLE].x);
736         else {
737                 if(i<0) return cuma->table[0].y;
738                 if(i>=CM_TABLE) return cuma->table[CM_TABLE].y;
739                 
740                 fi= fi-(float)i;
741                 return (1.0f-fi)*cuma->table[i].y + (fi)*cuma->table[i+1].y; 
742         }
743 }
744
745 /* works with curve 'cur' */
746 float curvemapping_evaluateF(CurveMapping *cumap, int cur, float value)
747 {
748         CurveMap *cuma= cumap->cm+cur;
749         
750         /* allocate or bail out */
751         if(cuma->table==NULL) {
752                 curvemap_make_table(cuma, &cumap->clipr);
753                 if(cuma->table==NULL)
754                         return 1.0f-value;
755         }
756         return curvemap_evaluateF(cuma, value);
757 }
758
759 /* vector case */
760 void curvemapping_evaluate3F(CurveMapping *cumap, float *vecout, const float *vecin)
761 {
762         vecout[0]= curvemapping_evaluateF(cumap, 0, vecin[0]);
763         vecout[1]= curvemapping_evaluateF(cumap, 1, vecin[1]);
764         vecout[2]= curvemapping_evaluateF(cumap, 2, vecin[2]);
765 }
766
767 /* RGB case, no black/white points, no premult */
768 void curvemapping_evaluateRGBF(CurveMapping *cumap, float *vecout, const float *vecin)
769 {
770         vecout[0]= curvemapping_evaluateF(cumap, 0, curvemapping_evaluateF(cumap, 3, vecin[0]));
771         vecout[1]= curvemapping_evaluateF(cumap, 1, curvemapping_evaluateF(cumap, 3, vecin[1]));
772         vecout[2]= curvemapping_evaluateF(cumap, 2, curvemapping_evaluateF(cumap, 3, vecin[2]));
773 }
774
775
776 /* RGB with black/white points and premult. tables are checked */
777 void curvemapping_evaluate_premulRGBF(CurveMapping *cumap, float *vecout, const float *vecin)
778 {
779         float fac;
780         
781         fac= (vecin[0] - cumap->black[0])*cumap->bwmul[0];
782         vecout[0]= curvemap_evaluateF(cumap->cm, fac);
783         
784         fac= (vecin[1] - cumap->black[1])*cumap->bwmul[1];
785         vecout[1]= curvemap_evaluateF(cumap->cm+1, fac);
786         
787         fac= (vecin[2] - cumap->black[2])*cumap->bwmul[2];
788         vecout[2]= curvemap_evaluateF(cumap->cm+2, fac);
789 }
790
791
792 #ifdef WITH_LCMS
793 /* basic error handler, if we dont do this blender will exit */
794 static int ErrorReportingFunction(int ErrorCode, const char *ErrorText)
795 {
796     fprintf(stderr, "%s:%d\n", ErrorText, ErrorCode);
797         return 1;
798 }
799 #endif
800
801 void colorcorrection_do_ibuf(ImBuf *ibuf, const char *profile)
802 {
803 #ifdef WITH_LCMS
804         if (ibuf->crect == NULL)
805         {
806                 cmsHPROFILE proofingProfile;
807                 
808                 /* TODO, move to initialization area of code */
809                 //cmsSetLogErrorHandler(ErrorReportingFunction);
810                 cmsSetErrorHandler(ErrorReportingFunction);
811                 
812                 /* will return NULL if the file isn't fount */
813                 proofingProfile = cmsOpenProfileFromFile(profile, "r");
814
815                 cmsErrorAction(LCMS_ERROR_SHOW);
816
817                 if(proofingProfile) {
818                         cmsHPROFILE imageProfile;
819                         cmsHTRANSFORM hTransform;
820
821                         ibuf->crect = MEM_mallocN(ibuf->x*ibuf->y*sizeof(int), "imbuf crect");
822
823                         imageProfile  = cmsCreate_sRGBProfile();
824
825
826                         hTransform = cmsCreateProofingTransform(imageProfile, TYPE_RGBA_8, imageProfile, TYPE_RGBA_8, 
827                                                                                                   proofingProfile,
828                                                                                                   INTENT_ABSOLUTE_COLORIMETRIC,
829                                                                                                   INTENT_ABSOLUTE_COLORIMETRIC,
830                                                                                                   cmsFLAGS_SOFTPROOFING);
831                 
832                         cmsDoTransform(hTransform, ibuf->rect, ibuf->crect, ibuf->x * ibuf->y);
833
834                         cmsDeleteTransform(hTransform);
835                         cmsCloseProfile(imageProfile);
836                         cmsCloseProfile(proofingProfile);
837                 }
838         }
839 #else
840         /* unused */
841         (void)ibuf;
842         (void)profile;
843 #endif
844 }
845
846 /* only used for image editor curves */
847 void curvemapping_do_ibuf(CurveMapping *cumap, ImBuf *ibuf)
848 {
849         ImBuf *tmpbuf;
850         int pixel;
851         float *pix_in;
852         float col[3];
853         int stride= 4;
854         float *pix_out;
855         
856         if(ibuf==NULL)
857                 return;
858         if(ibuf->rect_float==NULL)
859                 IMB_float_from_rect(ibuf);
860         else if(ibuf->rect==NULL)
861                 imb_addrectImBuf(ibuf);
862         
863         if (!ibuf->rect || !ibuf->rect_float)
864                 return;
865         
866         /* work on a temp buffer, so can color manage afterwards.
867          * No worse off memory wise than comp nodes */
868         tmpbuf = IMB_dupImBuf(ibuf);
869         
870         curvemapping_premultiply(cumap, 0);
871         
872         pix_in= ibuf->rect_float;
873         pix_out= tmpbuf->rect_float;
874
875         if(ibuf->channels)
876                 stride= ibuf->channels;
877         
878         for(pixel= ibuf->x*ibuf->y; pixel>0; pixel--, pix_in+=stride, pix_out+=stride) {
879                 if(stride<3) {
880                         col[0]= curvemap_evaluateF(cumap->cm, *pix_in);
881                         
882                         pix_out[1]= pix_out[2]= pix_out[3]= pix_out[0]= col[0];
883                 }
884                 else {
885                         curvemapping_evaluate_premulRGBF(cumap, col, pix_in);
886                         pix_out[0]= col[0];
887                         pix_out[1]= col[1];
888                         pix_out[2]= col[2];
889                         if(stride>3)
890                                 pix_out[3]= pix_in[3];
891                         else
892                                 pix_out[3]= 1.f;
893                 }
894         }
895         
896         IMB_rect_from_float(tmpbuf);
897         SWAP(unsigned int *, tmpbuf->rect, ibuf->rect);
898         IMB_freeImBuf(tmpbuf);
899         
900         curvemapping_premultiply(cumap, 1);
901 }
902
903 int curvemapping_RGBA_does_something(CurveMapping *cumap)
904 {
905         int a;
906         
907         if(cumap->black[0]!=0.0f) return 1;
908         if(cumap->black[1]!=0.0f) return 1;
909         if(cumap->black[2]!=0.0f) return 1;
910         if(cumap->white[0]!=1.0f) return 1;
911         if(cumap->white[1]!=1.0f) return 1;
912         if(cumap->white[2]!=1.0f) return 1;
913         
914         for(a=0; a<CM_TOT; a++) {
915                 if(cumap->cm[a].curve) {
916                         if(cumap->cm[a].totpoint!=2)  return 1;
917                         
918                         if(cumap->cm[a].curve[0].x != 0.0f) return 1;
919                         if(cumap->cm[a].curve[0].y != 0.0f) return 1;
920                         if(cumap->cm[a].curve[1].x != 1.0f) return 1;
921                         if(cumap->cm[a].curve[1].y != 1.0f) return 1;
922                 }
923         }
924         return 0;
925 }
926
927 void curvemapping_initialize(CurveMapping *cumap)
928 {
929         int a;
930         
931         if(cumap==NULL) return;
932         
933         for(a=0; a<CM_TOT; a++) {
934                 if(cumap->cm[a].table==NULL)
935                         curvemap_make_table(cumap->cm+a, &cumap->clipr);
936         }
937 }
938
939 void curvemapping_table_RGBA(CurveMapping *cumap, float **array, int *size)
940 {
941         int a;
942         
943         *size = CM_TABLE+1;
944         *array = MEM_callocN(sizeof(float)*(*size)*4, "CurveMapping");
945         curvemapping_initialize(cumap);
946
947         for(a=0; a<*size; a++) {
948                 if(cumap->cm[0].table)
949                         (*array)[a*4+0]= cumap->cm[0].table[a].y;
950                 if(cumap->cm[1].table)
951                         (*array)[a*4+1]= cumap->cm[1].table[a].y;
952                 if(cumap->cm[2].table)
953                         (*array)[a*4+2]= cumap->cm[2].table[a].y;
954                 if(cumap->cm[3].table)
955                         (*array)[a*4+3]= cumap->cm[3].table[a].y;
956         }
957 }
958
959 /* ***************** Histogram **************** */
960
961 #define INV_255         (1.f/255.f)
962
963 DO_INLINE int get_bin_float(float f)
964 {
965         int bin= (int)((f*255) + 0.5);  /* 0.5 to prevent quantisation differences */
966
967         /* note: clamp integer instead of float to avoid problems with NaN */
968         CLAMP(bin, 0, 255);
969
970         return bin;
971 }
972
973 DO_INLINE void save_sample_line(Scopes *scopes, const int idx, const float fx, float *rgb, float *ycc)
974 {
975         float yuv[3];
976
977         /* vectorscope*/
978         rgb_to_yuv(rgb[0], rgb[1], rgb[2], &yuv[0], &yuv[1], &yuv[2]);
979         scopes->vecscope[idx + 0] = yuv[1];
980         scopes->vecscope[idx + 1] = yuv[2];
981
982         /* waveform */
983         switch (scopes->wavefrm_mode) {
984                 case SCOPES_WAVEFRM_RGB:
985                         scopes->waveform_1[idx + 0] = fx;
986                         scopes->waveform_1[idx + 1] = rgb[0];
987                         scopes->waveform_2[idx + 0] = fx;
988                         scopes->waveform_2[idx + 1] = rgb[1];
989                         scopes->waveform_3[idx + 0] = fx;
990                         scopes->waveform_3[idx + 1] = rgb[2];
991                         break;
992                 case SCOPES_WAVEFRM_LUMA:
993                         scopes->waveform_1[idx + 0] = fx;
994                         scopes->waveform_1[idx + 1] = ycc[0];
995                         break;
996                 case SCOPES_WAVEFRM_YCC_JPEG:
997                 case SCOPES_WAVEFRM_YCC_709:
998                 case SCOPES_WAVEFRM_YCC_601:
999                         scopes->waveform_1[idx + 0] = fx;
1000                         scopes->waveform_1[idx + 1] = ycc[0];
1001                         scopes->waveform_2[idx + 0] = fx;
1002                         scopes->waveform_2[idx + 1] = ycc[1];
1003                         scopes->waveform_3[idx + 0] = fx;
1004                         scopes->waveform_3[idx + 1] = ycc[2];
1005                         break;
1006         }
1007 }
1008
1009 void scopes_update(Scopes *scopes, ImBuf *ibuf, int use_color_management)
1010 {
1011         int x, y, c;
1012         unsigned int n, nl;
1013         double div, divl;
1014         float *rf=NULL;
1015         unsigned char *rc=NULL;
1016         unsigned int *bin_r, *bin_g, *bin_b, *bin_lum;
1017         int savedlines, saveline;
1018         float rgb[3], ycc[3], luma;
1019         int ycc_mode=-1;
1020         const short is_float = (ibuf->rect_float != NULL);
1021
1022         if (ibuf->rect==NULL && ibuf->rect_float==NULL) return;
1023
1024         if (scopes->ok == 1 ) return;
1025
1026         if (scopes->hist.ymax == 0.f) scopes->hist.ymax = 1.f;
1027
1028         /* hmmmm */
1029         if (!(ELEM(ibuf->channels, 3, 4))) return;
1030
1031         scopes->hist.channels = 3;
1032         scopes->hist.x_resolution = 256;
1033
1034         switch (scopes->wavefrm_mode) {
1035                 case SCOPES_WAVEFRM_RGB:
1036                         ycc_mode = -1;
1037                         break;
1038                 case SCOPES_WAVEFRM_LUMA:
1039                 case SCOPES_WAVEFRM_YCC_JPEG:
1040                         ycc_mode = BLI_YCC_JFIF_0_255;
1041                         break;
1042                 case SCOPES_WAVEFRM_YCC_601:
1043                         ycc_mode = BLI_YCC_ITU_BT601;
1044                         break;
1045                 case SCOPES_WAVEFRM_YCC_709:
1046                         ycc_mode = BLI_YCC_ITU_BT709;
1047                         break;
1048         }
1049
1050         /* temp table to count pix value for histo */
1051         bin_r = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1052         bin_g = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1053         bin_b = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1054         bin_lum = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
1055
1056         /* convert to number of lines with logarithmic scale */
1057         scopes->sample_lines = (scopes->accuracy*0.01) * (scopes->accuracy*0.01) * ibuf->y;
1058         
1059         if (scopes->sample_full)
1060                 scopes->sample_lines = ibuf->y;
1061
1062         /* scan the image */
1063         savedlines=0;
1064         for (c=0; c<3; c++) {
1065                 scopes->minmax[c][0]=25500.0f;
1066                 scopes->minmax[c][1]=-25500.0f;
1067         }
1068         
1069         scopes->waveform_tot = ibuf->x*scopes->sample_lines;
1070         
1071         if (scopes->waveform_1)
1072                 MEM_freeN(scopes->waveform_1);
1073         if (scopes->waveform_2)
1074                 MEM_freeN(scopes->waveform_2);
1075         if (scopes->waveform_3)
1076                 MEM_freeN(scopes->waveform_3);
1077         if (scopes->vecscope)
1078                 MEM_freeN(scopes->vecscope);
1079         
1080         scopes->waveform_1= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 1");
1081         scopes->waveform_2= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 2");
1082         scopes->waveform_3= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 3");
1083         scopes->vecscope= MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "vectorscope point channel");
1084         
1085         if (is_float)
1086                 rf = ibuf->rect_float;
1087         else
1088                 rc = (unsigned char *)ibuf->rect;
1089
1090         for (y = 0; y < ibuf->y; y++) {
1091                 if (savedlines<scopes->sample_lines && y>=((savedlines)*ibuf->y)/(scopes->sample_lines+1)) {
1092                         saveline=1;
1093                 } else saveline=0;
1094                 for (x = 0; x < ibuf->x; x++) {
1095
1096                         if (is_float) {
1097                                 if (use_color_management)
1098                                         linearrgb_to_srgb_v3_v3(rgb, rf);
1099                                 else
1100                                         copy_v3_v3(rgb, rf);
1101                         }
1102                         else {
1103                                 for (c=0; c<3; c++)
1104                                         rgb[c] = rc[c] * INV_255;
1105                         }
1106
1107                         /* we still need luma for histogram */
1108                         luma = 0.299*rgb[0] + 0.587*rgb[1] + 0.114 * rgb[2];
1109
1110                         /* check for min max */
1111                         if(ycc_mode == -1 ) {
1112                                 for (c=0; c<3; c++) {
1113                                         if (rgb[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = rgb[c];
1114                                         if (rgb[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = rgb[c];
1115                                 }
1116                         }
1117                         else {
1118                                 rgb_to_ycc(rgb[0],rgb[1],rgb[2],&ycc[0],&ycc[1],&ycc[2], ycc_mode);
1119                                 for (c=0; c<3; c++) {
1120                                         ycc[c] *=INV_255;
1121                                         if (ycc[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = ycc[c];
1122                                         if (ycc[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = ycc[c];
1123                                 }
1124                         }
1125                         /* increment count for histo*/
1126                         bin_r[ get_bin_float(rgb[0]) ] += 1;
1127                         bin_g[ get_bin_float(rgb[1]) ] += 1;
1128                         bin_b[ get_bin_float(rgb[2]) ] += 1;
1129                         bin_lum[ get_bin_float(luma) ] += 1;
1130
1131                         /* save sample if needed */
1132                         if(saveline) {
1133                                 const float fx = (float)x / (float)ibuf->x;
1134                                 const int idx = 2*(ibuf->x*savedlines+x);
1135                                 save_sample_line(scopes, idx, fx, rgb, ycc);
1136                         }
1137
1138                         rf+= ibuf->channels;
1139                         rc+= ibuf->channels;
1140                 }
1141                 if (saveline)
1142                         savedlines +=1;
1143         }
1144
1145         /* convert hist data to float (proportional to max count) */
1146         n=0;
1147         nl=0;
1148         for (x=0; x<256; x++) {
1149                 if (bin_r[x] > n)
1150                         n = bin_r[x];
1151                 if (bin_g[x] > n)
1152                         n = bin_g[x];
1153                 if (bin_b[x] > n)
1154                         n = bin_b[x];
1155                 if (bin_lum[x] > nl)
1156                         nl = bin_lum[x];
1157         }
1158         div = 1.f/(double)n;
1159         divl = 1.f/(double)nl;
1160         for (x=0; x<256; x++) {
1161                 scopes->hist.data_r[x] = bin_r[x] * div;
1162                 scopes->hist.data_g[x] = bin_g[x] * div;
1163                 scopes->hist.data_b[x] = bin_b[x] * div;
1164                 scopes->hist.data_luma[x] = bin_lum[x] * divl;
1165         }
1166         MEM_freeN(bin_r);
1167         MEM_freeN(bin_g);
1168         MEM_freeN(bin_b);
1169         MEM_freeN(bin_lum);
1170
1171         scopes->ok = 1;
1172 }
1173
1174 void scopes_free(Scopes *scopes)
1175 {
1176         if (scopes->waveform_1) {
1177                 MEM_freeN(scopes->waveform_1);
1178                 scopes->waveform_1 = NULL;
1179         }
1180         if (scopes->waveform_2) {
1181                 MEM_freeN(scopes->waveform_2);
1182                 scopes->waveform_2 = NULL;
1183         }
1184         if (scopes->waveform_3) {
1185                 MEM_freeN(scopes->waveform_3);
1186                 scopes->waveform_3 = NULL;
1187         }
1188         if (scopes->vecscope) {
1189                 MEM_freeN(scopes->vecscope);
1190                 scopes->vecscope = NULL;
1191         }
1192 }
1193
1194 void scopes_new(Scopes *scopes)
1195 {
1196         scopes->accuracy=30.0;
1197         scopes->hist.mode=HISTO_MODE_RGB;
1198         scopes->wavefrm_alpha=0.3;
1199         scopes->vecscope_alpha=0.3;
1200         scopes->wavefrm_height= 100;
1201         scopes->vecscope_height= 100;
1202         scopes->hist.height= 100;
1203         scopes->ok= 0;
1204         scopes->waveform_1 = NULL;
1205         scopes->waveform_2 = NULL;
1206         scopes->waveform_3 = NULL;
1207         scopes->vecscope = NULL;
1208 }