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