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