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