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