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