ClangFormat: apply to source, most of intern
[blender.git] / source / blender / blenkernel / intern / colortools.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2005 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <string.h>
25 #include <math.h>
26 #include <stdlib.h>
27 #include <float.h>
28
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_color_types.h"
32 #include "DNA_curve_types.h"
33
34 #include "BLI_blenlib.h"
35 #include "BLI_math.h"
36 #include "BLI_utildefines.h"
37 #include "BLI_task.h"
38 #include "BLI_threads.h"
39
40 #include "BKE_colortools.h"
41 #include "BKE_curve.h"
42 #include "BKE_fcurve.h"
43
44 #include "IMB_colormanagement.h"
45 #include "IMB_imbuf_types.h"
46
47 /* ********************************* color curve ********************* */
48
49 /* ***************** operations on full struct ************* */
50
51 void curvemapping_set_defaults(
52     CurveMapping *cumap, int tot, float minx, float miny, float maxx, float maxy)
53 {
54   int a;
55   float clipminx, clipminy, clipmaxx, clipmaxy;
56
57   cumap->flag = CUMA_DO_CLIP;
58   if (tot == 4)
59     cumap->cur = 3; /* rhms, hack for 'col' curve? */
60
61   clipminx = min_ff(minx, maxx);
62   clipminy = min_ff(miny, maxy);
63   clipmaxx = max_ff(minx, maxx);
64   clipmaxy = max_ff(miny, maxy);
65
66   BLI_rctf_init(&cumap->curr, clipminx, clipmaxx, clipminy, clipmaxy);
67   cumap->clipr = cumap->curr;
68
69   cumap->white[0] = cumap->white[1] = cumap->white[2] = 1.0f;
70   cumap->bwmul[0] = cumap->bwmul[1] = cumap->bwmul[2] = 1.0f;
71
72   for (a = 0; a < tot; a++) {
73     cumap->cm[a].flag = CUMA_EXTEND_EXTRAPOLATE;
74     cumap->cm[a].totpoint = 2;
75     cumap->cm[a].curve = MEM_callocN(2 * sizeof(CurveMapPoint), "curve points");
76
77     cumap->cm[a].curve[0].x = minx;
78     cumap->cm[a].curve[0].y = miny;
79     cumap->cm[a].curve[1].x = maxx;
80     cumap->cm[a].curve[1].y = maxy;
81   }
82
83   cumap->changed_timestamp = 0;
84 }
85
86 CurveMapping *curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
87 {
88   CurveMapping *cumap;
89
90   cumap = MEM_callocN(sizeof(CurveMapping), "new curvemap");
91
92   curvemapping_set_defaults(cumap, tot, minx, miny, maxx, maxy);
93
94   return cumap;
95 }
96
97 void curvemapping_free_data(CurveMapping *cumap)
98 {
99   int a;
100
101   for (a = 0; a < CM_TOT; a++) {
102     if (cumap->cm[a].curve) {
103       MEM_freeN(cumap->cm[a].curve);
104       cumap->cm[a].curve = NULL;
105     }
106     if (cumap->cm[a].table) {
107       MEM_freeN(cumap->cm[a].table);
108       cumap->cm[a].table = NULL;
109     }
110     if (cumap->cm[a].premultable) {
111       MEM_freeN(cumap->cm[a].premultable);
112       cumap->cm[a].premultable = NULL;
113     }
114   }
115 }
116
117 void curvemapping_free(CurveMapping *cumap)
118 {
119   if (cumap) {
120     curvemapping_free_data(cumap);
121     MEM_freeN(cumap);
122   }
123 }
124
125 void curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap)
126 {
127   int a;
128
129   *target = *cumap;
130
131   for (a = 0; a < CM_TOT; a++) {
132     if (cumap->cm[a].curve)
133       target->cm[a].curve = MEM_dupallocN(cumap->cm[a].curve);
134     if (cumap->cm[a].table)
135       target->cm[a].table = MEM_dupallocN(cumap->cm[a].table);
136     if (cumap->cm[a].premultable)
137       target->cm[a].premultable = MEM_dupallocN(cumap->cm[a].premultable);
138   }
139 }
140
141 CurveMapping *curvemapping_copy(const CurveMapping *cumap)
142 {
143   if (cumap) {
144     CurveMapping *cumapn = MEM_dupallocN(cumap);
145     curvemapping_copy_data(cumapn, cumap);
146     return cumapn;
147   }
148   return NULL;
149 }
150
151 void curvemapping_set_black_white_ex(const float black[3], const float white[3], float r_bwmul[3])
152 {
153   int a;
154
155   for (a = 0; a < 3; a++) {
156     const float delta = max_ff(white[a] - black[a], 1e-5f);
157     r_bwmul[a] = 1.0f / delta;
158   }
159 }
160
161 void curvemapping_set_black_white(CurveMapping *cumap, const float black[3], const float white[3])
162 {
163   if (white) {
164     copy_v3_v3(cumap->white, white);
165   }
166   if (black) {
167     copy_v3_v3(cumap->black, black);
168   }
169
170   curvemapping_set_black_white_ex(cumap->black, cumap->white, cumap->bwmul);
171   cumap->changed_timestamp++;
172 }
173
174 /* ***************** operations on single curve ************* */
175 /* ********** NOTE: requires curvemapping_changed() call after ******** */
176
177 /* remove specified point */
178 bool curvemap_remove_point(CurveMap *cuma, CurveMapPoint *point)
179 {
180   CurveMapPoint *cmp;
181   int a, b, removed = 0;
182
183   /* must have 2 points minimum */
184   if (cuma->totpoint <= 2)
185     return false;
186
187   cmp = MEM_mallocN((cuma->totpoint) * sizeof(CurveMapPoint), "curve points");
188
189   /* well, lets keep the two outer points! */
190   for (a = 0, b = 0; a < cuma->totpoint; a++) {
191     if (&cuma->curve[a] != point) {
192       cmp[b] = cuma->curve[a];
193       b++;
194     }
195     else {
196       removed++;
197     }
198   }
199
200   MEM_freeN(cuma->curve);
201   cuma->curve = cmp;
202   cuma->totpoint -= removed;
203   return (removed != 0);
204 }
205
206 /* removes with flag set */
207 void curvemap_remove(CurveMap *cuma, const short flag)
208 {
209   CurveMapPoint *cmp = MEM_mallocN((cuma->totpoint) * sizeof(CurveMapPoint), "curve points");
210   int a, b, removed = 0;
211
212   /* well, lets keep the two outer points! */
213   cmp[0] = cuma->curve[0];
214   for (a = 1, b = 1; a < cuma->totpoint - 1; a++) {
215     if (!(cuma->curve[a].flag & flag)) {
216       cmp[b] = cuma->curve[a];
217       b++;
218     }
219     else {
220       removed++;
221     }
222   }
223   cmp[b] = cuma->curve[a];
224
225   MEM_freeN(cuma->curve);
226   cuma->curve = cmp;
227   cuma->totpoint -= removed;
228 }
229
230 CurveMapPoint *curvemap_insert(CurveMap *cuma, float x, float y)
231 {
232   CurveMapPoint *cmp = MEM_callocN((cuma->totpoint + 1) * sizeof(CurveMapPoint), "curve points");
233   CurveMapPoint *newcmp = NULL;
234   int a, b;
235   bool foundloc = false;
236
237   /* insert fragments of the old one and the new point to the new curve */
238   cuma->totpoint++;
239   for (a = 0, b = 0; a < cuma->totpoint; a++) {
240     if ((foundloc == false) && ((a + 1 == cuma->totpoint) || (x < cuma->curve[a].x))) {
241       cmp[a].x = x;
242       cmp[a].y = y;
243       cmp[a].flag = CUMA_SELECT;
244       foundloc = true;
245       newcmp = &cmp[a];
246     }
247     else {
248       cmp[a].x = cuma->curve[b].x;
249       cmp[a].y = cuma->curve[b].y;
250       /* make sure old points don't remain selected */
251       cmp[a].flag = cuma->curve[b].flag & ~CUMA_SELECT;
252       cmp[a].shorty = cuma->curve[b].shorty;
253       b++;
254     }
255   }
256
257   /* free old curve and replace it with new one */
258   MEM_freeN(cuma->curve);
259   cuma->curve = cmp;
260
261   return newcmp;
262 }
263
264 void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
265 {
266   if (cuma->curve)
267     MEM_freeN(cuma->curve);
268
269   switch (preset) {
270     case CURVE_PRESET_LINE:
271       cuma->totpoint = 2;
272       break;
273     case CURVE_PRESET_SHARP:
274       cuma->totpoint = 4;
275       break;
276     case CURVE_PRESET_SMOOTH:
277       cuma->totpoint = 4;
278       break;
279     case CURVE_PRESET_MAX:
280       cuma->totpoint = 2;
281       break;
282     case CURVE_PRESET_MID9:
283       cuma->totpoint = 9;
284       break;
285     case CURVE_PRESET_ROUND:
286       cuma->totpoint = 4;
287       break;
288     case CURVE_PRESET_ROOT:
289       cuma->totpoint = 4;
290       break;
291     case CURVE_PRESET_GAUSS:
292       cuma->totpoint = 7;
293       break;
294     case CURVE_PRESET_BELL:
295       cuma->totpoint = 3;
296       break;
297   }
298
299   cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points");
300
301   switch (preset) {
302     case CURVE_PRESET_LINE:
303       cuma->curve[0].x = clipr->xmin;
304       cuma->curve[0].y = clipr->ymax;
305       cuma->curve[1].x = clipr->xmax;
306       cuma->curve[1].y = clipr->ymin;
307       if (slope == CURVEMAP_SLOPE_POS_NEG) {
308         cuma->curve[0].flag |= CUMA_HANDLE_VECTOR;
309         cuma->curve[1].flag |= CUMA_HANDLE_VECTOR;
310       }
311       break;
312     case CURVE_PRESET_SHARP:
313       cuma->curve[0].x = 0;
314       cuma->curve[0].y = 1;
315       cuma->curve[1].x = 0.25;
316       cuma->curve[1].y = 0.50;
317       cuma->curve[2].x = 0.75;
318       cuma->curve[2].y = 0.04;
319       cuma->curve[3].x = 1;
320       cuma->curve[3].y = 0;
321       break;
322     case CURVE_PRESET_SMOOTH:
323       cuma->curve[0].x = 0;
324       cuma->curve[0].y = 1;
325       cuma->curve[1].x = 0.25;
326       cuma->curve[1].y = 0.94;
327       cuma->curve[2].x = 0.75;
328       cuma->curve[2].y = 0.06;
329       cuma->curve[3].x = 1;
330       cuma->curve[3].y = 0;
331       break;
332     case CURVE_PRESET_MAX:
333       cuma->curve[0].x = 0;
334       cuma->curve[0].y = 1;
335       cuma->curve[1].x = 1;
336       cuma->curve[1].y = 1;
337       break;
338     case CURVE_PRESET_MID9: {
339       int i;
340       for (i = 0; i < cuma->totpoint; i++) {
341         cuma->curve[i].x = i / ((float)cuma->totpoint - 1);
342         cuma->curve[i].y = 0.5;
343       }
344       break;
345     }
346     case CURVE_PRESET_ROUND:
347       cuma->curve[0].x = 0;
348       cuma->curve[0].y = 1;
349       cuma->curve[1].x = 0.5;
350       cuma->curve[1].y = 0.90;
351       cuma->curve[2].x = 0.86;
352       cuma->curve[2].y = 0.5;
353       cuma->curve[3].x = 1;
354       cuma->curve[3].y = 0;
355       break;
356     case CURVE_PRESET_ROOT:
357       cuma->curve[0].x = 0;
358       cuma->curve[0].y = 1;
359       cuma->curve[1].x = 0.25;
360       cuma->curve[1].y = 0.95;
361       cuma->curve[2].x = 0.75;
362       cuma->curve[2].y = 0.44;
363       cuma->curve[3].x = 1;
364       cuma->curve[3].y = 0;
365       break;
366     case CURVE_PRESET_GAUSS:
367       cuma->curve[0].x = 0;
368       cuma->curve[0].y = 0.025f;
369       cuma->curve[1].x = 0.16f;
370       cuma->curve[1].y = 0.135f;
371       cuma->curve[2].x = 0.298f;
372       cuma->curve[2].y = 0.36f;
373
374       cuma->curve[3].x = 0.50f;
375       cuma->curve[3].y = 1.0f;
376
377       cuma->curve[4].x = 0.70f;
378       cuma->curve[4].y = 0.36f;
379       cuma->curve[5].x = 0.84f;
380       cuma->curve[5].y = 0.135f;
381       cuma->curve[6].x = 1.0f;
382       cuma->curve[6].y = 0.025f;
383       break;
384     case CURVE_PRESET_BELL:
385       cuma->curve[0].x = 0;
386       cuma->curve[0].y = 0.025f;
387
388       cuma->curve[1].x = 0.50f;
389       cuma->curve[1].y = 1.0f;
390
391       cuma->curve[2].x = 1.0f;
392       cuma->curve[2].y = 0.025f;
393       break;
394   }
395
396   /* mirror curve in x direction to have positive slope
397    * rather than default negative slope */
398   if (slope == CURVEMAP_SLOPE_POSITIVE) {
399     int i, last = cuma->totpoint - 1;
400     CurveMapPoint *newpoints = MEM_dupallocN(cuma->curve);
401
402     for (i = 0; i < cuma->totpoint; i++) {
403       newpoints[i].y = cuma->curve[last - i].y;
404     }
405
406     MEM_freeN(cuma->curve);
407     cuma->curve = newpoints;
408   }
409   else if (slope == CURVEMAP_SLOPE_POS_NEG) {
410     const int num_points = cuma->totpoint * 2 - 1;
411     CurveMapPoint *new_points = MEM_mallocN(num_points * sizeof(CurveMapPoint),
412                                             "curve symmetric points");
413     int i;
414     for (i = 0; i < cuma->totpoint; i++) {
415       const int src_last_point = cuma->totpoint - i - 1;
416       const int dst_last_point = num_points - i - 1;
417       new_points[i] = cuma->curve[src_last_point];
418       new_points[i].x = (1.0f - cuma->curve[src_last_point].x) * 0.5f;
419       new_points[dst_last_point] = new_points[i];
420       new_points[dst_last_point].x = 0.5f + cuma->curve[src_last_point].x * 0.5f;
421     }
422     cuma->totpoint = num_points;
423     MEM_freeN(cuma->curve);
424     cuma->curve = new_points;
425   }
426
427   if (cuma->table) {
428     MEM_freeN(cuma->table);
429     cuma->table = NULL;
430   }
431 }
432
433 /**
434  * \param type: eBezTriple_Handle
435  */
436 void curvemap_handle_set(CurveMap *cuma, int type)
437 {
438   int a;
439
440   for (a = 0; a < cuma->totpoint; a++) {
441     if (cuma->curve[a].flag & CUMA_SELECT) {
442       cuma->curve[a].flag &= ~(CUMA_HANDLE_VECTOR | CUMA_HANDLE_AUTO_ANIM);
443       if (type == HD_VECT) {
444         cuma->curve[a].flag |= CUMA_HANDLE_VECTOR;
445       }
446       else if (type == HD_AUTO_ANIM) {
447         cuma->curve[a].flag |= CUMA_HANDLE_AUTO_ANIM;
448       }
449       else {
450         /* pass */
451       }
452     }
453   }
454 }
455
456 /* *********************** Making the tables and display ************** */
457
458 /**
459  * reduced copy of #calchandleNurb_intern code in curve.c
460  */
461 static void calchandle_curvemap(BezTriple *bezt, const BezTriple *prev, const BezTriple *next)
462 {
463   /* defines to avoid confusion */
464 #define p2_h1 ((p2)-3)
465 #define p2_h2 ((p2) + 3)
466
467   const float *p1, *p3;
468   float *p2;
469   float pt[3];
470   float len, len_a, len_b;
471   float dvec_a[2], dvec_b[2];
472
473   if (bezt->h1 == 0 && bezt->h2 == 0) {
474     return;
475   }
476
477   p2 = bezt->vec[1];
478
479   if (prev == NULL) {
480     p3 = next->vec[1];
481     pt[0] = 2.0f * p2[0] - p3[0];
482     pt[1] = 2.0f * p2[1] - p3[1];
483     p1 = pt;
484   }
485   else {
486     p1 = prev->vec[1];
487   }
488
489   if (next == NULL) {
490     p1 = prev->vec[1];
491     pt[0] = 2.0f * p2[0] - p1[0];
492     pt[1] = 2.0f * p2[1] - p1[1];
493     p3 = pt;
494   }
495   else {
496     p3 = next->vec[1];
497   }
498
499   sub_v2_v2v2(dvec_a, p2, p1);
500   sub_v2_v2v2(dvec_b, p3, p2);
501
502   len_a = len_v2(dvec_a);
503   len_b = len_v2(dvec_b);
504
505   if (len_a == 0.0f)
506     len_a = 1.0f;
507   if (len_b == 0.0f)
508     len_b = 1.0f;
509
510   if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { /* auto */
511     float tvec[2];
512     tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
513     tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
514
515     len = len_v2(tvec) * 2.5614f;
516     if (len != 0.0f) {
517
518       if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
519         len_a /= len;
520         madd_v2_v2v2fl(p2_h1, p2, tvec, -len_a);
521
522         if ((bezt->h1 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
523           const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
524           const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
525           if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
526             bezt->vec[0][1] = bezt->vec[1][1];
527           }
528           else { /* handles should not be beyond y coord of two others */
529             if (ydiff1 <= 0.0f) {
530               if (prev->vec[1][1] > bezt->vec[0][1]) {
531                 bezt->vec[0][1] = prev->vec[1][1];
532               }
533             }
534             else {
535               if (prev->vec[1][1] < bezt->vec[0][1]) {
536                 bezt->vec[0][1] = prev->vec[1][1];
537               }
538             }
539           }
540         }
541       }
542       if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {
543         len_b /= len;
544         madd_v2_v2v2fl(p2_h2, p2, tvec, len_b);
545
546         if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
547           const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
548           const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
549           if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
550             bezt->vec[2][1] = bezt->vec[1][1];
551           }
552           else { /* handles should not be beyond y coord of two others */
553             if (ydiff1 <= 0.0f) {
554               if (next->vec[1][1] < bezt->vec[2][1]) {
555                 bezt->vec[2][1] = next->vec[1][1];
556               }
557             }
558             else {
559               if (next->vec[1][1] > bezt->vec[2][1]) {
560                 bezt->vec[2][1] = next->vec[1][1];
561               }
562             }
563           }
564         }
565       }
566     }
567   }
568
569   if (bezt->h1 == HD_VECT) { /* vector */
570     madd_v2_v2v2fl(p2_h1, p2, dvec_a, -1.0f / 3.0f);
571   }
572   if (bezt->h2 == HD_VECT) {
573     madd_v2_v2v2fl(p2_h2, p2, dvec_b, 1.0f / 3.0f);
574   }
575
576 #undef p2_h1
577 #undef p2_h2
578 }
579
580 /* in X, out Y.
581  * X is presumed to be outside first or last */
582 static float curvemap_calc_extend(const CurveMap *cuma,
583                                   float x,
584                                   const float first[2],
585                                   const float last[2])
586 {
587   if (x <= first[0]) {
588     if ((cuma->flag & CUMA_EXTEND_EXTRAPOLATE) == 0) {
589       /* no extrapolate */
590       return first[1];
591     }
592     else {
593       if (cuma->ext_in[0] == 0.0f)
594         return first[1] + cuma->ext_in[1] * 10000.0f;
595       else
596         return first[1] + cuma->ext_in[1] * (x - first[0]) / cuma->ext_in[0];
597     }
598   }
599   else if (x >= last[0]) {
600     if ((cuma->flag & CUMA_EXTEND_EXTRAPOLATE) == 0) {
601       /* no extrapolate */
602       return last[1];
603     }
604     else {
605       if (cuma->ext_out[0] == 0.0f)
606         return last[1] - cuma->ext_out[1] * 10000.0f;
607       else
608         return last[1] + cuma->ext_out[1] * (x - last[0]) / cuma->ext_out[0];
609     }
610   }
611   return 0.0f;
612 }
613
614 /* only creates a table for a single channel in CurveMapping */
615 static void curvemap_make_table(CurveMap *cuma, const rctf *clipr)
616 {
617   CurveMapPoint *cmp = cuma->curve;
618   BezTriple *bezt;
619   float *fp, *allpoints, *lastpoint, curf, range;
620   int a, totpoint;
621
622   if (cuma->curve == NULL)
623     return;
624
625   /* default rect also is table range */
626   cuma->mintable = clipr->xmin;
627   cuma->maxtable = clipr->xmax;
628
629   /* hrmf... we now rely on blender ipo beziers, these are more advanced */
630   bezt = MEM_callocN(cuma->totpoint * sizeof(BezTriple), "beztarr");
631
632   for (a = 0; a < cuma->totpoint; a++) {
633     cuma->mintable = min_ff(cuma->mintable, cmp[a].x);
634     cuma->maxtable = max_ff(cuma->maxtable, cmp[a].x);
635     bezt[a].vec[1][0] = cmp[a].x;
636     bezt[a].vec[1][1] = cmp[a].y;
637     if (cmp[a].flag & CUMA_HANDLE_VECTOR) {
638       bezt[a].h1 = bezt[a].h2 = HD_VECT;
639     }
640     else if (cmp[a].flag & CUMA_HANDLE_AUTO_ANIM) {
641       bezt[a].h1 = bezt[a].h2 = HD_AUTO_ANIM;
642     }
643     else {
644       bezt[a].h1 = bezt[a].h2 = HD_AUTO;
645     }
646   }
647
648   const BezTriple *bezt_prev = NULL;
649   for (a = 0; a < cuma->totpoint; a++) {
650     const BezTriple *bezt_next = (a != cuma->totpoint - 1) ? &bezt[a + 1] : NULL;
651     calchandle_curvemap(&bezt[a], bezt_prev, bezt_next);
652     bezt_prev = &bezt[a];
653   }
654
655   /* first and last handle need correction, instead of pointing to center of next/prev,
656    * we let it point to the closest handle */
657   if (cuma->totpoint > 2) {
658     float hlen, nlen, vec[3];
659
660     if (bezt[0].h2 == HD_AUTO) {
661
662       hlen = len_v3v3(bezt[0].vec[1], bezt[0].vec[2]); /* original handle length */
663       /* clip handle point */
664       copy_v3_v3(vec, bezt[1].vec[0]);
665       if (vec[0] < bezt[0].vec[1][0])
666         vec[0] = bezt[0].vec[1][0];
667
668       sub_v3_v3(vec, bezt[0].vec[1]);
669       nlen = len_v3(vec);
670       if (nlen > FLT_EPSILON) {
671         mul_v3_fl(vec, hlen / nlen);
672         add_v3_v3v3(bezt[0].vec[2], vec, bezt[0].vec[1]);
673         sub_v3_v3v3(bezt[0].vec[0], bezt[0].vec[1], vec);
674       }
675     }
676     a = cuma->totpoint - 1;
677     if (bezt[a].h2 == HD_AUTO) {
678
679       hlen = len_v3v3(bezt[a].vec[1], bezt[a].vec[0]); /* original handle length */
680       /* clip handle point */
681       copy_v3_v3(vec, bezt[a - 1].vec[2]);
682       if (vec[0] > bezt[a].vec[1][0])
683         vec[0] = bezt[a].vec[1][0];
684
685       sub_v3_v3(vec, bezt[a].vec[1]);
686       nlen = len_v3(vec);
687       if (nlen > FLT_EPSILON) {
688         mul_v3_fl(vec, hlen / nlen);
689         add_v3_v3v3(bezt[a].vec[0], vec, bezt[a].vec[1]);
690         sub_v3_v3v3(bezt[a].vec[2], bezt[a].vec[1], vec);
691       }
692     }
693   }
694   /* make the bezier curve */
695   if (cuma->table)
696     MEM_freeN(cuma->table);
697   totpoint = (cuma->totpoint - 1) * CM_RESOL;
698   fp = allpoints = MEM_callocN(totpoint * 2 * sizeof(float), "table");
699
700   for (a = 0; a < cuma->totpoint - 1; a++, fp += 2 * CM_RESOL) {
701     correct_bezpart(bezt[a].vec[1], bezt[a].vec[2], bezt[a + 1].vec[0], bezt[a + 1].vec[1]);
702     BKE_curve_forward_diff_bezier(bezt[a].vec[1][0],
703                                   bezt[a].vec[2][0],
704                                   bezt[a + 1].vec[0][0],
705                                   bezt[a + 1].vec[1][0],
706                                   fp,
707                                   CM_RESOL - 1,
708                                   2 * sizeof(float));
709     BKE_curve_forward_diff_bezier(bezt[a].vec[1][1],
710                                   bezt[a].vec[2][1],
711                                   bezt[a + 1].vec[0][1],
712                                   bezt[a + 1].vec[1][1],
713                                   fp + 1,
714                                   CM_RESOL - 1,
715                                   2 * sizeof(float));
716   }
717
718   /* store first and last handle for extrapolation, unit length */
719   cuma->ext_in[0] = bezt[0].vec[0][0] - bezt[0].vec[1][0];
720   cuma->ext_in[1] = bezt[0].vec[0][1] - bezt[0].vec[1][1];
721   range = sqrtf(cuma->ext_in[0] * cuma->ext_in[0] + cuma->ext_in[1] * cuma->ext_in[1]);
722   cuma->ext_in[0] /= range;
723   cuma->ext_in[1] /= range;
724
725   a = cuma->totpoint - 1;
726   cuma->ext_out[0] = bezt[a].vec[1][0] - bezt[a].vec[2][0];
727   cuma->ext_out[1] = bezt[a].vec[1][1] - bezt[a].vec[2][1];
728   range = sqrtf(cuma->ext_out[0] * cuma->ext_out[0] + cuma->ext_out[1] * cuma->ext_out[1]);
729   cuma->ext_out[0] /= range;
730   cuma->ext_out[1] /= range;
731
732   /* cleanup */
733   MEM_freeN(bezt);
734
735   range = CM_TABLEDIV * (cuma->maxtable - cuma->mintable);
736   cuma->range = 1.0f / range;
737
738   /* now make a table with CM_TABLE equal x distances */
739   fp = allpoints;
740   lastpoint = allpoints + 2 * (totpoint - 1);
741   cmp = MEM_callocN((CM_TABLE + 1) * sizeof(CurveMapPoint), "dist table");
742
743   for (a = 0; a <= CM_TABLE; a++) {
744     curf = cuma->mintable + range * (float)a;
745     cmp[a].x = curf;
746
747     /* get the first x coordinate larger than curf */
748     while (curf >= fp[0] && fp != lastpoint) {
749       fp += 2;
750     }
751     if (fp == allpoints || (curf >= fp[0] && fp == lastpoint))
752       cmp[a].y = curvemap_calc_extend(cuma, curf, allpoints, lastpoint);
753     else {
754       float fac1 = fp[0] - fp[-2];
755       float fac2 = fp[0] - curf;
756       if (fac1 > FLT_EPSILON)
757         fac1 = fac2 / fac1;
758       else
759         fac1 = 0.0f;
760       cmp[a].y = fac1 * fp[-1] + (1.0f - fac1) * fp[1];
761     }
762   }
763
764   MEM_freeN(allpoints);
765   cuma->table = cmp;
766 }
767
768 /* call when you do images etc, needs restore too. also verifies tables */
769 /* it uses a flag to prevent premul or free to happen twice */
770 void curvemapping_premultiply(CurveMapping *cumap, int restore)
771 {
772   int a;
773
774   if (restore) {
775     if (cumap->flag & CUMA_PREMULLED) {
776       for (a = 0; a < 3; a++) {
777         MEM_freeN(cumap->cm[a].table);
778         cumap->cm[a].table = cumap->cm[a].premultable;
779         cumap->cm[a].premultable = NULL;
780
781         copy_v2_v2(cumap->cm[a].ext_in, cumap->cm[a].premul_ext_in);
782         copy_v2_v2(cumap->cm[a].ext_out, cumap->cm[a].premul_ext_out);
783         zero_v2(cumap->cm[a].premul_ext_in);
784         zero_v2(cumap->cm[a].premul_ext_out);
785       }
786
787       cumap->flag &= ~CUMA_PREMULLED;
788     }
789   }
790   else {
791     if ((cumap->flag & CUMA_PREMULLED) == 0) {
792       /* verify and copy */
793       for (a = 0; a < 3; a++) {
794         if (cumap->cm[a].table == NULL)
795           curvemap_make_table(cumap->cm + a, &cumap->clipr);
796         cumap->cm[a].premultable = cumap->cm[a].table;
797         cumap->cm[a].table = MEM_mallocN((CM_TABLE + 1) * sizeof(CurveMapPoint), "premul table");
798         memcpy(
799             cumap->cm[a].table, cumap->cm[a].premultable, (CM_TABLE + 1) * sizeof(CurveMapPoint));
800       }
801
802       if (cumap->cm[3].table == NULL)
803         curvemap_make_table(cumap->cm + 3, &cumap->clipr);
804
805       /* premul */
806       for (a = 0; a < 3; a++) {
807         int b;
808         for (b = 0; b <= CM_TABLE; b++) {
809           cumap->cm[a].table[b].y = curvemap_evaluateF(cumap->cm + 3, cumap->cm[a].table[b].y);
810         }
811
812         copy_v2_v2(cumap->cm[a].premul_ext_in, cumap->cm[a].ext_in);
813         copy_v2_v2(cumap->cm[a].premul_ext_out, cumap->cm[a].ext_out);
814         mul_v2_v2(cumap->cm[a].ext_in, cumap->cm[3].ext_in);
815         mul_v2_v2(cumap->cm[a].ext_out, cumap->cm[3].ext_out);
816       }
817
818       cumap->flag |= CUMA_PREMULLED;
819     }
820   }
821 }
822
823 static int sort_curvepoints(const void *a1, const void *a2)
824 {
825   const struct CurveMapPoint *x1 = a1, *x2 = a2;
826
827   if (x1->x > x2->x)
828     return 1;
829   else if (x1->x < x2->x)
830     return -1;
831   return 0;
832 }
833
834 /* ************************ more CurveMapping calls *************** */
835
836 /* note; only does current curvemap! */
837 void curvemapping_changed(CurveMapping *cumap, const bool rem_doubles)
838 {
839   CurveMap *cuma = cumap->cm + cumap->cur;
840   CurveMapPoint *cmp = cuma->curve;
841   rctf *clipr = &cumap->clipr;
842   float thresh = 0.01f * BLI_rctf_size_x(clipr);
843   float dx = 0.0f, dy = 0.0f;
844   int a;
845
846   cumap->changed_timestamp++;
847
848   /* clamp with clip */
849   if (cumap->flag & CUMA_DO_CLIP) {
850     for (a = 0; a < cuma->totpoint; a++) {
851       if (cmp[a].flag & CUMA_SELECT) {
852         if (cmp[a].x < clipr->xmin)
853           dx = min_ff(dx, cmp[a].x - clipr->xmin);
854         else if (cmp[a].x > clipr->xmax)
855           dx = max_ff(dx, cmp[a].x - clipr->xmax);
856         if (cmp[a].y < clipr->ymin)
857           dy = min_ff(dy, cmp[a].y - clipr->ymin);
858         else if (cmp[a].y > clipr->ymax)
859           dy = max_ff(dy, cmp[a].y - clipr->ymax);
860       }
861     }
862     for (a = 0; a < cuma->totpoint; a++) {
863       if (cmp[a].flag & CUMA_SELECT) {
864         cmp[a].x -= dx;
865         cmp[a].y -= dy;
866       }
867     }
868
869     /* ensure zoom-level respects clipping */
870     if (BLI_rctf_size_x(&cumap->curr) > BLI_rctf_size_x(&cumap->clipr)) {
871       cumap->curr.xmin = cumap->clipr.xmin;
872       cumap->curr.xmax = cumap->clipr.xmax;
873     }
874     if (BLI_rctf_size_y(&cumap->curr) > BLI_rctf_size_y(&cumap->clipr)) {
875       cumap->curr.ymin = cumap->clipr.ymin;
876       cumap->curr.ymax = cumap->clipr.ymax;
877     }
878   }
879
880   qsort(cmp, cuma->totpoint, sizeof(CurveMapPoint), sort_curvepoints);
881
882   /* remove doubles, threshold set on 1% of default range */
883   if (rem_doubles && cuma->totpoint > 2) {
884     for (a = 0; a < cuma->totpoint - 1; a++) {
885       dx = cmp[a].x - cmp[a + 1].x;
886       dy = cmp[a].y - cmp[a + 1].y;
887       if (sqrtf(dx * dx + dy * dy) < thresh) {
888         if (a == 0) {
889           cmp[a + 1].flag |= CUMA_HANDLE_VECTOR;
890           if (cmp[a + 1].flag & CUMA_SELECT)
891             cmp[a].flag |= CUMA_SELECT;
892         }
893         else {
894           cmp[a].flag |= CUMA_HANDLE_VECTOR;
895           if (cmp[a].flag & CUMA_SELECT)
896             cmp[a + 1].flag |= CUMA_SELECT;
897         }
898         break; /* we assume 1 deletion per edit is ok */
899       }
900     }
901     if (a != cuma->totpoint - 1)
902       curvemap_remove(cuma, 2);
903   }
904   curvemap_make_table(cuma, clipr);
905 }
906
907 void curvemapping_changed_all(CurveMapping *cumap)
908 {
909   int a, cur = cumap->cur;
910
911   for (a = 0; a < CM_TOT; a++) {
912     if (cumap->cm[a].curve) {
913       cumap->cur = a;
914       curvemapping_changed(cumap, false);
915     }
916   }
917
918   cumap->cur = cur;
919 }
920
921 /* table should be verified */
922 float curvemap_evaluateF(const CurveMap *cuma, float value)
923 {
924   float fi;
925   int i;
926
927   /* index in table */
928   fi = (value - cuma->mintable) * cuma->range;
929   i = (int)fi;
930
931   /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */
932   if (fi < 0.0f || fi > CM_TABLE)
933     return curvemap_calc_extend(cuma, value, &cuma->table[0].x, &cuma->table[CM_TABLE].x);
934   else {
935     if (i < 0)
936       return cuma->table[0].y;
937     if (i >= CM_TABLE)
938       return cuma->table[CM_TABLE].y;
939
940     fi = fi - (float)i;
941     return (1.0f - fi) * cuma->table[i].y + (fi)*cuma->table[i + 1].y;
942   }
943 }
944
945 /* works with curve 'cur' */
946 float curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
947 {
948   const CurveMap *cuma = cumap->cm + cur;
949   float val = curvemap_evaluateF(cuma, value);
950
951   /* account for clipping */
952   if (cumap->flag & CUMA_DO_CLIP) {
953     if (val < cumap->curr.ymin)
954       val = cumap->curr.ymin;
955     else if (val > cumap->curr.ymax)
956       val = cumap->curr.ymax;
957   }
958
959   return val;
960 }
961
962 /* vector case */
963 void curvemapping_evaluate3F(const CurveMapping *cumap, float vecout[3], const float vecin[3])
964 {
965   vecout[0] = curvemap_evaluateF(&cumap->cm[0], vecin[0]);
966   vecout[1] = curvemap_evaluateF(&cumap->cm[1], vecin[1]);
967   vecout[2] = curvemap_evaluateF(&cumap->cm[2], vecin[2]);
968 }
969
970 /* RGB case, no black/white points, no premult */
971 void curvemapping_evaluateRGBF(const CurveMapping *cumap, float vecout[3], const float vecin[3])
972 {
973   vecout[0] = curvemap_evaluateF(&cumap->cm[0], curvemap_evaluateF(&cumap->cm[3], vecin[0]));
974   vecout[1] = curvemap_evaluateF(&cumap->cm[1], curvemap_evaluateF(&cumap->cm[3], vecin[1]));
975   vecout[2] = curvemap_evaluateF(&cumap->cm[2], curvemap_evaluateF(&cumap->cm[3], vecin[2]));
976 }
977
978 static void curvemapping_evaluateRGBF_filmlike(const CurveMapping *cumap,
979                                                float vecout[3],
980                                                const float vecin[3],
981                                                const int channel_offset[3])
982 {
983   const float v0in = vecin[channel_offset[0]];
984   const float v1in = vecin[channel_offset[1]];
985   const float v2in = vecin[channel_offset[2]];
986
987   const float v0 = curvemap_evaluateF(&cumap->cm[channel_offset[0]], v0in);
988   const float v2 = curvemap_evaluateF(&cumap->cm[channel_offset[2]], v2in);
989   const float v1 = v2 + ((v0 - v2) * (v1in - v2in) / (v0in - v2in));
990
991   vecout[channel_offset[0]] = v0;
992   vecout[channel_offset[1]] = v1;
993   vecout[channel_offset[2]] = v2;
994 }
995
996 /** same as #curvemapping_evaluate_premulRGBF
997  * but black/bwmul are passed as args for the compositor
998  * where they can change per pixel.
999  *
1000  * Use in conjunction with #curvemapping_set_black_white_ex
1001  *
1002  * \param black: Use instead of cumap->black
1003  * \param bwmul: Use instead of cumap->bwmul
1004  */
1005 void curvemapping_evaluate_premulRGBF_ex(const CurveMapping *cumap,
1006                                          float vecout[3],
1007                                          const float vecin[3],
1008                                          const float black[3],
1009                                          const float bwmul[3])
1010 {
1011   const float r = (vecin[0] - black[0]) * bwmul[0];
1012   const float g = (vecin[1] - black[1]) * bwmul[1];
1013   const float b = (vecin[2] - black[2]) * bwmul[2];
1014
1015   switch (cumap->tone) {
1016     default:
1017     case CURVE_TONE_STANDARD: {
1018       vecout[0] = curvemap_evaluateF(&cumap->cm[0], r);
1019       vecout[1] = curvemap_evaluateF(&cumap->cm[1], g);
1020       vecout[2] = curvemap_evaluateF(&cumap->cm[2], b);
1021       break;
1022     }
1023     case CURVE_TONE_FILMLIKE: {
1024       if (r >= g) {
1025         if (g > b) {
1026           /* Case 1: r >= g >  b */
1027           const int shuffeled_channels[] = {0, 1, 2};
1028           curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
1029         }
1030         else if (b > r) {
1031           /* Case 2: b >  r >= g */
1032           const int shuffeled_channels[] = {2, 0, 1};
1033           curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
1034         }
1035         else if (b > g) {
1036           /* Case 3: r >= b >  g */
1037           const int shuffeled_channels[] = {0, 2, 1};
1038           curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
1039         }
1040         else {
1041           /* Case 4: r >= g == b */
1042           copy_v2_fl2(
1043               vecout, curvemap_evaluateF(&cumap->cm[0], r), curvemap_evaluateF(&cumap->cm[1], g));
1044           vecout[2] = vecout[1];
1045         }
1046       }
1047       else {
1048         if (r >= b) {
1049           /* Case 5: g >  r >= b */
1050           const int shuffeled_channels[] = {1, 0, 2};
1051           curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
1052         }
1053         else if (b > g) {
1054           /* Case 6: b >  g >  r */
1055           const int shuffeled_channels[] = {2, 1, 0};
1056           curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
1057         }
1058         else {
1059           /* Case 7: g >= b >  r */
1060           const int shuffeled_channels[] = {1, 2, 0};
1061           curvemapping_evaluateRGBF_filmlike(cumap, vecout, vecin, shuffeled_channels);
1062         }
1063       }
1064       break;
1065     }
1066   }
1067 }
1068
1069 /* RGB with black/white points and premult. tables are checked */
1070 void curvemapping_evaluate_premulRGBF(const CurveMapping *cumap,
1071                                       float vecout[3],
1072                                       const float vecin[3])
1073 {
1074   curvemapping_evaluate_premulRGBF_ex(cumap, vecout, vecin, cumap->black, cumap->bwmul);
1075 }
1076
1077 /* same as above, byte version */
1078 void curvemapping_evaluate_premulRGB(const CurveMapping *cumap,
1079                                      unsigned char vecout_byte[3],
1080                                      const unsigned char vecin_byte[3])
1081 {
1082   float vecin[3], vecout[3];
1083
1084   vecin[0] = (float)vecin_byte[0] / 255.0f;
1085   vecin[1] = (float)vecin_byte[1] / 255.0f;
1086   vecin[2] = (float)vecin_byte[2] / 255.0f;
1087
1088   curvemapping_evaluate_premulRGBF(cumap, vecout, vecin);
1089
1090   vecout_byte[0] = unit_float_to_uchar_clamp(vecout[0]);
1091   vecout_byte[1] = unit_float_to_uchar_clamp(vecout[1]);
1092   vecout_byte[2] = unit_float_to_uchar_clamp(vecout[2]);
1093 }
1094
1095 int curvemapping_RGBA_does_something(const CurveMapping *cumap)
1096 {
1097   int a;
1098
1099   if (cumap->black[0] != 0.0f)
1100     return 1;
1101   if (cumap->black[1] != 0.0f)
1102     return 1;
1103   if (cumap->black[2] != 0.0f)
1104     return 1;
1105   if (cumap->white[0] != 1.0f)
1106     return 1;
1107   if (cumap->white[1] != 1.0f)
1108     return 1;
1109   if (cumap->white[2] != 1.0f)
1110     return 1;
1111
1112   for (a = 0; a < CM_TOT; a++) {
1113     if (cumap->cm[a].curve) {
1114       if (cumap->cm[a].totpoint != 2)
1115         return 1;
1116
1117       if (cumap->cm[a].curve[0].x != 0.0f)
1118         return 1;
1119       if (cumap->cm[a].curve[0].y != 0.0f)
1120         return 1;
1121       if (cumap->cm[a].curve[1].x != 1.0f)
1122         return 1;
1123       if (cumap->cm[a].curve[1].y != 1.0f)
1124         return 1;
1125     }
1126   }
1127   return 0;
1128 }
1129
1130 void curvemapping_initialize(CurveMapping *cumap)
1131 {
1132   int a;
1133
1134   if (cumap == NULL)
1135     return;
1136
1137   for (a = 0; a < CM_TOT; a++) {
1138     if (cumap->cm[a].table == NULL)
1139       curvemap_make_table(cumap->cm + a, &cumap->clipr);
1140   }
1141 }
1142
1143 void curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size)
1144 {
1145   int a;
1146
1147   *size = CM_TABLE + 1;
1148   *array = MEM_callocN(sizeof(float) * (*size) * 4, "CurveMapping");
1149
1150   for (a = 0; a < *size; a++) {
1151     if (cumap->cm[0].table)
1152       (*array)[a * 4 + 0] = cumap->cm[0].table[a].y;
1153     if (cumap->cm[1].table)
1154       (*array)[a * 4 + 1] = cumap->cm[1].table[a].y;
1155     if (cumap->cm[2].table)
1156       (*array)[a * 4 + 2] = cumap->cm[2].table[a].y;
1157     if (cumap->cm[3].table)
1158       (*array)[a * 4 + 3] = cumap->cm[3].table[a].y;
1159   }
1160 }
1161
1162 /* ***************** Histogram **************** */
1163
1164 #define INV_255 (1.f / 255.f)
1165
1166 BLI_INLINE int get_bin_float(float f)
1167 {
1168   int bin = (int)((f * 255.0f) + 0.5f); /* 0.5 to prevent quantisation differences */
1169
1170   /* note: clamp integer instead of float to avoid problems with NaN */
1171   CLAMP(bin, 0, 255);
1172
1173   return bin;
1174 }
1175
1176 static void save_sample_line(
1177     Scopes *scopes, const int idx, const float fx, const float rgb[3], const float ycc[3])
1178 {
1179   float yuv[3];
1180
1181   /* vectorscope*/
1182   rgb_to_yuv(rgb[0], rgb[1], rgb[2], &yuv[0], &yuv[1], &yuv[2], BLI_YUV_ITU_BT709);
1183   scopes->vecscope[idx + 0] = yuv[1];
1184   scopes->vecscope[idx + 1] = yuv[2];
1185
1186   /* waveform */
1187   switch (scopes->wavefrm_mode) {
1188     case SCOPES_WAVEFRM_RGB:
1189     case SCOPES_WAVEFRM_RGB_PARADE:
1190       scopes->waveform_1[idx + 0] = fx;
1191       scopes->waveform_1[idx + 1] = rgb[0];
1192       scopes->waveform_2[idx + 0] = fx;
1193       scopes->waveform_2[idx + 1] = rgb[1];
1194       scopes->waveform_3[idx + 0] = fx;
1195       scopes->waveform_3[idx + 1] = rgb[2];
1196       break;
1197     case SCOPES_WAVEFRM_LUMA:
1198       scopes->waveform_1[idx + 0] = fx;
1199       scopes->waveform_1[idx + 1] = ycc[0];
1200       break;
1201     case SCOPES_WAVEFRM_YCC_JPEG:
1202     case SCOPES_WAVEFRM_YCC_709:
1203     case SCOPES_WAVEFRM_YCC_601:
1204       scopes->waveform_1[idx + 0] = fx;
1205       scopes->waveform_1[idx + 1] = ycc[0];
1206       scopes->waveform_2[idx + 0] = fx;
1207       scopes->waveform_2[idx + 1] = ycc[1];
1208       scopes->waveform_3[idx + 0] = fx;
1209       scopes->waveform_3[idx + 1] = ycc[2];
1210       break;
1211   }
1212 }
1213
1214 void BKE_histogram_update_sample_line(Histogram *hist,
1215                                       ImBuf *ibuf,
1216                                       const ColorManagedViewSettings *view_settings,
1217                                       const ColorManagedDisplaySettings *display_settings)
1218 {
1219   int i, x, y;
1220   const float *fp;
1221   unsigned char *cp;
1222
1223   int x1 = 0.5f + hist->co[0][0] * ibuf->x;
1224   int x2 = 0.5f + hist->co[1][0] * ibuf->x;
1225   int y1 = 0.5f + hist->co[0][1] * ibuf->y;
1226   int y2 = 0.5f + hist->co[1][1] * ibuf->y;
1227
1228   struct ColormanageProcessor *cm_processor = NULL;
1229
1230   hist->channels = 3;
1231   hist->x_resolution = 256;
1232   hist->xmax = 1.0f;
1233   /* hist->ymax = 1.0f; */ /* now do this on the operator _only_ */
1234
1235   if (ibuf->rect == NULL && ibuf->rect_float == NULL)
1236     return;
1237
1238   if (ibuf->rect_float)
1239     cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1240
1241   for (i = 0; i < 256; i++) {
1242     x = (int)(0.5f + x1 + (float)i * (x2 - x1) / 255.0f);
1243     y = (int)(0.5f + y1 + (float)i * (y2 - y1) / 255.0f);
1244
1245     if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y) {
1246       hist->data_luma[i] = hist->data_r[i] = hist->data_g[i] = hist->data_b[i] = hist->data_a[i] =
1247           0.0f;
1248     }
1249     else {
1250       if (ibuf->rect_float) {
1251         float rgba[4];
1252         fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
1253
1254         switch (ibuf->channels) {
1255           case 4:
1256             copy_v4_v4(rgba, fp);
1257             IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
1258             break;
1259           case 3:
1260             copy_v3_v3(rgba, fp);
1261             IMB_colormanagement_processor_apply_v3(cm_processor, rgba);
1262             rgba[3] = 1.0f;
1263             break;
1264           case 2:
1265             copy_v3_fl(rgba, fp[0]);
1266             rgba[3] = fp[1];
1267             break;
1268           case 1:
1269             copy_v3_fl(rgba, fp[0]);
1270             rgba[3] = 1.0f;
1271             break;
1272           default:
1273             BLI_assert(0);
1274         }
1275
1276         hist->data_luma[i] = IMB_colormanagement_get_luminance(rgba);
1277         hist->data_r[i] = rgba[0];
1278         hist->data_g[i] = rgba[1];
1279         hist->data_b[i] = rgba[2];
1280         hist->data_a[i] = rgba[3];
1281       }
1282       else if (ibuf->rect) {
1283         cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x);
1284         hist->data_luma[i] = (float)IMB_colormanagement_get_luminance_byte(cp) / 255.0f;
1285         hist->data_r[i] = (float)cp[0] / 255.0f;
1286         hist->data_g[i] = (float)cp[1] / 255.0f;
1287         hist->data_b[i] = (float)cp[2] / 255.0f;
1288         hist->data_a[i] = (float)cp[3] / 255.0f;
1289       }
1290     }
1291   }
1292
1293   if (cm_processor)
1294     IMB_colormanagement_processor_free(cm_processor);
1295 }
1296
1297 /* if view_settings, it also applies this to byte buffers */
1298 typedef struct ScopesUpdateData {
1299   Scopes *scopes;
1300   const ImBuf *ibuf;
1301   struct ColormanageProcessor *cm_processor;
1302   const unsigned char *display_buffer;
1303   const int ycc_mode;
1304
1305   unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a;
1306 } ScopesUpdateData;
1307
1308 typedef struct ScopesUpdateDataChunk {
1309   unsigned int bin_lum[256];
1310   unsigned int bin_r[256];
1311   unsigned int bin_g[256];
1312   unsigned int bin_b[256];
1313   unsigned int bin_a[256];
1314   float min[3], max[3];
1315 } ScopesUpdateDataChunk;
1316
1317 static void scopes_update_cb(void *__restrict userdata,
1318                              const int y,
1319                              const ParallelRangeTLS *__restrict tls)
1320 {
1321   const ScopesUpdateData *data = userdata;
1322
1323   Scopes *scopes = data->scopes;
1324   const ImBuf *ibuf = data->ibuf;
1325   struct ColormanageProcessor *cm_processor = data->cm_processor;
1326   const unsigned char *display_buffer = data->display_buffer;
1327   const int ycc_mode = data->ycc_mode;
1328
1329   ScopesUpdateDataChunk *data_chunk = tls->userdata_chunk;
1330   unsigned int *bin_lum = data_chunk->bin_lum;
1331   unsigned int *bin_r = data_chunk->bin_r;
1332   unsigned int *bin_g = data_chunk->bin_g;
1333   unsigned int *bin_b = data_chunk->bin_b;
1334   unsigned int *bin_a = data_chunk->bin_a;
1335   float *min = data_chunk->min;
1336   float *max = data_chunk->max;
1337
1338   const float *rf = NULL;
1339   const unsigned char *rc = NULL;
1340   const int rows_per_sample_line = ibuf->y / scopes->sample_lines;
1341   const int savedlines = y / rows_per_sample_line;
1342   const bool do_sample_line = (savedlines < scopes->sample_lines) &&
1343                               (y % rows_per_sample_line) == 0;
1344   const bool is_float = (ibuf->rect_float != NULL);
1345
1346   if (is_float)
1347     rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels;
1348   else {
1349     rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels;
1350   }
1351
1352   for (int x = 0; x < ibuf->x; x++) {
1353     float rgba[4], ycc[3], luma;
1354
1355     if (is_float) {
1356       switch (ibuf->channels) {
1357         case 4:
1358           copy_v4_v4(rgba, rf);
1359           IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
1360           break;
1361         case 3:
1362           copy_v3_v3(rgba, rf);
1363           IMB_colormanagement_processor_apply_v3(cm_processor, rgba);
1364           rgba[3] = 1.0f;
1365           break;
1366         case 2:
1367           copy_v3_fl(rgba, rf[0]);
1368           rgba[3] = rf[1];
1369           break;
1370         case 1:
1371           copy_v3_fl(rgba, rf[0]);
1372           rgba[3] = 1.0f;
1373           break;
1374         default:
1375           BLI_assert(0);
1376       }
1377     }
1378     else {
1379       for (int c = 4; c--;)
1380         rgba[c] = rc[c] * INV_255;
1381     }
1382
1383     /* we still need luma for histogram */
1384     luma = IMB_colormanagement_get_luminance(rgba);
1385
1386     /* check for min max */
1387     if (ycc_mode == -1) {
1388       minmax_v3v3_v3(min, max, rgba);
1389     }
1390     else {
1391       rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode);
1392       mul_v3_fl(ycc, INV_255);
1393       minmax_v3v3_v3(min, max, ycc);
1394     }
1395     /* increment count for histo*/
1396     bin_lum[get_bin_float(luma)]++;
1397     bin_r[get_bin_float(rgba[0])]++;
1398     bin_g[get_bin_float(rgba[1])]++;
1399     bin_b[get_bin_float(rgba[2])]++;
1400     bin_a[get_bin_float(rgba[3])]++;
1401
1402     /* save sample if needed */
1403     if (do_sample_line) {
1404       const float fx = (float)x / (float)ibuf->x;
1405       const int idx = 2 * (ibuf->x * savedlines + x);
1406       save_sample_line(scopes, idx, fx, rgba, ycc);
1407     }
1408
1409     rf += ibuf->channels;
1410     rc += ibuf->channels;
1411   }
1412 }
1413
1414 static void scopes_update_finalize(void *__restrict userdata, void *__restrict userdata_chunk)
1415 {
1416   const ScopesUpdateData *data = userdata;
1417   const ScopesUpdateDataChunk *data_chunk = userdata_chunk;
1418
1419   unsigned int *bin_lum = data->bin_lum;
1420   unsigned int *bin_r = data->bin_r;
1421   unsigned int *bin_g = data->bin_g;
1422   unsigned int *bin_b = data->bin_b;
1423   unsigned int *bin_a = data->bin_a;
1424   const unsigned int *bin_lum_c = data_chunk->bin_lum;
1425   const unsigned int *bin_r_c = data_chunk->bin_r;
1426   const unsigned int *bin_g_c = data_chunk->bin_g;
1427   const unsigned int *bin_b_c = data_chunk->bin_b;
1428   const unsigned int *bin_a_c = data_chunk->bin_a;
1429
1430   float(*minmax)[2] = data->scopes->minmax;
1431   const float *min = data_chunk->min;
1432   const float *max = data_chunk->max;
1433
1434   for (int b = 256; b--;) {
1435     bin_lum[b] += bin_lum_c[b];
1436     bin_r[b] += bin_r_c[b];
1437     bin_g[b] += bin_g_c[b];
1438     bin_b[b] += bin_b_c[b];
1439     bin_a[b] += bin_a_c[b];
1440   }
1441
1442   for (int c = 3; c--;) {
1443     if (min[c] < minmax[c][0])
1444       minmax[c][0] = min[c];
1445     if (max[c] > minmax[c][1])
1446       minmax[c][1] = max[c];
1447   }
1448 }
1449
1450 void scopes_update(Scopes *scopes,
1451                    ImBuf *ibuf,
1452                    const ColorManagedViewSettings *view_settings,
1453                    const ColorManagedDisplaySettings *display_settings)
1454 {
1455   int a;
1456   unsigned int nl, na, nr, ng, nb;
1457   double divl, diva, divr, divg, divb;
1458   const unsigned char *display_buffer = NULL;
1459   unsigned int bin_lum[256] = {0}, bin_r[256] = {0}, bin_g[256] = {0}, bin_b[256] = {0},
1460                bin_a[256] = {0};
1461   int ycc_mode = -1;
1462   void *cache_handle = NULL;
1463   struct ColormanageProcessor *cm_processor = NULL;
1464
1465   if (ibuf->rect == NULL && ibuf->rect_float == NULL)
1466     return;
1467
1468   if (scopes->ok == 1)
1469     return;
1470
1471   if (scopes->hist.ymax == 0.f)
1472     scopes->hist.ymax = 1.f;
1473
1474   /* hmmmm */
1475   if (!(ELEM(ibuf->channels, 3, 4)))
1476     return;
1477
1478   scopes->hist.channels = 3;
1479   scopes->hist.x_resolution = 256;
1480
1481   switch (scopes->wavefrm_mode) {
1482     case SCOPES_WAVEFRM_RGB:
1483       /* fall-through */
1484     case SCOPES_WAVEFRM_RGB_PARADE:
1485       ycc_mode = -1;
1486       break;
1487     case SCOPES_WAVEFRM_LUMA:
1488     case SCOPES_WAVEFRM_YCC_JPEG:
1489       ycc_mode = BLI_YCC_JFIF_0_255;
1490       break;
1491     case SCOPES_WAVEFRM_YCC_601:
1492       ycc_mode = BLI_YCC_ITU_BT601;
1493       break;
1494     case SCOPES_WAVEFRM_YCC_709:
1495       ycc_mode = BLI_YCC_ITU_BT709;
1496       break;
1497   }
1498
1499   /* convert to number of lines with logarithmic scale */
1500   scopes->sample_lines = (scopes->accuracy * 0.01f) * (scopes->accuracy * 0.01f) * ibuf->y;
1501   CLAMP_MIN(scopes->sample_lines, 1);
1502
1503   if (scopes->sample_full)
1504     scopes->sample_lines = ibuf->y;
1505
1506   /* scan the image */
1507   for (a = 0; a < 3; a++) {
1508     scopes->minmax[a][0] = 25500.0f;
1509     scopes->minmax[a][1] = -25500.0f;
1510   }
1511
1512   scopes->waveform_tot = ibuf->x * scopes->sample_lines;
1513
1514   if (scopes->waveform_1)
1515     MEM_freeN(scopes->waveform_1);
1516   if (scopes->waveform_2)
1517     MEM_freeN(scopes->waveform_2);
1518   if (scopes->waveform_3)
1519     MEM_freeN(scopes->waveform_3);
1520   if (scopes->vecscope)
1521     MEM_freeN(scopes->vecscope);
1522
1523   scopes->waveform_1 = MEM_callocN(scopes->waveform_tot * 2 * sizeof(float),
1524                                    "waveform point channel 1");
1525   scopes->waveform_2 = MEM_callocN(scopes->waveform_tot * 2 * sizeof(float),
1526                                    "waveform point channel 2");
1527   scopes->waveform_3 = MEM_callocN(scopes->waveform_tot * 2 * sizeof(float),
1528                                    "waveform point channel 3");
1529   scopes->vecscope = MEM_callocN(scopes->waveform_tot * 2 * sizeof(float),
1530                                  "vectorscope point channel");
1531
1532   if (ibuf->rect_float) {
1533     cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1534   }
1535   else {
1536     display_buffer = (const unsigned char *)IMB_display_buffer_acquire(
1537         ibuf, view_settings, display_settings, &cache_handle);
1538   }
1539
1540   /* Keep number of threads in sync with the merge parts below. */
1541   ScopesUpdateData data = {
1542       .scopes = scopes,
1543       .ibuf = ibuf,
1544       .cm_processor = cm_processor,
1545       .display_buffer = display_buffer,
1546       .ycc_mode = ycc_mode,
1547       .bin_lum = bin_lum,
1548       .bin_r = bin_r,
1549       .bin_g = bin_g,
1550       .bin_b = bin_b,
1551       .bin_a = bin_a,
1552   };
1553   ScopesUpdateDataChunk data_chunk = {{0}};
1554   INIT_MINMAX(data_chunk.min, data_chunk.max);
1555
1556   ParallelRangeSettings settings;
1557   BLI_parallel_range_settings_defaults(&settings);
1558   settings.use_threading = (ibuf->y > 256);
1559   settings.userdata_chunk = &data_chunk;
1560   settings.userdata_chunk_size = sizeof(data_chunk);
1561   settings.func_finalize = scopes_update_finalize;
1562   BLI_task_parallel_range(0, ibuf->y, &data, scopes_update_cb, &settings);
1563
1564   /* convert hist data to float (proportional to max count) */
1565   nl = na = nr = nb = ng = 0;
1566   for (a = 0; a < 256; a++) {
1567     if (bin_lum[a] > nl)
1568       nl = bin_lum[a];
1569     if (bin_r[a] > nr)
1570       nr = bin_r[a];
1571     if (bin_g[a] > ng)
1572       ng = bin_g[a];
1573     if (bin_b[a] > nb)
1574       nb = bin_b[a];
1575     if (bin_a[a] > na)
1576       na = bin_a[a];
1577   }
1578   divl = nl ? 1.0 / (double)nl : 1.0;
1579   diva = na ? 1.0 / (double)na : 1.0;
1580   divr = nr ? 1.0 / (double)nr : 1.0;
1581   divg = ng ? 1.0 / (double)ng : 1.0;
1582   divb = nb ? 1.0 / (double)nb : 1.0;
1583
1584   for (a = 0; a < 256; a++) {
1585     scopes->hist.data_luma[a] = bin_lum[a] * divl;
1586     scopes->hist.data_r[a] = bin_r[a] * divr;
1587     scopes->hist.data_g[a] = bin_g[a] * divg;
1588     scopes->hist.data_b[a] = bin_b[a] * divb;
1589     scopes->hist.data_a[a] = bin_a[a] * diva;
1590   }
1591
1592   if (cm_processor)
1593     IMB_colormanagement_processor_free(cm_processor);
1594   if (cache_handle)
1595     IMB_display_buffer_release(cache_handle);
1596
1597   scopes->ok = 1;
1598 }
1599
1600 void scopes_free(Scopes *scopes)
1601 {
1602   if (scopes->waveform_1) {
1603     MEM_freeN(scopes->waveform_1);
1604     scopes->waveform_1 = NULL;
1605   }
1606   if (scopes->waveform_2) {
1607     MEM_freeN(scopes->waveform_2);
1608     scopes->waveform_2 = NULL;
1609   }
1610   if (scopes->waveform_3) {
1611     MEM_freeN(scopes->waveform_3);
1612     scopes->waveform_3 = NULL;
1613   }
1614   if (scopes->vecscope) {
1615     MEM_freeN(scopes->vecscope);
1616     scopes->vecscope = NULL;
1617   }
1618 }
1619
1620 void scopes_new(Scopes *scopes)
1621 {
1622   scopes->accuracy = 30.0;
1623   scopes->hist.mode = HISTO_MODE_RGB;
1624   scopes->wavefrm_alpha = 0.3;
1625   scopes->vecscope_alpha = 0.3;
1626   scopes->wavefrm_height = 100;
1627   scopes->vecscope_height = 100;
1628   scopes->hist.height = 100;
1629   scopes->ok = 0;
1630   scopes->waveform_1 = NULL;
1631   scopes->waveform_2 = NULL;
1632   scopes->waveform_3 = NULL;
1633   scopes->vecscope = NULL;
1634 }
1635
1636 void BKE_color_managed_display_settings_init(ColorManagedDisplaySettings *settings)
1637 {
1638   const char *display_name = IMB_colormanagement_display_get_default_name();
1639
1640   BLI_strncpy(settings->display_device, display_name, sizeof(settings->display_device));
1641 }
1642
1643 void BKE_color_managed_display_settings_copy(ColorManagedDisplaySettings *new_settings,
1644                                              const ColorManagedDisplaySettings *settings)
1645 {
1646   BLI_strncpy(new_settings->display_device,
1647               settings->display_device,
1648               sizeof(new_settings->display_device));
1649 }
1650
1651 void BKE_color_managed_view_settings_init_render(
1652     ColorManagedViewSettings *view_settings,
1653     const ColorManagedDisplaySettings *display_settings,
1654     const char *view_transform)
1655 {
1656   struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(
1657       display_settings->display_device);
1658
1659   if (!view_transform) {
1660     view_transform = IMB_colormanagement_display_get_default_view_transform_name(display);
1661   }
1662
1663   /* TODO(sergey): Find a way to make look query more reliable with non
1664    * default configuration. */
1665   STRNCPY(view_settings->view_transform, view_transform);
1666   STRNCPY(view_settings->look, "None");
1667
1668   view_settings->flag = 0;
1669   view_settings->gamma = 1.0f;
1670   view_settings->exposure = 0.0f;
1671   view_settings->curve_mapping = NULL;
1672
1673   IMB_colormanagement_validate_settings(display_settings, view_settings);
1674 }
1675
1676 void BKE_color_managed_view_settings_init_default(
1677     struct ColorManagedViewSettings *view_settings,
1678     const struct ColorManagedDisplaySettings *display_settings)
1679 {
1680   IMB_colormanagement_init_default_view_settings(view_settings, display_settings);
1681 }
1682
1683 void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings,
1684                                           const ColorManagedViewSettings *settings)
1685 {
1686   BLI_strncpy(new_settings->look, settings->look, sizeof(new_settings->look));
1687   BLI_strncpy(new_settings->view_transform,
1688               settings->view_transform,
1689               sizeof(new_settings->view_transform));
1690
1691   new_settings->flag = settings->flag;
1692   new_settings->exposure = settings->exposure;
1693   new_settings->gamma = settings->gamma;
1694
1695   if (settings->curve_mapping)
1696     new_settings->curve_mapping = curvemapping_copy(settings->curve_mapping);
1697   else
1698     new_settings->curve_mapping = NULL;
1699 }
1700
1701 void BKE_color_managed_view_settings_free(ColorManagedViewSettings *settings)
1702 {
1703   if (settings->curve_mapping)
1704     curvemapping_free(settings->curve_mapping);
1705 }
1706
1707 void BKE_color_managed_colorspace_settings_init(
1708     ColorManagedColorspaceSettings *colorspace_settings)
1709 {
1710   BLI_strncpy(colorspace_settings->name, "", sizeof(colorspace_settings->name));
1711 }
1712
1713 void BKE_color_managed_colorspace_settings_copy(
1714     ColorManagedColorspaceSettings *colorspace_settings,
1715     const ColorManagedColorspaceSettings *settings)
1716 {
1717   BLI_strncpy(colorspace_settings->name, settings->name, sizeof(colorspace_settings->name));
1718 }
1719
1720 bool BKE_color_managed_colorspace_settings_equals(const ColorManagedColorspaceSettings *settings1,
1721                                                   const ColorManagedColorspaceSettings *settings2)
1722 {
1723   return STREQ(settings1->name, settings2->name);
1724 }