Blender V5.0
colortools.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2005 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <cfloat>
11#include <cmath>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "DNA_color_types.h"
18#include "DNA_curve_types.h"
19
20#include "BLI_math_base.hh"
21#include "BLI_math_vector.hh"
22#include "BLI_rect.h"
23#include "BLI_string_utf8.h"
24#include "BLI_task.h"
25#include "BLI_utildefines.h"
26
27#include "BKE_colortools.hh"
28#include "BKE_curve.hh"
29#include "BKE_fcurve.hh"
30
32#include "IMB_imbuf_types.hh"
33
34#include "BLO_read_write.hh"
35
36/* ********************************* color curve ********************* */
37
38/* ***************** operations on full struct ************* */
39
41 int tot,
42 float minx,
43 float miny,
44 float maxx,
45 float maxy,
46 short default_handle_type)
47{
48 int a;
49 float clipminx, clipminy, clipmaxx, clipmaxy;
50
52 if (tot == 4) {
53 cumap->cur = 3; /* rhms, hack for 'col' curve? */
54 }
55
56 clipminx = min_ff(minx, maxx);
57 clipminy = min_ff(miny, maxy);
58 clipmaxx = max_ff(minx, maxx);
59 clipmaxy = max_ff(miny, maxy);
60
61 BLI_rctf_init(&cumap->curr, clipminx, clipmaxx, clipminy, clipmaxy);
62 cumap->clipr = cumap->curr;
63
64 cumap->white[0] = cumap->white[1] = cumap->white[2] = 1.0f;
65 cumap->bwmul[0] = cumap->bwmul[1] = cumap->bwmul[2] = 1.0f;
66
67 for (a = 0; a < tot; a++) {
68 if (default_handle_type == HD_VECT) {
70 }
71 else if (default_handle_type == HD_AUTO_ANIM) {
73 }
74
75 cumap->cm[a].totpoint = 2;
76 cumap->cm[a].curve = MEM_calloc_arrayN<CurveMapPoint>(2, "curve points");
77
78 cumap->cm[a].curve[0].x = minx;
79 cumap->cm[a].curve[0].y = miny;
80 cumap->cm[a].curve[0].flag |= default_handle_type;
81 cumap->cm[a].curve[1].x = maxx;
82 cumap->cm[a].curve[1].y = maxy;
83 cumap->cm[a].curve[1].flag |= default_handle_type;
84 }
85
86 cumap->changed_timestamp = 0;
87}
88
89CurveMapping *BKE_curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
90{
91 CurveMapping *cumap;
92
93 cumap = MEM_callocN<CurveMapping>("new curvemap");
94
95 BKE_curvemapping_set_defaults(cumap, tot, minx, miny, maxx, maxy, HD_AUTO);
96
97 return cumap;
98}
99
101{
102 int a;
103
104 for (a = 0; a < CM_TOT; a++) {
105 if (cumap->cm[a].curve) {
106 MEM_freeN(cumap->cm[a].curve);
107 cumap->cm[a].curve = nullptr;
108 }
109 if (cumap->cm[a].table) {
110 MEM_freeN(cumap->cm[a].table);
111 cumap->cm[a].table = nullptr;
112 }
113 if (cumap->cm[a].premultable) {
114 MEM_freeN(cumap->cm[a].premultable);
115 cumap->cm[a].premultable = nullptr;
116 }
117 }
118}
119
121{
122 if (cumap) {
124 MEM_freeN(cumap);
125 }
126}
127
129{
130 int a;
131
132 *target = *cumap;
133
134 for (a = 0; a < CM_TOT; a++) {
135 if (cumap->cm[a].curve) {
136 target->cm[a].curve = static_cast<CurveMapPoint *>(MEM_dupallocN(cumap->cm[a].curve));
137 }
138 if (cumap->cm[a].table) {
139 target->cm[a].table = static_cast<CurveMapPoint *>(MEM_dupallocN(cumap->cm[a].table));
140 }
141 if (cumap->cm[a].premultable) {
142 target->cm[a].premultable = static_cast<CurveMapPoint *>(
143 MEM_dupallocN(cumap->cm[a].premultable));
144 }
145 }
146}
147
149{
150 if (cumap) {
151 CurveMapping *cumapn = static_cast<CurveMapping *>(MEM_dupallocN(cumap));
152 BKE_curvemapping_copy_data(cumapn, cumap);
153 return cumapn;
154 }
155 return nullptr;
156}
157
158void BKE_curvemapping_set_black_white_ex(const float black[3],
159 const float white[3],
160 float r_bwmul[3])
161{
162 int a;
163
164 for (a = 0; a < 3; a++) {
165 const float delta = max_ff(white[a] - black[a], 1e-5f);
166 r_bwmul[a] = 1.0f / delta;
167 }
168}
169
171 const float black[3],
172 const float white[3])
173{
174 if (white) {
175 copy_v3_v3(cumap->white, white);
176 }
177 if (black) {
178 copy_v3_v3(cumap->black, black);
179 }
180
182 cumap->changed_timestamp++;
183}
184
185/* ***************** operations on single curve ************* */
186/* ********** NOTE: requires BKE_curvemapping_changed() call after ******** */
187
189{
190 CurveMapPoint *cmp;
191 int a, b, removed = 0;
192
193 /* must have 2 points minimum */
194 if (cuma->totpoint <= 2) {
195 return false;
196 }
197
198 cmp = MEM_malloc_arrayN<CurveMapPoint>(size_t(cuma->totpoint), "curve points");
199
200 /* well, lets keep the two outer points! */
201 for (a = 0, b = 0; a < cuma->totpoint; a++) {
202 if (&cuma->curve[a] != point) {
203 cmp[b] = cuma->curve[a];
204 b++;
205 }
206 else {
207 removed++;
208 }
209 }
210
211 MEM_freeN(cuma->curve);
212 cuma->curve = cmp;
213 cuma->totpoint -= removed;
214 return (removed != 0);
215}
216
217void BKE_curvemap_remove(CurveMap *cuma, const short flag)
218{
219 CurveMapPoint *cmp = MEM_malloc_arrayN<CurveMapPoint>(size_t(cuma->totpoint), "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
241{
243 "curve points");
244 CurveMapPoint *newcmp = nullptr;
245 int a, b;
246 bool foundloc = false;
247
248 /* insert fragments of the old one and the new point to the new curve */
249 cuma->totpoint++;
250 for (a = 0, b = 0; a < cuma->totpoint; a++) {
251 if ((foundloc == false) && ((a + 1 == cuma->totpoint) || (x < cuma->curve[a].x))) {
252 cmp[a].x = x;
253 cmp[a].y = y;
254 cmp[a].flag = CUMA_SELECT;
255 cmp[a].flag |= cuma->default_handle_type;
256 foundloc = true;
257 newcmp = &cmp[a];
258 }
259 else {
260 cmp[a].x = cuma->curve[b].x;
261 cmp[a].y = cuma->curve[b].y;
262 /* make sure old points don't remain selected */
263 cmp[a].flag = cuma->curve[b].flag & ~CUMA_SELECT;
264 cmp[a].shorty = cuma->curve[b].shorty;
265 b++;
266 }
267 }
268
269 /* free old curve and replace it with new one */
270 MEM_freeN(cuma->curve);
271 cuma->curve = cmp;
272
273 return newcmp;
274}
275
276void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, CurveMapSlopeType slope)
277{
278 if (cuma->curve) {
279 MEM_freeN(cuma->curve);
280 }
281
282 switch (preset) {
285 cuma->totpoint = 2;
286 break;
288 cuma->totpoint = 5;
289 break;
291 cuma->totpoint = 8;
292 break;
293 case CURVE_PRESET_MAX:
294 cuma->totpoint = 2;
295 break;
297 cuma->totpoint = 8;
298 break;
300 cuma->totpoint = 6;
301 break;
303 cuma->totpoint = 6;
304 break;
306 cuma->totpoint = 7;
307 break;
309 cuma->totpoint = 3;
310 break;
311 }
312
313 cuma->curve = MEM_calloc_arrayN<CurveMapPoint>(cuma->totpoint, "curve points");
314
315 for (int i = 0; i < cuma->totpoint; i++) {
316 cuma->curve[i].flag = cuma->default_handle_type;
317 }
318
319 switch (preset) {
321 cuma->curve[0].x = clipr->xmin;
322 cuma->curve[0].y = clipr->ymax;
323 cuma->curve[1].x = clipr->xmax;
324 cuma->curve[1].y = clipr->ymin;
328 cuma->curve[0].flag |= CUMA_HANDLE_VECTOR;
329 cuma->curve[1].flag |= CUMA_HANDLE_VECTOR;
330 }
331 break;
333 cuma->curve[0].x = clipr->xmin;
334 cuma->curve[0].y = (clipr->ymin + clipr->ymax) / 2.0f;
335 cuma->curve[1].x = clipr->xmax;
336 cuma->curve[1].y = (clipr->ymin + clipr->ymax) / 2.0f;
337 break;
339 cuma->curve[0].x = 0;
340 cuma->curve[0].y = 1;
341 cuma->curve[1].x = 0.25;
342 cuma->curve[1].y = 0.5625;
343 cuma->curve[2].x = 0.50;
344 cuma->curve[2].y = 0.25;
345 cuma->curve[3].x = 0.75;
346 cuma->curve[3].y = 0.0625;
347 cuma->curve[4].x = 1;
348 cuma->curve[4].y = 0;
351 cuma->curve[0].flag |= CUMA_HANDLE_VECTOR;
353 cuma->curve[4].flag |= CUMA_HANDLE_VECTOR;
354 }
355 break;
357 cuma->curve[0].x = 0;
358 cuma->curve[0].y = 1;
359 cuma->curve[1].x = 0.12;
360 cuma->curve[1].y = 0.96;
361 cuma->curve[2].x = 0.25;
362 cuma->curve[2].y = 0.84;
363 cuma->curve[3].x = 0.42;
364 cuma->curve[3].y = 0.62;
365 cuma->curve[4].x = 0.58;
366 cuma->curve[4].y = 0.38;
367 cuma->curve[5].x = 0.75;
368 cuma->curve[5].y = 0.16;
369 cuma->curve[6].x = 0.88;
370 cuma->curve[6].y = 0.04;
371 cuma->curve[7].x = 1;
372 cuma->curve[7].y = 0;
373 break;
374 case CURVE_PRESET_MAX:
375 cuma->curve[0].x = 0;
376 cuma->curve[0].y = 1;
377 cuma->curve[1].x = 1;
378 cuma->curve[1].y = 1;
379 break;
380 case CURVE_PRESET_MID8: {
381 for (int i = 0; i < cuma->totpoint; i++) {
382 cuma->curve[i].x = i / float(cuma->totpoint);
383 cuma->curve[i].y = 0.5;
384 }
385 break;
386 }
388 cuma->curve[0].x = 0;
389 cuma->curve[0].y = 1;
390 cuma->curve[1].x = 0.5;
391 cuma->curve[1].y = 0.866;
392 cuma->curve[2].x = 0.6765;
393 cuma->curve[2].y = 0.7364;
394 cuma->curve[3].x = 0.8582;
395 cuma->curve[3].y = 0.5133;
396 cuma->curve[4].x = 0.967;
397 cuma->curve[4].y = 0.2547;
398 cuma->curve[5].x = 1;
399 cuma->curve[5].y = 0;
400 break;
402 cuma->curve[0].x = 0;
403 cuma->curve[0].y = 1;
404 cuma->curve[1].x = 0.25;
405 cuma->curve[1].y = 0.866;
406 cuma->curve[2].x = 0.5;
407 cuma->curve[2].y = 0.707;
408 cuma->curve[3].x = 0.75;
409 cuma->curve[3].y = 0.5;
410 cuma->curve[4].x = 0.9375;
411 cuma->curve[4].y = 0.25;
412 cuma->curve[5].x = 1;
413 cuma->curve[5].y = 0;
416 cuma->curve[0].flag |= CUMA_HANDLE_VECTOR;
418 cuma->curve[5].flag |= CUMA_HANDLE_VECTOR;
419 }
420 break;
422 cuma->curve[0].x = 0;
423 cuma->curve[0].y = 0.025f;
424 cuma->curve[1].x = 0.16f;
425 cuma->curve[1].y = 0.135f;
426 cuma->curve[2].x = 0.298f;
427 cuma->curve[2].y = 0.36f;
428
429 cuma->curve[3].x = 0.50f;
430 cuma->curve[3].y = 1.0f;
431
432 cuma->curve[4].x = 0.70f;
433 cuma->curve[4].y = 0.36f;
434 cuma->curve[5].x = 0.84f;
435 cuma->curve[5].y = 0.135f;
436 cuma->curve[6].x = 1.0f;
437 cuma->curve[6].y = 0.025f;
438 break;
440 cuma->curve[0].x = 0.0f;
441 cuma->curve[0].y = 0.025f;
442
443 cuma->curve[1].x = 0.50f;
444 cuma->curve[1].y = 1.0f;
445
446 cuma->curve[2].x = 1.0f;
447 cuma->curve[2].y = 0.025f;
448 break;
449 }
450
451 /* mirror curve in x direction to have positive slope
452 * rather than default negative slope */
453 if (slope == CurveMapSlopeType::Positive) {
455 BLI_assert(cuma->totpoint == 2);
456 /* The LINE and CONSTANT_MEDIAN presets are defined by a single pair of points, relative to
457 * the clip region. */
458 std::swap(cuma->curve[0].y, cuma->curve[1].y);
459 }
460 else {
461 int i, last = cuma->totpoint - 1;
462 /* For all curves other than the LINE and CONSTANT_MEDIAN curves, we assume that the x period
463 * is from [0.0, 1.0] inclusive. Resetting the curve for these presets does not take into
464 * account the current clipping region. */
465 BLI_assert(cuma->curve[0].x == 0.0f && cuma->curve[last].x == 1.0f);
466 CurveMapPoint *newpoints = static_cast<CurveMapPoint *>(MEM_dupallocN(cuma->curve));
467 for (i = 0; i < cuma->totpoint; i++) {
468 newpoints[i].x = 1.0f - cuma->curve[last - i].x;
469 newpoints[i].y = cuma->curve[last - i].y;
470 }
471 MEM_freeN(cuma->curve);
472 cuma->curve = newpoints;
473 }
474 }
475 else if (slope == CurveMapSlopeType::PositiveNegative) {
476 const int num_points = cuma->totpoint * 2 - 1;
477 CurveMapPoint *new_points = MEM_malloc_arrayN<CurveMapPoint>(size_t(num_points),
478 "curve symmetric points");
479 for (int i = 0; i < cuma->totpoint; i++) {
480 const int src_last_point = cuma->totpoint - i - 1;
481 const int dst_last_point = num_points - i - 1;
482 new_points[i] = cuma->curve[src_last_point];
483 new_points[i].x = (1.0f - cuma->curve[src_last_point].x) * 0.5f;
484 new_points[dst_last_point] = new_points[i];
485 new_points[dst_last_point].x = 0.5f + cuma->curve[src_last_point].x * 0.5f;
486 }
487 cuma->totpoint = num_points;
488 MEM_freeN(cuma->curve);
489 cuma->curve = new_points;
490 }
491
492 if (cuma->table) {
493 MEM_freeN(cuma->table);
494 cuma->table = nullptr;
495 }
496}
497
499{
500 int a;
501
502 for (a = 0; a < cuma->totpoint; a++) {
503 if (cuma->curve[a].flag & CUMA_SELECT) {
505 if (type == HD_VECT) {
506 cuma->curve[a].flag |= CUMA_HANDLE_VECTOR;
507 }
508 else if (type == HD_AUTO_ANIM) {
509 cuma->curve[a].flag |= CUMA_HANDLE_AUTO_ANIM;
510 }
511 else {
512 /* pass */
513 }
514 }
515 }
516}
517
518/* *********************** Making the tables and display ************** */
519
523static void calchandle_curvemap(BezTriple *bezt, const BezTriple *prev, const BezTriple *next)
524{
525/* defines to avoid confusion */
526#define p2_h1 ((p2) - 3)
527#define p2_h2 ((p2) + 3)
528
529 const float *p1, *p3;
530 float *p2;
531 float pt[3];
532 float len, len_a, len_b;
533 float dvec_a[2], dvec_b[2];
534
535 if (bezt->h1 == 0 && bezt->h2 == 0) {
536 return;
537 }
538
539 p2 = bezt->vec[1];
540
541 if (prev == nullptr) {
542 p3 = next->vec[1];
543 pt[0] = 2.0f * p2[0] - p3[0];
544 pt[1] = 2.0f * p2[1] - p3[1];
545 p1 = pt;
546 }
547 else {
548 p1 = prev->vec[1];
549 }
550
551 if (next == nullptr) {
552 p1 = prev->vec[1];
553 pt[0] = 2.0f * p2[0] - p1[0];
554 pt[1] = 2.0f * p2[1] - p1[1];
555 p3 = pt;
556 }
557 else {
558 p3 = next->vec[1];
559 }
560
561 sub_v2_v2v2(dvec_a, p2, p1);
562 sub_v2_v2v2(dvec_b, p3, p2);
563
564 len_a = len_v2(dvec_a);
565 len_b = len_v2(dvec_b);
566
567 if (len_a == 0.0f) {
568 len_a = 1.0f;
569 }
570 if (len_b == 0.0f) {
571 len_b = 1.0f;
572 }
573
574 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) { /* auto */
575 float tvec[2];
576 tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
577 tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
578
579 len = len_v2(tvec) * 2.5614f;
580 if (len != 0.0f) {
581
582 if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
583 len_a /= len;
584 madd_v2_v2v2fl(p2_h1, p2, tvec, -len_a);
585
586 if ((bezt->h1 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
587 const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
588 const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
589 if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
590 bezt->vec[0][1] = bezt->vec[1][1];
591 }
592 else { /* handles should not be beyond y coord of two others */
593 if (ydiff1 <= 0.0f) {
594 bezt->vec[0][1] = std::max(prev->vec[1][1], bezt->vec[0][1]);
595 }
596 else {
597 bezt->vec[0][1] = std::min(prev->vec[1][1], bezt->vec[0][1]);
598 }
599 }
600 }
601 }
602 if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {
603 len_b /= len;
604 madd_v2_v2v2fl(p2_h2, p2, tvec, len_b);
605
606 if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
607 const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
608 const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
609 if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
610 bezt->vec[2][1] = bezt->vec[1][1];
611 }
612 else { /* handles should not be beyond y coord of two others */
613 if (ydiff1 <= 0.0f) {
614 bezt->vec[2][1] = std::min(next->vec[1][1], bezt->vec[2][1]);
615 }
616 else {
617 bezt->vec[2][1] = std::max(next->vec[1][1], bezt->vec[2][1]);
618 }
619 }
620 }
621 }
622 }
623 }
624
625 if (bezt->h1 == HD_VECT) { /* vector */
626 madd_v2_v2v2fl(p2_h1, p2, dvec_a, -1.0f / 3.0f);
627 }
628 if (bezt->h2 == HD_VECT) {
629 madd_v2_v2v2fl(p2_h2, p2, dvec_b, 1.0f / 3.0f);
630 }
631
632#undef p2_h1
633#undef p2_h2
634}
635
636/* in X, out Y.
637 * X is presumed to be outside first or last */
638static float curvemap_calc_extend(const CurveMapping *cumap,
639 const CurveMap *cuma,
640 float x,
641 const float first[2],
642 const float last[2])
643{
644 if (x <= first[0]) {
645 if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) == 0) {
646 /* extrapolate horizontally */
647 return first[1];
648 }
649
650 if (cuma->ext_in[0] == 0.0f) {
651 return first[1] + cuma->ext_in[1] * 10000.0f;
652 }
653
654 return first[1] + cuma->ext_in[1] * (x - first[0]) / cuma->ext_in[0];
655 }
656 if (x >= last[0]) {
657 if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) == 0) {
658 /* extrapolate horizontally */
659 return last[1];
660 }
661
662 if (cuma->ext_out[0] == 0.0f) {
663 return last[1] - cuma->ext_out[1] * 10000.0f;
664 }
665
666 return last[1] + cuma->ext_out[1] * (x - last[0]) / cuma->ext_out[0];
667 }
668 return 0.0f;
669}
670
671/* Evaluates CM_RESOL number of points on the Bezier segment defined by the given start and end
672 * Bezier triples, writing the output to the points array. */
673static void curve_eval_bezier_point(float start[3][3], float end[3][3], float *point)
674{
675 BKE_curve_correct_bezpart(start[1], start[2], end[0], end[1]);
677 start[1][0], start[2][0], end[0][0], end[1][0], point, CM_RESOL - 1, sizeof(float[2]));
679 start[1][1], start[2][1], end[0][1], end[1][1], point + 1, CM_RESOL - 1, sizeof(float[2]));
680}
681
682/* only creates a table for a single channel in CurveMapping */
683static void curvemap_make_table(const CurveMapping *cumap, CurveMap *cuma)
684{
685 const rctf *clipr = &cumap->clipr;
686
687 /* Wrapping ensures that the heights of the first and last points are the same. It adds two
688 * virtual points, which are copies of the first and last points, and moves them to the opposite
689 * side of the curve offset by the table range. The handles of these points are calculated, as if
690 * they were between the last and first real points. */
691
692 const bool use_wrapping = cumap->flag & CUMA_USE_WRAPPING;
693
694 if (cuma->curve == nullptr) {
695 return;
696 }
697
698 /* default rect also is table range */
699 cuma->mintable = clipr->xmin;
700 cuma->maxtable = clipr->xmax;
701 const int bezt_totpoint = max_ii(cuma->totpoint, 2);
702
703 /* Rely on Blender interpolation for bezier curves, support extra functionality here as well. */
704 BezTriple *bezt = MEM_calloc_arrayN<BezTriple>(bezt_totpoint, "beztarr");
705
706 /* Valid curve has at least 2 points. */
707 if (cuma->totpoint >= 2) {
708 CurveMapPoint *cmp = cuma->curve;
709
710 for (int a = 0; a < bezt_totpoint; a++) {
711 cuma->mintable = min_ff(cuma->mintable, cmp[a].x);
712 cuma->maxtable = max_ff(cuma->maxtable, cmp[a].x);
713 bezt[a].vec[1][0] = cmp[a].x;
714 bezt[a].vec[1][1] = cmp[a].y;
715 if (cmp[a].flag & CUMA_HANDLE_VECTOR) {
716 bezt[a].h1 = bezt[a].h2 = HD_VECT;
717 }
718 else if (cmp[a].flag & CUMA_HANDLE_AUTO_ANIM) {
719 bezt[a].h1 = bezt[a].h2 = HD_AUTO_ANIM;
720 }
721 else {
722 bezt[a].h1 = bezt[a].h2 = HD_AUTO;
723 }
724 }
725 }
726 else {
727 /* Fallback when points are missing. */
728 cuma->mintable = 0.0f;
729 cuma->maxtable = 0.0f;
730 zero_v2(bezt[0].vec[1]);
731 zero_v2(bezt[1].vec[1]);
732 bezt[0].h1 = HD_AUTO;
733 bezt[0].h2 = HD_AUTO;
734 bezt[1].h1 = HD_AUTO;
735 bezt[1].h2 = HD_AUTO;
736 }
737
738 const BezTriple *bezt_next = nullptr;
739 const BezTriple *bezt_prev = nullptr;
740
741 /* Create two extra points for wrapping curves. */
742 BezTriple bezt_pre = bezt[bezt_totpoint - 1];
743 BezTriple bezt_post = bezt[0];
744
745 BezTriple *bezt_post_ptr;
746
747 float table_range = cuma->maxtable - cuma->mintable;
748 if (use_wrapping) {
749 /* Handle location of pre and post points for wrapping curves. */
750 bezt_pre.h1 = bezt_pre.h2 = bezt[bezt_totpoint - 1].h2;
751 bezt_pre.vec[1][0] = bezt[bezt_totpoint - 1].vec[1][0] - table_range;
752 bezt_pre.vec[1][1] = bezt[bezt_totpoint - 1].vec[1][1];
753
754 bezt_post.h1 = bezt_post.h2 = bezt[0].h1;
755 bezt_post.vec[1][0] = bezt[0].vec[1][0] + table_range;
756 bezt_post.vec[1][1] = bezt[0].vec[1][1];
757
758 bezt_prev = &bezt_pre;
759 bezt_post_ptr = &bezt_post;
760 }
761 else {
762 bezt_prev = nullptr;
763 bezt_post_ptr = nullptr;
764 }
765
766 /* Process middle elements */
767 for (int a = 0; a < bezt_totpoint; a++) {
768 bezt_next = (a != bezt_totpoint - 1) ? &bezt[a + 1] : bezt_post_ptr;
769 calchandle_curvemap(&bezt[a], bezt_prev, bezt_next);
770 bezt_prev = &bezt[a];
771 }
772
773 /* Correct handles of pre and post points for wrapping curves. */
774 bezt_pre.vec[0][0] = bezt[bezt_totpoint - 1].vec[0][0] - table_range;
775 bezt_pre.vec[0][1] = bezt[bezt_totpoint - 1].vec[0][1];
776 bezt_pre.vec[2][0] = bezt[bezt_totpoint - 1].vec[2][0] - table_range;
777 bezt_pre.vec[2][1] = bezt[bezt_totpoint - 1].vec[2][1];
778
779 bezt_post.vec[0][0] = bezt[0].vec[0][0] + table_range;
780 bezt_post.vec[0][1] = bezt[0].vec[0][1];
781 bezt_post.vec[2][0] = bezt[0].vec[2][0] + table_range;
782 bezt_post.vec[2][1] = bezt[0].vec[2][1];
783
784 /* first and last handle need correction, instead of pointing to center of next/prev,
785 * we let it point to the closest handle */
786 if (bezt_totpoint > 2 && !use_wrapping) {
787 float hlen, nlen, vec[3];
788
789 if (bezt[0].h2 == HD_AUTO) {
790
791 hlen = len_v3v3(bezt[0].vec[1], bezt[0].vec[2]); /* original handle length */
792 /* clip handle point */
793 copy_v3_v3(vec, bezt[1].vec[0]);
794 vec[0] = std::max(vec[0], bezt[0].vec[1][0]);
795
796 sub_v3_v3(vec, bezt[0].vec[1]);
797 nlen = len_v3(vec);
798 if (nlen > FLT_EPSILON) {
799 mul_v3_fl(vec, hlen / nlen);
800 add_v3_v3v3(bezt[0].vec[2], vec, bezt[0].vec[1]);
801 sub_v3_v3v3(bezt[0].vec[0], bezt[0].vec[1], vec);
802 }
803 }
804 int a = bezt_totpoint - 1;
805 if (bezt[a].h2 == HD_AUTO) {
806
807 hlen = len_v3v3(bezt[a].vec[1], bezt[a].vec[0]); /* original handle length */
808 /* clip handle point */
809 copy_v3_v3(vec, bezt[a - 1].vec[2]);
810 vec[0] = std::min(vec[0], bezt[a].vec[1][0]);
811
812 sub_v3_v3(vec, bezt[a].vec[1]);
813 nlen = len_v3(vec);
814 if (nlen > FLT_EPSILON) {
815 mul_v3_fl(vec, hlen / nlen);
816 add_v3_v3v3(bezt[a].vec[0], vec, bezt[a].vec[1]);
817 sub_v3_v3v3(bezt[a].vec[2], bezt[a].vec[1], vec);
818 }
819 }
820 }
821
822 /* make the bezier curve */
823 if (cuma->table) {
824 MEM_freeN(cuma->table);
825 }
826
827 const int totpoint = use_wrapping ? (bezt_totpoint + 1) * CM_RESOL :
828 (bezt_totpoint - 1) * CM_RESOL;
829 float *allpoints = MEM_calloc_arrayN<float>(size_t(totpoint) * 2, "table");
830 float *point = allpoints;
831
832 /* Handle pre point for wrapping */
833 if (use_wrapping) {
834 curve_eval_bezier_point(bezt_pre.vec, bezt[0].vec, point);
835 point += 2 * CM_RESOL;
836 }
837
838 /* Process middle elements */
839 for (int a = 0; a < bezt_totpoint - 1; a++, point += 2 * CM_RESOL) {
840 int b = a + 1;
841 curve_eval_bezier_point(bezt[a].vec, bezt[b].vec, point);
842 }
843
844 if (use_wrapping) {
845 /* Handle post point for wrapping */
846 curve_eval_bezier_point(bezt[bezt_totpoint - 1].vec, bezt_post.vec, point);
847 }
848 /* Store first and last handle for extrapolation, unit length. (Only relevant when not using
849 * wrapping.) */
850 cuma->ext_in[0] = bezt[0].vec[0][0] - bezt[0].vec[1][0];
851 cuma->ext_in[1] = bezt[0].vec[0][1] - bezt[0].vec[1][1];
852 float ext_in_range = sqrtf(cuma->ext_in[0] * cuma->ext_in[0] +
853 cuma->ext_in[1] * cuma->ext_in[1]);
854 cuma->ext_in[0] /= ext_in_range;
855 cuma->ext_in[1] /= ext_in_range;
856
857 int out_a = bezt_totpoint - 1;
858 cuma->ext_out[0] = bezt[out_a].vec[1][0] - bezt[out_a].vec[2][0];
859 cuma->ext_out[1] = bezt[out_a].vec[1][1] - bezt[out_a].vec[2][1];
860 float ext_out_range = sqrtf(cuma->ext_out[0] * cuma->ext_out[0] +
861 cuma->ext_out[1] * cuma->ext_out[1]);
862 cuma->ext_out[0] /= ext_out_range;
863 cuma->ext_out[1] /= ext_out_range;
864
865 /* cleanup */
866 MEM_freeN(bezt);
867
868 float range = CM_TABLEDIV * table_range;
869 cuma->range = 1.0f / range;
870
871 /* now make a table with CM_TABLE equal x distances */
872 float *firstpoint = allpoints;
873 float *lastpoint = allpoints + 2 * (totpoint - 1);
874 point = allpoints;
875
877
878 for (int a = 0; a <= CM_TABLE; a++) {
879 float cur_x = cuma->mintable + range * float(a);
880 cmp[a].x = cur_x;
881
882 /* Get the first point with x coordinate larger than cur_x. */
883 while (cur_x >= point[0] && point != lastpoint) {
884 point += 2;
885 }
886 /* Check if we are on or outside the start or end point. */
887 if ((point == firstpoint || (point == lastpoint && cur_x >= point[0])) && !use_wrapping) {
888 if (compare_ff(cur_x, point[0], 1e-6f)) {
889 /* When on the point exactly, use the value directly to avoid precision
890 * issues with extrapolation of extreme slopes. */
891 cmp[a].y = point[1];
892 }
893 else {
894 /* Extrapolate values that lie outside the start and end point. */
895 cmp[a].y = curvemap_calc_extend(cumap, cuma, cur_x, firstpoint, lastpoint);
896 }
897 }
898 else {
899 float fac1 = point[0] - point[-2];
900 float fac2 = point[0] - cur_x;
901 if (fac1 > FLT_EPSILON) {
902 fac1 = fac2 / fac1;
903 }
904 else {
905 fac1 = 0.0f;
906 }
907 cmp[a].y = fac1 * point[-1] + (1.0f - fac1) * point[1];
908 }
909 }
910
911 MEM_freeN(allpoints);
912 cuma->table = cmp;
913}
914
916{
917 /* It uses a flag to prevent pre-multiply or free to happen twice. */
918
919 int a;
920
921 if (restore) {
922 if (cumap->flag & CUMA_PREMULLED) {
923 for (a = 0; a < 3; a++) {
924 MEM_freeN(cumap->cm[a].table);
925 cumap->cm[a].table = cumap->cm[a].premultable;
926 cumap->cm[a].premultable = nullptr;
927
928 copy_v2_v2(cumap->cm[a].ext_in, cumap->cm[a].premul_ext_in);
929 copy_v2_v2(cumap->cm[a].ext_out, cumap->cm[a].premul_ext_out);
930 zero_v2(cumap->cm[a].premul_ext_in);
931 zero_v2(cumap->cm[a].premul_ext_out);
932 }
933
934 cumap->flag &= ~CUMA_PREMULLED;
935 }
936 }
937 else {
938 if ((cumap->flag & CUMA_PREMULLED) == 0) {
939 /* verify and copy */
940 for (a = 0; a < 3; a++) {
941 if (cumap->cm[a].table == nullptr) {
942 curvemap_make_table(cumap, cumap->cm + a);
943 }
944 cumap->cm[a].premultable = cumap->cm[a].table;
945 cumap->cm[a].table = MEM_malloc_arrayN<CurveMapPoint>(CM_TABLE + 1, "premul table");
946 memcpy(
947 cumap->cm[a].table, cumap->cm[a].premultable, (CM_TABLE + 1) * sizeof(CurveMapPoint));
948 }
949
950 if (cumap->cm[3].table == nullptr) {
951 curvemap_make_table(cumap, cumap->cm + 3);
952 }
953
954 /* premul */
955 for (a = 0; a < 3; a++) {
956 int b;
957 for (b = 0; b <= CM_TABLE; b++) {
958 cumap->cm[a].table[b].y = BKE_curvemap_evaluateF(
959 cumap, cumap->cm + 3, cumap->cm[a].table[b].y);
960 }
961
962 copy_v2_v2(cumap->cm[a].premul_ext_in, cumap->cm[a].ext_in);
963 copy_v2_v2(cumap->cm[a].premul_ext_out, cumap->cm[a].ext_out);
964 mul_v2_v2(cumap->cm[a].ext_in, cumap->cm[3].ext_in);
965 mul_v2_v2(cumap->cm[a].ext_out, cumap->cm[3].ext_out);
966 }
967
968 cumap->flag |= CUMA_PREMULLED;
969 }
970 }
971}
972
973/* ************************ more CurveMapping calls *************** */
974
975void BKE_curvemapping_changed(CurveMapping *cumap, const bool rem_doubles)
976{
977 CurveMap *cuma = cumap->cm + cumap->cur;
978 CurveMapPoint *cmp = cuma->curve;
979 const rctf *clipr = &cumap->clipr;
980 float thresh = 0.01f * BLI_rctf_size_x(clipr);
981 float dx = 0.0f, dy = 0.0f;
982 int a;
983
984 cumap->changed_timestamp++;
985
986 /* clamp with clip */
987 if (cumap->flag & CUMA_DO_CLIP) {
988 for (a = 0; a < cuma->totpoint; a++) {
989 if (cmp[a].flag & CUMA_SELECT) {
990 if (cmp[a].x < clipr->xmin) {
991 dx = min_ff(dx, cmp[a].x - clipr->xmin);
992 }
993 else if (cmp[a].x > clipr->xmax) {
994 dx = max_ff(dx, cmp[a].x - clipr->xmax);
995 }
996 if (cmp[a].y < clipr->ymin) {
997 dy = min_ff(dy, cmp[a].y - clipr->ymin);
998 }
999 else if (cmp[a].y > clipr->ymax) {
1000 dy = max_ff(dy, cmp[a].y - clipr->ymax);
1001 }
1002 }
1003 }
1004 for (a = 0; a < cuma->totpoint; a++) {
1005 if (cmp[a].flag & CUMA_SELECT) {
1006 cmp[a].x -= dx;
1007 cmp[a].y -= dy;
1008 }
1009 }
1010
1011 /* ensure zoom-level respects clipping */
1012 if (BLI_rctf_size_x(&cumap->curr) > BLI_rctf_size_x(&cumap->clipr)) {
1013 cumap->curr.xmin = cumap->clipr.xmin;
1014 cumap->curr.xmax = cumap->clipr.xmax;
1015 }
1016 if (BLI_rctf_size_y(&cumap->curr) > BLI_rctf_size_y(&cumap->clipr)) {
1017 cumap->curr.ymin = cumap->clipr.ymin;
1018 cumap->curr.ymax = cumap->clipr.ymax;
1019 }
1020 }
1021
1022 std::stable_sort(cuma->curve,
1023 cuma->curve + cuma->totpoint,
1024 [](const CurveMapPoint &a, const CurveMapPoint &b) { return a.x < b.x; });
1025
1026 /* remove doubles, threshold set on 1% of default range */
1027 if (rem_doubles && cuma->totpoint > 2) {
1028 for (a = 0; a < cuma->totpoint - 1; a++) {
1029 dx = cmp[a].x - cmp[a + 1].x;
1030 dy = cmp[a].y - cmp[a + 1].y;
1031 if (sqrtf(dx * dx + dy * dy) < thresh) {
1032 if (a == 0) {
1033 cmp[a + 1].flag |= CUMA_REMOVE;
1034 if (cmp[a + 1].flag & CUMA_SELECT) {
1035 cmp[a].flag |= CUMA_SELECT;
1036 }
1037 }
1038 else {
1039 cmp[a].flag |= CUMA_REMOVE;
1040 if (cmp[a].flag & CUMA_SELECT) {
1041 cmp[a + 1].flag |= CUMA_SELECT;
1042 }
1043 }
1044 break; /* we assume 1 deletion per edit is ok */
1045 }
1046 }
1047 if (a != cuma->totpoint - 1) {
1049 }
1050 }
1051 curvemap_make_table(cumap, cuma);
1052}
1053
1055{
1056 int a, cur = cumap->cur;
1057
1058 for (a = 0; a < CM_TOT; a++) {
1059 if (cumap->cm[a].curve) {
1060 cumap->cur = a;
1061 BKE_curvemapping_changed(cumap, false);
1062 }
1063 }
1064
1065 cumap->cur = cur;
1066}
1067
1069{
1070 cumap->curr = cumap->clipr;
1071}
1072
1073float BKE_curvemap_evaluateF(const CurveMapping *cumap, const CurveMap *cuma, float value)
1074{
1075 /* index in table */
1076 float fi = (value - cuma->mintable) * cuma->range;
1077 int i = int(fi);
1078
1079 /* fi is table float index and should check against table range i.e. [0.0 CM_TABLE] */
1080 if (fi < 0.0f || fi > CM_TABLE) {
1081 return curvemap_calc_extend(cumap, cuma, value, &cuma->table[0].x, &cuma->table[CM_TABLE].x);
1082 }
1083
1084 if (i < 0) {
1085 return cuma->table[0].y;
1086 }
1087 if (i >= CM_TABLE) {
1088 return cuma->table[CM_TABLE].y;
1089 }
1090
1091 fi = fi - float(i);
1092 return (1.0f - fi) * cuma->table[i].y + (fi)*cuma->table[i + 1].y;
1093}
1094
1095float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
1096{
1097 const CurveMap *cuma = cumap->cm + cur;
1098 float val = BKE_curvemap_evaluateF(cumap, cuma, value);
1099
1100 /* account for clipping */
1101 if (cumap->flag & CUMA_DO_CLIP) {
1102 if (val < cumap->clipr.ymin) {
1103 val = cumap->clipr.ymin;
1104 }
1105 else if (val > cumap->clipr.ymax) {
1106 val = cumap->clipr.ymax;
1107 }
1108 }
1109
1110 return val;
1111}
1112
1113void BKE_curvemapping_evaluate3F(const CurveMapping *cumap, float vecout[3], const float vecin[3])
1114{
1115 vecout[0] = BKE_curvemap_evaluateF(cumap, &cumap->cm[0], vecin[0]);
1116 vecout[1] = BKE_curvemap_evaluateF(cumap, &cumap->cm[1], vecin[1]);
1117 vecout[2] = BKE_curvemap_evaluateF(cumap, &cumap->cm[2], vecin[2]);
1118}
1119
1121 float vecout[3],
1122 const float vecin[3])
1123{
1124 vecout[0] = BKE_curvemap_evaluateF(
1125 cumap, &cumap->cm[0], BKE_curvemap_evaluateF(cumap, &cumap->cm[3], vecin[0]));
1126 vecout[1] = BKE_curvemap_evaluateF(
1127 cumap, &cumap->cm[1], BKE_curvemap_evaluateF(cumap, &cumap->cm[3], vecin[1]));
1128 vecout[2] = BKE_curvemap_evaluateF(
1129 cumap, &cumap->cm[2], BKE_curvemap_evaluateF(cumap, &cumap->cm[3], vecin[2]));
1130}
1131
1132/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve
1133 * the hue of the colors as much as possible. To understand why this might be a problem, consider
1134 * the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the
1135 * color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity, which
1136 * is what film-like tone curves tries to avoid.
1137 *
1138 * First, the channels with the lowest and highest values are identified and evaluated at the
1139 * curve. Then, the third channel---the median---is computed while maintaining the original hue of
1140 * the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming
1141 * the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the
1142 * hue, the equation is:
1143 *
1144 * hue = (median - min) / (max - min) [1]
1145 *
1146 * Since we have the new values for the minimum and maximum after evaluating at the curve, we also
1147 * have:
1148 *
1149 * hue = (new_median - new_min) / (new_max - new_min) [2]
1150 *
1151 * Since we want the hue to be equivalent, by equating [1] and [2] and rearranging:
1152 *
1153 * (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min)
1154 * new_median - new_min = (new_max - new_min) * (median - min) / (max - min)
1155 * new_median = new_min + (new_max - new_min) * (median - min) / (max - min)
1156 * new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED]
1157 *
1158 * Which gives us the median color that preserves the hue. More intuitively, the median is computed
1159 * such that the change in the distance from the median to the minimum is proportional to the
1160 * change in the distance from the minimum to the maximum. Finally, each of the new minimum,
1161 * maximum, and median values are written to the color channel that they were originally extracted
1162 * from. */
1164{
1165 /* Film-like curves are only evaluated on the combined curve, which is the fourth curve map. */
1166 const CurveMap *curve_map = curve_mapping->cm + 3;
1167
1168 /* Find the maximum, minimum, and median of the color channels. */
1169 const float minimum = blender::math::reduce_min(input);
1170 const float maximum = blender::math::reduce_max(input);
1171 const float median = blender::math::max(
1174
1175 const float new_min = BKE_curvemap_evaluateF(curve_mapping, curve_map, minimum);
1176 const float new_max = BKE_curvemap_evaluateF(curve_mapping, curve_map, maximum);
1177
1178 /* Compute the new median using the ratio between the new and the original range. */
1179 const float scaling_ratio = (new_max - new_min) / (maximum - minimum);
1180 const float new_median = new_min + (median - minimum) * scaling_ratio;
1181
1182 /* Write each value to its original channel. */
1183 const blender::float3 median_or_min = blender::float3(input.x == minimum ? new_min : new_median,
1184 input.y == minimum ? new_min : new_median,
1185 input.z == minimum ? new_min : new_median);
1186 return blender::float3(input.x == maximum ? new_max : median_or_min.x,
1187 input.y == maximum ? new_max : median_or_min.y,
1188 input.z == maximum ? new_max : median_or_min.z);
1189}
1190
1192 float vecout[3],
1193 const float vecin[3],
1194 const float black[3],
1195 const float bwmul[3])
1196{
1197 const float r = (vecin[0] - black[0]) * bwmul[0];
1198 const float g = (vecin[1] - black[1]) * bwmul[1];
1199 const float b = (vecin[2] - black[2]) * bwmul[2];
1200 const float balanced_color[3] = {r, g, b};
1201
1202 switch (cumap->tone) {
1203 default:
1204 case CURVE_TONE_STANDARD: {
1205 vecout[0] = BKE_curvemap_evaluateF(cumap, &cumap->cm[0], r);
1206 vecout[1] = BKE_curvemap_evaluateF(cumap, &cumap->cm[1], g);
1207 vecout[2] = BKE_curvemap_evaluateF(cumap, &cumap->cm[2], b);
1208 break;
1209 }
1210 case CURVE_TONE_FILMLIKE: {
1211 const blender::float3 output = evaluate_film_like(cumap, balanced_color);
1212 copy_v3_v3(vecout, output);
1213 break;
1214 }
1215 }
1216}
1217
1219 float vecout[3],
1220 const float vecin[3])
1221{
1222 BKE_curvemapping_evaluate_premulRGBF_ex(cumap, vecout, vecin, cumap->black, cumap->bwmul);
1223}
1224
1226 uchar vecout_byte[3],
1227 const uchar vecin_byte[3])
1228{
1229 float vecin[3], vecout[3];
1230
1231 vecin[0] = float(vecin_byte[0]) / 255.0f;
1232 vecin[1] = float(vecin_byte[1]) / 255.0f;
1233 vecin[2] = float(vecin_byte[2]) / 255.0f;
1234
1235 BKE_curvemapping_evaluate_premulRGBF(cumap, vecout, vecin);
1236
1237 vecout_byte[0] = unit_float_to_uchar_clamp(vecout[0]);
1238 vecout_byte[1] = unit_float_to_uchar_clamp(vecout[1]);
1239 vecout_byte[2] = unit_float_to_uchar_clamp(vecout[2]);
1240}
1241
1243{
1244 if (cumap->black[0] != 0.0f) {
1245 return true;
1246 }
1247 if (cumap->black[1] != 0.0f) {
1248 return true;
1249 }
1250 if (cumap->black[2] != 0.0f) {
1251 return true;
1252 }
1253 if (cumap->white[0] != 1.0f) {
1254 return true;
1255 }
1256 if (cumap->white[1] != 1.0f) {
1257 return true;
1258 }
1259 if (cumap->white[2] != 1.0f) {
1260 return true;
1261 }
1262
1263 for (int a = 0; a < CM_TOT; a++) {
1264 if (cumap->cm[a].curve) {
1265 if (cumap->cm[a].totpoint != 2) {
1266 return true;
1267 }
1268
1269 if (cumap->cm[a].curve[0].x != 0.0f) {
1270 return true;
1271 }
1272 if (cumap->cm[a].curve[0].y != 0.0f) {
1273 return true;
1274 }
1275 if (cumap->cm[a].curve[1].x != 1.0f) {
1276 return true;
1277 }
1278 if (cumap->cm[a].curve[1].y != 1.0f) {
1279 return true;
1280 }
1281 }
1282 }
1283 return false;
1284}
1285
1286void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
1287{
1288 for (int i = 0; i < CM_TOT; i++) {
1289 minimums[i] = curve_mapping->cm[i].mintable;
1290 }
1291}
1292
1294 float dividers[CM_TOT])
1295{
1296 for (int i = 0; i < CM_TOT; i++) {
1297 const CurveMap *curve_map = &curve_mapping->cm[i];
1298 dividers[i] = 1.0f / max_ff(1e-8f, curve_map->maxtable - curve_map->mintable);
1299 }
1300}
1301
1303 float start_slopes[CM_TOT],
1304 float end_slopes[CM_TOT])
1305{
1306 float range_dividers[CM_TOT];
1307 BKE_curvemapping_compute_range_dividers(curve_mapping, range_dividers);
1308 for (int i = 0; i < CM_TOT; i++) {
1309 const CurveMap *curve_map = &curve_mapping->cm[i];
1310 /* If extrapolation is not enabled, the slopes are horizontal. */
1311 if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
1312 start_slopes[i] = 0.0f;
1313 end_slopes[i] = 0.0f;
1314 continue;
1315 }
1316
1317 if (curve_map->ext_in[0] != 0.0f) {
1318 start_slopes[i] = curve_map->ext_in[1] / (curve_map->ext_in[0] * range_dividers[i]);
1319 }
1320 else {
1321 start_slopes[i] = 1e8f;
1322 }
1323
1324 if (curve_map->ext_out[0] != 0.0f) {
1325 end_slopes[i] = curve_map->ext_out[1] / (curve_map->ext_out[0] * range_dividers[i]);
1326 }
1327 else {
1328 end_slopes[i] = 1e8f;
1329 }
1330 }
1331}
1332
1333bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
1334{
1335 if (!(curve_mapping->flag & CUMA_EXTEND_EXTRAPOLATE)) {
1336 return false;
1337 }
1338 const CurveMap *curve_map = &curve_mapping->cm[index];
1339 if (curve_map->maxtable - curve_map->mintable != 1.0f) {
1340 return false;
1341 }
1342 if (curve_map->ext_in[0] != curve_map->ext_in[1]) {
1343 return false;
1344 }
1345 if (curve_map->ext_out[0] != curve_map->ext_out[1]) {
1346 return false;
1347 }
1348 if (curve_map->totpoint != 2) {
1349 return false;
1350 }
1351 if (curve_map->curve[0].x != 0 || curve_map->curve[0].y != 0) {
1352 return false;
1353 }
1354 if (curve_map->curve[1].x != 0 || curve_map->curve[1].y != 0) {
1355 return false;
1356 }
1357 return true;
1358}
1359
1361{
1362 int a;
1363
1364 if (cumap == nullptr) {
1365 return;
1366 }
1367
1368 for (a = 0; a < CM_TOT; a++) {
1369 if (cumap->cm[a].table == nullptr) {
1370 curvemap_make_table(cumap, cumap->cm + a);
1371 }
1372 }
1373}
1374
1375void BKE_curvemapping_table_F(const CurveMapping *cumap, float **array, int *size)
1376{
1377 int a;
1378
1379 *size = CM_TABLE + 1;
1380 *array = MEM_calloc_arrayN<float>(4 * size_t(*size), "CurveMapping");
1381
1382 for (a = 0; a < *size; a++) {
1383 if (cumap->cm[0].table) {
1384 (*array)[a * 4 + 0] = cumap->cm[0].table[a].y;
1385 }
1386 }
1387}
1388
1389void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size)
1390{
1391 int a;
1392
1393 *size = CM_TABLE + 1;
1394 *array = MEM_calloc_arrayN<float>(4 * size_t(*size), "CurveMapping");
1395
1396 for (a = 0; a < *size; a++) {
1397 if (cumap->cm[0].table) {
1398 (*array)[a * 4 + 0] = cumap->cm[0].table[a].y;
1399 }
1400 if (cumap->cm[1].table) {
1401 (*array)[a * 4 + 1] = cumap->cm[1].table[a].y;
1402 }
1403 if (cumap->cm[2].table) {
1404 (*array)[a * 4 + 2] = cumap->cm[2].table[a].y;
1405 }
1406 if (cumap->cm[3].table) {
1407 (*array)[a * 4 + 3] = cumap->cm[3].table[a].y;
1408 }
1409 }
1410}
1411
1413{
1414 BLO_write_struct(writer, CurveMapping, cumap);
1416}
1417
1419{
1420 for (int a = 0; a < CM_TOT; a++) {
1421 BLO_write_struct_array(writer, CurveMapPoint, cumap->cm[a].totpoint, cumap->cm[a].curve);
1422 }
1423}
1424
1426{
1427 /* flag seems to be able to hang? Maybe old files... not bad to clear anyway */
1428 cumap->flag &= ~CUMA_PREMULLED;
1429
1430 for (int a = 0; a < CM_TOT; a++) {
1431 BLO_read_struct_array(reader, CurveMapPoint, cumap->cm[a].totpoint, &cumap->cm[a].curve);
1432 cumap->cm[a].table = nullptr;
1433 cumap->cm[a].premultable = nullptr;
1434 }
1435}
1436
1437/* ***************** Histogram **************** */
1438
1439#define INV_255 (1.0f / 255.0f)
1440
1442{
1443 int bin = int((f * 255.0f) + 0.5f); /* 0.5 to prevent quantization differences */
1444
1445 /* NOTE: clamp integer instead of float to avoid problems with NaN. */
1446 CLAMP(bin, 0, 255);
1447
1448 return bin;
1449}
1450
1452 Scopes *scopes, const int idx, const float fx, const float rgb[3], const float ycc[3])
1453{
1454 float yuv[3];
1455
1456 /* Vector-scope. */
1457 rgb_to_yuv(rgb[0], rgb[1], rgb[2], &yuv[0], &yuv[1], &yuv[2], BLI_YUV_ITU_BT709);
1458 scopes->vecscope[idx + 0] = yuv[1] * SCOPES_VEC_U_SCALE;
1459 scopes->vecscope[idx + 1] = yuv[2] * SCOPES_VEC_V_SCALE;
1460
1461 int color_idx = (idx / 2) * 3;
1462 scopes->vecscope_rgb[color_idx + 0] = rgb[0];
1463 scopes->vecscope_rgb[color_idx + 1] = rgb[1];
1464 scopes->vecscope_rgb[color_idx + 2] = rgb[2];
1465
1466 /* Waveform. */
1467 switch (scopes->wavefrm_mode) {
1468 case SCOPES_WAVEFRM_RGB:
1470 scopes->waveform_1[idx + 0] = fx;
1471 scopes->waveform_1[idx + 1] = rgb[0];
1472 scopes->waveform_2[idx + 0] = fx;
1473 scopes->waveform_2[idx + 1] = rgb[1];
1474 scopes->waveform_3[idx + 0] = fx;
1475 scopes->waveform_3[idx + 1] = rgb[2];
1476 break;
1478 scopes->waveform_1[idx + 0] = fx;
1479 scopes->waveform_1[idx + 1] = ycc[0];
1480 break;
1484 scopes->waveform_1[idx + 0] = fx;
1485 scopes->waveform_1[idx + 1] = ycc[0];
1486 scopes->waveform_2[idx + 0] = fx;
1487 scopes->waveform_2[idx + 1] = ycc[1];
1488 scopes->waveform_3[idx + 0] = fx;
1489 scopes->waveform_3[idx + 1] = ycc[2];
1490 break;
1491 }
1492}
1493
1495 ImBuf *ibuf,
1496 const ColorManagedViewSettings *view_settings,
1497 const ColorManagedDisplaySettings *display_settings)
1498{
1499 int i, x, y;
1500 const float *fp;
1501 uchar *cp;
1502
1503 int x1 = roundf(hist->co[0][0] * ibuf->x);
1504 int x2 = roundf(hist->co[1][0] * ibuf->x);
1505 int y1 = roundf(hist->co[0][1] * ibuf->y);
1506 int y2 = roundf(hist->co[1][1] * ibuf->y);
1507
1508 ColormanageProcessor *cm_processor = nullptr;
1509
1510 hist->channels = 3;
1511 hist->x_resolution = 256;
1512 hist->xmax = 1.0f;
1513 // hist->ymax = 1.0f; /* now do this on the operator _only_ */
1514
1515 if (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.data == nullptr) {
1516 return;
1517 }
1518
1519 if (ibuf->float_buffer.data) {
1520 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1521 }
1522
1523 for (i = 0; i < 256; i++) {
1524 x = int(0.5f + x1 + float(i) * (x2 - x1) / 255.0f);
1525 y = int(0.5f + y1 + float(i) * (y2 - y1) / 255.0f);
1526
1527 if (x < 0 || y < 0 || x >= ibuf->x || y >= ibuf->y) {
1528 hist->data_luma[i] = hist->data_r[i] = hist->data_g[i] = hist->data_b[i] = hist->data_a[i] =
1529 0.0f;
1530 }
1531 else {
1532 if (ibuf->float_buffer.data) {
1533 float rgba[4];
1534 fp = (ibuf->float_buffer.data + (ibuf->channels) * (y * ibuf->x + x));
1535
1536 switch (ibuf->channels) {
1537 case 4:
1538 copy_v4_v4(rgba, fp);
1539 IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
1540 break;
1541 case 3:
1542 copy_v3_v3(rgba, fp);
1543 IMB_colormanagement_processor_apply_v3(cm_processor, rgba);
1544 rgba[3] = 1.0f;
1545 break;
1546 case 2:
1547 copy_v3_fl(rgba, fp[0]);
1548 rgba[3] = fp[1];
1549 break;
1550 case 1:
1551 copy_v3_fl(rgba, fp[0]);
1552 rgba[3] = 1.0f;
1553 break;
1554 default:
1556 }
1557
1559 hist->data_r[i] = rgba[0];
1560 hist->data_g[i] = rgba[1];
1561 hist->data_b[i] = rgba[2];
1562 hist->data_a[i] = rgba[3];
1563 }
1564 else if (ibuf->byte_buffer.data) {
1565 cp = ibuf->byte_buffer.data + 4 * (y * ibuf->x + x);
1567 hist->data_r[i] = float(cp[0]) / 255.0f;
1568 hist->data_g[i] = float(cp[1]) / 255.0f;
1569 hist->data_b[i] = float(cp[2]) / 255.0f;
1570 hist->data_a[i] = float(cp[3]) / 255.0f;
1571 }
1572 }
1573 }
1574
1575 if (cm_processor) {
1577 }
1578}
1579
1580/* if view_settings, it also applies this to byte buffers */
1588
1595 float min[3], max[3];
1596};
1597
1598static void scopes_update_cb(void *__restrict userdata,
1599 const int y,
1600 const TaskParallelTLS *__restrict tls)
1601{
1602 const ScopesUpdateData *data = static_cast<const ScopesUpdateData *>(userdata);
1603
1604 Scopes *scopes = data->scopes;
1605 const ImBuf *ibuf = data->ibuf;
1606 ColormanageProcessor *cm_processor = data->cm_processor;
1607 const uchar *display_buffer = data->display_buffer;
1608 const int ycc_mode = data->ycc_mode;
1609
1610 ScopesUpdateDataChunk *data_chunk = static_cast<ScopesUpdateDataChunk *>(tls->userdata_chunk);
1611 uint *bin_lum = data_chunk->bin_lum;
1612 uint *bin_r = data_chunk->bin_r;
1613 uint *bin_g = data_chunk->bin_g;
1614 uint *bin_b = data_chunk->bin_b;
1615 uint *bin_a = data_chunk->bin_a;
1616 float *min = data_chunk->min;
1617 float *max = data_chunk->max;
1618
1619 const float *rf = nullptr;
1620 const uchar *rc = nullptr;
1621 const int rows_per_sample_line = ibuf->y / scopes->sample_lines;
1622 const int savedlines = y / rows_per_sample_line;
1623 const bool do_sample_line = (savedlines < scopes->sample_lines) &&
1624 (y % rows_per_sample_line) == 0;
1625 const bool is_float = (ibuf->float_buffer.data != nullptr);
1626
1627 if (is_float) {
1628 rf = ibuf->float_buffer.data + size_t(y) * ibuf->x * ibuf->channels;
1629 }
1630 else {
1631 rc = display_buffer + size_t(y) * ibuf->x * ibuf->channels;
1632 }
1633
1634 for (int x = 0; x < ibuf->x; x++) {
1635 float rgba[4], ycc[3], luma;
1636
1637 if (is_float) {
1638 switch (ibuf->channels) {
1639 case 4:
1640 copy_v4_v4(rgba, rf);
1641 IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
1642 break;
1643 case 3:
1644 copy_v3_v3(rgba, rf);
1645 IMB_colormanagement_processor_apply_v3(cm_processor, rgba);
1646 rgba[3] = 1.0f;
1647 break;
1648 case 2:
1649 copy_v3_fl(rgba, rf[0]);
1650 rgba[3] = rf[1];
1651 break;
1652 case 1:
1653 copy_v3_fl(rgba, rf[0]);
1654 rgba[3] = 1.0f;
1655 break;
1656 default:
1658 }
1659 }
1660 else {
1661 for (int c = 4; c--;) {
1662 rgba[c] = rc[c] * INV_255;
1663 }
1664 }
1665
1666 /* we still need luma for histogram */
1668
1669 /* check for min max */
1670 if (ycc_mode == -1) {
1671 minmax_v3v3_v3(min, max, rgba);
1672 }
1673 else {
1674 rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode);
1675 mul_v3_fl(ycc, INV_255);
1676 minmax_v3v3_v3(min, max, ycc);
1677 }
1678 /* Increment count for histogram. */
1679 bin_lum[get_bin_float(luma)]++;
1680 bin_r[get_bin_float(rgba[0])]++;
1681 bin_g[get_bin_float(rgba[1])]++;
1682 bin_b[get_bin_float(rgba[2])]++;
1683 bin_a[get_bin_float(rgba[3])]++;
1684
1685 /* save sample if needed */
1686 if (do_sample_line) {
1687 const float fx = float(x) / float(ibuf->x);
1688 const int idx = 2 * (ibuf->x * savedlines + x);
1689 save_sample_line(scopes, idx, fx, rgba, ycc);
1690 }
1691
1692 rf += ibuf->channels;
1693 rc += ibuf->channels;
1694 }
1695}
1696
1697static void scopes_update_reduce(const void *__restrict /*userdata*/,
1698 void *__restrict chunk_join,
1699 void *__restrict chunk)
1700{
1701 ScopesUpdateDataChunk *join_chunk = static_cast<ScopesUpdateDataChunk *>(chunk_join);
1702 const ScopesUpdateDataChunk *data_chunk = static_cast<const ScopesUpdateDataChunk *>(chunk);
1703
1704 uint *bin_lum = join_chunk->bin_lum;
1705 uint *bin_r = join_chunk->bin_r;
1706 uint *bin_g = join_chunk->bin_g;
1707 uint *bin_b = join_chunk->bin_b;
1708 uint *bin_a = join_chunk->bin_a;
1709 const uint *bin_lum_c = data_chunk->bin_lum;
1710 const uint *bin_r_c = data_chunk->bin_r;
1711 const uint *bin_g_c = data_chunk->bin_g;
1712 const uint *bin_b_c = data_chunk->bin_b;
1713 const uint *bin_a_c = data_chunk->bin_a;
1714
1715 const float *min = data_chunk->min;
1716 const float *max = data_chunk->max;
1717
1718 for (int b = 256; b--;) {
1719 bin_lum[b] += bin_lum_c[b];
1720 bin_r[b] += bin_r_c[b];
1721 bin_g[b] += bin_g_c[b];
1722 bin_b[b] += bin_b_c[b];
1723 bin_a[b] += bin_a_c[b];
1724 }
1725
1726 for (int c = 3; c--;) {
1727 join_chunk->min[c] = std::min(min[c], join_chunk->min[c]);
1728 join_chunk->max[c] = std::max(max[c], join_chunk->max[c]);
1729 }
1730}
1731
1733 ImBuf *ibuf,
1734 const ColorManagedViewSettings *view_settings,
1735 const ColorManagedDisplaySettings *display_settings)
1736{
1737 int a;
1738 uint nl, na, nr, ng, nb;
1739 double divl, diva, divr, divg, divb;
1740 const uchar *display_buffer = nullptr;
1741 int ycc_mode = -1;
1742 void *cache_handle = nullptr;
1743 ColormanageProcessor *cm_processor = nullptr;
1744
1745 if (ibuf->byte_buffer.data == nullptr && ibuf->float_buffer.data == nullptr) {
1746 return;
1747 }
1748
1749 if (scopes->ok == 1) {
1750 return;
1751 }
1752
1753 if (scopes->hist.ymax == 0.0f) {
1754 scopes->hist.ymax = 1.0f;
1755 }
1756
1757 /* hmmmm */
1758 if (!ELEM(ibuf->channels, 3, 4)) {
1759 return;
1760 }
1761
1762 scopes->hist.channels = 3;
1763 scopes->hist.x_resolution = 256;
1764
1765 switch (scopes->wavefrm_mode) {
1766 case SCOPES_WAVEFRM_RGB:
1767 /* fall-through */
1769 ycc_mode = -1;
1770 break;
1773 ycc_mode = BLI_YCC_JFIF_0_255;
1774 break;
1776 ycc_mode = BLI_YCC_ITU_BT601;
1777 break;
1779 ycc_mode = BLI_YCC_ITU_BT709;
1780 break;
1781 }
1782
1783 /* convert to number of lines with logarithmic scale */
1784 scopes->sample_lines = (scopes->accuracy * 0.01f) * (scopes->accuracy * 0.01f) * ibuf->y;
1785 CLAMP_MIN(scopes->sample_lines, 1);
1786
1787 if (scopes->sample_full) {
1788 scopes->sample_lines = ibuf->y;
1789 }
1790
1791 /* scan the image */
1792 for (a = 0; a < 3; a++) {
1793 scopes->minmax[a][0] = 25500.0f;
1794 scopes->minmax[a][1] = -25500.0f;
1795 }
1796
1797 scopes->waveform_tot = ibuf->x * scopes->sample_lines;
1798
1799 if (scopes->waveform_1) {
1800 MEM_freeN(scopes->waveform_1);
1801 }
1802 if (scopes->waveform_2) {
1803 MEM_freeN(scopes->waveform_2);
1804 }
1805 if (scopes->waveform_3) {
1806 MEM_freeN(scopes->waveform_3);
1807 }
1808 if (scopes->vecscope) {
1809 MEM_freeN(scopes->vecscope);
1810 }
1811 if (scopes->vecscope_rgb) {
1812 MEM_freeN(scopes->vecscope_rgb);
1813 }
1814
1815 scopes->waveform_1 = MEM_calloc_arrayN<float>(2 * size_t(scopes->waveform_tot),
1816 "waveform point channel 1");
1817 scopes->waveform_2 = MEM_calloc_arrayN<float>(2 * size_t(scopes->waveform_tot),
1818 "waveform point channel 2");
1819 scopes->waveform_3 = MEM_calloc_arrayN<float>(2 * size_t(scopes->waveform_tot),
1820 "waveform point channel 3");
1821 scopes->vecscope = MEM_calloc_arrayN<float>(2 * size_t(scopes->waveform_tot),
1822 "vectorscope point channel");
1823 scopes->vecscope_rgb = MEM_calloc_arrayN<float>(3 * size_t(scopes->waveform_tot),
1824 "vectorscope color channel");
1825
1826 if (ibuf->float_buffer.data) {
1827 cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
1828 }
1829 else {
1830 display_buffer = (const uchar *)IMB_display_buffer_acquire(
1831 ibuf, view_settings, display_settings, &cache_handle);
1832 }
1833
1834 /* Keep number of threads in sync with the merge parts below. */
1836 data.scopes = scopes;
1837 data.ibuf = ibuf;
1838 data.cm_processor = cm_processor;
1839 data.display_buffer = display_buffer;
1840 data.ycc_mode = ycc_mode;
1841
1842 ScopesUpdateDataChunk data_chunk = {{0}};
1843 INIT_MINMAX(data_chunk.min, data_chunk.max);
1844
1845 TaskParallelSettings settings;
1847 settings.use_threading = (ibuf->y > 256);
1848 settings.userdata_chunk = &data_chunk;
1849 settings.userdata_chunk_size = sizeof(data_chunk);
1851 BLI_task_parallel_range(0, ibuf->y, &data, scopes_update_cb, &settings);
1852
1853 /* convert hist data to float (proportional to max count) */
1854 nl = na = nr = nb = ng = 0;
1855 for (a = 0; a < 256; a++) {
1856 nl = std::max(data_chunk.bin_lum[a], nl);
1857 nr = std::max(data_chunk.bin_r[a], nr);
1858 ng = std::max(data_chunk.bin_g[a], ng);
1859 nb = std::max(data_chunk.bin_b[a], nb);
1860 na = std::max(data_chunk.bin_a[a], na);
1861 }
1862 divl = nl ? 1.0 / double(nl) : 1.0;
1863 diva = na ? 1.0 / double(na) : 1.0;
1864 divr = nr ? 1.0 / double(nr) : 1.0;
1865 divg = ng ? 1.0 / double(ng) : 1.0;
1866 divb = nb ? 1.0 / double(nb) : 1.0;
1867
1868 for (a = 0; a < 256; a++) {
1869 scopes->hist.data_luma[a] = data_chunk.bin_lum[a] * divl;
1870 scopes->hist.data_r[a] = data_chunk.bin_r[a] * divr;
1871 scopes->hist.data_g[a] = data_chunk.bin_g[a] * divg;
1872 scopes->hist.data_b[a] = data_chunk.bin_b[a] * divb;
1873 scopes->hist.data_a[a] = data_chunk.bin_a[a] * diva;
1874 }
1875
1876 if (cm_processor) {
1878 }
1879 if (cache_handle) {
1880 IMB_display_buffer_release(cache_handle);
1881 }
1882
1883 scopes->ok = 1;
1884}
1885
1887{
1888 MEM_SAFE_FREE(scopes->waveform_1);
1889 MEM_SAFE_FREE(scopes->waveform_2);
1890 MEM_SAFE_FREE(scopes->waveform_3);
1891 MEM_SAFE_FREE(scopes->vecscope);
1892 MEM_SAFE_FREE(scopes->vecscope_rgb);
1893}
1894
1896{
1897 scopes->accuracy = 30.0;
1898 scopes->hist.mode = HISTO_MODE_RGB;
1899 scopes->wavefrm_alpha = 0.3;
1900 scopes->vecscope_alpha = 0.3;
1901 scopes->wavefrm_height = 100;
1902 scopes->vecscope_height = 100;
1903 scopes->hist.height = 100;
1904 scopes->ok = 0;
1905 scopes->waveform_1 = nullptr;
1906 scopes->waveform_2 = nullptr;
1907 scopes->waveform_3 = nullptr;
1908 scopes->vecscope = nullptr;
1909 scopes->vecscope_rgb = nullptr;
1910}
1911
1913{
1914 const char *display_name = IMB_colormanagement_display_get_default_name();
1915
1916 STRNCPY_UTF8(settings->display_device, display_name);
1918}
1919
1921 const ColorManagedDisplaySettings *settings)
1922{
1923 STRNCPY_UTF8(new_settings->display_device, settings->display_device);
1924 new_settings->emulation = settings->emulation;
1925}
1926
1928 const ColorManagedDisplaySettings *display_settings,
1929 const char *view_transform)
1930{
1932 display_settings->display_device);
1933 BLI_assert(display);
1934
1935 if (!view_transform) {
1937 }
1938
1939 /* TODO(sergey): Find a way to make look query more reliable with non
1940 * default configuration. */
1941 STRNCPY_UTF8(view_settings->view_transform, view_transform);
1942 STRNCPY_UTF8(view_settings->look, "None");
1943
1944 view_settings->flag = 0;
1945 view_settings->gamma = 1.0f;
1946 view_settings->exposure = 0.0f;
1947 view_settings->curve_mapping = nullptr;
1948
1949 IMB_colormanagement_validate_settings(display_settings, view_settings);
1950}
1951
1953 const ColorManagedViewSettings *settings)
1954{
1956
1957 if (settings->curve_mapping) {
1958 new_settings->curve_mapping = BKE_curvemapping_copy(settings->curve_mapping);
1959 }
1960 else {
1961 new_settings->curve_mapping = nullptr;
1962 }
1963}
1964
1966 ColorManagedViewSettings *new_settings, const ColorManagedViewSettings *settings)
1967{
1968 STRNCPY_UTF8(new_settings->look, settings->look);
1969 STRNCPY_UTF8(new_settings->view_transform, settings->view_transform);
1970
1971 new_settings->flag = settings->flag;
1972 new_settings->exposure = settings->exposure;
1973 new_settings->gamma = settings->gamma;
1974 new_settings->temperature = settings->temperature;
1975 new_settings->tint = settings->tint;
1976}
1977
1979{
1980 if (settings->curve_mapping) {
1982 settings->curve_mapping = nullptr;
1983 }
1984}
1985
1987 const ColorManagedViewSettings *settings)
1988{
1989 if (settings->curve_mapping) {
1991 }
1992}
1993
1995 ColorManagedViewSettings *settings)
1996{
1997 BLO_read_struct(reader, CurveMapping, &settings->curve_mapping);
1998
1999 if (settings->curve_mapping) {
2000 BKE_curvemapping_blend_read(reader, settings->curve_mapping);
2001 }
2002}
2003
2005 ColorManagedColorspaceSettings *colorspace_settings)
2006{
2007 STRNCPY_UTF8(colorspace_settings->name, "");
2008}
2009
2011 ColorManagedColorspaceSettings *colorspace_settings,
2012 const ColorManagedColorspaceSettings *settings)
2013{
2014 STRNCPY_UTF8(colorspace_settings->name, settings->name);
2015}
2016
2018 const ColorManagedColorspaceSettings *settings2)
2019{
2020 return STREQ(settings1->name, settings2->name);
2021}
CurveMapSlopeType
float BKE_curvemap_evaluateF(const CurveMapping *cumap, const CurveMap *cuma, float value)
void BKE_curve_correct_bezpart(const float v1[2], float v2[2], float v3[2], const float v4[2])
Definition curve.cc:5430
void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride)
Definition curve.cc:1669
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE int compare_ff(float a, float b, float max_diff)
#define BLI_YUV_ITU_BT709
#define BLI_YCC_JFIF_0_255
#define BLI_YCC_ITU_BT601
void rgb_to_ycc(float r, float g, float b, float *r_y, float *r_cb, float *r_cr, int colorspace)
void rgb_to_yuv(float r, float g, float b, float *r_y, float *r_u, float *r_v, int colorspace)
Definition math_color.cc:67
#define BLI_YCC_ITU_BT709
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
MINLINE void madd_v2_v2v2fl(float r[2], const float a[2], const float b[2], float f)
MINLINE void sub_v3_v3(float r[3], const float a[3])
MINLINE void mul_v2_v2(float r[2], const float a[2])
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void zero_v2(float r[2])
MINLINE void copy_v3_fl(float r[3], float f)
MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT
void BLI_rctf_init(struct rctf *rect, float xmin, float xmax, float ymin, float ymax)
Definition rct.cc:404
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
BLI_INLINE float BLI_rctf_size_y(const struct rctf *rct)
Definition BLI_rect.h:206
#define STRNCPY_UTF8(dst, src)
unsigned char uchar
unsigned int uint
void BLI_task_parallel_range(int start, int stop, void *userdata, TaskParallelRangeFunc func, const TaskParallelSettings *settings)
Definition task_range.cc:99
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:221
#define CLAMP(a, b, c)
#define INIT_MINMAX(min, max)
#define ELEM(...)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
#define BLO_write_struct(writer, struct_name, data_ptr)
#define BLO_write_struct_array(writer, struct_name, array_size, data_ptr)
#define BLO_read_struct_array(reader, struct_name, array_size, ptr_p)
#define BLO_read_struct(reader, struct_name, ptr_p)
#define CM_TOT
@ CUMA_PREMULLED
@ CUMA_EXTEND_EXTRAPOLATE
@ CUMA_DO_CLIP
@ CUMA_USE_WRAPPING
@ HISTO_MODE_RGB
@ CUMA_HANDLE_AUTO_ANIM
@ CUMA_SELECT
@ CUMA_REMOVE
@ CUMA_HANDLE_VECTOR
#define CM_TABLEDIV
@ SCOPES_WAVEFRM_YCC_JPEG
@ SCOPES_WAVEFRM_RGB
@ SCOPES_WAVEFRM_YCC_601
@ SCOPES_WAVEFRM_YCC_709
@ SCOPES_WAVEFRM_LUMA
@ SCOPES_WAVEFRM_RGB_PARADE
#define SCOPES_VEC_U_SCALE
#define SCOPES_VEC_V_SCALE
@ CURVE_TONE_STANDARD
@ CURVE_TONE_FILMLIKE
#define CM_RESOL
#define CM_TABLE
@ COLORMANAGE_DISPLAY_EMULATION_AUTO
@ CURVE_PRESET_ROOT
@ CURVE_PRESET_GAUSS
@ CURVE_PRESET_SMOOTH
@ CURVE_PRESET_CONSTANT_MEDIAN
@ CURVE_PRESET_ROUND
@ CURVE_PRESET_MID8
@ CURVE_PRESET_LINE
@ CURVE_PRESET_SHARP
@ CURVE_PRESET_BELL
@ CURVE_PRESET_MAX
@ HD_AUTO_ANIM
@ HD_VECT
@ HD_AUTO
BLI_INLINE unsigned char IMB_colormanagement_get_luminance_byte(const unsigned char rgb[3])
unsigned char * IMB_display_buffer_acquire(ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, void **cache_handle)
void IMB_colormanagement_validate_settings(const ColorManagedDisplaySettings *display_settings, ColorManagedViewSettings *view_settings)
void IMB_colormanagement_processor_apply_v3(ColormanageProcessor *cm_processor, float pixel[3])
void IMB_display_buffer_release(void *cache_handle)
BLI_INLINE float IMB_colormanagement_get_luminance(const float rgb[3])
ColormanageProcessor * IMB_colormanagement_display_processor_new(const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const ColorManagedDisplaySpace display_space=DISPLAY_SPACE_DRAW, const bool inverse=false)
const char * IMB_colormanagement_display_get_default_view_transform_name(const ColorManagedDisplay *display)
const ColorManagedDisplay * IMB_colormanagement_display_get_named(const char *name)
void IMB_colormanagement_processor_free(ColormanageProcessor *cm_processor)
void IMB_colormanagement_processor_apply_v4(ColormanageProcessor *cm_processor, float pixel[4])
const char * IMB_colormanagement_display_get_default_name()
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
blender::ocio::Display ColorManagedDisplay
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
nullptr float
void BKE_curvemapping_evaluate_premulRGBF(const CurveMapping *cumap, float vecout[3], const float vecin[3])
void BKE_color_managed_colorspace_settings_init(ColorManagedColorspaceSettings *colorspace_settings)
void BKE_curvemapping_table_F(const CurveMapping *cumap, float **array, int *size)
void BKE_curvemapping_premultiply(CurveMapping *cumap, bool restore)
void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, CurveMapSlopeType slope)
static void curve_eval_bezier_point(float start[3][3], float end[3][3], float *point)
static void calchandle_curvemap(BezTriple *bezt, const BezTriple *prev, const BezTriple *next)
static void scopes_update_cb(void *__restrict userdata, const int y, const TaskParallelTLS *__restrict tls)
static blender::float3 evaluate_film_like(const CurveMapping *curve_mapping, blender::float3 input)
static void curvemap_make_table(const CurveMapping *cumap, CurveMap *cuma)
void BKE_curvemapping_free_data(CurveMapping *cumap)
void BKE_curvemapping_evaluate_premulRGBF_ex(const CurveMapping *cumap, float vecout[3], const float vecin[3], const float black[3], const float bwmul[3])
void BKE_curvemapping_set_black_white(CurveMapping *cumap, const float black[3], const float white[3])
#define INV_255
bool BKE_curvemapping_is_map_identity(const CurveMapping *curve_mapping, int index)
CurveMapPoint * BKE_curvemap_insert(CurveMap *cuma, float x, float y)
void BKE_color_managed_view_settings_blend_read_data(BlendDataReader *reader, ColorManagedViewSettings *settings)
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_changed_all(CurveMapping *cumap)
void BKE_curvemapping_curves_blend_write(BlendWriter *writer, const CurveMapping *cumap)
bool BKE_curvemap_remove_point(CurveMap *cuma, CurveMapPoint *point)
void BKE_curvemapping_blend_read(BlendDataReader *reader, CurveMapping *cumap)
void BKE_curvemapping_evaluate_premulRGB(const CurveMapping *cumap, uchar vecout_byte[3], const uchar vecin_byte[3])
static float curvemap_calc_extend(const CurveMapping *cumap, const CurveMap *cuma, float x, const float first[2], const float last[2])
void BKE_curvemapping_evaluateRGBF(const CurveMapping *cumap, float vecout[3], const float vecin[3])
void BKE_color_managed_display_settings_copy(ColorManagedDisplaySettings *new_settings, const ColorManagedDisplaySettings *settings)
void BKE_color_managed_display_settings_init(ColorManagedDisplaySettings *settings)
void BKE_curvemapping_set_defaults(CurveMapping *cumap, int tot, float minx, float miny, float maxx, float maxy, short default_handle_type)
Definition colortools.cc:40
void BKE_curvemapping_set_black_white_ex(const float black[3], const float white[3], float r_bwmul[3])
BLI_INLINE int get_bin_float(float f)
void BKE_color_managed_view_settings_free(ColorManagedViewSettings *settings)
void BKE_curvemapping_reset_view(CurveMapping *cumap)
CurveMapping * BKE_curvemapping_copy(const CurveMapping *cumap)
void BKE_curvemap_remove(CurveMap *cuma, const short flag)
void BKE_curvemapping_compute_slopes(const CurveMapping *curve_mapping, float start_slopes[CM_TOT], float end_slopes[CM_TOT])
void BKE_scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
void BKE_curvemapping_init(CurveMapping *cumap)
void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings, const ColorManagedViewSettings *settings)
#define p2_h1
CurveMapping * BKE_curvemapping_add(int tot, float minx, float miny, float maxx, float maxy)
Definition colortools.cc:89
static void scopes_update_reduce(const void *__restrict, void *__restrict chunk_join, void *__restrict chunk)
void BKE_curvemapping_compute_range_dividers(const CurveMapping *curve_mapping, float dividers[CM_TOT])
static void save_sample_line(Scopes *scopes, const int idx, const float fx, const float rgb[3], const float ycc[3])
void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size)
void BKE_curvemapping_free(CurveMapping *cumap)
void BKE_curvemapping_blend_write(BlendWriter *writer, const CurveMapping *cumap)
void BKE_curvemapping_get_range_minimums(const CurveMapping *curve_mapping, float minimums[CM_TOT])
void BKE_curvemapping_evaluate3F(const CurveMapping *cumap, float vecout[3], const float vecin[3])
#define p2_h2
void BKE_curvemapping_changed(CurveMapping *cumap, const bool rem_doubles)
void BKE_curvemap_handle_set(CurveMap *cuma, int type)
void BKE_color_managed_view_settings_init(ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, const char *view_transform)
void BKE_color_managed_view_settings_blend_write(BlendWriter *writer, const ColorManagedViewSettings *settings)
float BKE_curvemap_evaluateF(const CurveMapping *cumap, const CurveMap *cuma, float value)
void BKE_scopes_new(Scopes *scopes)
void BKE_curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap)
void BKE_color_managed_view_settings_copy_keep_curve_mapping(ColorManagedViewSettings *new_settings, const ColorManagedViewSettings *settings)
void BKE_scopes_free(Scopes *scopes)
void BKE_color_managed_colorspace_settings_copy(ColorManagedColorspaceSettings *colorspace_settings, const ColorManagedColorspaceSettings *settings)
bool BKE_color_managed_colorspace_settings_equals(const ColorManagedColorspaceSettings *settings1, const ColorManagedColorspaceSettings *settings2)
bool BKE_curvemapping_RGBA_does_something(const CurveMapping *cumap)
#define roundf(x)
#define input
#define output
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void * MEM_dupallocN(const void *vmemh)
Definition mallocn.cc:143
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
MINLINE unsigned char unit_float_to_uchar_clamp(float val)
static ulong * next
T reduce_max(const VecBase< T, Size > &a)
T reduce_min(const VecBase< T, Size > &a)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
VecBase< float, 3 > float3
#define sqrtf
#define min(a, b)
Definition sort.cc:36
float vec[3][3]
struct CurveMapping * curve_mapping
CurveMapPoint * table
CurveMapPoint * curve
CurveMapPoint * premultable
float ext_out[2]
float ext_in[2]
short default_handle_type
float premul_ext_out[2]
float premul_ext_in[2]
CurveMap cm[4]
float data_a[256]
float data_luma[256]
float data_r[256]
float co[2][2]
float data_b[256]
float data_g[256]
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
const ImBuf * ibuf
ColormanageProcessor * cm_processor
const uchar * display_buffer
int wavefrm_height
float * waveform_3
float * waveform_2
int vecscope_height
float wavefrm_alpha
float * vecscope_rgb
float minmax[3][2]
float vecscope_alpha
float * waveform_1
float * vecscope
float accuracy
struct Histogram hist
TaskParallelReduceFunc func_reduce
Definition BLI_task.h:176
size_t userdata_chunk_size
Definition BLI_task.h:164
float xmax
float xmin
float ymax
float ymin
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
uint len
uint8_t flag
Definition wm_window.cc:145