Blender V4.3
effects.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 * SPDX-FileCopyrightText: 2003-2024 Blender Authors
3 * SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later */
6
11#include <cmath>
12#include <cstdlib>
13#include <cstring>
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_array.hh"
18#include "BLI_math_rotation.h"
19#include "BLI_math_vector.hh"
21#include "BLI_path_utils.hh"
22#include "BLI_rect.h"
23#include "BLI_string.h"
24#include "BLI_task.hh"
25#include "BLI_threads.h"
26#include "BLI_utildefines.h"
27
29#include "DNA_scene_types.h"
30#include "DNA_sequence_types.h"
31#include "DNA_space_types.h"
32#include "DNA_vfont_types.h"
33
34#include "BKE_fcurve.hh"
35#include "BKE_lib_id.hh"
36#include "BKE_main.hh"
37
39#include "IMB_imbuf.hh"
40#include "IMB_imbuf_types.hh"
41#include "IMB_interp.hh"
42#include "IMB_metadata.hh"
43
45
46#include "RNA_prototypes.hh"
47
48#include "RE_pipeline.h"
49
50#include "SEQ_channels.hh"
51#include "SEQ_effects.hh"
52#include "SEQ_proxy.hh"
53#include "SEQ_relations.hh"
54#include "SEQ_render.hh"
55#include "SEQ_time.hh"
56#include "SEQ_utils.hh"
57
58#include "BLF_api.hh"
59
60#include "effects.hh"
61#include "render.hh"
62
63using namespace blender;
64
65static SeqEffectHandle get_sequence_effect_impl(int seq_type);
66
67/* -------------------------------------------------------------------- */
71static void slice_get_byte_buffers(const SeqRenderData *context,
72 const ImBuf *ibuf1,
73 const ImBuf *ibuf2,
74 const ImBuf *out,
75 int start_line,
76 uchar **rect1,
77 uchar **rect2,
78 uchar **rect_out)
79{
80 int offset = 4 * start_line * context->rectx;
81
82 *rect1 = ibuf1->byte_buffer.data + offset;
83 *rect_out = out->byte_buffer.data + offset;
84
85 if (ibuf2) {
86 *rect2 = ibuf2->byte_buffer.data + offset;
87 }
88}
89
90static void slice_get_float_buffers(const SeqRenderData *context,
91 const ImBuf *ibuf1,
92 const ImBuf *ibuf2,
93 const ImBuf *out,
94 int start_line,
95 float **rect1,
96 float **rect2,
97 float **rect_out)
98{
99 int offset = 4 * start_line * context->rectx;
100
101 *rect1 = ibuf1->float_buffer.data + offset;
102 *rect_out = out->float_buffer.data + offset;
103
104 if (ibuf2) {
105 *rect2 = ibuf2->float_buffer.data + offset;
106 }
107}
108
110{
111 float4 res;
113 return res;
114}
115
116static float4 load_premul_pixel(const float *ptr)
117{
118 return float4(ptr);
119}
120
121static void store_premul_pixel(const float4 &pix, uchar *dst)
122{
124}
125
126static void store_premul_pixel(const float4 &pix, float *dst)
127{
128 *reinterpret_cast<float4 *>(dst) = pix;
129}
130
132{
133 dst[0] = 0;
134 dst[1] = 0;
135 dst[2] = 0;
136 dst[3] = 255;
137}
138
139static void store_opaque_black_pixel(float *dst)
140{
141 dst[0] = 0.0f;
142 dst[1] = 0.0f;
143 dst[2] = 0.0f;
144 dst[3] = 1.0f;
145}
146
149/* -------------------------------------------------------------------- */
154 ImBuf *ibuf1,
155 ImBuf *ibuf2,
156 bool uninitialized_pixels = true)
157{
158 ImBuf *out;
159 Scene *scene = context->scene;
160 int x = context->rectx;
161 int y = context->recty;
162 int base_flags = uninitialized_pixels ? IB_uninitialized_pixels : 0;
163
164 if (!ibuf1 && !ibuf2) {
165 /* hmmm, global float option ? */
166 out = IMB_allocImBuf(x, y, 32, IB_rect | base_flags);
167 }
168 else if ((ibuf1 && ibuf1->float_buffer.data) || (ibuf2 && ibuf2->float_buffer.data)) {
169 /* if any inputs are float, output is float too */
170 out = IMB_allocImBuf(x, y, 32, IB_rectfloat | base_flags);
171 }
172 else {
173 out = IMB_allocImBuf(x, y, 32, IB_rect | base_flags);
174 }
175
176 if (out->float_buffer.data) {
177 if (ibuf1 && !ibuf1->float_buffer.data) {
178 seq_imbuf_to_sequencer_space(scene, ibuf1, true);
179 }
180
181 if (ibuf2 && !ibuf2->float_buffer.data) {
182 seq_imbuf_to_sequencer_space(scene, ibuf2, true);
183 }
184
185 IMB_colormanagement_assign_float_colorspace(out, scene->sequencer_colorspace_settings.name);
186 }
187 else {
188 if (ibuf1 && !ibuf1->byte_buffer.data) {
189 IMB_rect_from_float(ibuf1);
190 }
191
192 if (ibuf2 && !ibuf2->byte_buffer.data) {
193 IMB_rect_from_float(ibuf2);
194 }
195 }
196
197 /* If effect only affecting a single channel, forward input's metadata to the output. */
198 if (ibuf1 != nullptr && ibuf1 == ibuf2) {
199 IMB_metadata_copy(out, ibuf1);
200 }
201
202 return out;
203}
204
207/* -------------------------------------------------------------------- */
212{
213 Sequence *seq1 = seq->seq1;
214 Sequence *seq2 = seq->seq2;
215
216 seq->seq2 = seq1;
217 seq->seq1 = seq2;
218}
219
220static bool alpha_opaque(uchar alpha)
221{
222 return alpha == 255;
223}
224
225static bool alpha_opaque(float alpha)
226{
227 return alpha >= 1.0f;
228}
229
230/* dst = src1 over src2 (alpha from src1) */
231template<typename T>
233 float fac, int width, int height, const T *src1, const T *src2, T *dst)
234{
235 if (fac <= 0.0f) {
236 memcpy(dst, src2, sizeof(T) * 4 * width * height);
237 return;
238 }
239
240 for (int pixel_idx = 0; pixel_idx < width * height; pixel_idx++) {
241 if (src1[3] <= 0.0f) {
242 /* Alpha of zero. No color addition will happen as the colors are pre-multiplied. */
243 memcpy(dst, src2, sizeof(T) * 4);
244 }
245 else if (fac == 1.0f && alpha_opaque(src1[3])) {
246 /* No change to `src1` as `fac == 1` and fully opaque. */
247 memcpy(dst, src1, sizeof(T) * 4);
248 }
249 else {
250 float4 col1 = load_premul_pixel(src1);
251 float mfac = 1.0f - fac * col1.w;
252 float4 col2 = load_premul_pixel(src2);
253 float4 col = fac * col1 + mfac * col2;
255 }
256 src1 += 4;
257 src2 += 4;
258 dst += 4;
259 }
260}
261
262static void do_alphaover_effect(const SeqRenderData *context,
263 Sequence * /*seq*/,
264 float /*timeline_frame*/,
265 float fac,
266 const ImBuf *ibuf1,
267 const ImBuf *ibuf2,
268 int start_line,
269 int total_lines,
270 ImBuf *out)
271{
272 if (out->float_buffer.data) {
273 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
274
275 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
276
277 do_alphaover_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
278 }
279 else {
280 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
281
282 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
283
284 do_alphaover_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
285 }
286}
287
290/* -------------------------------------------------------------------- */
294/* dst = src1 under src2 (alpha from src2) */
295template<typename T>
297 float fac, int width, int height, const T *src1, const T *src2, T *dst)
298{
299 if (fac <= 0.0f) {
300 memcpy(dst, src2, sizeof(T) * 4 * width * height);
301 return;
302 }
303
304 for (int pixel_idx = 0; pixel_idx < width * height; pixel_idx++) {
305 if (src2[3] <= 0.0f && fac >= 1.0f) {
306 memcpy(dst, src1, sizeof(T) * 4);
307 }
308 else if (alpha_opaque(src2[3])) {
309 memcpy(dst, src2, sizeof(T) * 4);
310 }
311 else {
312 float4 col2 = load_premul_pixel(src2);
313 float mfac = fac * (1.0f - col2.w);
314 float4 col1 = load_premul_pixel(src1);
315 float4 col = mfac * col1 + col2;
317 }
318 src1 += 4;
319 src2 += 4;
320 dst += 4;
321 }
322}
323
324static void do_alphaunder_effect(const SeqRenderData *context,
325 Sequence * /*seq*/,
326 float /*timeline_frame*/,
327 float fac,
328 const ImBuf *ibuf1,
329 const ImBuf *ibuf2,
330 int start_line,
331 int total_lines,
332 ImBuf *out)
333{
334 if (out->float_buffer.data) {
335 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
336
337 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
338
339 do_alphaunder_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
340 }
341 else {
342 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
343
344 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
345
346 do_alphaunder_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
347 }
348}
349
352/* -------------------------------------------------------------------- */
356static void do_cross_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
357{
358 uchar *rt1 = rect1;
359 uchar *rt2 = rect2;
360 uchar *rt = out;
361
362 int temp_fac = int(256.0f * fac);
363 int temp_mfac = 256 - temp_fac;
364
365 for (int i = 0; i < y; i++) {
366 for (int j = 0; j < x; j++) {
367 rt[0] = (temp_mfac * rt1[0] + temp_fac * rt2[0]) >> 8;
368 rt[1] = (temp_mfac * rt1[1] + temp_fac * rt2[1]) >> 8;
369 rt[2] = (temp_mfac * rt1[2] + temp_fac * rt2[2]) >> 8;
370 rt[3] = (temp_mfac * rt1[3] + temp_fac * rt2[3]) >> 8;
371
372 rt1 += 4;
373 rt2 += 4;
374 rt += 4;
375 }
376 }
377}
378
379static void do_cross_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
380{
381 float *rt1 = rect1;
382 float *rt2 = rect2;
383 float *rt = out;
384
385 float mfac = 1.0f - fac;
386
387 for (int i = 0; i < y; i++) {
388 for (int j = 0; j < x; j++) {
389 rt[0] = mfac * rt1[0] + fac * rt2[0];
390 rt[1] = mfac * rt1[1] + fac * rt2[1];
391 rt[2] = mfac * rt1[2] + fac * rt2[2];
392 rt[3] = mfac * rt1[3] + fac * rt2[3];
393
394 rt1 += 4;
395 rt2 += 4;
396 rt += 4;
397 }
398 }
399}
400
401static void do_cross_effect(const SeqRenderData *context,
402 Sequence * /*seq*/,
403 float /*timeline_frame*/,
404 float fac,
405 const ImBuf *ibuf1,
406 const ImBuf *ibuf2,
407 int start_line,
408 int total_lines,
409 ImBuf *out)
410{
411 if (out->float_buffer.data) {
412 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
413
414 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
415
416 do_cross_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
417 }
418 else {
419 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
420
421 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
422
423 do_cross_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
424 }
425}
426
429/* -------------------------------------------------------------------- */
433/* One could argue that gamma cross should not be hardcoded to 2.0 gamma,
434 * but instead either do proper input->linear conversion (often sRGB). Or
435 * maybe not even that, but do interpolation in some perceptual color space
436 * like OKLAB. But currently it is fixed to just 2.0 gamma. */
437
438static float gammaCorrect(float c)
439{
440 if (UNLIKELY(c < 0)) {
441 return -(c * c);
442 }
443 return c * c;
444}
445
446static float invGammaCorrect(float c)
447{
448 return sqrtf_signed(c);
449}
450
451template<typename T>
453 float fac, int width, int height, const T *src1, const T *src2, T *dst)
454{
455 float mfac = 1.0f - fac;
456
457 for (int y = 0; y < height; y++) {
458 for (int x = 0; x < width; x++) {
459 float4 col1 = load_premul_pixel(src1);
460 float4 col2 = load_premul_pixel(src2);
461 float4 col;
462 for (int c = 0; c < 4; ++c) {
463 col[c] = gammaCorrect(mfac * invGammaCorrect(col1[c]) + fac * invGammaCorrect(col2[c]));
464 }
466 src1 += 4;
467 src2 += 4;
468 dst += 4;
469 }
470 }
471}
472
473static void do_gammacross_effect(const SeqRenderData *context,
474 Sequence * /*seq*/,
475 float /*timeline_frame*/,
476 float fac,
477 const ImBuf *ibuf1,
478 const ImBuf *ibuf2,
479 int start_line,
480 int total_lines,
481 ImBuf *out)
482{
483 if (out->float_buffer.data) {
484 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
485
486 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
487
488 do_gammacross_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
489 }
490 else {
491 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
492
493 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
494
495 do_gammacross_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
496 }
497}
498
501/* -------------------------------------------------------------------- */
505static void do_add_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
506{
507 uchar *cp1 = rect1;
508 uchar *cp2 = rect2;
509 uchar *rt = out;
510
511 int temp_fac = int(256.0f * fac);
512
513 for (int i = 0; i < y; i++) {
514 for (int j = 0; j < x; j++) {
515 const int temp_fac2 = temp_fac * int(cp2[3]);
516 rt[0] = min_ii(cp1[0] + ((temp_fac2 * cp2[0]) >> 16), 255);
517 rt[1] = min_ii(cp1[1] + ((temp_fac2 * cp2[1]) >> 16), 255);
518 rt[2] = min_ii(cp1[2] + ((temp_fac2 * cp2[2]) >> 16), 255);
519 rt[3] = cp1[3];
520
521 cp1 += 4;
522 cp2 += 4;
523 rt += 4;
524 }
525 }
526}
527
528static void do_add_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
529{
530 float *rt1 = rect1;
531 float *rt2 = rect2;
532 float *rt = out;
533
534 for (int i = 0; i < y; i++) {
535 for (int j = 0; j < x; j++) {
536 const float temp_fac = (1.0f - (rt1[3] * (1.0f - fac))) * rt2[3];
537 rt[0] = rt1[0] + temp_fac * rt2[0];
538 rt[1] = rt1[1] + temp_fac * rt2[1];
539 rt[2] = rt1[2] + temp_fac * rt2[2];
540 rt[3] = rt1[3];
541
542 rt1 += 4;
543 rt2 += 4;
544 rt += 4;
545 }
546 }
547}
548
549static void do_add_effect(const SeqRenderData *context,
550 Sequence * /*seq*/,
551 float /*timeline_frame*/,
552 float fac,
553 const ImBuf *ibuf1,
554 const ImBuf *ibuf2,
555 int start_line,
556 int total_lines,
557 ImBuf *out)
558{
559 if (out->float_buffer.data) {
560 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
561
562 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
563
564 do_add_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
565 }
566 else {
567 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
568
569 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
570
571 do_add_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
572 }
573}
574
577/* -------------------------------------------------------------------- */
581static void do_sub_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
582{
583 uchar *cp1 = rect1;
584 uchar *cp2 = rect2;
585 uchar *rt = out;
586
587 int temp_fac = int(256.0f * fac);
588
589 for (int i = 0; i < y; i++) {
590 for (int j = 0; j < x; j++) {
591 const int temp_fac2 = temp_fac * int(cp2[3]);
592 rt[0] = max_ii(cp1[0] - ((temp_fac2 * cp2[0]) >> 16), 0);
593 rt[1] = max_ii(cp1[1] - ((temp_fac2 * cp2[1]) >> 16), 0);
594 rt[2] = max_ii(cp1[2] - ((temp_fac2 * cp2[2]) >> 16), 0);
595 rt[3] = cp1[3];
596
597 cp1 += 4;
598 cp2 += 4;
599 rt += 4;
600 }
601 }
602}
603
604static void do_sub_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
605{
606 float *rt1 = rect1;
607 float *rt2 = rect2;
608 float *rt = out;
609
610 float mfac = 1.0f - fac;
611
612 for (int i = 0; i < y; i++) {
613 for (int j = 0; j < x; j++) {
614 const float temp_fac = (1.0f - (rt1[3] * mfac)) * rt2[3];
615 rt[0] = max_ff(rt1[0] - temp_fac * rt2[0], 0.0f);
616 rt[1] = max_ff(rt1[1] - temp_fac * rt2[1], 0.0f);
617 rt[2] = max_ff(rt1[2] - temp_fac * rt2[2], 0.0f);
618 rt[3] = rt1[3];
619
620 rt1 += 4;
621 rt2 += 4;
622 rt += 4;
623 }
624 }
625}
626
627static void do_sub_effect(const SeqRenderData *context,
628 Sequence * /*seq*/,
629 float /*timeline_frame*/,
630 float fac,
631 const ImBuf *ibuf1,
632 const ImBuf *ibuf2,
633 int start_line,
634 int total_lines,
635 ImBuf *out)
636{
637 if (out->float_buffer.data) {
638 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
639
640 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
641
642 do_sub_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
643 }
644 else {
645 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
646
647 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
648
649 do_sub_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
650 }
651}
652
655/* -------------------------------------------------------------------- */
659/* Must be > 0 or add pre-copy, etc to the function. */
660#define XOFF 8
661#define YOFF 8
662
663static void do_drop_effect_byte(float fac, int x, int y, uchar *rect2i, uchar *rect1i, uchar *outi)
664{
665 const int xoff = min_ii(XOFF, x);
666 const int yoff = min_ii(YOFF, y);
667
668 int temp_fac = int(70.0f * fac);
669
670 uchar *rt2 = rect2i + yoff * 4 * x;
671 uchar *rt1 = rect1i;
672 uchar *out = outi;
673 for (int i = 0; i < y - yoff; i++) {
674 memcpy(out, rt1, sizeof(*out) * xoff * 4);
675 rt1 += xoff * 4;
676 out += xoff * 4;
677
678 for (int j = xoff; j < x; j++) {
679 int temp_fac2 = ((temp_fac * rt2[3]) >> 8);
680
681 *(out++) = std::max(0, *rt1 - temp_fac2);
682 rt1++;
683 *(out++) = std::max(0, *rt1 - temp_fac2);
684 rt1++;
685 *(out++) = std::max(0, *rt1 - temp_fac2);
686 rt1++;
687 *(out++) = std::max(0, *rt1 - temp_fac2);
688 rt1++;
689 rt2 += 4;
690 }
691 rt2 += xoff * 4;
692 }
693 memcpy(out, rt1, sizeof(*out) * yoff * 4 * x);
694}
695
697 float fac, int x, int y, float *rect2i, float *rect1i, float *outi)
698{
699 const int xoff = min_ii(XOFF, x);
700 const int yoff = min_ii(YOFF, y);
701
702 float temp_fac = 70.0f * fac;
703
704 float *rt2 = rect2i + yoff * 4 * x;
705 float *rt1 = rect1i;
706 float *out = outi;
707 for (int i = 0; i < y - yoff; i++) {
708 memcpy(out, rt1, sizeof(*out) * xoff * 4);
709 rt1 += xoff * 4;
710 out += xoff * 4;
711
712 for (int j = xoff; j < x; j++) {
713 float temp_fac2 = temp_fac * rt2[3];
714
715 *(out++) = std::max(0.0f, *rt1 - temp_fac2);
716 rt1++;
717 *(out++) = std::max(0.0f, *rt1 - temp_fac2);
718 rt1++;
719 *(out++) = std::max(0.0f, *rt1 - temp_fac2);
720 rt1++;
721 *(out++) = std::max(0.0f, *rt1 - temp_fac2);
722 rt1++;
723 rt2 += 4;
724 }
725 rt2 += xoff * 4;
726 }
727 memcpy(out, rt1, sizeof(*out) * yoff * 4 * x);
728}
729
732/* -------------------------------------------------------------------- */
736static void do_mul_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
737{
738 uchar *rt1 = rect1;
739 uchar *rt2 = rect2;
740 uchar *rt = out;
741
742 int temp_fac = int(256.0f * fac);
743
744 /* Formula:
745 * `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s;` // + centx
746 * `yaux = -s * px + c * py;` // + centy */
747
748 for (int i = 0; i < y; i++) {
749 for (int j = 0; j < x; j++) {
750 rt[0] = rt1[0] + ((temp_fac * rt1[0] * (rt2[0] - 255)) >> 16);
751 rt[1] = rt1[1] + ((temp_fac * rt1[1] * (rt2[1] - 255)) >> 16);
752 rt[2] = rt1[2] + ((temp_fac * rt1[2] * (rt2[2] - 255)) >> 16);
753 rt[3] = rt1[3] + ((temp_fac * rt1[3] * (rt2[3] - 255)) >> 16);
754
755 rt1 += 4;
756 rt2 += 4;
757 rt += 4;
758 }
759 }
760}
761
762static void do_mul_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
763{
764 float *rt1 = rect1;
765 float *rt2 = rect2;
766 float *rt = out;
767
768 /* Formula:
769 * `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a`. */
770
771 for (int i = 0; i < y; i++) {
772 for (int j = 0; j < x; j++) {
773 rt[0] = rt1[0] + fac * rt1[0] * (rt2[0] - 1.0f);
774 rt[1] = rt1[1] + fac * rt1[1] * (rt2[1] - 1.0f);
775 rt[2] = rt1[2] + fac * rt1[2] * (rt2[2] - 1.0f);
776 rt[3] = rt1[3] + fac * rt1[3] * (rt2[3] - 1.0f);
777
778 rt1 += 4;
779 rt2 += 4;
780 rt += 4;
781 }
782 }
783}
784
785static void do_mul_effect(const SeqRenderData *context,
786 Sequence * /*seq*/,
787 float /*timeline_frame*/,
788 float fac,
789 const ImBuf *ibuf1,
790 const ImBuf *ibuf2,
791 int start_line,
792 int total_lines,
793 ImBuf *out)
794{
795 if (out->float_buffer.data) {
796 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
797
798 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
799
800 do_mul_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
801 }
802 else {
803 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
804
805 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
806
807 do_mul_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
808 }
809}
810
813/* -------------------------------------------------------------------- */
817/* blend_function has to be: void (T* dst, const T *src1, const T *src2) */
818template<typename T, typename Func>
820 float fac, int width, int height, const T *src1, T *src2, T *dst, Func blend_function)
821{
822 for (int y = 0; y < height; y++) {
823 for (int x = 0; x < width; x++) {
824 T achannel = src2[3];
825 src2[3] = T(achannel * fac);
826 blend_function(dst, src1, src2);
827 src2[3] = achannel;
828 dst[3] = src1[3];
829 src1 += 4;
830 src2 += 4;
831 dst += 4;
832 }
833 }
834}
835
837 float fac, int x, int y, const float *rect1, float *rect2, int btype, float *out)
838{
839 switch (btype) {
840 case SEQ_TYPE_ADD:
841 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_add_float);
842 break;
843 case SEQ_TYPE_SUB:
844 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_sub_float);
845 break;
846 case SEQ_TYPE_MUL:
847 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_mul_float);
848 break;
849 case SEQ_TYPE_DARKEN:
850 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_darken_float);
851 break;
853 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_burn_float);
854 break;
856 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearburn_float);
857 break;
858 case SEQ_TYPE_SCREEN:
859 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_screen_float);
860 break;
861 case SEQ_TYPE_LIGHTEN:
862 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_lighten_float);
863 break;
864 case SEQ_TYPE_DODGE:
865 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_dodge_float);
866 break;
867 case SEQ_TYPE_OVERLAY:
868 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_overlay_float);
869 break;
871 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_softlight_float);
872 break;
874 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hardlight_float);
875 break;
877 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_pinlight_float);
878 break;
880 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearlight_float);
881 break;
883 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_vividlight_float);
884 break;
886 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_color_float);
887 break;
888 case SEQ_TYPE_HUE:
889 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hue_float);
890 break;
892 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_saturation_float);
893 break;
894 case SEQ_TYPE_VALUE:
895 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_luminosity_float);
896 break;
898 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_difference_float);
899 break;
901 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_exclusion_float);
902 break;
903 default:
904 break;
905 }
906}
907
909 float fac, int x, int y, const uchar *rect1, uchar *rect2, int btype, uchar *out)
910{
911 switch (btype) {
912 case SEQ_TYPE_ADD:
913 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_add_byte);
914 break;
915 case SEQ_TYPE_SUB:
916 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_sub_byte);
917 break;
918 case SEQ_TYPE_MUL:
919 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_mul_byte);
920 break;
921 case SEQ_TYPE_DARKEN:
922 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_darken_byte);
923 break;
925 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_burn_byte);
926 break;
928 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearburn_byte);
929 break;
930 case SEQ_TYPE_SCREEN:
931 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_screen_byte);
932 break;
933 case SEQ_TYPE_LIGHTEN:
934 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_lighten_byte);
935 break;
936 case SEQ_TYPE_DODGE:
937 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_dodge_byte);
938 break;
939 case SEQ_TYPE_OVERLAY:
940 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_overlay_byte);
941 break;
943 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_softlight_byte);
944 break;
946 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hardlight_byte);
947 break;
949 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_pinlight_byte);
950 break;
952 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearlight_byte);
953 break;
955 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_vividlight_byte);
956 break;
958 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_color_byte);
959 break;
960 case SEQ_TYPE_HUE:
961 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hue_byte);
962 break;
964 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_saturation_byte);
965 break;
966 case SEQ_TYPE_VALUE:
967 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_luminosity_byte);
968 break;
970 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_difference_byte);
971 break;
973 apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_exclusion_byte);
974 break;
975 default:
976 break;
977 }
978}
979
980static void do_blend_mode_effect(const SeqRenderData *context,
981 Sequence *seq,
982 float /*timeline_frame*/,
983 float fac,
984 const ImBuf *ibuf1,
985 const ImBuf *ibuf2,
986 int start_line,
987 int total_lines,
988 ImBuf *out)
989{
990 if (out->float_buffer.data) {
991 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
992 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
994 fac, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out);
995 }
996 else {
997 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
998 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
1000 fac, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out);
1001 }
1002}
1003
1006/* -------------------------------------------------------------------- */
1011{
1013
1014 if (seq->effectdata) {
1015 MEM_freeN(seq->effectdata);
1016 }
1017 seq->effectdata = MEM_callocN(sizeof(ColorMixVars), "colormixvars");
1018 data = (ColorMixVars *)seq->effectdata;
1019 data->blend_effect = SEQ_TYPE_OVERLAY;
1020 data->factor = 1.0f;
1021}
1022
1023static void do_colormix_effect(const SeqRenderData *context,
1024 Sequence *seq,
1025 float /*timeline_frame*/,
1026 float /*fac*/,
1027 const ImBuf *ibuf1,
1028 const ImBuf *ibuf2,
1029 int start_line,
1030 int total_lines,
1031 ImBuf *out)
1032{
1033 float fac;
1034
1035 ColorMixVars *data = static_cast<ColorMixVars *>(seq->effectdata);
1036 fac = data->factor;
1037
1038 if (out->float_buffer.data) {
1039 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
1040 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
1042 fac, context->rectx, total_lines, rect1, rect2, data->blend_effect, rect_out);
1043 }
1044 else {
1045 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
1046 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
1048 fac, context->rectx, total_lines, rect1, rect2, data->blend_effect, rect_out);
1049 }
1050}
1051
1054/* -------------------------------------------------------------------- */
1058struct WipeZone {
1059 float angle;
1060 int flip;
1061 int xo, yo;
1065 int type;
1067};
1068
1069static WipeZone precalc_wipe_zone(const WipeVars *wipe, int xo, int yo)
1070{
1071 WipeZone zone;
1072 zone.flip = (wipe->angle < 0.0f);
1073 zone.angle = tanf(fabsf(wipe->angle));
1074 zone.xo = xo;
1075 zone.yo = yo;
1076 zone.width = int(wipe->edgeWidth * ((xo + yo) / 2.0f));
1077 zone.pythangle = 1.0f / sqrtf(zone.angle * zone.angle + 1.0f);
1078 zone.clockWidth = wipe->edgeWidth * float(M_PI);
1079 zone.type = wipe->wipetype;
1080 zone.forward = wipe->forward != 0;
1081 return zone;
1082}
1083
1087static float in_band(float width, float dist, int side, int dir)
1088{
1089 float alpha;
1090
1091 if (width == 0) {
1092 return float(side);
1093 }
1094
1095 if (width < dist) {
1096 return float(side);
1097 }
1098
1099 if (side == 1) {
1100 alpha = (dist + 0.5f * width) / (width);
1101 }
1102 else {
1103 alpha = (0.5f * width - dist) / (width);
1104 }
1105
1106 if (dir == 0) {
1107 alpha = 1 - alpha;
1108 }
1109
1110 return alpha;
1111}
1112
1113static float check_zone(const WipeZone *wipezone, int x, int y, float fac)
1114{
1115 float posx, posy, hyp, hyp2, angle, hwidth, b1, b2, b3, pointdist;
1116 float temp1, temp2, temp3, temp4; /* some placeholder variables */
1117 int xo = wipezone->xo;
1118 int yo = wipezone->yo;
1119 float halfx = xo * 0.5f;
1120 float halfy = yo * 0.5f;
1121 float widthf, output = 0;
1122 int width;
1123
1124 if (wipezone->flip) {
1125 x = xo - x;
1126 }
1127 angle = wipezone->angle;
1128
1129 if (wipezone->forward) {
1130 posx = fac * xo;
1131 posy = fac * yo;
1132 }
1133 else {
1134 posx = xo - fac * xo;
1135 posy = yo - fac * yo;
1136 }
1137
1138 switch (wipezone->type) {
1139 case DO_SINGLE_WIPE:
1140 width = min_ii(wipezone->width, fac * yo);
1141 width = min_ii(width, yo - fac * yo);
1142
1143 if (angle == 0.0f) {
1144 b1 = posy;
1145 b2 = y;
1146 hyp = fabsf(y - posy);
1147 }
1148 else {
1149 b1 = posy - (-angle) * posx;
1150 b2 = y - (-angle) * x;
1151 hyp = fabsf(angle * x + y + (-posy - angle * posx)) * wipezone->pythangle;
1152 }
1153
1154 if (angle < 0) {
1155 temp1 = b1;
1156 b1 = b2;
1157 b2 = temp1;
1158 }
1159
1160 if (wipezone->forward) {
1161 if (b1 < b2) {
1162 output = in_band(width, hyp, 1, 1);
1163 }
1164 else {
1165 output = in_band(width, hyp, 0, 1);
1166 }
1167 }
1168 else {
1169 if (b1 < b2) {
1170 output = in_band(width, hyp, 0, 1);
1171 }
1172 else {
1173 output = in_band(width, hyp, 1, 1);
1174 }
1175 }
1176 break;
1177
1178 case DO_DOUBLE_WIPE:
1179 if (!wipezone->forward) {
1180 fac = 1.0f - fac; /* Go the other direction */
1181 }
1182
1183 width = wipezone->width; /* calculate the blur width */
1184 hwidth = width * 0.5f;
1185 if (angle == 0) {
1186 b1 = posy * 0.5f;
1187 b3 = yo - posy * 0.5f;
1188 b2 = y;
1189
1190 hyp = fabsf(y - posy * 0.5f);
1191 hyp2 = fabsf(y - (yo - posy * 0.5f));
1192 }
1193 else {
1194 b1 = posy * 0.5f - (-angle) * posx * 0.5f;
1195 b3 = (yo - posy * 0.5f) - (-angle) * (xo - posx * 0.5f);
1196 b2 = y - (-angle) * x;
1197
1198 hyp = fabsf(angle * x + y + (-posy * 0.5f - angle * posx * 0.5f)) * wipezone->pythangle;
1199 hyp2 = fabsf(angle * x + y + (-(yo - posy * 0.5f) - angle * (xo - posx * 0.5f))) *
1200 wipezone->pythangle;
1201 }
1202
1203 hwidth = min_ff(hwidth, fabsf(b3 - b1) / 2.0f);
1204
1205 if (b2 < b1 && b2 < b3) {
1206 output = in_band(hwidth, hyp, 0, 1);
1207 }
1208 else if (b2 > b1 && b2 > b3) {
1209 output = in_band(hwidth, hyp2, 0, 1);
1210 }
1211 else {
1212 if (hyp < hwidth && hyp2 > hwidth) {
1213 output = in_band(hwidth, hyp, 1, 1);
1214 }
1215 else if (hyp > hwidth && hyp2 < hwidth) {
1216 output = in_band(hwidth, hyp2, 1, 1);
1217 }
1218 else {
1219 output = in_band(hwidth, hyp2, 1, 1) * in_band(hwidth, hyp, 1, 1);
1220 }
1221 }
1222 if (!wipezone->forward) {
1223 output = 1 - output;
1224 }
1225 break;
1226 case DO_CLOCK_WIPE:
1227 /*
1228 * temp1: angle of effect center in rads
1229 * temp2: angle of line through (halfx, halfy) and (x, y) in rads
1230 * temp3: angle of low side of blur
1231 * temp4: angle of high side of blur
1232 */
1233 output = 1.0f - fac;
1234 widthf = wipezone->clockWidth;
1235 temp1 = 2.0f * float(M_PI) * fac;
1236
1237 if (wipezone->forward) {
1238 temp1 = 2.0f * float(M_PI) - temp1;
1239 }
1240
1241 x = x - halfx;
1242 y = y - halfy;
1243
1244 temp2 = atan2f(y, x);
1245 if (temp2 < 0.0f) {
1246 temp2 += 2.0f * float(M_PI);
1247 }
1248
1249 if (wipezone->forward) {
1250 temp3 = temp1 - widthf * fac;
1251 temp4 = temp1 + widthf * (1 - fac);
1252 }
1253 else {
1254 temp3 = temp1 - widthf * (1 - fac);
1255 temp4 = temp1 + widthf * fac;
1256 }
1257 if (temp3 < 0) {
1258 temp3 = 0;
1259 }
1260 if (temp4 > 2.0f * float(M_PI)) {
1261 temp4 = 2.0f * float(M_PI);
1262 }
1263
1264 if (temp2 < temp3) {
1265 output = 0;
1266 }
1267 else if (temp2 > temp4) {
1268 output = 1;
1269 }
1270 else {
1271 output = (temp2 - temp3) / (temp4 - temp3);
1272 }
1273 if (x == 0 && y == 0) {
1274 output = 1;
1275 }
1276 if (output != output) {
1277 output = 1;
1278 }
1279 if (wipezone->forward) {
1280 output = 1 - output;
1281 }
1282 break;
1283 case DO_IRIS_WIPE:
1284 if (xo > yo) {
1285 yo = xo;
1286 }
1287 else {
1288 xo = yo;
1289 }
1290
1291 if (!wipezone->forward) {
1292 fac = 1 - fac;
1293 }
1294
1295 width = wipezone->width;
1296 hwidth = width * 0.5f;
1297
1298 temp1 = (halfx - (halfx)*fac);
1299 pointdist = hypotf(temp1, temp1);
1300
1301 temp2 = hypotf(halfx - x, halfy - y);
1302 if (temp2 > pointdist) {
1303 output = in_band(hwidth, fabsf(temp2 - pointdist), 0, 1);
1304 }
1305 else {
1306 output = in_band(hwidth, fabsf(temp2 - pointdist), 1, 1);
1307 }
1308
1309 if (!wipezone->forward) {
1310 output = 1 - output;
1311 }
1312
1313 break;
1314 }
1315 if (output < 0) {
1316 output = 0;
1317 }
1318 else if (output > 1) {
1319 output = 1;
1320 }
1321 return output;
1322}
1323
1325{
1326 if (seq->effectdata) {
1327 MEM_freeN(seq->effectdata);
1328 }
1329
1330 seq->effectdata = MEM_callocN(sizeof(WipeVars), "wipevars");
1331}
1332
1334{
1335 return 2;
1336}
1337
1338static void free_wipe_effect(Sequence *seq, const bool /*do_id_user*/)
1339{
1341}
1342
1343static void copy_wipe_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
1344{
1345 dst->effectdata = MEM_dupallocN(src->effectdata);
1346}
1347
1348template<typename T>
1349static void do_wipe_effect(
1350 const Sequence *seq, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
1351{
1352 using namespace blender;
1353 const WipeVars *wipe = (const WipeVars *)seq->effectdata;
1354 const WipeZone wipezone = precalc_wipe_zone(wipe, width, height);
1355
1356 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
1357 const T *cp1 = rect1 ? rect1 + y_range.first() * width * 4 : nullptr;
1358 const T *cp2 = rect2 ? rect2 + y_range.first() * width * 4 : nullptr;
1359 T *rt = out + y_range.first() * width * 4;
1360 for (const int y : y_range) {
1361 for (int x = 0; x < width; x++) {
1362 float check = check_zone(&wipezone, x, y, fac);
1363 if (check) {
1364 if (cp1) {
1365 float4 col1 = load_premul_pixel(cp1);
1366 float4 col2 = load_premul_pixel(cp2);
1367 float4 col = col1 * check + col2 * (1.0f - check);
1369 }
1370 else {
1372 }
1373 }
1374 else {
1375 if (cp2) {
1376 memcpy(rt, cp2, sizeof(T) * 4);
1377 }
1378 else {
1380 }
1381 }
1382
1383 rt += 4;
1384 if (cp1 != nullptr) {
1385 cp1 += 4;
1386 }
1387 if (cp2 != nullptr) {
1388 cp2 += 4;
1389 }
1390 }
1391 }
1392 });
1393}
1394
1395static ImBuf *do_wipe_effect(const SeqRenderData *context,
1396 Sequence *seq,
1397 float /*timeline_frame*/,
1398 float fac,
1399 ImBuf *ibuf1,
1400 ImBuf *ibuf2)
1401{
1402 ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
1403
1404 if (out->float_buffer.data) {
1405 do_wipe_effect(seq,
1406 fac,
1407 context->rectx,
1408 context->recty,
1409 ibuf1->float_buffer.data,
1410 ibuf2->float_buffer.data,
1411 out->float_buffer.data);
1412 }
1413 else {
1414 do_wipe_effect(seq,
1415 fac,
1416 context->rectx,
1417 context->recty,
1418 ibuf1->byte_buffer.data,
1419 ibuf2->byte_buffer.data,
1420 out->byte_buffer.data);
1421 }
1422
1423 return out;
1424}
1425
1428/* -------------------------------------------------------------------- */
1433{
1435
1436 if (seq->effectdata) {
1437 MEM_freeN(seq->effectdata);
1438 }
1439
1440 seq->effectdata = MEM_callocN(sizeof(TransformVars), "transformvars");
1441
1442 transform = (TransformVars *)seq->effectdata;
1443
1444 transform->ScalexIni = 1.0f;
1445 transform->ScaleyIni = 1.0f;
1446
1447 transform->xIni = 0.0f;
1448 transform->yIni = 0.0f;
1449
1450 transform->rotIni = 0.0f;
1451
1452 transform->interpolation = 1;
1453 transform->percent = 1;
1454 transform->uniform_scale = 0;
1455}
1456
1458{
1459 return 1;
1460}
1461
1462static void free_transform_effect(Sequence *seq, const bool /*do_id_user*/)
1463{
1465}
1466
1467static void copy_transform_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
1468{
1469 dst->effectdata = MEM_dupallocN(src->effectdata);
1470}
1471
1472static void transform_image(int x,
1473 int y,
1474 int start_line,
1475 int total_lines,
1476 const ImBuf *ibuf,
1477 ImBuf *out,
1478 float scale_x,
1479 float scale_y,
1480 float translate_x,
1481 float translate_y,
1482 float rotate,
1483 int interpolation)
1484{
1485 /* Rotate */
1486 float s = sinf(rotate);
1487 float c = cosf(rotate);
1488
1489 float4 *dst_fl = reinterpret_cast<float4 *>(out->float_buffer.data);
1490 uchar4 *dst_ch = reinterpret_cast<uchar4 *>(out->byte_buffer.data);
1491
1492 size_t offset = size_t(x) * start_line;
1493 for (int yi = start_line; yi < start_line + total_lines; yi++) {
1494 for (int xi = 0; xi < x; xi++) {
1495 /* Translate point. */
1496 float xt = xi - translate_x;
1497 float yt = yi - translate_y;
1498
1499 /* Rotate point with center ref. */
1500 float xr = c * xt + s * yt;
1501 float yr = -s * xt + c * yt;
1502
1503 /* Scale point with center ref. */
1504 xt = xr / scale_x;
1505 yt = yr / scale_y;
1506
1507 /* Undo reference center point. */
1508 xt += (x / 2.0f);
1509 yt += (y / 2.0f);
1510
1511 /* interpolate */
1512 switch (interpolation) {
1513 case 0:
1514 if (dst_fl) {
1515 dst_fl[offset] = imbuf::interpolate_nearest_border_fl(ibuf, xt, yt);
1516 }
1517 else {
1518 dst_ch[offset] = imbuf::interpolate_nearest_border_byte(ibuf, xt, yt);
1519 }
1520 break;
1521 case 1:
1522 if (dst_fl) {
1523 dst_fl[offset] = imbuf::interpolate_bilinear_border_fl(ibuf, xt, yt);
1524 }
1525 else {
1526 dst_ch[offset] = imbuf::interpolate_bilinear_border_byte(ibuf, xt, yt);
1527 }
1528 break;
1529 case 2:
1530 if (dst_fl) {
1531 dst_fl[offset] = imbuf::interpolate_cubic_bspline_fl(ibuf, xt, yt);
1532 }
1533 else {
1534 dst_ch[offset] = imbuf::interpolate_cubic_bspline_byte(ibuf, xt, yt);
1535 }
1536 break;
1537 }
1538 offset++;
1539 }
1540 }
1541}
1542
1543static void do_transform_effect(const SeqRenderData *context,
1544 Sequence *seq,
1545 float /*timeline_frame*/,
1546 float /*fac*/,
1547 const ImBuf *ibuf1,
1548 const ImBuf * /*ibuf2*/,
1549 int start_line,
1550 int total_lines,
1551 ImBuf *out)
1552{
1553 TransformVars *transform = (TransformVars *)seq->effectdata;
1554 float scale_x, scale_y, translate_x, translate_y, rotate_radians;
1555
1556 /* Scale */
1557 if (transform->uniform_scale) {
1558 scale_x = scale_y = transform->ScalexIni;
1559 }
1560 else {
1561 scale_x = transform->ScalexIni;
1562 scale_y = transform->ScaleyIni;
1563 }
1564
1565 int x = context->rectx;
1566 int y = context->recty;
1567
1568 /* Translate */
1569 if (!transform->percent) {
1570 /* Compensate text size for preview render size. */
1571 double proxy_size_comp = context->scene->r.size / 100.0;
1572 if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
1573 proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size);
1574 }
1575
1576 translate_x = transform->xIni * proxy_size_comp + (x / 2.0f);
1577 translate_y = transform->yIni * proxy_size_comp + (y / 2.0f);
1578 }
1579 else {
1580 translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f);
1581 translate_y = y * (transform->yIni / 100.0f) + (y / 2.0f);
1582 }
1583
1584 /* Rotate */
1585 rotate_radians = DEG2RADF(transform->rotIni);
1586
1588 y,
1589 start_line,
1590 total_lines,
1591 ibuf1,
1592 out,
1593 scale_x,
1594 scale_y,
1595 translate_x,
1596 translate_y,
1597 rotate_radians,
1598 transform->interpolation);
1599}
1600
1603/* -------------------------------------------------------------------- */
1608 const float4 *src, float4 *map, int width, int height, float blur, int quality)
1609{
1610 using namespace blender;
1611
1612 /* If we're not really blurring, bail out */
1613 if (blur <= 0) {
1614 return;
1615 }
1616
1617 /* If result would be no blurring, early out. */
1618 const int halfWidth = ((quality + 1) * blur);
1619 if (halfWidth == 0) {
1620 return;
1621 }
1622
1623 Array<float4> temp(width * height);
1624
1625 /* Initialize the gaussian filter. @TODO: use code from RE_filter_value */
1626 Array<float> filter(halfWidth * 2);
1627 const float k = -1.0f / (2.0f * float(M_PI) * blur * blur);
1628 float weight = 0;
1629 for (int ix = 0; ix < halfWidth; ix++) {
1630 weight = float(exp(k * (ix * ix)));
1631 filter[halfWidth - ix] = weight;
1632 filter[halfWidth + ix] = weight;
1633 }
1634 filter[0] = weight;
1635 /* Normalize the array */
1636 float fval = 0;
1637 for (int ix = 0; ix < halfWidth * 2; ix++) {
1638 fval += filter[ix];
1639 }
1640 for (int ix = 0; ix < halfWidth * 2; ix++) {
1641 filter[ix] /= fval;
1642 }
1643
1644 /* Blur the rows: read map, write temp */
1645 threading::parallel_for(IndexRange(height), 32, [&](const IndexRange y_range) {
1646 for (const int y : y_range) {
1647 for (int x = 0; x < width; x++) {
1648 float4 curColor = float4(0.0f);
1649 int xmin = math::max(x - halfWidth, 0);
1650 int xmax = math::min(x + halfWidth, width);
1651 for (int nx = xmin, index = (xmin - x) + halfWidth; nx < xmax; nx++, index++) {
1652 curColor += map[nx + y * width] * filter[index];
1653 }
1654 temp[x + y * width] = curColor;
1655 }
1656 }
1657 });
1658
1659 /* Blur the columns: read temp, write map */
1660 threading::parallel_for(IndexRange(width), 32, [&](const IndexRange x_range) {
1661 const float4 one = float4(1.0f);
1662 for (const int x : x_range) {
1663 for (int y = 0; y < height; y++) {
1664 float4 curColor = float4(0.0f);
1665 int ymin = math::max(y - halfWidth, 0);
1666 int ymax = math::min(y + halfWidth, height);
1667 for (int ny = ymin, index = (ymin - y) + halfWidth; ny < ymax; ny++, index++) {
1668 curColor += temp[x + ny * width] * filter[index];
1669 }
1670 if (src != nullptr) {
1671 curColor = math::min(one, src[x + y * width] + curColor);
1672 }
1673 map[x + y * width] = curColor;
1674 }
1675 }
1676 });
1677}
1678
1679static void blur_isolate_highlights(const float4 *in,
1680 float4 *out,
1681 int width,
1682 int height,
1683 float threshold,
1684 float boost,
1685 float clamp)
1686{
1687 using namespace blender;
1688 threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
1689 const float4 clampv = float4(clamp);
1690 for (const int y : y_range) {
1691 int index = y * width;
1692 for (int x = 0; x < width; x++, index++) {
1693
1694 /* Isolate the intensity */
1695 float intensity = (in[index].x + in[index].y + in[index].z - threshold);
1696 float4 val;
1697 if (intensity > 0) {
1698 val = math::min(clampv, in[index] * (boost * intensity));
1699 }
1700 else {
1701 val = float4(0.0f);
1702 }
1703 out[index] = val;
1704 }
1705 }
1706 });
1707}
1708
1710{
1711 GlowVars *glow;
1712
1713 if (seq->effectdata) {
1714 MEM_freeN(seq->effectdata);
1715 }
1716
1717 seq->effectdata = MEM_callocN(sizeof(GlowVars), "glowvars");
1718
1719 glow = (GlowVars *)seq->effectdata;
1720 glow->fMini = 0.25;
1721 glow->fClamp = 1.0;
1722 glow->fBoost = 0.5;
1723 glow->dDist = 3.0;
1724 glow->dQuality = 3;
1725 glow->bNoComp = 0;
1726}
1727
1729{
1730 return 1;
1731}
1732
1733static void free_glow_effect(Sequence *seq, const bool /*do_id_user*/)
1734{
1736}
1737
1738static void copy_glow_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
1739{
1740 dst->effectdata = MEM_dupallocN(src->effectdata);
1741}
1742
1744 int render_size,
1745 float fac,
1746 int x,
1747 int y,
1748 uchar *rect1,
1749 uchar * /*rect2*/,
1750 uchar *out)
1751{
1752 using namespace blender;
1753 GlowVars *glow = (GlowVars *)seq->effectdata;
1754
1755 Array<float4> inbuf(x * y);
1756 Array<float4> outbuf(x * y);
1757
1758 using namespace blender;
1759 IMB_colormanagement_transform_from_byte_threaded(*inbuf.data(), rect1, x, y, 4, "sRGB", "sRGB");
1760
1762 inbuf.data(), outbuf.data(), x, y, glow->fMini * 3.0f, glow->fBoost * fac, glow->fClamp);
1763 glow_blur_bitmap(glow->bNoComp ? nullptr : inbuf.data(),
1764 outbuf.data(),
1765 x,
1766 y,
1767 glow->dDist * (render_size / 100.0f),
1768 glow->dQuality);
1769
1770 threading::parallel_for(IndexRange(y), 64, [&](const IndexRange y_range) {
1771 size_t offset = y_range.first() * x;
1772 IMB_buffer_byte_from_float(out + offset * 4,
1773 *(outbuf.data() + offset),
1774 4,
1775 0.0f,
1778 true,
1779 x,
1780 y_range.size(),
1781 x,
1782 x);
1783 });
1784}
1785
1787 int render_size,
1788 float fac,
1789 int x,
1790 int y,
1791 float *rect1,
1792 float * /*rect2*/,
1793 float *out)
1794{
1795 using namespace blender;
1796 float4 *outbuf = reinterpret_cast<float4 *>(out);
1797 float4 *inbuf = reinterpret_cast<float4 *>(rect1);
1798 GlowVars *glow = (GlowVars *)seq->effectdata;
1799
1801 inbuf, outbuf, x, y, glow->fMini * 3.0f, glow->fBoost * fac, glow->fClamp);
1802 glow_blur_bitmap(glow->bNoComp ? nullptr : inbuf,
1803 outbuf,
1804 x,
1805 y,
1806 glow->dDist * (render_size / 100.0f),
1807 glow->dQuality);
1808}
1809
1810static ImBuf *do_glow_effect(const SeqRenderData *context,
1811 Sequence *seq,
1812 float /*timeline_frame*/,
1813 float fac,
1814 ImBuf *ibuf1,
1815 ImBuf *ibuf2)
1816{
1817 ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
1818
1819 int render_size = 100 * context->rectx / context->scene->r.xsch;
1820
1821 if (out->float_buffer.data) {
1823 render_size,
1824 fac,
1825 context->rectx,
1826 context->recty,
1827 ibuf1->float_buffer.data,
1828 nullptr,
1829 out->float_buffer.data);
1830 }
1831 else {
1833 render_size,
1834 fac,
1835 context->rectx,
1836 context->recty,
1837 ibuf1->byte_buffer.data,
1838 nullptr,
1839 out->byte_buffer.data);
1840 }
1841
1842 return out;
1843}
1844
1847/* -------------------------------------------------------------------- */
1852{
1853 SolidColorVars *cv;
1854
1855 if (seq->effectdata) {
1856 MEM_freeN(seq->effectdata);
1857 }
1858
1859 seq->effectdata = MEM_callocN(sizeof(SolidColorVars), "solidcolor");
1860
1861 cv = (SolidColorVars *)seq->effectdata;
1862 cv->col[0] = cv->col[1] = cv->col[2] = 0.5;
1863}
1864
1866{
1867 return 0;
1868}
1869
1870static void free_solid_color(Sequence *seq, const bool /*do_id_user*/)
1871{
1873}
1874
1875static void copy_solid_color(Sequence *dst, const Sequence *src, const int /*flag*/)
1876{
1877 dst->effectdata = MEM_dupallocN(src->effectdata);
1878}
1879
1880static StripEarlyOut early_out_color(const Sequence * /*seq*/, float /*fac*/)
1881{
1883}
1884
1885static ImBuf *do_solid_color(const SeqRenderData *context,
1886 Sequence *seq,
1887 float /*timeline_frame*/,
1888 float /*fac*/,
1889 ImBuf *ibuf1,
1890 ImBuf *ibuf2)
1891{
1892 using namespace blender;
1893 ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
1894
1896
1897 threading::parallel_for(IndexRange(out->y), 64, [&](const IndexRange y_range) {
1898 if (out->byte_buffer.data) {
1899 /* Byte image. */
1900 uchar color[4];
1901 rgb_float_to_uchar(color, cv->col);
1902 color[3] = 255;
1903
1904 uchar *dst = out->byte_buffer.data + y_range.first() * out->x * 4;
1905 uchar *dst_end = dst + y_range.size() * out->x * 4;
1906 while (dst < dst_end) {
1907 memcpy(dst, color, sizeof(color));
1908 dst += 4;
1909 }
1910 }
1911 else {
1912 /* Float image. */
1913 float color[4];
1914 color[0] = cv->col[0];
1915 color[1] = cv->col[1];
1916 color[2] = cv->col[2];
1917 color[3] = 1.0f;
1918
1919 float *dst = out->float_buffer.data + y_range.first() * out->x * 4;
1920 float *dst_end = dst + y_range.size() * out->x * 4;
1921 while (dst < dst_end) {
1922 memcpy(dst, color, sizeof(color));
1923 dst += 4;
1924 }
1925 }
1926 });
1927
1928 out->planes = R_IMF_PLANES_RGB;
1929
1930 return out;
1931}
1932
1935/* -------------------------------------------------------------------- */
1941{
1942 return 0;
1943}
1944
1945static StripEarlyOut early_out_multicam(const Sequence * /*seq*/, float /*fac*/)
1946{
1948}
1949
1950static ImBuf *do_multicam(const SeqRenderData *context,
1951 Sequence *seq,
1952 float timeline_frame,
1953 float /*fac*/,
1954 ImBuf * /*ibuf1*/,
1955 ImBuf * /*ibuf2*/)
1956{
1957 ImBuf *out;
1958 Editing *ed;
1959
1960 if (seq->multicam_source == 0 || seq->multicam_source >= seq->machine) {
1961 return nullptr;
1962 }
1963
1964 ed = context->scene->ed;
1965 if (!ed) {
1966 return nullptr;
1967 }
1968 ListBase *seqbasep = SEQ_get_seqbase_by_seq(context->scene, seq);
1969 ListBase *channels = SEQ_get_channels_by_seq(&ed->seqbase, &ed->channels, seq);
1970 if (!seqbasep) {
1971 return nullptr;
1972 }
1973
1975 context, timeline_frame, seq->multicam_source, channels, seqbasep);
1976
1977 return out;
1978}
1979
1982/* -------------------------------------------------------------------- */
1988{
1989 return 0;
1990}
1991
1992static StripEarlyOut early_out_adjustment(const Sequence * /*seq*/, float /*fac*/)
1993{
1995}
1996
1997static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, float timeline_frame)
1998{
1999 Editing *ed;
2000 ImBuf *i = nullptr;
2001
2002 ed = context->scene->ed;
2003
2004 ListBase *seqbasep = SEQ_get_seqbase_by_seq(context->scene, seq);
2005 ListBase *channels = SEQ_get_channels_by_seq(&ed->seqbase, &ed->channels, seq);
2006
2007 /* Clamp timeline_frame to strip range so it behaves as if it had "still frame" offset (last
2008 * frame is static after end of strip). This is how most strips behave. This way transition
2009 * effects that doesn't overlap or speed effect can't fail rendering outside of strip range. */
2010 timeline_frame = clamp_i(timeline_frame,
2011 SEQ_time_left_handle_frame_get(context->scene, seq),
2012 SEQ_time_right_handle_frame_get(context->scene, seq) - 1);
2013
2014 if (seq->machine > 1) {
2016 context, timeline_frame, seq->machine - 1, channels, seqbasep);
2017 }
2018
2019 /* Found nothing? so let's work the way up the meta-strip stack, so
2020 * that it is possible to group a bunch of adjustment strips into
2021 * a meta-strip and have that work on everything below the meta-strip. */
2022
2023 if (!i) {
2024 Sequence *meta;
2025
2026 meta = SEQ_find_metastrip_by_sequence(&ed->seqbase, nullptr, seq);
2027
2028 if (meta) {
2029 i = do_adjustment_impl(context, meta, timeline_frame);
2030 }
2031 }
2032
2033 return i;
2034}
2035
2036static ImBuf *do_adjustment(const SeqRenderData *context,
2037 Sequence *seq,
2038 float timeline_frame,
2039 float /*fac*/,
2040 ImBuf * /*ibuf1*/,
2041 ImBuf * /*ibuf2*/)
2042{
2043 ImBuf *out;
2044 Editing *ed;
2045
2046 ed = context->scene->ed;
2047
2048 if (!ed) {
2049 return nullptr;
2050 }
2051
2052 out = do_adjustment_impl(context, seq, timeline_frame);
2053
2054 return out;
2055}
2056
2059/* -------------------------------------------------------------------- */
2064{
2066
2067 if (seq->effectdata) {
2068 MEM_freeN(seq->effectdata);
2069 }
2070
2071 seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars");
2072
2073 v = (SpeedControlVars *)seq->effectdata;
2074 v->speed_control_type = SEQ_SPEED_STRETCH;
2075 v->speed_fader = 1.0f;
2076 v->speed_fader_length = 0.0f;
2077 v->speed_fader_frame_number = 0.0f;
2078}
2079
2081{
2083 v->frameMap = nullptr;
2084}
2085
2087{
2088 return 1;
2089}
2090
2091static void free_speed_effect(Sequence *seq, const bool /*do_id_user*/)
2092{
2094 if (v->frameMap) {
2095 MEM_freeN(v->frameMap);
2096 }
2098}
2099
2100static void copy_speed_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
2101{
2103 dst->effectdata = MEM_dupallocN(src->effectdata);
2104 v = (SpeedControlVars *)dst->effectdata;
2105 v->frameMap = nullptr;
2106}
2107
2108static StripEarlyOut early_out_speed(const Sequence * /*seq*/, float /*fac*/)
2109{
2111}
2112
2114{
2115 return id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, nullptr);
2116}
2117
2119{
2120 const int effect_strip_length = SEQ_time_right_handle_frame_get(scene, seq) -
2122
2123 if ((seq->seq1 == nullptr) || (effect_strip_length < 1)) {
2124 return; /* Make COVERITY happy and check for (CID 598) input strip. */
2125 }
2126
2127 const FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq);
2128 if (fcu == nullptr) {
2129 return;
2130 }
2131
2133 if (v->frameMap) {
2134 MEM_freeN(v->frameMap);
2135 }
2136
2137 v->frameMap = static_cast<float *>(MEM_mallocN(sizeof(float) * effect_strip_length, __func__));
2138 v->frameMap[0] = 0.0f;
2139
2140 float target_frame = 0;
2141 for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) {
2142 target_frame += evaluate_fcurve(fcu, SEQ_time_left_handle_frame_get(scene, seq) + frame_index);
2143 const int target_frame_max = SEQ_time_strip_length_get(scene, seq->seq1);
2144 CLAMP(target_frame, 0, target_frame_max);
2145 v->frameMap[frame_index] = target_frame;
2146 }
2147}
2148
2150{
2152 if (v->frameMap != nullptr) {
2153 return;
2154 }
2155
2156 seq_effect_speed_rebuild_map(scene, seq);
2157}
2158
2160 Sequence *seq_speed,
2161 float timeline_frame,
2162 int input)
2163{
2164 if (seq_speed->seq1 == nullptr) {
2165 return 0.0f;
2166 }
2167
2168 SEQ_effect_handle_get(seq_speed); /* Ensure, that data are initialized. */
2169 int frame_index = round_fl_to_int(SEQ_give_frame_index(scene, seq_speed, timeline_frame));
2170 SpeedControlVars *s = (SpeedControlVars *)seq_speed->effectdata;
2171 const Sequence *source = seq_speed->seq1;
2172
2173 float target_frame = 0.0f;
2174 switch (s->speed_control_type) {
2175 case SEQ_SPEED_STRETCH: {
2176 /* Only right handle controls effect speed! */
2177 const float target_content_length = SEQ_time_strip_length_get(scene, source) -
2178 source->startofs;
2179 const float speed_effetct_length = SEQ_time_right_handle_frame_get(scene, seq_speed) -
2180 SEQ_time_left_handle_frame_get(scene, seq_speed);
2181 const float ratio = frame_index / speed_effetct_length;
2182 target_frame = target_content_length * ratio;
2183 break;
2184 }
2185 case SEQ_SPEED_MULTIPLY: {
2186 const FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq_speed);
2187 if (fcu != nullptr) {
2188 seq_effect_speed_frame_map_ensure(scene, seq_speed);
2189 target_frame = s->frameMap[frame_index];
2190 }
2191 else {
2192 target_frame = frame_index * s->speed_fader;
2193 }
2194 break;
2195 }
2196 case SEQ_SPEED_LENGTH:
2197 target_frame = SEQ_time_strip_length_get(scene, source) * (s->speed_fader_length / 100.0f);
2198 break;
2200 target_frame = s->speed_fader_frame_number;
2201 break;
2202 }
2203
2204 CLAMP(target_frame, 0, SEQ_time_strip_length_get(scene, source));
2205 target_frame += seq_speed->start;
2206
2207 /* No interpolation. */
2208 if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) {
2209 return target_frame;
2210 }
2211
2212 /* Interpolation is used, switch between current and next frame based on which input is
2213 * requested. */
2214 return input == 0 ? target_frame : ceil(target_frame);
2215}
2216
2218 Sequence *seq_speed,
2219 float timeline_frame)
2220{
2221 const float target_frame = seq_speed_effect_target_frame_get(
2222 scene, seq_speed, timeline_frame, 0);
2223 return target_frame - floor(target_frame);
2224}
2225
2226static ImBuf *do_speed_effect(const SeqRenderData *context,
2227 Sequence *seq,
2228 float timeline_frame,
2229 float fac,
2230 ImBuf *ibuf1,
2231 ImBuf *ibuf2)
2232{
2233 const SpeedControlVars *s = (SpeedControlVars *)seq->effectdata;
2235 ImBuf *out;
2236
2237 if (s->flags & SEQ_SPEED_USE_INTERPOLATION) {
2238 fac = speed_effect_interpolation_ratio_get(context->scene, seq, timeline_frame);
2239 /* Current frame is ibuf1, next frame is ibuf2. */
2241 &cross_effect, context, nullptr, timeline_frame, fac, ibuf1, ibuf2);
2242 return out;
2243 }
2244
2245 /* No interpolation. */
2246 return IMB_dupImBuf(ibuf1);
2247}
2248
2251/* -------------------------------------------------------------------- */
2255static void do_overdrop_effect(const SeqRenderData *context,
2256 Sequence * /*seq*/,
2257 float /*timeline_frame*/,
2258 float fac,
2259 const ImBuf *ibuf1,
2260 const ImBuf *ibuf2,
2261 int start_line,
2262 int total_lines,
2263 ImBuf *out)
2264{
2265 int x = context->rectx;
2266 int y = total_lines;
2267
2268 if (out->float_buffer.data) {
2269 float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
2270
2271 slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
2272
2273 do_drop_effect_float(fac, x, y, rect1, rect2, rect_out);
2274 do_alphaover_effect(fac, x, y, rect1, rect2, rect_out);
2275 }
2276 else {
2277 uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
2278
2279 slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
2280
2281 do_drop_effect_byte(fac, x, y, rect1, rect2, rect_out);
2282 do_alphaover_effect(fac, x, y, rect1, rect2, rect_out);
2283 }
2284}
2285
2288/* -------------------------------------------------------------------- */
2293{
2294 if (seq->effectdata) {
2295 MEM_freeN(seq->effectdata);
2296 }
2297
2298 seq->effectdata = MEM_callocN(sizeof(WipeVars), "wipevars");
2299}
2300
2302{
2303 return 1;
2304}
2305
2306static void free_gaussian_blur_effect(Sequence *seq, const bool /*do_id_user*/)
2307{
2309}
2310
2311static void copy_gaussian_blur_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
2312{
2313 dst->effectdata = MEM_dupallocN(src->effectdata);
2314}
2315
2316static StripEarlyOut early_out_gaussian_blur(const Sequence *seq, float /*fac*/)
2317{
2318 GaussianBlurVars *data = static_cast<GaussianBlurVars *>(seq->effectdata);
2319 if (data->size_x == 0.0f && data->size_y == 0) {
2321 }
2323}
2324
2325static Array<float> make_gaussian_blur_kernel(float rad, int size)
2326{
2327 int n = 2 * size + 1;
2328 Array<float> gaussian(n);
2329
2330 float sum = 0.0f;
2331 float fac = (rad > 0.0f ? 1.0f / rad : 0.0f);
2332 for (int i = -size; i <= size; i++) {
2333 float val = RE_filter_value(R_FILTER_GAUSS, float(i) * fac);
2334 sum += val;
2335 gaussian[i + size] = val;
2336 }
2337
2338 float inv_sum = 1.0f / sum;
2339 for (int i = 0; i < n; i++) {
2340 gaussian[i] *= inv_sum;
2341 }
2342
2343 return gaussian;
2344}
2345
2346template<typename T>
2347static void gaussian_blur_x(const Span<float> gaussian,
2348 int half_size,
2349 int start_line,
2350 int width,
2351 int height,
2352 int /*frame_height*/,
2353 const T *rect,
2354 T *dst)
2355{
2356 dst += int64_t(start_line) * width * 4;
2357 for (int y = start_line; y < start_line + height; y++) {
2358 for (int x = 0; x < width; x++) {
2359 float4 accum(0.0f);
2360 float accum_weight = 0.0f;
2361
2362 int xmin = math::max(x - half_size, 0);
2363 int xmax = math::min(x + half_size, width - 1);
2364 for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
2365 float weight = gaussian[index];
2366 int offset = (y * width + nx) * 4;
2367 accum += float4(rect + offset) * weight;
2368 accum_weight += weight;
2369 }
2370 accum *= (1.0f / accum_weight);
2371 dst[0] = accum[0];
2372 dst[1] = accum[1];
2373 dst[2] = accum[2];
2374 dst[3] = accum[3];
2375 dst += 4;
2376 }
2377 }
2378}
2379
2380template<typename T>
2381static void gaussian_blur_y(const Span<float> gaussian,
2382 int half_size,
2383 int start_line,
2384 int width,
2385 int height,
2386 int frame_height,
2387 const T *rect,
2388 T *dst)
2389{
2390 dst += int64_t(start_line) * width * 4;
2391 for (int y = start_line; y < start_line + height; y++) {
2392 for (int x = 0; x < width; x++) {
2393 float4 accum(0.0f);
2394 float accum_weight = 0.0f;
2395 int ymin = math::max(y - half_size, 0);
2396 int ymax = math::min(y + half_size, frame_height - 1);
2397 for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
2398 float weight = gaussian[index];
2399 int offset = (ny * width + x) * 4;
2400 accum += float4(rect + offset) * weight;
2401 accum_weight += weight;
2402 }
2403 accum *= (1.0f / accum_weight);
2404 dst[0] = accum[0];
2405 dst[1] = accum[1];
2406 dst[2] = accum[2];
2407 dst[3] = accum[3];
2408 dst += 4;
2409 }
2410 }
2411}
2412
2414 Sequence *seq,
2415 float /*timeline_frame*/,
2416 float /*fac*/,
2417 ImBuf *ibuf1,
2418 ImBuf * /*ibuf2*/)
2419{
2420 using namespace blender;
2421
2422 /* Create blur kernel weights. */
2423 const GaussianBlurVars *data = static_cast<const GaussianBlurVars *>(seq->effectdata);
2424 const int half_size_x = int(data->size_x + 0.5f);
2425 const int half_size_y = int(data->size_y + 0.5f);
2426 Array<float> gaussian_x = make_gaussian_blur_kernel(data->size_x, half_size_x);
2427 Array<float> gaussian_y = make_gaussian_blur_kernel(data->size_y, half_size_y);
2428
2429 const int width = context->rectx;
2430 const int height = context->recty;
2431 const bool is_float = ibuf1->float_buffer.data;
2432
2433 /* Horizontal blur: create output, blur ibuf1 into it. */
2434 ImBuf *out = prepare_effect_imbufs(context, ibuf1, nullptr);
2435 threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
2436 const int y_first = y_range.first();
2437 const int y_size = y_range.size();
2438 if (is_float) {
2439 gaussian_blur_x(gaussian_x,
2440 half_size_x,
2441 y_first,
2442 width,
2443 y_size,
2444 height,
2445 ibuf1->float_buffer.data,
2446 out->float_buffer.data);
2447 }
2448 else {
2449 gaussian_blur_x(gaussian_x,
2450 half_size_x,
2451 y_first,
2452 width,
2453 y_size,
2454 height,
2455 ibuf1->byte_buffer.data,
2456 out->byte_buffer.data);
2457 }
2458 });
2459
2460 /* Vertical blur: create output, blur previous output into it. */
2461 ibuf1 = out;
2462 out = prepare_effect_imbufs(context, ibuf1, nullptr);
2463 threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
2464 const int y_first = y_range.first();
2465 const int y_size = y_range.size();
2466 if (is_float) {
2467 gaussian_blur_y(gaussian_y,
2468 half_size_y,
2469 y_first,
2470 width,
2471 y_size,
2472 height,
2473 ibuf1->float_buffer.data,
2474 out->float_buffer.data);
2475 }
2476 else {
2477 gaussian_blur_y(gaussian_y,
2478 half_size_y,
2479 y_first,
2480 width,
2481 y_size,
2482 height,
2483 ibuf1->byte_buffer.data,
2484 out->byte_buffer.data);
2485 }
2486 });
2487
2488 /* Free the first output. */
2489 IMB_freeImBuf(ibuf1);
2490
2491 return out;
2492}
2493
2496/* -------------------------------------------------------------------- */
2501{
2502 TextVars *data;
2503
2504 if (seq->effectdata) {
2505 MEM_freeN(seq->effectdata);
2506 }
2507
2508 data = static_cast<TextVars *>(seq->effectdata = MEM_callocN(sizeof(TextVars), "textvars"));
2509 data->text_font = nullptr;
2510 data->text_blf_id = -1;
2511 data->text_size = 60.0f;
2512
2513 copy_v4_fl(data->color, 1.0f);
2514 data->shadow_color[3] = 0.7f;
2515 data->shadow_angle = DEG2RADF(65.0f);
2516 data->shadow_offset = 0.04f;
2517 data->shadow_blur = 0.0f;
2518 data->box_color[0] = 0.2f;
2519 data->box_color[1] = 0.2f;
2520 data->box_color[2] = 0.2f;
2521 data->box_color[3] = 0.7f;
2522 data->box_margin = 0.01f;
2523 data->outline_color[3] = 0.7f;
2524 data->outline_width = 0.05f;
2525
2526 STRNCPY(data->text, "Text");
2527
2528 data->loc[0] = 0.5f;
2529 data->loc[1] = 0.5f;
2530 data->align = SEQ_TEXT_ALIGN_X_CENTER;
2531 data->align_y = SEQ_TEXT_ALIGN_Y_CENTER;
2532 data->wrap_width = 1.0f;
2533}
2534
2535void SEQ_effect_text_font_unload(TextVars *data, const bool do_id_user)
2536{
2537 if (data == nullptr) {
2538 return;
2539 }
2540
2541 /* Unlink the VFont */
2542 if (do_id_user && data->text_font != nullptr) {
2543 id_us_min(&data->text_font->id);
2544 data->text_font = nullptr;
2545 }
2546
2547 /* Unload the BLF font. */
2548 if (data->text_blf_id >= 0) {
2549 BLF_unload_id(data->text_blf_id);
2550 }
2551}
2552
2553void SEQ_effect_text_font_load(TextVars *data, const bool do_id_user)
2554{
2555 VFont *vfont = data->text_font;
2556 if (vfont == nullptr) {
2557 return;
2558 }
2559
2560 if (do_id_user) {
2561 id_us_plus(&vfont->id);
2562 }
2563
2564 if (vfont->packedfile != nullptr) {
2565 PackedFile *pf = vfont->packedfile;
2566 /* Create a name that's unique between library data-blocks to avoid loading
2567 * a font per strip which will load fonts many times.
2568 *
2569 * WARNING: this isn't fool proof!
2570 * The #VFont may be renamed which will cause this to load multiple times,
2571 * in practice this isn't so likely though. */
2572 char name[MAX_ID_FULL_NAME];
2573 BKE_id_full_name_get(name, &vfont->id, 0);
2574
2575 data->text_blf_id = BLF_load_mem(name, static_cast<const uchar *>(pf->data), pf->size);
2576 }
2577 else {
2578 char filepath[FILE_MAX];
2579 STRNCPY(filepath, vfont->filepath);
2580 if (BLI_thread_is_main()) {
2581 /* FIXME: This is a band-aid fix.
2582 * A proper solution has to be worked on by the sequencer team.
2583 *
2584 * This code can be called from non-main thread, e.g. when copying sequences as part of
2585 * depsgraph evaluated copy of the evaluated scene. Just skip font loading in that case, BLF
2586 * code is not thread-safe, and if this happens from threaded context, it almost certainly
2587 * means that a previous attempt to load the font already failed, e.g. because font file-path
2588 * is invalid. Proposer fix would likely be to not attempt to reload a failed-to-load font
2589 * every time. */
2590 BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
2591
2592 data->text_blf_id = BLF_load(filepath);
2593 }
2594 }
2595}
2596
2597static void free_text_effect(Sequence *seq, const bool do_id_user)
2598{
2599 TextVars *data = static_cast<TextVars *>(seq->effectdata);
2600 SEQ_effect_text_font_unload(data, do_id_user);
2601
2602 if (data) {
2603 MEM_freeN(data);
2604 seq->effectdata = nullptr;
2605 }
2606}
2607
2609{
2610 TextVars *data = static_cast<TextVars *>(seq->effectdata);
2611 SEQ_effect_text_font_load(data, false);
2612}
2613
2614static void copy_text_effect(Sequence *dst, const Sequence *src, const int flag)
2615{
2616 dst->effectdata = MEM_dupallocN(src->effectdata);
2617 TextVars *data = static_cast<TextVars *>(dst->effectdata);
2618
2619 data->text_blf_id = -1;
2621}
2622
2624{
2625 return 0;
2626}
2627
2628static StripEarlyOut early_out_text(const Sequence *seq, float /*fac*/)
2629{
2630 TextVars *data = static_cast<TextVars *>(seq->effectdata);
2631 if (data->text[0] == 0 || data->text_size < 1.0f ||
2632 ((data->color[3] == 0.0f) &&
2633 (data->shadow_color[3] == 0.0f || (data->flag & SEQ_TEXT_SHADOW) == 0) &&
2634 (data->outline_color[3] == 0.0f || data->outline_width <= 0.0f ||
2635 (data->flag & SEQ_TEXT_OUTLINE) == 0)))
2636 {
2638 }
2640}
2641
2642/* Simplified version of gaussian blur specifically for text shadow blurring:
2643 * - Data is only the alpha channel,
2644 * - Skips blur outside of shadow rectangle. */
2645static void text_gaussian_blur_x(const Span<float> gaussian,
2646 int half_size,
2647 int start_line,
2648 int width,
2649 int height,
2650 const uchar *rect,
2651 uchar *dst,
2652 const rcti &shadow_rect)
2653{
2654 dst += int64_t(start_line) * width;
2655 for (int y = start_line; y < start_line + height; y++) {
2656 for (int x = 0; x < width; x++) {
2657 float accum(0.0f);
2658 if (x >= shadow_rect.xmin && x <= shadow_rect.xmax) {
2659 float accum_weight = 0.0f;
2660 int xmin = math::max(x - half_size, shadow_rect.xmin);
2661 int xmax = math::min(x + half_size, shadow_rect.xmax);
2662 for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
2663 float weight = gaussian[index];
2664 int offset = y * width + nx;
2665 accum += rect[offset] * weight;
2666 accum_weight += weight;
2667 }
2668 accum *= (1.0f / accum_weight);
2669 }
2670
2671 *dst = accum;
2672 dst++;
2673 }
2674 }
2675}
2676
2677static void text_gaussian_blur_y(const Span<float> gaussian,
2678 int half_size,
2679 int start_line,
2680 int width,
2681 int height,
2682 const uchar *rect,
2683 uchar *dst,
2684 const rcti &shadow_rect)
2685{
2686 dst += int64_t(start_line) * width;
2687 for (int y = start_line; y < start_line + height; y++) {
2688 for (int x = 0; x < width; x++) {
2689 float accum(0.0f);
2690 if (x >= shadow_rect.xmin && x <= shadow_rect.xmax) {
2691 float accum_weight = 0.0f;
2692 int ymin = math::max(y - half_size, shadow_rect.ymin);
2693 int ymax = math::min(y + half_size, shadow_rect.ymax);
2694 for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
2695 float weight = gaussian[index];
2696 int offset = ny * width + x;
2697 accum += rect[offset] * weight;
2698 accum_weight += weight;
2699 }
2700 accum *= (1.0f / accum_weight);
2701 }
2702 *dst = accum;
2703 dst++;
2704 }
2705 }
2706}
2707
2708static void clamp_rect(int width, int height, rcti &r_rect)
2709{
2710 r_rect.xmin = math::clamp(r_rect.xmin, 0, width - 1);
2711 r_rect.xmax = math::clamp(r_rect.xmax, 0, width - 1);
2712 r_rect.ymin = math::clamp(r_rect.ymin, 0, height - 1);
2713 r_rect.ymax = math::clamp(r_rect.ymax, 0, height - 1);
2714}
2715
2716static void initialize_shadow_alpha(int width,
2717 int height,
2718 int2 offset,
2719 const rcti &shadow_rect,
2720 const uchar *input,
2721 Array<uchar> &r_shadow_mask)
2722{
2723 const IndexRange shadow_y_range(shadow_rect.ymin, shadow_rect.ymax - shadow_rect.ymin + 1);
2724 threading::parallel_for(shadow_y_range, 8, [&](const IndexRange y_range) {
2725 for (const int64_t y : y_range) {
2726 const int64_t src_y = math::clamp<int64_t>(y + offset.y, 0, height - 1);
2727 for (int x = shadow_rect.xmin; x <= shadow_rect.xmax; x++) {
2728 int src_x = math::clamp(x - offset.x, 0, width - 1);
2729 size_t src_offset = width * src_y + src_x;
2730 size_t dst_offset = width * y + x;
2731 r_shadow_mask[dst_offset] = input[src_offset * 4 + 3];
2732 }
2733 }
2734 });
2735}
2736
2737static void composite_shadow(int width,
2738 const rcti &shadow_rect,
2739 const float4 &shadow_color,
2740 const Array<uchar> &shadow_mask,
2741 uchar *output)
2742{
2743 const IndexRange shadow_y_range(shadow_rect.ymin, shadow_rect.ymax - shadow_rect.ymin + 1);
2744 threading::parallel_for(shadow_y_range, 8, [&](const IndexRange y_range) {
2745 for (const int64_t y : y_range) {
2746 size_t offset = y * width + shadow_rect.xmin;
2747 uchar *dst = output + offset * 4;
2748 for (int x = shadow_rect.xmin; x <= shadow_rect.xmax; x++, offset++, dst += 4) {
2749 uchar a = shadow_mask[offset];
2750 if (a == 0) {
2751 /* Fully transparent, leave output pixel as is. */
2752 continue;
2753 }
2754 float4 col1 = load_premul_pixel(dst);
2755 float4 col2 = shadow_color * (a * (1.0f / 255.0f));
2756 /* Blend under the output. */
2757 float fac = 1.0f - col1.w;
2758 float4 col = col1 + fac * col2;
2759 store_premul_pixel(col, dst);
2760 }
2761 }
2762 });
2763}
2764
2765static void draw_text_shadow(const SeqRenderData *context,
2766 const TextVars *data,
2767 int line_height,
2768 const rcti &rect,
2769 ImBuf *out)
2770{
2771 const int width = context->rectx;
2772 const int height = context->recty;
2773 /* Blur value of 1.0 applies blur kernel that is half of text line height. */
2774 const float blur_amount = line_height * 0.5f * data->shadow_blur;
2775 bool do_blur = blur_amount >= 1.0f;
2776
2777 Array<uchar> shadow_mask(size_t(width) * height, 0);
2778
2779 const int2 offset = int2(cosf(data->shadow_angle) * line_height * data->shadow_offset,
2780 sinf(data->shadow_angle) * line_height * data->shadow_offset);
2781
2782 rcti shadow_rect = rect;
2783 BLI_rcti_translate(&shadow_rect, offset.x, -offset.y);
2784 BLI_rcti_pad(&shadow_rect, 1, 1);
2785 clamp_rect(width, height, shadow_rect);
2786
2787 /* Initialize shadow by copying existing text/outline alpha. */
2788 initialize_shadow_alpha(width, height, offset, shadow_rect, out->byte_buffer.data, shadow_mask);
2789
2790 if (do_blur) {
2791 /* Create blur kernel weights. */
2792 const int half_size = int(blur_amount + 0.5f);
2793 Array<float> gaussian = make_gaussian_blur_kernel(blur_amount, half_size);
2794
2795 BLI_rcti_pad(&shadow_rect, half_size + 1, half_size + 1);
2796 clamp_rect(width, height, shadow_rect);
2797
2798 /* Horizontal blur: blur shadow_mask into blur_buffer. */
2799 Array<uchar> blur_buffer(size_t(width) * height, NoInitialization());
2800 IndexRange blur_y_range(shadow_rect.ymin, shadow_rect.ymax - shadow_rect.ymin + 1);
2801 threading::parallel_for(blur_y_range, 8, [&](const IndexRange y_range) {
2802 const int y_first = y_range.first();
2803 const int y_size = y_range.size();
2804 text_gaussian_blur_x(gaussian,
2805 half_size,
2806 y_first,
2807 width,
2808 y_size,
2809 shadow_mask.data(),
2810 blur_buffer.data(),
2811 shadow_rect);
2812 });
2813
2814 /* Vertical blur: blur blur_buffer into shadow_mask. */
2815 threading::parallel_for(blur_y_range, 8, [&](const IndexRange y_range) {
2816 const int y_first = y_range.first();
2817 const int y_size = y_range.size();
2818 text_gaussian_blur_y(gaussian,
2819 half_size,
2820 y_first,
2821 width,
2822 y_size,
2823 blur_buffer.data(),
2824 shadow_mask.data(),
2825 shadow_rect);
2826 });
2827 }
2828
2829 /* Composite shadow under regular output. */
2830 float4 color = data->shadow_color;
2831 color.x *= color.w;
2832 color.y *= color.w;
2833 color.z *= color.w;
2834 composite_shadow(width, shadow_rect, color, shadow_mask, out->byte_buffer.data);
2835}
2836
2837/* Text outline calculation is done by Jump Flooding Algorithm (JFA).
2838 * This is similar to inpaint/jump_flooding in Compositor, also to
2839 * "The Quest for Very Wide Outlines", Ben Golus 2020
2840 * https://bgolus.medium.com/the-quest-for-very-wide-outlines-ba82ed442cd9 */
2841
2842constexpr uint16_t JFA_INVALID = 0xFFFF;
2843
2848
2850 MutableSpan<JFACoord> output,
2851 int2 size,
2852 IndexRange x_range,
2853 IndexRange y_range,
2854 int step_size)
2855{
2856 threading::parallel_for(y_range, 8, [&](const IndexRange sub_y_range) {
2857 for (const int64_t y : sub_y_range) {
2858 size_t index = y * size.x;
2859 for (const int64_t x : x_range) {
2860 float2 coord = float2(x, y);
2861
2862 /* For each pixel, sample 9 pixels at +/- step size pattern,
2863 * and output coordinate of closest to the boundary. */
2864 JFACoord closest_texel{JFA_INVALID, JFA_INVALID};
2865 float minimum_squared_distance = std::numeric_limits<float>::max();
2866 for (int dy = -step_size; dy <= step_size; dy += step_size) {
2867 int yy = y + dy;
2868 if (yy < 0 || yy >= size.y) {
2869 continue;
2870 }
2871 for (int dx = -step_size; dx <= step_size; dx += step_size) {
2872 int xx = x + dx;
2873 if (xx < 0 || xx >= size.x) {
2874 continue;
2875 }
2876 JFACoord val = input[size_t(yy) * size.x + xx];
2877 if (val.x == JFA_INVALID) {
2878 continue;
2879 }
2880
2881 float squared_distance = math::distance_squared(float2(val.x, val.y), coord);
2882 if (squared_distance < minimum_squared_distance) {
2883 minimum_squared_distance = squared_distance;
2884 closest_texel = val;
2885 }
2886 }
2887 }
2888
2889 output[index + x] = closest_texel;
2890 }
2891 }
2892 });
2893}
2894
2896 const TextVars *data,
2897 int font,
2898 ColorManagedDisplay *display,
2899 int x,
2900 int y,
2901 int line_height,
2902 const rcti &rect,
2903 ImBuf *out)
2904{
2905 /* Outline width of 1.0 maps to half of text line height. */
2906 const int outline_width = int(line_height * 0.5f * data->outline_width);
2907 if (outline_width < 1 || data->outline_color[3] <= 0.0f) {
2908 return rect;
2909 }
2910
2911 const int2 size = int2(context->rectx, context->recty);
2912
2913 /* Draw white text into temporary buffer. */
2914 const size_t pixel_count = size_t(size.x) * size.y;
2915 Array<uchar4> tmp_buf(pixel_count, uchar4(0));
2916 BLF_buffer(font, nullptr, (uchar *)tmp_buf.data(), size.x, size.y, display);
2917 BLF_position(font, x, y, 0.0f);
2918 BLF_buffer_col(font, float4(1.0f));
2919 BLF_draw_buffer(font, data->text, sizeof(data->text));
2920
2921 rcti outline_rect = rect;
2922 BLI_rcti_pad(&outline_rect, outline_width + 1, outline_width + 1);
2923 outline_rect.xmin = clamp_i(outline_rect.xmin, 0, size.x - 1);
2924 outline_rect.xmax = clamp_i(outline_rect.xmax, 0, size.x - 1);
2925 outline_rect.ymin = clamp_i(outline_rect.ymin, 0, size.y - 1);
2926 outline_rect.ymax = clamp_i(outline_rect.ymax, 0, size.y - 1);
2927 const IndexRange rect_x_range(outline_rect.xmin, outline_rect.xmax - outline_rect.xmin + 1);
2928 const IndexRange rect_y_range(outline_rect.ymin, outline_rect.ymax - outline_rect.ymin + 1);
2929
2930 /* Initialize JFA: invalid values for empty regions, pixel coordinates
2931 * for opaque regions. */
2932 Array<JFACoord> boundary(pixel_count, NoInitialization());
2933 threading::parallel_for(IndexRange(size.y), 16, [&](const IndexRange y_range) {
2934 for (const int y : y_range) {
2935 size_t index = size_t(y) * size.x;
2936 for (int x = 0; x < size.x; x++, index++) {
2937 bool is_opaque = tmp_buf[index].w >= 128;
2938 JFACoord coord;
2939 coord.x = is_opaque ? x : JFA_INVALID;
2940 coord.y = is_opaque ? y : JFA_INVALID;
2941 boundary[index] = coord;
2942 }
2943 }
2944 });
2945
2946 /* Do jump flooding calculations. */
2947 JFACoord invalid_coord{JFA_INVALID, JFA_INVALID};
2948 Array<JFACoord> initial_flooded_result(pixel_count, invalid_coord);
2949 jump_flooding_pass(boundary, initial_flooded_result, size, rect_x_range, rect_y_range, 1);
2950
2951 Array<JFACoord> *result_to_flood = &initial_flooded_result;
2952 Array<JFACoord> intermediate_result(pixel_count, invalid_coord);
2953 Array<JFACoord> *result_after_flooding = &intermediate_result;
2954
2955 int step_size = power_of_2_max_i(outline_width) / 2;
2956
2957 while (step_size != 0) {
2959 *result_to_flood, *result_after_flooding, size, rect_x_range, rect_y_range, step_size);
2960 std::swap(result_to_flood, result_after_flooding);
2961 step_size /= 2;
2962 }
2963
2964 /* Premultiplied outline color. */
2965 float4 color = data->outline_color;
2966 color.x *= color.w;
2967 color.y *= color.w;
2968 color.z *= color.w;
2969
2970 const float text_color_alpha = data->color[3];
2971
2972 /* We have distances to the closest opaque parts of the image now. Composite the
2973 * outline into the output image. */
2974
2975 threading::parallel_for(rect_y_range, 8, [&](const IndexRange y_range) {
2976 for (const int y : y_range) {
2977 size_t index = size_t(y) * size.x + rect_x_range.start();
2978 uchar *dst = out->byte_buffer.data + index * 4;
2979 for (int x = rect_x_range.start(); x < rect_x_range.one_after_last(); x++, index++, dst += 4)
2980 {
2981 JFACoord closest_texel = (*result_to_flood)[index];
2982 if (closest_texel.x == JFA_INVALID) {
2983 /* Outside of outline, leave output pixel as is. */
2984 continue;
2985 }
2986
2987 /* Fade out / anti-alias the outline over one pixel towards outline distance. */
2988 float distance = math::distance(float2(x, y), float2(closest_texel.x, closest_texel.y));
2989 float alpha = math::clamp(outline_width - distance + 1.0f, 0.0f, 1.0f);
2990
2991 /* Do not put outline inside the text shape:
2992 * - When overall text color is fully opaque, we want to make
2993 * outline fully transparent only where text is fully opaque.
2994 * This ensures that combined anti-aliased pixels at text boundary
2995 * are properly fully opaque.
2996 * - However when text color is fully transparent, we want to
2997 * Use opposite alpha of text, to anti-alias the inner edge of
2998 * the outline.
2999 * In between those two, interpolate the alpha modulation factor. */
3000 float text_alpha = tmp_buf[index].w * (1.0f / 255.0f);
3001 float mul_opaque_text = text_alpha >= 1.0f ? 0.0f : 1.0f;
3002 float mul_transparent_text = 1.0f - text_alpha;
3003 float mul = math::interpolate(mul_transparent_text, mul_opaque_text, text_color_alpha);
3004 alpha *= mul;
3005
3006 float4 col1 = color;
3007 col1 *= alpha;
3008
3009 /* Blend over the output. */
3010 float mfac = 1.0f - col1.w;
3011 float4 col2 = load_premul_pixel(dst);
3012 float4 col = col1 + mfac * col2;
3013 store_premul_pixel(col, dst);
3014 }
3015 }
3016 });
3017 BLF_buffer(font, nullptr, out->byte_buffer.data, size.x, size.y, display);
3018
3019 return outline_rect;
3020}
3021
3022/* Similar to #IMB_rectfill_area but blends the given color under the
3023 * existing image. Also only works on byte buffers. */
3025 const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2)
3026{
3027 const int width = ibuf->x;
3028 const int height = ibuf->y;
3029 x1 = math::clamp(x1, 0, width);
3030 x2 = math::clamp(x2, 0, width);
3031 y1 = math::clamp(y1, 0, height);
3032 y2 = math::clamp(y2, 0, height);
3033 if (x1 > x2) {
3034 std::swap(x1, x2);
3035 }
3036 if (y1 > y2) {
3037 std::swap(y1, y2);
3038 }
3039 if (x1 == x2 || y1 == y2) {
3040 return;
3041 }
3042
3043 float4 premul_col = col;
3044 straight_to_premul_v4(premul_col);
3045
3046 for (int y = y1; y < y2; y++) {
3047 uchar *dst = ibuf->byte_buffer.data + (size_t(width) * y + x1) * 4;
3048 for (int x = x1; x < x2; x++) {
3049 float4 pix = load_premul_pixel(dst);
3050 float fac = 1.0f - pix.w;
3051 float4 dst_fl = fac * premul_col + pix;
3052 store_premul_pixel(dst_fl, dst);
3053 dst += 4;
3054 }
3055 }
3056}
3057
3058static ImBuf *do_text_effect(const SeqRenderData *context,
3059 Sequence *seq,
3060 float /*timeline_frame*/,
3061 float /*fac*/,
3062 ImBuf * /*ibuf1*/,
3063 ImBuf * /*ibuf2*/)
3064{
3065 /* NOTE: text rasterization only fills in part of output image,
3066 * need to clear it. */
3067 ImBuf *out = prepare_effect_imbufs(context, nullptr, nullptr, false);
3068 TextVars *data = static_cast<TextVars *>(seq->effectdata);
3069 const int width = out->x;
3070 const int height = out->y;
3071 int font = blf_mono_font_render;
3072 int y_ofs, x, y;
3073 double proxy_size_comp;
3074
3075 if (data->text_blf_id == SEQ_FONT_NOT_LOADED) {
3076 data->text_blf_id = -1;
3077
3078 SEQ_effect_text_font_load(data, false);
3079 }
3080
3081 if (data->text_blf_id >= 0) {
3082 font = data->text_blf_id;
3083 }
3084
3085 const char *display_device = context->scene->display_settings.display_device;
3087
3088 /* Compensate text size for preview render size. */
3089 proxy_size_comp = context->scene->r.size / 100.0;
3090 if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
3091 proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size);
3092 }
3093
3094 /* set before return */
3095 BLF_size(font, proxy_size_comp * data->text_size);
3096
3097 const int font_flags = BLF_WORD_WRAP | /* Always allow wrapping. */
3098 ((data->flag & SEQ_TEXT_BOLD) ? BLF_BOLD : 0) |
3099 ((data->flag & SEQ_TEXT_ITALIC) ? BLF_ITALIC : 0);
3100 BLF_enable(font, font_flags);
3101
3102 /* use max width to enable newlines only */
3103 BLF_wordwrap(font, (data->wrap_width != 0.0f) ? data->wrap_width * width : -1);
3104
3105 BLF_buffer(font, nullptr, out->byte_buffer.data, width, height, display);
3106
3107 const int line_height = BLF_height_max(font);
3108
3109 y_ofs = -BLF_descender(font);
3110
3111 x = (data->loc[0] * width);
3112 y = (data->loc[1] * height) + y_ofs;
3113
3114 /* Calculate bounding box and wrapping information. */
3115 rcti rect;
3116 ResultBLF wrap_info;
3117 BLF_boundbox(font, data->text, sizeof(data->text), &rect, &wrap_info);
3118
3119 if ((data->align == SEQ_TEXT_ALIGN_X_LEFT) && (data->align_y == SEQ_TEXT_ALIGN_Y_TOP)) {
3120 y -= line_height;
3121 }
3122 else {
3123 if (data->align == SEQ_TEXT_ALIGN_X_RIGHT) {
3124 x -= BLI_rcti_size_x(&rect);
3125 }
3126 else if (data->align == SEQ_TEXT_ALIGN_X_CENTER) {
3127 x -= BLI_rcti_size_x(&rect) / 2;
3128 }
3129
3130 if (data->align_y == SEQ_TEXT_ALIGN_Y_TOP) {
3131 y -= line_height;
3132 }
3133 else if (data->align_y == SEQ_TEXT_ALIGN_Y_BOTTOM) {
3134 y += (wrap_info.lines - 1) * line_height;
3135 }
3136 else if (data->align_y == SEQ_TEXT_ALIGN_Y_CENTER) {
3137 y += (((wrap_info.lines - 1) / 2) * line_height) - (line_height / 2);
3138 }
3139 }
3140 BLI_rcti_translate(&rect, x, y);
3141
3142 /* Draw text outline. */
3143 rcti outline_rect = rect;
3144 if (data->flag & SEQ_TEXT_OUTLINE) {
3145 outline_rect = draw_text_outline(context, data, font, display, x, y, line_height, rect, out);
3146 }
3147
3148 /* Draw text itself. */
3149 BLF_position(font, x, y, 0.0f);
3150 BLF_buffer_col(font, data->color);
3151 BLF_draw_buffer(font, data->text, sizeof(data->text));
3152
3153 BLF_buffer(font, nullptr, nullptr, 0, 0, nullptr);
3154 BLF_disable(font, font_flags);
3155
3156 /* Draw shadow. */
3157 if (data->flag & SEQ_TEXT_SHADOW) {
3158 draw_text_shadow(context, data, line_height, outline_rect, out);
3159 }
3160
3161 /* Draw box under text. */
3162 if (data->flag & SEQ_TEXT_BOX) {
3163 if (out->byte_buffer.data) {
3164 const int margin = data->box_margin * width;
3165 const int minx = rect.xmin - margin;
3166 const int maxx = rect.xmax + margin;
3167 const int miny = rect.ymin - margin;
3168 const int maxy = rect.ymax + margin;
3169 fill_rect_alpha_under(out, data->box_color, minx, miny, maxx, maxy);
3170 }
3171 }
3172
3173 return out;
3174}
3175
3178/* -------------------------------------------------------------------- */
3182static void init_noop(Sequence * /*seq*/) {}
3183
3184static void load_noop(Sequence * /*seq*/) {}
3185
3186static void free_noop(Sequence * /*seq*/, const bool /*do_id_user*/) {}
3187
3189{
3190 return 2;
3191}
3192
3193static void copy_effect_default(Sequence *dst, const Sequence *src, const int /*flag*/)
3194{
3195 dst->effectdata = MEM_dupallocN(src->effectdata);
3196}
3197
3198static void free_effect_default(Sequence *seq, const bool /*do_id_user*/)
3199{
3201}
3202
3203static StripEarlyOut early_out_noop(const Sequence * /*seq*/, float /*fac*/)
3204{
3206}
3207
3208static StripEarlyOut early_out_fade(const Sequence * /*seq*/, float fac)
3209{
3210 if (fac == 0.0f) {
3212 }
3213 if (fac == 1.0f) {
3215 }
3217}
3218
3219static StripEarlyOut early_out_mul_input2(const Sequence * /*seq*/, float fac)
3220{
3221 if (fac == 0.0f) {
3223 }
3225}
3226
3227static StripEarlyOut early_out_mul_input1(const Sequence * /*seq*/, float fac)
3228{
3229 if (fac == 0.0f) {
3231 }
3233}
3234
3235static void get_default_fac_noop(const Scene * /*scene*/,
3236 const Sequence * /*seq*/,
3237 float /*timeline_frame*/,
3238 float *fac)
3239{
3240 *fac = 1.0f;
3241}
3242
3243static void get_default_fac_fade(const Scene *scene,
3244 const Sequence *seq,
3245 float timeline_frame,
3246 float *fac)
3247{
3248 *fac = float(timeline_frame - SEQ_time_left_handle_frame_get(scene, seq));
3249 *fac /= SEQ_time_strip_length_get(scene, seq);
3250 *fac = math::clamp(*fac, 0.0f, 1.0f);
3251}
3252
3253static ImBuf *init_execution(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2)
3254{
3255 ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
3256 return out;
3257}
3258
3260{
3261 SeqEffectHandle rval;
3262 int sequence_type = seq_type;
3263
3264 rval.multithreaded = false;
3265 rval.supports_mask = false;
3266 rval.init = init_noop;
3268 rval.load = load_noop;
3269 rval.free = free_noop;
3272 rval.execute = nullptr;
3274 rval.execute_slice = nullptr;
3275 rval.copy = nullptr;
3276
3277 switch (sequence_type) {
3278 case SEQ_TYPE_CROSS:
3279 rval.multithreaded = true;
3283 break;
3284 case SEQ_TYPE_GAMCROSS:
3285 rval.multithreaded = true;
3289 break;
3290 case SEQ_TYPE_ADD:
3291 rval.multithreaded = true;
3294 break;
3295 case SEQ_TYPE_SUB:
3296 rval.multithreaded = true;
3299 break;
3300 case SEQ_TYPE_MUL:
3301 rval.multithreaded = true;
3304 break;
3305 case SEQ_TYPE_SCREEN:
3306 case SEQ_TYPE_OVERLAY:
3309 case SEQ_TYPE_DARKEN:
3310 case SEQ_TYPE_LIGHTEN:
3311 case SEQ_TYPE_DODGE:
3314 case SEQ_TYPE_PIN_LIGHT:
3315 case SEQ_TYPE_LIN_LIGHT:
3318 case SEQ_TYPE_HUE:
3320 case SEQ_TYPE_VALUE:
3322 case SEQ_TYPE_EXCLUSION:
3323 rval.multithreaded = true;
3326 break;
3327 case SEQ_TYPE_COLORMIX:
3328 rval.multithreaded = true;
3334 break;
3335 case SEQ_TYPE_ALPHAOVER:
3336 rval.multithreaded = true;
3340 break;
3341 case SEQ_TYPE_OVERDROP:
3342 rval.multithreaded = true;
3344 break;
3346 rval.multithreaded = true;
3349 break;
3350 case SEQ_TYPE_WIPE:
3351 rval.init = init_wipe_effect;
3353 rval.free = free_wipe_effect;
3354 rval.copy = copy_wipe_effect;
3357 rval.execute = do_wipe_effect;
3358 break;
3359 case SEQ_TYPE_GLOW:
3360 rval.init = init_glow_effect;
3362 rval.free = free_glow_effect;
3363 rval.copy = copy_glow_effect;
3364 rval.execute = do_glow_effect;
3365 break;
3366 case SEQ_TYPE_TRANSFORM:
3367 rval.multithreaded = true;
3373 break;
3374 case SEQ_TYPE_SPEED:
3375 rval.init = init_speed_effect;
3377 rval.load = load_speed_effect;
3378 rval.free = free_speed_effect;
3379 rval.copy = copy_speed_effect;
3380 rval.execute = do_speed_effect;
3382 break;
3383 case SEQ_TYPE_COLOR:
3384 rval.init = init_solid_color;
3387 rval.free = free_solid_color;
3388 rval.copy = copy_solid_color;
3389 rval.execute = do_solid_color;
3390 break;
3391 case SEQ_TYPE_MULTICAM:
3394 rval.execute = do_multicam;
3395 break;
3397 rval.supports_mask = true;
3400 rval.execute = do_adjustment;
3401 break;
3409 break;
3410 case SEQ_TYPE_TEXT:
3412 rval.init = init_text_effect;
3413 rval.free = free_text_effect;
3414 rval.load = load_text_effect;
3415 rval.copy = copy_text_effect;
3417 rval.execute = do_text_effect;
3418 break;
3419 }
3420
3421 return rval;
3422}
3423
3426/* -------------------------------------------------------------------- */
3431{
3432 SeqEffectHandle rval = {false, false, nullptr};
3433
3434 if (seq->type & SEQ_TYPE_EFFECT) {
3435 rval = get_sequence_effect_impl(seq->type);
3436 if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
3437 rval.load(seq);
3438 seq->flag &= ~SEQ_EFFECT_NOT_LOADED;
3439 }
3440 }
3441
3442 return rval;
3443}
3444
3446{
3447 SeqEffectHandle rval = {false, false, nullptr};
3448
3449 if (seq->blend_mode != 0) {
3450 if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
3451 /* load the effect first */
3452 rval = get_sequence_effect_impl(seq->type);
3453 rval.load(seq);
3454 }
3455
3457 if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
3458 /* now load the blend and unset unloaded flag */
3459 rval.load(seq);
3460 seq->flag &= ~SEQ_EFFECT_NOT_LOADED;
3461 }
3462 }
3463
3464 return rval;
3465}
3466
3468{
3470
3471 int count = rval.num_inputs();
3472 if (rval.execute || (rval.execute_slice && rval.init_execution)) {
3473 return count;
3474 }
3475 return 0;
3476}
3477
float evaluate_fcurve(const FCurve *fcu, float evaltime)
FCurve * id_data_find_fcurve(ID *id, void *data, StructRNA *type, const char *prop_name, int index, bool *r_driven)
void BKE_id_full_name_get(char name[MAX_ID_FULL_NAME], const ID *id, char separator_char)
Definition lib_id.cc:2365
@ LIB_ID_CREATE_NO_USER_REFCOUNT
void id_us_plus(ID *id)
Definition lib_id.cc:351
void id_us_min(ID *id)
Definition lib_id.cc:359
#define MAX_ID_FULL_NAME
void BLF_size(int fontid, float size)
Definition blf.cc:426
int BLF_descender(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:850
void BLF_draw_buffer(int fontid, const char *str, size_t str_len, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:962
int blf_mono_font_render
Definition blf.cc:52
void BLF_boundbox(int fontid, const char *str, size_t str_len, rcti *r_box, ResultBLF *r_info=nullptr) ATTR_NONNULL(2)
Definition blf.cc:761
void BLF_disable(int fontid, int option)
Definition blf.cc:321
void BLF_buffer_col(int fontid, const float rgba[4]) ATTR_NONNULL(2)
Definition blf.cc:937
void BLF_buffer(int fontid, float *fbuf, unsigned char *cbuf, int w, int h, ColorManagedDisplay *display)
Definition blf.cc:924
void BLF_unload_id(int fontid)
Definition blf.cc:284
int BLF_load(const char *filepath) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition blf.cc:174
void BLF_enable(int fontid, int option)
Definition blf.cc:312
int BLF_load_mem(const char *name, const unsigned char *mem, int mem_size) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int BLF_height_max(int fontid) ATTR_WARN_UNUSED_RESULT
Definition blf.cc:828
@ BLF_ITALIC
Definition BLF_api.hh:374
@ BLF_WORD_WRAP
Definition BLF_api.hh:367
@ BLF_BOLD
Definition BLF_api.hh:373
void BLF_wordwrap(int fontid, int wrap_width)
Definition blf.cc:893
void BLF_position(int fontid, float x, float y, float z)
Definition blf.cc:371
MINLINE int round_fl_to_int(float a)
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE int power_of_2_max_i(int n)
MINLINE float min_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE float sqrtf_signed(float f)
MINLINE int clamp_i(int value, int min, int max)
#define M_PI
MINLINE void straight_uchar_to_premul_float(float result[4], const unsigned char color[4])
MINLINE void straight_to_premul_v4(float color[4])
MINLINE void premul_float_to_straight_uchar(unsigned char *result, const float color[4])
MINLINE void blend_color_add_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_exclusion_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_linearburn_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_overlay_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_saturation_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_burn_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_color_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_linearlight_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_sub_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void blend_color_saturation_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_hue_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_difference_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_dodge_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_luminosity_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_pinlight_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_mul_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_pinlight_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_screen_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_screen_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_vividlight_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_linearburn_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_mul_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void blend_color_vividlight_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_darken_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void blend_color_luminosity_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_difference_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_burn_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_color_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_overlay_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_dodge_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_hardlight_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_sub_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_darken_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_softlight_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_add_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void blend_color_hue_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_lighten_byte(unsigned char dst[4], const unsigned char src1[4], const unsigned char src2[4])
MINLINE void blend_color_exclusion_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_hardlight_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_linearlight_byte(unsigned char dst[4], const uchar src1[4], const uchar src2[4])
MINLINE void blend_color_lighten_float(float dst[4], const float src1[4], const float src2[4])
MINLINE void blend_color_softlight_float(float dst[4], const float src1[4], const float src2[4])
#define DEG2RADF(_deg)
MINLINE void copy_v4_fl(float r[4], float f)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define FILE_MAX
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
Definition rct.c:623
void BLI_rcti_translate(struct rcti *rect, int x, int y)
Definition rct.c:560
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
#define STRNCPY(dst, src)
Definition BLI_string.h:593
unsigned char uchar
int BLI_thread_is_main(void)
Definition threads.cc:179
#define CLAMP(a, b, c)
#define UNLIKELY(x)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
Definition DNA_ID.h:649
@ R_FILTER_GAUSS
@ R_IMF_PLANES_RGB
@ SEQ_TYPE_TRANSFORM
@ SEQ_TYPE_COLOR_BURN
@ SEQ_TYPE_HARD_LIGHT
@ SEQ_TYPE_EXCLUSION
@ SEQ_TYPE_CROSS
@ SEQ_TYPE_GLOW
@ SEQ_TYPE_VALUE
@ SEQ_TYPE_COLORMIX
@ SEQ_TYPE_WIPE
@ SEQ_TYPE_OVERDROP
@ SEQ_TYPE_PIN_LIGHT
@ SEQ_TYPE_ALPHAUNDER
@ SEQ_TYPE_DODGE
@ SEQ_TYPE_HUE
@ SEQ_TYPE_LINEAR_BURN
@ SEQ_TYPE_GAMCROSS
@ SEQ_TYPE_MULTICAM
@ SEQ_TYPE_BLEND_COLOR
@ SEQ_TYPE_DARKEN
@ SEQ_TYPE_MUL
@ SEQ_TYPE_GAUSSIAN_BLUR
@ SEQ_TYPE_ADD
@ SEQ_TYPE_ALPHAOVER
@ SEQ_TYPE_TEXT
@ SEQ_TYPE_SCREEN
@ SEQ_TYPE_SOFT_LIGHT
@ SEQ_TYPE_SUB
@ SEQ_TYPE_VIVID_LIGHT
@ SEQ_TYPE_OVERLAY
@ SEQ_TYPE_SPEED
@ SEQ_TYPE_COLOR
@ SEQ_TYPE_SATURATION
@ SEQ_TYPE_LIN_LIGHT
@ SEQ_TYPE_EFFECT
@ SEQ_TYPE_DIFFERENCE
@ SEQ_TYPE_ADJUSTMENT
@ SEQ_TYPE_LIGHTEN
@ SEQ_SPEED_STRETCH
@ SEQ_SPEED_MULTIPLY
@ SEQ_SPEED_LENGTH
@ SEQ_SPEED_FRAME_NUMBER
@ SEQ_TEXT_ALIGN_Y_BOTTOM
@ SEQ_TEXT_ALIGN_Y_TOP
@ SEQ_TEXT_ALIGN_Y_CENTER
@ SEQ_TEXT_ALIGN_X_RIGHT
@ SEQ_TEXT_ALIGN_X_CENTER
@ SEQ_TEXT_ALIGN_X_LEFT
#define SEQ_FONT_NOT_LOADED
@ SEQ_TEXT_ITALIC
@ SEQ_TEXT_SHADOW
@ SEQ_TEXT_BOLD
@ SEQ_TEXT_OUTLINE
@ SEQ_TEXT_BOX
@ SEQ_EFFECT_NOT_LOADED
@ SEQ_SPEED_USE_INTERPOLATION
@ SEQ_RENDER_SIZE_SCENE
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:125
void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
ColorManagedDisplay * IMB_colormanagement_display_get_named(const char *name)
void IMB_colormanagement_transform_from_byte_threaded(float *float_buffer, unsigned char *byte_buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace)
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_rect_from_float(ImBuf *ibuf)
Definition divers.cc:694
void IMB_buffer_byte_from_float(unsigned char *rect_to, const float *rect_from, int channels_from, float dither, int profile_to, int profile_from, bool predivide, int width, int height, int stride_to, int stride_from)
Definition divers.cc:96
Contains defines and structs used throughout the imbuf module.
#define IB_PROFILE_SRGB
@ IB_rectfloat
@ IB_uninitialized_pixels
@ IB_rect
void IMB_metadata_copy(ImBuf *ibuf_dst, const ImBuf *ibuf_src)
Definition metadata.cc:60
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
StripEarlyOut
@ DO_IRIS_WIPE
@ DO_CLOCK_WIPE
@ DO_DOUBLE_WIPE
@ DO_SINGLE_WIPE
ATTR_WARN_UNUSED_RESULT const BMVert * v
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define output
static void mul(btAlignedObjectArray< T > &items, const Q &value)
static T sum(const btAlignedObjectArray< T > &items)
ListBase * SEQ_get_channels_by_seq(ListBase *seqbase, ListBase *channels, const Sequence *seq)
Definition channels.cc:87
const T * data() const
Definition BLI_array.hh:301
constexpr int64_t first() const
constexpr int64_t size() const
#define sinf(x)
#define cosf(x)
#define tanf(x)
#define atan2f(x, y)
#define hypotf(x, y)
#define fabsf(x)
#define sqrtf(x)
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
static void do_blend_effect_byte(float fac, int x, int y, const uchar *rect1, uchar *rect2, int btype, uchar *out)
Definition effects.cc:908
static StripEarlyOut early_out_fade(const Sequence *, float fac)
Definition effects.cc:3208
static void gaussian_blur_y(const Span< float > gaussian, int half_size, int start_line, int width, int height, int frame_height, const T *rect, T *dst)
Definition effects.cc:2381
constexpr uint16_t JFA_INVALID
Definition effects.cc:2842
static void do_cross_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
Definition effects.cc:356
static FCurve * seq_effect_speed_speed_factor_curve_get(Scene *scene, Sequence *seq)
Definition effects.cc:2113
static void init_text_effect(Sequence *seq)
Definition effects.cc:2500
static WipeZone precalc_wipe_zone(const WipeVars *wipe, int xo, int yo)
Definition effects.cc:1069
static void do_mul_effect(const SeqRenderData *context, Sequence *, float, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:785
static int num_inputs_default()
Definition effects.cc:3188
static ImBuf * do_text_effect(const SeqRenderData *context, Sequence *seq, float, float, ImBuf *, ImBuf *)
Definition effects.cc:3058
static int num_inputs_text()
Definition effects.cc:2623
static void free_transform_effect(Sequence *seq, const bool)
Definition effects.cc:1462
static float in_band(float width, float dist, int side, int dir)
Definition effects.cc:1087
static void do_overdrop_effect(const SeqRenderData *context, Sequence *, float, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:2255
static bool alpha_opaque(uchar alpha)
Definition effects.cc:220
static void free_effect_default(Sequence *seq, const bool)
Definition effects.cc:3198
static void text_gaussian_blur_x(const Span< float > gaussian, int half_size, int start_line, int width, int height, const uchar *rect, uchar *dst, const rcti &shadow_rect)
Definition effects.cc:2645
static StripEarlyOut early_out_gaussian_blur(const Sequence *seq, float)
Definition effects.cc:2316
static void copy_gaussian_blur_effect(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:2311
static void free_speed_effect(Sequence *seq, const bool)
Definition effects.cc:2091
static void get_default_fac_fade(const Scene *scene, const Sequence *seq, float timeline_frame, float *fac)
Definition effects.cc:3243
float seq_speed_effect_target_frame_get(Scene *scene, Sequence *seq_speed, float timeline_frame, int input)
Definition effects.cc:2159
static void copy_glow_effect(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:1738
static void do_mul_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
Definition effects.cc:736
static void init_speed_effect(Sequence *seq)
Definition effects.cc:2063
static ImBuf * prepare_effect_imbufs(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2, bool uninitialized_pixels=true)
Definition effects.cc:153
static StripEarlyOut early_out_text(const Sequence *seq, float)
Definition effects.cc:2628
static SeqEffectHandle get_sequence_effect_impl(int seq_type)
Definition effects.cc:3259
static void do_alphaunder_effect(float fac, int width, int height, const T *src1, const T *src2, T *dst)
Definition effects.cc:296
static void composite_shadow(int width, const rcti &shadow_rect, const float4 &shadow_color, const Array< uchar > &shadow_mask, uchar *output)
Definition effects.cc:2737
static void fill_rect_alpha_under(const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2)
Definition effects.cc:3024
static void text_gaussian_blur_y(const Span< float > gaussian, int half_size, int start_line, int width, int height, const uchar *rect, uchar *dst, const rcti &shadow_rect)
Definition effects.cc:2677
static void init_colormix_effect(Sequence *seq)
Definition effects.cc:1010
static void free_glow_effect(Sequence *seq, const bool)
Definition effects.cc:1733
static void glow_blur_bitmap(const float4 *src, float4 *map, int width, int height, float blur, int quality)
Definition effects.cc:1607
static void init_alpha_over_or_under(Sequence *seq)
Definition effects.cc:211
static void draw_text_shadow(const SeqRenderData *context, const TextVars *data, int line_height, const rcti &rect, ImBuf *out)
Definition effects.cc:2765
static void init_glow_effect(Sequence *seq)
Definition effects.cc:1709
void SEQ_effect_text_font_unload(TextVars *data, const bool do_id_user)
Definition effects.cc:2535
static rcti draw_text_outline(const SeqRenderData *context, const TextVars *data, int font, ColorManagedDisplay *display, int x, int y, int line_height, const rcti &rect, ImBuf *out)
Definition effects.cc:2895
static void initialize_shadow_alpha(int width, int height, int2 offset, const rcti &shadow_rect, const uchar *input, Array< uchar > &r_shadow_mask)
Definition effects.cc:2716
static void load_text_effect(Sequence *seq)
Definition effects.cc:2608
SeqEffectHandle SEQ_effect_handle_get(Sequence *seq)
Definition effects.cc:3430
int SEQ_effect_get_num_inputs(int seq_type)
Definition effects.cc:3467
static void free_gaussian_blur_effect(Sequence *seq, const bool)
Definition effects.cc:2306
static ImBuf * init_execution(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2)
Definition effects.cc:3253
static void get_default_fac_noop(const Scene *, const Sequence *, float, float *fac)
Definition effects.cc:3235
static StripEarlyOut early_out_multicam(const Sequence *, float)
Definition effects.cc:1945
static void do_add_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
Definition effects.cc:505
#define YOFF
Definition effects.cc:661
static void do_add_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
Definition effects.cc:528
static void slice_get_float_buffers(const SeqRenderData *context, const ImBuf *ibuf1, const ImBuf *ibuf2, const ImBuf *out, int start_line, float **rect1, float **rect2, float **rect_out)
Definition effects.cc:90
#define XOFF
Definition effects.cc:660
static StripEarlyOut early_out_mul_input2(const Sequence *, float fac)
Definition effects.cc:3219
static void do_sub_effect(const SeqRenderData *context, Sequence *, float, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:627
static void init_wipe_effect(Sequence *seq)
Definition effects.cc:1324
static StripEarlyOut early_out_speed(const Sequence *, float)
Definition effects.cc:2108
static float invGammaCorrect(float c)
Definition effects.cc:446
static StripEarlyOut early_out_mul_input1(const Sequence *, float fac)
Definition effects.cc:3227
static void free_solid_color(Sequence *seq, const bool)
Definition effects.cc:1870
static int num_inputs_gaussian_blur()
Definition effects.cc:2301
static void do_glow_effect_float(Sequence *seq, int render_size, float fac, int x, int y, float *rect1, float *, float *out)
Definition effects.cc:1786
static void apply_blend_function(float fac, int width, int height, const T *src1, T *src2, T *dst, Func blend_function)
Definition effects.cc:819
static float gammaCorrect(float c)
Definition effects.cc:438
static void slice_get_byte_buffers(const SeqRenderData *context, const ImBuf *ibuf1, const ImBuf *ibuf2, const ImBuf *out, int start_line, uchar **rect1, uchar **rect2, uchar **rect_out)
Definition effects.cc:71
static void copy_speed_effect(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:2100
static void do_gammacross_effect(float fac, int width, int height, const T *src1, const T *src2, T *dst)
Definition effects.cc:452
static void do_cross_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
Definition effects.cc:379
static void copy_transform_effect(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:1467
static ImBuf * do_glow_effect(const SeqRenderData *context, Sequence *seq, float, float fac, ImBuf *ibuf1, ImBuf *ibuf2)
Definition effects.cc:1810
static void clamp_rect(int width, int height, rcti &r_rect)
Definition effects.cc:2708
static void do_transform_effect(const SeqRenderData *context, Sequence *seq, float, float, const ImBuf *ibuf1, const ImBuf *, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:1543
static StripEarlyOut early_out_noop(const Sequence *, float)
Definition effects.cc:3203
static void copy_text_effect(Sequence *dst, const Sequence *src, const int flag)
Definition effects.cc:2614
static ImBuf * do_adjustment_impl(const SeqRenderData *context, Sequence *seq, float timeline_frame)
Definition effects.cc:1997
static int num_inputs_glow()
Definition effects.cc:1728
static void load_speed_effect(Sequence *seq)
Definition effects.cc:2080
static void transform_image(int x, int y, int start_line, int total_lines, const ImBuf *ibuf, ImBuf *out, float scale_x, float scale_y, float translate_x, float translate_y, float rotate, int interpolation)
Definition effects.cc:1472
static void do_colormix_effect(const SeqRenderData *context, Sequence *seq, float, float, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:1023
static void do_drop_effect_float(float fac, int x, int y, float *rect2i, float *rect1i, float *outi)
Definition effects.cc:696
static int num_inputs_wipe()
Definition effects.cc:1333
static ImBuf * do_speed_effect(const SeqRenderData *context, Sequence *seq, float timeline_frame, float fac, ImBuf *ibuf1, ImBuf *ibuf2)
Definition effects.cc:2226
static int num_inputs_speed()
Definition effects.cc:2086
static void blur_isolate_highlights(const float4 *in, float4 *out, int width, int height, float threshold, float boost, float clamp)
Definition effects.cc:1679
static float4 load_premul_pixel(const uchar *ptr)
Definition effects.cc:109
static ImBuf * do_adjustment(const SeqRenderData *context, Sequence *seq, float timeline_frame, float, ImBuf *, ImBuf *)
Definition effects.cc:2036
static void free_noop(Sequence *, const bool)
Definition effects.cc:3186
static ImBuf * do_multicam(const SeqRenderData *context, Sequence *seq, float timeline_frame, float, ImBuf *, ImBuf *)
Definition effects.cc:1950
static void seq_effect_speed_frame_map_ensure(Scene *scene, Sequence *seq)
Definition effects.cc:2149
static void do_drop_effect_byte(float fac, int x, int y, uchar *rect2i, uchar *rect1i, uchar *outi)
Definition effects.cc:663
static void init_solid_color(Sequence *seq)
Definition effects.cc:1851
static void copy_wipe_effect(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:1343
static void copy_solid_color(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:1875
static int num_inputs_transform()
Definition effects.cc:1457
static void do_cross_effect(const SeqRenderData *context, Sequence *, float, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:401
static void free_text_effect(Sequence *seq, const bool do_id_user)
Definition effects.cc:2597
static void copy_effect_default(Sequence *dst, const Sequence *src, const int)
Definition effects.cc:3193
static void load_noop(Sequence *)
Definition effects.cc:3184
static StripEarlyOut early_out_adjustment(const Sequence *, float)
Definition effects.cc:1992
static void do_sub_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
Definition effects.cc:581
static void do_blend_effect_float(float fac, int x, int y, const float *rect1, float *rect2, int btype, float *out)
Definition effects.cc:836
static void do_mul_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
Definition effects.cc:762
static ImBuf * do_solid_color(const SeqRenderData *context, Sequence *seq, float, float, ImBuf *ibuf1, ImBuf *ibuf2)
Definition effects.cc:1885
static float speed_effect_interpolation_ratio_get(Scene *scene, Sequence *seq_speed, float timeline_frame)
Definition effects.cc:2217
static void do_add_effect(const SeqRenderData *context, Sequence *, float, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:549
static void store_opaque_black_pixel(uchar *dst)
Definition effects.cc:131
static void do_wipe_effect(const Sequence *seq, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
Definition effects.cc:1349
static void do_blend_mode_effect(const SeqRenderData *context, Sequence *seq, float, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
Definition effects.cc:980
static void init_noop(Sequence *)
Definition effects.cc:3182
void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq)
Definition effects.cc:2118
static int num_inputs_color()
Definition effects.cc:1865
static void gaussian_blur_x(const Span< float > gaussian, int half_size, int start_line, int width, int height, int, const T *rect, T *dst)
Definition effects.cc:2347
static void jump_flooding_pass(Span< JFACoord > input, MutableSpan< JFACoord > output, int2 size, IndexRange x_range, IndexRange y_range, int step_size)
Definition effects.cc:2849
static void store_premul_pixel(const float4 &pix, uchar *dst)
Definition effects.cc:121
static void init_transform_effect(Sequence *seq)
Definition effects.cc:1432
static void init_gaussian_blur_effect(Sequence *seq)
Definition effects.cc:2292
static int num_inputs_multicam()
Definition effects.cc:1940
static float check_zone(const WipeZone *wipezone, int x, int y, float fac)
Definition effects.cc:1113
static void do_alphaover_effect(float fac, int width, int height, const T *src1, const T *src2, T *dst)
Definition effects.cc:232
static void do_sub_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
Definition effects.cc:604
static ImBuf * do_gaussian_blur_effect(const SeqRenderData *context, Sequence *seq, float, float, ImBuf *ibuf1, ImBuf *)
Definition effects.cc:2413
static Array< float > make_gaussian_blur_kernel(float rad, int size)
Definition effects.cc:2325
static void free_wipe_effect(Sequence *seq, const bool)
Definition effects.cc:1338
static void do_glow_effect_byte(Sequence *seq, int render_size, float fac, int x, int y, uchar *rect1, uchar *, uchar *out)
Definition effects.cc:1743
void SEQ_effect_text_font_load(TextVars *data, const bool do_id_user)
Definition effects.cc:2553
static int num_inputs_adjustment()
Definition effects.cc:1987
static StripEarlyOut early_out_color(const Sequence *, float)
Definition effects.cc:1880
SeqEffectHandle seq_effect_get_sequence_blend(Sequence *seq)
Definition effects.cc:3445
#define pf(_x, _i)
Prefetch 64.
Definition gim_memory.h:48
uint col
struct ImBuf * IMB_allocImBuf(unsigned int, unsigned int, unsigned char, unsigned int)
void IMB_freeImBuf(ImBuf *)
DO_INLINE void filter(lfVector *V, fmatrix3x3 *S)
float RE_filter_value(int type, float x)
int count
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
ccl_device_inline float2 floor(const float2 a)
ccl_device_inline float3 exp(float3 v)
ccl_device_inline float3 ceil(const float3 a)
#define T
float4 interpolate_nearest_border_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:27
uchar4 interpolate_nearest_border_byte(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:23
uchar4 interpolate_cubic_bspline_byte(const ImBuf *in, float u, float v)
float4 interpolate_cubic_bspline_fl(const ImBuf *in, float u, float v)
uchar4 interpolate_bilinear_border_byte(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:74
float4 interpolate_bilinear_border_fl(const ImBuf *in, float u, float v)
Definition IMB_interp.hh:78
T clamp(const T &a, const T &min, const T &max)
T distance(const T &a, const T &b)
T min(const T &a, const T &b)
T interpolate(const T &a, const T &b, const FactorT &t)
T distance_squared(const VecBase< T, Size > &a, const VecBase< T, Size > &b)
T max(const T &a, const T &b)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
blender::VecBase< uint8_t, 4 > uchar4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
double SEQ_rendersize_to_scale_factor(int render_size)
Definition proxy.cc:82
void seq_imbuf_to_sequencer_space(const Scene *scene, ImBuf *ibuf, bool make_float)
Definition render.cc:106
ImBuf * seq_render_give_ibuf_seqbase(const SeqRenderData *context, float timeline_frame, int chan_shown, ListBase *channels, ListBase *seqbasep)
Definition render.cc:2133
ImBuf * seq_render_effect_execute_threaded(SeqEffectHandle *sh, const SeqRenderData *context, Sequence *seq, float timeline_frame, float fac, ImBuf *ibuf1, ImBuf *ibuf2)
Definition render.cc:799
ListBase * SEQ_get_seqbase_by_seq(const Scene *scene, Sequence *seq)
unsigned short uint16_t
Definition stdint.h:79
__int64 int64_t
Definition stdint.h:89
Sequence * SEQ_find_metastrip_by_sequence(ListBase *seqbase, Sequence *meta, Sequence *seq)
float SEQ_give_frame_index(const Scene *scene, const Sequence *seq, float timeline_frame)
Definition strip_time.cc:60
int SEQ_time_strip_length_get(const Scene *scene, const Sequence *seq)
int SEQ_time_left_handle_frame_get(const Scene *, const Sequence *seq)
int SEQ_time_right_handle_frame_get(const Scene *scene, const Sequence *seq)
ListBase seqbase
ListBase channels
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
uint16_t x
Definition effects.cc:2845
uint16_t y
Definition effects.cc:2846
int lines
Definition BLF_api.hh:406
void(* free)(Sequence *seq, bool do_id_user)
void(* get_default_fac)(const Scene *scene, const Sequence *seq, float timeline_frame, float *fac)
void(* copy)(Sequence *dst, const Sequence *src, int flag)
void(* init)(Sequence *seq)
ImBuf *(* init_execution)(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2)
void(* load)(Sequence *seqconst)
ImBuf *(* execute)(const SeqRenderData *context, Sequence *seq, float timeline_frame, float fac, ImBuf *ibuf1, ImBuf *ibuf2)
StripEarlyOut(* early_out)(const Sequence *seq, float fac)
int(* num_inputs)()
void(* execute_slice)(const SeqRenderData *context, Sequence *seq, float timeline_frame, float fac, const ImBuf *ibuf1, const ImBuf *ibuf2, int start_line, int total_lines, ImBuf *out)
struct Sequence * seq1
struct Sequence * seq2
struct VFont * text_font
char filepath[1024]
struct PackedFile * packedfile
bool forward
Definition effects.cc:1066
float angle
Definition effects.cc:1059
float clockWidth
Definition effects.cc:1064
float pythangle
Definition effects.cc:1063
int width
Definition effects.cc:1062
int ymin
int ymax
int xmin
int xmax
PointerRNA * ptr
Definition wm_files.cc:4126
uint8_t flag
Definition wm_window.cc:138