Blender V4.3
COM_MixOperation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "COM_MixOperation.h"
6
7#include "BLI_math_color.h"
8
9namespace blender::compositor {
10
11/* ******** Mix Base Operation ******** */
12
23
24void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area)
25{
26 NodeOperationInput *socket;
27 rcti temp_area = COM_AREA_NONE;
28
29 socket = this->get_input_socket(1);
30 bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area);
31 if (determined) {
33 }
34 else {
35 socket = this->get_input_socket(2);
36 determined = socket->determine_canvas(COM_AREA_NONE, temp_area);
37 if (determined) {
39 }
40 else {
42 }
43 }
44 NodeOperation::determine_canvas(preferred_area, r_area);
45}
46
48 const rcti &area,
50{
51 const MemoryBuffer *input_value = inputs[0];
52 const MemoryBuffer *input_color1 = inputs[1];
53 const MemoryBuffer *input_color2 = inputs[2];
54 const int width = BLI_rcti_size_x(&area);
56 p.out_stride = output->elem_stride;
57 p.value_stride = input_value->elem_stride;
58 p.color1_stride = input_color1->elem_stride;
59 p.color2_stride = input_color2->elem_stride;
60 for (int y = area.ymin; y < area.ymax; y++) {
61 p.out = output->get_elem(area.xmin, y);
62 p.row_end = p.out + width * output->elem_stride;
63 p.value = input_value->get_elem(area.xmin, y);
64 p.color1 = input_color1->get_elem(area.xmin, y);
65 p.color2 = input_color2->get_elem(area.xmin, y);
67 }
68}
69
71{
72 while (p.out < p.row_end) {
73 float value = p.value[0];
74 if (this->use_value_alpha_multiply()) {
75 value *= p.color2[3];
76 }
77 const float value_m = 1.0f - value;
78 p.out[0] = value_m * p.color1[0] + value * p.color2[0];
79 p.out[1] = value_m * p.color1[1] + value * p.color2[1];
80 p.out[2] = value_m * p.color1[2] + value * p.color2[2];
81 p.out[3] = p.color1[3];
82 p.next();
83 }
84}
85
86/* ******** Mix Add Operation ******** */
87
89{
90 while (p.out < p.row_end) {
91 float value = p.value[0];
92 if (this->use_value_alpha_multiply()) {
93 value *= p.color2[3];
94 }
95 p.out[0] = p.color1[0] + value * p.color2[0];
96 p.out[1] = p.color1[1] + value * p.color2[1];
97 p.out[2] = p.color1[2] + value * p.color2[2];
98 p.out[3] = p.color1[3];
99
101 p.next();
102 }
103}
104
105/* ******** Mix Blend Operation ******** */
106
108{
109 while (p.out < p.row_end) {
110 float value = p.value[0];
111 if (this->use_value_alpha_multiply()) {
112 value *= p.color2[3];
113 }
114 float value_m = 1.0f - value;
115 p.out[0] = value_m * p.color1[0] + value * p.color2[0];
116 p.out[1] = value_m * p.color1[1] + value * p.color2[1];
117 p.out[2] = value_m * p.color1[2] + value * p.color2[2];
118 p.out[3] = p.color1[3];
119
121 p.next();
122 }
123}
124
125/* ******** Mix Burn Operation ******** */
126
128{
129 while (p.out < p.row_end) {
130 float value = p.value[0];
131 if (this->use_value_alpha_multiply()) {
132 value *= p.color2[3];
133 }
134 const float value_m = 1.0f - value;
135
136 float tmp = value_m + value * p.color2[0];
137 if (tmp <= 0.0f) {
138 p.out[0] = 0.0f;
139 }
140 else {
141 tmp = 1.0f - (1.0f - p.color1[0]) / tmp;
142 p.out[0] = std::clamp(tmp, 0.0f, 1.0f);
143 }
144
145 tmp = value_m + value * p.color2[1];
146 if (tmp <= 0.0f) {
147 p.out[1] = 0.0f;
148 }
149 else {
150 tmp = 1.0f - (1.0f - p.color1[1]) / tmp;
151 p.out[1] = std::clamp(tmp, 0.0f, 1.0f);
152 }
153
154 tmp = value_m + value * p.color2[2];
155 if (tmp <= 0.0f) {
156 p.out[2] = 0.0f;
157 }
158 else {
159 tmp = 1.0f - (1.0f - p.color1[2]) / tmp;
160 p.out[2] = std::clamp(tmp, 0.0f, 1.0f);
161 }
162 p.out[3] = p.color1[3];
163
165 p.next();
166 }
167}
168
169/* ******** Mix Color Operation ******** */
170
172{
173 while (p.out < p.row_end) {
174 float value = p.value[0];
175 if (this->use_value_alpha_multiply()) {
176 value *= p.color2[3];
177 }
178 const float value_m = 1.0f - value;
179
180 float colH, colS, colV;
181 rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
182 if (colS != 0.0f) {
183 float rH, rS, rV;
184 float tmpr, tmpg, tmpb;
185 rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
186 hsv_to_rgb(colH, colS, rV, &tmpr, &tmpg, &tmpb);
187 p.out[0] = (value_m * p.color1[0]) + (value * tmpr);
188 p.out[1] = (value_m * p.color1[1]) + (value * tmpg);
189 p.out[2] = (value_m * p.color1[2]) + (value * tmpb);
190 }
191 else {
192 copy_v3_v3(p.out, p.color1);
193 }
194 p.out[3] = p.color1[3];
195
197 p.next();
198 }
199}
200
201/* ******** Mix Darken Operation ******** */
202
204{
205 while (p.out < p.row_end) {
206 float value = p.value[0];
207 if (this->use_value_alpha_multiply()) {
208 value *= p.color2[3];
209 }
210 float value_m = 1.0f - value;
211 p.out[0] = min_ff(p.color1[0], p.color2[0]) * value + p.color1[0] * value_m;
212 p.out[1] = min_ff(p.color1[1], p.color2[1]) * value + p.color1[1] * value_m;
213 p.out[2] = min_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m;
214 p.out[3] = p.color1[3];
215
217 p.next();
218 }
219}
220
221/* ******** Mix Difference Operation ******** */
222
224{
225 while (p.out < p.row_end) {
226 float value = p.value[0];
227 if (this->use_value_alpha_multiply()) {
228 value *= p.color2[3];
229 }
230 const float value_m = 1.0f - value;
231 p.out[0] = value_m * p.color1[0] + value * fabsf(p.color1[0] - p.color2[0]);
232 p.out[1] = value_m * p.color1[1] + value * fabsf(p.color1[1] - p.color2[1]);
233 p.out[2] = value_m * p.color1[2] + value * fabsf(p.color1[2] - p.color2[2]);
234 p.out[3] = p.color1[3];
235
237 p.next();
238 }
239}
240
241/* ******** Mix Exclusion Operation ******** */
242
244{
245 while (p.out < p.row_end) {
246 float value = p.value[0];
247 if (this->use_value_alpha_multiply()) {
248 value *= p.color2[3];
249 }
250 const float value_m = 1.0f - value;
251 p.out[0] = max_ff(value_m * p.color1[0] +
252 value * (p.color1[0] + p.color2[0] - 2.0f * p.color1[0] * p.color2[0]),
253 0.0f);
254 p.out[1] = max_ff(value_m * p.color1[1] +
255 value * (p.color1[1] + p.color2[1] - 2.0f * p.color1[1] * p.color2[1]),
256 0.0f);
257 p.out[2] = max_ff(value_m * p.color1[2] +
258 value * (p.color1[2] + p.color2[2] - 2.0f * p.color1[2] * p.color2[2]),
259 0.0f);
260 p.out[3] = p.color1[3];
261
263 p.next();
264 }
265}
266
267/* ******** Mix Divide Operation ******** */
268
270{
271 while (p.out < p.row_end) {
272 float value = p.value[0];
273 if (this->use_value_alpha_multiply()) {
274 value *= p.color2[3];
275 }
276 const float value_m = 1.0f - value;
277
278 if (p.color2[0] != 0.0f) {
279 p.out[0] = value_m * (p.color1[0]) + value * (p.color1[0]) / p.color2[0];
280 }
281 else {
282 p.out[0] = 0.0f;
283 }
284 if (p.color2[1] != 0.0f) {
285 p.out[1] = value_m * (p.color1[1]) + value * (p.color1[1]) / p.color2[1];
286 }
287 else {
288 p.out[1] = 0.0f;
289 }
290 if (p.color2[2] != 0.0f) {
291 p.out[2] = value_m * (p.color1[2]) + value * (p.color1[2]) / p.color2[2];
292 }
293 else {
294 p.out[2] = 0.0f;
295 }
296
297 p.out[3] = p.color1[3];
298
300 p.next();
301 }
302}
303
304/* ******** Mix Dodge Operation ******** */
305
307{
308 while (p.out < p.row_end) {
309 float value = p.value[0];
310 if (this->use_value_alpha_multiply()) {
311 value *= p.color2[3];
312 }
313
314 float tmp;
315 if (p.color1[0] != 0.0f) {
316 tmp = 1.0f - value * p.color2[0];
317 if (tmp <= 0.0f) {
318 p.out[0] = 1.0f;
319 }
320 else {
321 p.out[0] = p.color1[0] / tmp;
322 CLAMP_MAX(p.out[0], 1.0f);
323 }
324 }
325 else {
326 p.out[0] = 0.0f;
327 }
328
329 if (p.color1[1] != 0.0f) {
330 tmp = 1.0f - value * p.color2[1];
331 if (tmp <= 0.0f) {
332 p.out[1] = 1.0f;
333 }
334 else {
335 p.out[1] = p.color1[1] / tmp;
336 CLAMP_MAX(p.out[1], 1.0f);
337 }
338 }
339 else {
340 p.out[1] = 0.0f;
341 }
342
343 if (p.color1[2] != 0.0f) {
344 tmp = 1.0f - value * p.color2[2];
345 if (tmp <= 0.0f) {
346 p.out[2] = 1.0f;
347 }
348 else {
349 p.out[2] = p.color1[2] / tmp;
350 CLAMP_MAX(p.out[2], 1.0f);
351 }
352 }
353 else {
354 p.out[2] = 0.0f;
355 }
356
357 p.out[3] = p.color1[3];
358
360 p.next();
361 }
362}
363
364/* ******** Mix Glare Operation ******** */
365
367{
368 while (p.out < p.row_end) {
369 const float value = p.value[0];
370 /* Linear interpolation between 3 cases:
371 * value=-1:output=input value=0:output=input+glare value=1:output=glare
372 */
373 float input_weight;
374 float glare_weight;
375 if (value < 0.0f) {
376 input_weight = 1.0f;
377 glare_weight = 1.0f + value;
378 }
379 else {
380 input_weight = 1.0f - value;
381 glare_weight = 1.0f;
382 }
383 p.out[0] = input_weight * std::max(p.color1[0], 0.0f) + glare_weight * p.color2[0];
384 p.out[1] = input_weight * std::max(p.color1[1], 0.0f) + glare_weight * p.color2[1];
385 p.out[2] = input_weight * std::max(p.color1[2], 0.0f) + glare_weight * p.color2[2];
386 p.out[3] = p.color1[3];
387
389 p.next();
390 }
391}
392
393/* ******** Mix Hue Operation ******** */
394
396{
397 while (p.out < p.row_end) {
398 float value = p.value[0];
399 if (this->use_value_alpha_multiply()) {
400 value *= p.color2[3];
401 }
402 const float value_m = 1.0f - value;
403
404 float colH, colS, colV;
405 rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
406 if (colS != 0.0f) {
407 float rH, rS, rV;
408 float tmpr, tmpg, tmpb;
409 rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
410 hsv_to_rgb(colH, rS, rV, &tmpr, &tmpg, &tmpb);
411 p.out[0] = value_m * p.color1[0] + value * tmpr;
412 p.out[1] = value_m * p.color1[1] + value * tmpg;
413 p.out[2] = value_m * p.color1[2] + value * tmpb;
414 }
415 else {
416 copy_v3_v3(p.out, p.color1);
417 }
418 p.out[3] = p.color1[3];
419
421 p.next();
422 }
423}
424
425/* ******** Mix Lighten Operation ******** */
426
428{
429 while (p.out < p.row_end) {
430 float value = p.value[0];
431 if (this->use_value_alpha_multiply()) {
432 value *= p.color2[3];
433 }
434 float value_m = 1.0f - value;
435 p.out[0] = max_ff(p.color1[0], p.color2[0]) * value + p.color1[0] * value_m;
436 p.out[1] = max_ff(p.color1[1], p.color2[1]) * value + p.color1[1] * value_m;
437 p.out[2] = max_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m;
438 p.out[3] = p.color1[3];
439
441 p.next();
442 }
443}
444
445/* ******** Mix Linear Light Operation ******** */
446
448{
449 while (p.out < p.row_end) {
450 float value = p.value[0];
451 if (this->use_value_alpha_multiply()) {
452 value *= p.color2[3];
453 }
454 if (p.color2[0] > 0.5f) {
455 p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0] - 0.5f));
456 }
457 else {
458 p.out[0] = p.color1[0] + value * (2.0f * (p.color2[0]) - 1.0f);
459 }
460 if (p.color2[1] > 0.5f) {
461 p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1] - 0.5f));
462 }
463 else {
464 p.out[1] = p.color1[1] + value * (2.0f * (p.color2[1]) - 1.0f);
465 }
466 if (p.color2[2] > 0.5f) {
467 p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2] - 0.5f));
468 }
469 else {
470 p.out[2] = p.color1[2] + value * (2.0f * (p.color2[2]) - 1.0f);
471 }
472
473 p.out[3] = p.color1[3];
474
476 p.next();
477 }
478}
479
480/* ******** Mix Multiply Operation ******** */
481
483{
484 while (p.out < p.row_end) {
485 float value = p.value[0];
486 if (this->use_value_alpha_multiply()) {
487 value *= p.color2[3];
488 }
489 const float value_m = 1.0f - value;
490 p.out[0] = p.color1[0] * (value_m + value * p.color2[0]);
491 p.out[1] = p.color1[1] * (value_m + value * p.color2[1]);
492 p.out[2] = p.color1[2] * (value_m + value * p.color2[2]);
493
494 p.out[3] = p.color1[3];
495
497 p.next();
498 }
499}
500
501/* ******** Mix Overlay Operation ******** */
502
504{
505 while (p.out < p.row_end) {
506 float value = p.value[0];
507 if (this->use_value_alpha_multiply()) {
508 value *= p.color2[3];
509 }
510 const float value_m = 1.0f - value;
511 if (p.color1[0] < 0.5f) {
512 p.out[0] = p.color1[0] * (value_m + 2.0f * value * p.color2[0]);
513 }
514 else {
515 p.out[0] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]);
516 }
517 if (p.color1[1] < 0.5f) {
518 p.out[1] = p.color1[1] * (value_m + 2.0f * value * p.color2[1]);
519 }
520 else {
521 p.out[1] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]);
522 }
523 if (p.color1[2] < 0.5f) {
524 p.out[2] = p.color1[2] * (value_m + 2.0f * value * p.color2[2]);
525 }
526 else {
527 p.out[2] = 1.0f - (value_m + 2.0f * value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]);
528 }
529
530 p.out[3] = p.color1[3];
531
533 p.next();
534 }
535}
536
537/* ******** Mix Saturation Operation ******** */
538
540{
541 while (p.out < p.row_end) {
542 float value = p.value[0];
543 if (this->use_value_alpha_multiply()) {
544 value *= p.color2[3];
545 }
546 const float value_m = 1.0f - value;
547
548 float rH, rS, rV;
549 rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
550 if (rS != 0.0f) {
551 float colH, colS, colV;
552 rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
553 hsv_to_rgb(rH, (value_m * rS + value * colS), rV, &p.out[0], &p.out[1], &p.out[2]);
554 }
555 else {
556 copy_v3_v3(p.out, p.color1);
557 }
558
559 p.out[3] = p.color1[3];
560
562 p.next();
563 }
564}
565
566/* ******** Mix Screen Operation ******** */
567
569{
570 while (p.out < p.row_end) {
571 float value = p.value[0];
572 if (this->use_value_alpha_multiply()) {
573 value *= p.color2[3];
574 }
575 const float value_m = 1.0f - value;
576
577 p.out[0] = 1.0f - (value_m + value * (1.0f - p.color2[0])) * (1.0f - p.color1[0]);
578 p.out[1] = 1.0f - (value_m + value * (1.0f - p.color2[1])) * (1.0f - p.color1[1]);
579 p.out[2] = 1.0f - (value_m + value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]);
580 p.out[3] = p.color1[3];
581
583 p.next();
584 }
585}
586
587/* ******** Mix Soft Light Operation ******** */
588
590{
591 while (p.out < p.row_end) {
592 float value = p.value[0];
593 if (this->use_value_alpha_multiply()) {
594 value *= p.color2[3];
595 }
596 const float value_m = 1.0f - value;
597 float scr, scg, scb;
598
599 /* First calculate non-fac based Screen mix. */
600 scr = 1.0f - (1.0f - p.color2[0]) * (1.0f - p.color1[0]);
601 scg = 1.0f - (1.0f - p.color2[1]) * (1.0f - p.color1[1]);
602 scb = 1.0f - (1.0f - p.color2[2]) * (1.0f - p.color1[2]);
603
604 p.out[0] = value_m * p.color1[0] +
605 value * ((1.0f - p.color1[0]) * p.color2[0] * p.color1[0] + p.color1[0] * scr);
606 p.out[1] = value_m * p.color1[1] +
607 value * ((1.0f - p.color1[1]) * p.color2[1] * p.color1[1] + p.color1[1] * scg);
608 p.out[2] = value_m * p.color1[2] +
609 value * ((1.0f - p.color1[2]) * p.color2[2] * p.color1[2] + p.color1[2] * scb);
610 p.out[3] = p.color1[3];
611
613 p.next();
614 }
615}
616
617/* ******** Mix Subtract Operation ******** */
618
620{
621 while (p.out < p.row_end) {
622 float value = p.value[0];
623 if (this->use_value_alpha_multiply()) {
624 value *= p.color2[3];
625 }
626 p.out[0] = p.color1[0] - value * p.color2[0];
627 p.out[1] = p.color1[1] - value * p.color2[1];
628 p.out[2] = p.color1[2] - value * p.color2[2];
629 p.out[3] = p.color1[3];
630
632 p.next();
633 }
634}
635
636/* ******** Mix Value Operation ******** */
637
639{
640 while (p.out < p.row_end) {
641 float value = p.value[0];
642 if (this->use_value_alpha_multiply()) {
643 value *= p.color2[3];
644 }
645 float value_m = 1.0f - value;
646
647 float rH, rS, rV;
648 float colH, colS, colV;
649 rgb_to_hsv(p.color1[0], p.color1[1], p.color1[2], &rH, &rS, &rV);
650 rgb_to_hsv(p.color2[0], p.color2[1], p.color2[2], &colH, &colS, &colV);
651 hsv_to_rgb(rH, rS, (value_m * rV + value * colV), &p.out[0], &p.out[1], &p.out[2]);
652 p.out[3] = p.color1[3];
653
655 p.next();
656 }
657}
658
659} // namespace blender::compositor
MINLINE float max_ff(float a, float b)
MINLINE float min_ff(float a, float b)
void rgb_to_hsv(float r, float g, float b, float *r_h, float *r_s, float *r_v)
void hsv_to_rgb(float h, float s, float v, float *r_r, float *r_g, float *r_b)
Definition math_color.cc:21
MINLINE void copy_v3_v3(float r[3], const float a[3])
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
#define CLAMP_MAX(a, c)
a MemoryBuffer contains access to the data
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span< MemoryBuffer * > inputs) final
void determine_canvas(const rcti &preferred_area, rcti &r_area) override
virtual void update_memory_buffer_row(PixelCursor &p)
void set_use_value_alpha_multiply(const bool value)
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
void update_memory_buffer_row(PixelCursor &p) override
bool determine_canvas(const rcti &preferred_area, rcti &r_area)
void add_output_socket(DataType datatype)
NodeOperationInput * get_input_socket(unsigned int index)
void add_input_socket(DataType datatype, ResizeMode resize_mode=ResizeMode::Center)
void set_canvas_input_index(unsigned int index)
set the index of the input socket that will determine the canvas of this operation
virtual void determine_canvas(const rcti &preferred_area, rcti &r_area)
#define fabsf(x)
constexpr rcti COM_AREA_NONE
Definition COM_defines.h:89