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
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) {
330 cuma->curve[0].flag &= ~CUMA_HANDLE_AUTO_ANIM;
331 cuma->curve[1].flag &= ~CUMA_HANDLE_AUTO_ANIM;
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. */
1817 ScopesUpdateData data{};
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);
1832 settings.func_reduce = scopes_update_reduce;
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) {
1971 BKE_curvemapping_free(settings->curve_mapping);
1972 settings->curve_mapping = nullptr;
1973 }
1974}
1975
1977 ColorManagedViewSettings *settings)
1978{
1979 if (settings->curve_mapping) {
1980 BKE_curvemapping_blend_write(writer, 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
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
@ 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
@ 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
@ 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)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
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
IndexRange range
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
float xmax
float xmin
float ymax
float ymin
uint8_t flag
Definition wm_window.cc:138