2023-05-31 16:19:06 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
* SPDX-FileCopyrightText: 2003-2024 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
* SPDX-FileCopyrightText: 2005-2006 Peter Schlaile <peter [at] schlaile [dot] de>
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup bke
|
2011-02-27 20:40:57 +00:00
|
|
|
*/
|
|
|
|
|
|
2023-07-22 11:27:25 +10:00
|
|
|
#include <cmath>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
2009-01-12 19:02:08 +00:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
#include "BLI_vector.hh"
|
|
|
|
|
#include "DNA_vec_types.h"
|
2009-01-12 19:02:08 +00:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
2023-12-06 19:39:42 +01:00
|
|
|
#include "BLI_array.hh"
|
Cleanup: reduce amount of math-related includes
Using ClangBuildAnalyzer on the whole Blender build, it was pointing
out that BLI_math.h is the heaviest "header hub" (i.e. non tiny file
that is included a lot).
However, there's very little (actually zero) source files in Blender
that need "all the math" (base, colors, vectors, matrices,
quaternions, intersection, interpolation, statistics, solvers and
time). A common use case is source files needing just vectors, or
just vectors & matrices, or just colors etc. Actually, 181 files
were including the whole math thing without needing it at all.
This change removes BLI_math.h completely, and instead in all the
places that need it, includes BLI_math_vector.h or BLI_math_color.h
and so on.
Change from that:
- BLI_math_color.h was included 1399 times -> now 408 (took 114.0sec
to parse -> now 36.3sec)
- BLI_simd.h 1403 -> 418 (109.7sec -> 34.9sec).
Full rebuild of Blender (Apple M1, Xcode, RelWithDebInfo) is not
affected much (342sec -> 334sec). Most of benefit would be when
someone's changing BLI_simd.h or BLI_math_color.h or similar files,
that now there's 3x fewer files result in a recompile.
Pull Request #110944
2023-08-09 11:39:20 +03:00
|
|
|
#include "BLI_math_rotation.h"
|
2023-12-06 19:39:42 +01:00
|
|
|
#include "BLI_math_vector.hh"
|
|
|
|
|
#include "BLI_math_vector_types.hh"
|
2024-09-26 21:13:39 +10:00
|
|
|
#include "BLI_path_utils.hh"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_rect.h"
|
2015-07-01 20:29:18 +02:00
|
|
|
#include "BLI_string.h"
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
#include "BLI_string_utf8.h"
|
2023-12-06 19:39:42 +01:00
|
|
|
#include "BLI_task.hh"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_threads.h"
|
|
|
|
|
#include "BLI_utildefines.h"
|
2010-03-21 15:59:08 +00:00
|
|
|
|
2021-08-27 13:09:33 +10:00
|
|
|
#include "DNA_packedFile_types.h"
|
2009-01-12 19:02:08 +00:00
|
|
|
#include "DNA_scene_types.h"
|
|
|
|
|
#include "DNA_sequence_types.h"
|
2015-09-09 14:36:12 +02:00
|
|
|
#include "DNA_space_types.h"
|
2020-12-15 10:47:58 +11:00
|
|
|
#include "DNA_vfont_types.h"
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2024-03-05 18:39:08 +01:00
|
|
|
#include "BKE_fcurve.hh"
|
2024-01-15 12:44:04 -05:00
|
|
|
#include "BKE_lib_id.hh"
|
2023-12-01 19:43:16 +01:00
|
|
|
#include "BKE_main.hh"
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_colormanagement.hh"
|
|
|
|
|
#include "IMB_imbuf.hh"
|
|
|
|
|
#include "IMB_imbuf_types.hh"
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
#include "IMB_interp.hh"
|
2024-01-18 22:50:23 +02:00
|
|
|
#include "IMB_metadata.hh"
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2017-11-27 23:33:08 +01:00
|
|
|
#include "BLI_math_color_blend.h"
|
|
|
|
|
|
2024-07-10 18:30:02 +02:00
|
|
|
#include "RNA_prototypes.hh"
|
2009-11-14 14:58:19 +00:00
|
|
|
|
2014-07-19 22:16:10 +06:00
|
|
|
#include "RE_pipeline.h"
|
|
|
|
|
|
2023-11-02 01:05:06 +01:00
|
|
|
#include "SEQ_channels.hh"
|
|
|
|
|
#include "SEQ_effects.hh"
|
|
|
|
|
#include "SEQ_proxy.hh"
|
|
|
|
|
#include "SEQ_relations.hh"
|
|
|
|
|
#include "SEQ_render.hh"
|
|
|
|
|
#include "SEQ_time.hh"
|
|
|
|
|
#include "SEQ_utils.hh"
|
2020-11-01 21:03:31 +01:00
|
|
|
|
2024-01-31 14:04:56 -05:00
|
|
|
#include "BLF_api.hh"
|
2015-07-01 20:29:18 +02:00
|
|
|
|
2023-11-02 01:05:06 +01:00
|
|
|
#include "effects.hh"
|
|
|
|
|
#include "render.hh"
|
2020-10-26 00:47:06 +01:00
|
|
|
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
using namespace blender;
|
2023-12-14 17:31:05 +01:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static SeqEffectHandle get_sequence_effect_impl(int seq_type);
|
2020-04-30 20:45:41 +02:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Internal Utilities
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void slice_get_byte_buffers(const SeqRenderData *context,
|
|
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
|
|
|
|
const ImBuf *out,
|
|
|
|
|
int start_line,
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar **rect1,
|
|
|
|
|
uchar **rect2,
|
|
|
|
|
uchar **rect_out)
|
2012-08-08 16:46:45 +00:00
|
|
|
{
|
|
|
|
|
int offset = 4 * start_line * context->rectx;
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
*rect1 = ibuf1->byte_buffer.data + offset;
|
|
|
|
|
*rect_out = out->byte_buffer.data + offset;
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ibuf2) {
|
2023-05-18 10:19:01 +02:00
|
|
|
*rect2 = ibuf2->byte_buffer.data + offset;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
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)
|
2012-08-08 16:46:45 +00:00
|
|
|
{
|
|
|
|
|
int offset = 4 * start_line * context->rectx;
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
*rect1 = ibuf1->float_buffer.data + offset;
|
|
|
|
|
*rect_out = out->float_buffer.data + offset;
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (ibuf2) {
|
2023-05-18 10:19:01 +02:00
|
|
|
*rect2 = ibuf2->float_buffer.data + offset;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
static float4 load_premul_pixel(const uchar *ptr)
|
|
|
|
|
{
|
|
|
|
|
float4 res;
|
|
|
|
|
straight_uchar_to_premul_float(res, ptr);
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float4 load_premul_pixel(const float *ptr)
|
|
|
|
|
{
|
|
|
|
|
return float4(ptr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void store_premul_pixel(const float4 &pix, uchar *dst)
|
|
|
|
|
{
|
|
|
|
|
premul_float_to_straight_uchar(dst, pix);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void store_premul_pixel(const float4 &pix, float *dst)
|
|
|
|
|
{
|
|
|
|
|
*reinterpret_cast<float4 *>(dst) = pix;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void store_opaque_black_pixel(uchar *dst)
|
|
|
|
|
{
|
|
|
|
|
dst[0] = 0;
|
|
|
|
|
dst[1] = 0;
|
|
|
|
|
dst[2] = 0;
|
|
|
|
|
dst[3] = 255;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void store_opaque_black_pixel(float *dst)
|
|
|
|
|
{
|
|
|
|
|
dst[0] = 0.0f;
|
|
|
|
|
dst[1] = 0.0f;
|
|
|
|
|
dst[2] = 0.0f;
|
|
|
|
|
dst[3] = 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Glow Effect
|
|
|
|
|
* \{ */
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2014-01-19 00:16:19 +06:00
|
|
|
static ImBuf *prepare_effect_imbufs(const SeqRenderData *context,
|
|
|
|
|
ImBuf *ibuf1,
|
|
|
|
|
ImBuf *ibuf2,
|
2024-02-15 14:54:57 +01:00
|
|
|
bool uninitialized_pixels = true)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2012-08-08 11:15:36 +00:00
|
|
|
ImBuf *out;
|
2014-01-19 00:16:19 +06:00
|
|
|
Scene *scene = context->scene;
|
|
|
|
|
int x = context->rectx;
|
|
|
|
|
int y = context->recty;
|
2024-02-15 14:54:57 +01:00
|
|
|
int base_flags = uninitialized_pixels ? IB_uninitialized_pixels : 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
if (!ibuf1 && !ibuf2) {
|
2010-07-25 17:19:55 +00:00
|
|
|
/* hmmm, global float option ? */
|
2024-02-15 14:54:57 +01:00
|
|
|
out = IMB_allocImBuf(x, y, 32, IB_rect | base_flags);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2024-09-09 18:02:59 +02:00
|
|
|
else if ((ibuf1 && ibuf1->float_buffer.data) || (ibuf2 && ibuf2->float_buffer.data)) {
|
|
|
|
|
/* if any inputs are float, output is float too */
|
2024-02-15 14:54:57 +01:00
|
|
|
out = IMB_allocImBuf(x, y, 32, IB_rectfloat | base_flags);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2024-02-15 14:54:57 +01:00
|
|
|
out = IMB_allocImBuf(x, y, 32, IB_rect | base_flags);
|
2010-07-25 17:19:55 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
|
|
|
|
if (ibuf1 && !ibuf1->float_buffer.data) {
|
2020-11-05 13:33:27 +01:00
|
|
|
seq_imbuf_to_sequencer_space(scene, ibuf1, true);
|
2016-07-14 14:15:43 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf2 && !ibuf2->float_buffer.data) {
|
2020-11-05 13:33:27 +01:00
|
|
|
seq_imbuf_to_sequencer_space(scene, ibuf2, true);
|
2016-07-14 14:15:43 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-07-14 14:15:43 +02:00
|
|
|
IMB_colormanagement_assign_float_colorspace(out, scene->sequencer_colorspace_settings.name);
|
2010-07-25 17:19:55 +00:00
|
|
|
}
|
2016-07-14 14:15:43 +02:00
|
|
|
else {
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf1 && !ibuf1->byte_buffer.data) {
|
2016-07-14 14:15:43 +02:00
|
|
|
IMB_rect_from_float(ibuf1);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (ibuf2 && !ibuf2->byte_buffer.data) {
|
2016-07-14 14:15:43 +02:00
|
|
|
IMB_rect_from_float(ibuf2);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2016-02-01 10:19:55 +01:00
|
|
|
/* If effect only affecting a single channel, forward input's metadata to the output. */
|
2024-09-09 18:02:59 +02:00
|
|
|
if (ibuf1 != nullptr && ibuf1 == ibuf2) {
|
2016-02-01 10:19:55 +01:00
|
|
|
IMB_metadata_copy(out, ibuf1);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-07-25 17:19:55 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Alpha Over Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
static void init_alpha_over_or_under(Sequence *seq)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-05-08 09:31:25 +00:00
|
|
|
Sequence *seq1 = seq->seq1;
|
|
|
|
|
Sequence *seq2 = seq->seq2;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
seq->seq2 = seq1;
|
|
|
|
|
seq->seq1 = seq2;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-01-15 18:12:51 +01:00
|
|
|
static bool alpha_opaque(uchar alpha)
|
|
|
|
|
{
|
|
|
|
|
return alpha == 255;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool alpha_opaque(float alpha)
|
|
|
|
|
{
|
|
|
|
|
return alpha >= 1.0f;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* dst = src1 over src2 (alpha from src1) */
|
|
|
|
|
template<typename T>
|
|
|
|
|
static void do_alphaover_effect(
|
|
|
|
|
float fac, int width, int height, const T *src1, const T *src2, T *dst)
|
2021-12-28 14:28:52 +01:00
|
|
|
{
|
2023-12-14 17:31:05 +01:00
|
|
|
if (fac <= 0.0f) {
|
|
|
|
|
memcpy(dst, src2, sizeof(T) * 4 * width * height);
|
|
|
|
|
return;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-01-15 18:12:51 +01:00
|
|
|
for (int pixel_idx = 0; pixel_idx < width * height; pixel_idx++) {
|
|
|
|
|
if (src1[3] <= 0.0f) {
|
2024-01-18 10:37:49 +11:00
|
|
|
/* Alpha of zero. No color addition will happen as the colors are pre-multiplied. */
|
2024-01-15 18:12:51 +01:00
|
|
|
memcpy(dst, src2, sizeof(T) * 4);
|
|
|
|
|
}
|
|
|
|
|
else if (fac == 1.0f && alpha_opaque(src1[3])) {
|
2024-01-18 10:37:49 +11:00
|
|
|
/* No change to `src1` as `fac == 1` and fully opaque. */
|
2024-01-15 18:12:51 +01:00
|
|
|
memcpy(dst, src1, sizeof(T) * 4);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-14 17:31:05 +01:00
|
|
|
float4 col1 = load_premul_pixel(src1);
|
|
|
|
|
float mfac = 1.0f - fac * col1.w;
|
2024-01-15 18:12:51 +01:00
|
|
|
float4 col2 = load_premul_pixel(src2);
|
|
|
|
|
float4 col = fac * col1 + mfac * col2;
|
|
|
|
|
store_premul_pixel(col, dst);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2024-01-15 18:12:51 +01:00
|
|
|
src1 += 4;
|
|
|
|
|
src2 += 4;
|
|
|
|
|
dst += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_alphaover_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
do_alphaover_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
do_alphaover_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Alpha Under Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* dst = src1 under src2 (alpha from src2) */
|
|
|
|
|
template<typename T>
|
|
|
|
|
static void do_alphaunder_effect(
|
|
|
|
|
float fac, int width, int height, const T *src1, const T *src2, T *dst)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2024-04-30 08:32:25 +02:00
|
|
|
if (fac <= 0.0f) {
|
|
|
|
|
memcpy(dst, src2, sizeof(T) * 4 * width * height);
|
2023-12-14 17:31:05 +01:00
|
|
|
return;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-01-15 18:12:51 +01:00
|
|
|
for (int pixel_idx = 0; pixel_idx < width * height; pixel_idx++) {
|
2024-04-30 08:32:25 +02:00
|
|
|
if (src2[3] <= 0.0f && fac >= 1.0f) {
|
2024-01-15 18:12:51 +01:00
|
|
|
memcpy(dst, src1, sizeof(T) * 4);
|
|
|
|
|
}
|
2024-04-30 08:32:25 +02:00
|
|
|
else if (alpha_opaque(src2[3])) {
|
2024-01-15 18:12:51 +01:00
|
|
|
memcpy(dst, src2, sizeof(T) * 4);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-14 17:31:05 +01:00
|
|
|
float4 col2 = load_premul_pixel(src2);
|
2024-01-15 18:12:51 +01:00
|
|
|
float mfac = fac * (1.0f - col2.w);
|
|
|
|
|
float4 col1 = load_premul_pixel(src1);
|
|
|
|
|
float4 col = mfac * col1 + col2;
|
|
|
|
|
store_premul_pixel(col, dst);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2024-01-15 18:12:51 +01:00
|
|
|
src1 += 4;
|
|
|
|
|
src2 += 4;
|
|
|
|
|
dst += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_alphaunder_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
do_alphaunder_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
do_alphaunder_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Cross Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
static void do_cross_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *rt1 = rect1;
|
|
|
|
|
uchar *rt2 = rect2;
|
|
|
|
|
uchar *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
int temp_fac = int(256.0f * fac);
|
2021-12-28 14:28:52 +01:00
|
|
|
int temp_mfac = 256 - temp_fac;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
2021-12-28 14:28:52 +01:00
|
|
|
rt[0] = (temp_mfac * rt1[0] + temp_fac * rt2[0]) >> 8;
|
|
|
|
|
rt[1] = (temp_mfac * rt1[1] + temp_fac * rt2[1]) >> 8;
|
|
|
|
|
rt[2] = (temp_mfac * rt1[2] + temp_fac * rt2[2]) >> 8;
|
|
|
|
|
rt[3] = (temp_mfac * rt1[3] + temp_fac * rt2[3]) >> 8;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1 += 4;
|
|
|
|
|
rt2 += 4;
|
|
|
|
|
rt += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
static void do_cross_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 16:34:00 +01:00
|
|
|
float *rt1 = rect1;
|
|
|
|
|
float *rt2 = rect2;
|
|
|
|
|
float *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
float mfac = 1.0f - fac;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
2021-12-28 14:28:52 +01:00
|
|
|
rt[0] = mfac * rt1[0] + fac * rt2[0];
|
|
|
|
|
rt[1] = mfac * rt1[1] + fac * rt2[1];
|
|
|
|
|
rt[2] = mfac * rt1[2] + fac * rt2[2];
|
|
|
|
|
rt[3] = mfac * rt1[3] + fac * rt2[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1 += 4;
|
|
|
|
|
rt2 += 4;
|
|
|
|
|
rt += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_cross_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_cross_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_cross_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Gamma Cross
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-06 19:37:00 +01:00
|
|
|
/* One could argue that gamma cross should not be hardcoded to 2.0 gamma,
|
|
|
|
|
* but instead either do proper input->linear conversion (often sRGB). Or
|
|
|
|
|
* maybe not even that, but do interpolation in some perceptual color space
|
2023-12-07 12:15:45 +11:00
|
|
|
* like OKLAB. But currently it is fixed to just 2.0 gamma. */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
|
|
|
|
static float gammaCorrect(float c)
|
|
|
|
|
{
|
2023-12-06 19:37:00 +01:00
|
|
|
if (UNLIKELY(c < 0)) {
|
|
|
|
|
return -(c * c);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-12-06 19:37:00 +01:00
|
|
|
return c * c;
|
2012-08-08 11:15:36 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2014-03-05 22:33:47 +11:00
|
|
|
static float invGammaCorrect(float c)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:37:00 +01:00
|
|
|
return sqrtf_signed(c);
|
2012-08-08 11:15:36 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
template<typename T>
|
|
|
|
|
static void do_gammacross_effect(
|
|
|
|
|
float fac, int width, int height, const T *src1, const T *src2, T *dst)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 14:28:52 +01:00
|
|
|
float mfac = 1.0f - fac;
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float4 col1 = load_premul_pixel(src1);
|
|
|
|
|
float4 col2 = load_premul_pixel(src2);
|
|
|
|
|
float4 col;
|
|
|
|
|
for (int c = 0; c < 4; ++c) {
|
|
|
|
|
col[c] = gammaCorrect(mfac * invGammaCorrect(col1[c]) + fac * invGammaCorrect(col2[c]));
|
|
|
|
|
}
|
|
|
|
|
store_premul_pixel(col, dst);
|
|
|
|
|
src1 += 4;
|
|
|
|
|
src2 += 4;
|
|
|
|
|
dst += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_gammacross_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2012-08-08 16:46:45 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
do_gammacross_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
do_gammacross_effect(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Color Add Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
static void do_add_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *cp1 = rect1;
|
|
|
|
|
uchar *cp2 = rect2;
|
|
|
|
|
uchar *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
int temp_fac = int(256.0f * fac);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
2023-07-20 20:11:08 +10:00
|
|
|
const int temp_fac2 = temp_fac * int(cp2[3]);
|
2021-12-28 16:34:00 +01:00
|
|
|
rt[0] = min_ii(cp1[0] + ((temp_fac2 * cp2[0]) >> 16), 255);
|
|
|
|
|
rt[1] = min_ii(cp1[1] + ((temp_fac2 * cp2[1]) >> 16), 255);
|
|
|
|
|
rt[2] = min_ii(cp1[2] + ((temp_fac2 * cp2[2]) >> 16), 255);
|
2013-04-09 16:20:24 +00:00
|
|
|
rt[3] = cp1[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-31 13:52:13 +00:00
|
|
|
cp1 += 4;
|
|
|
|
|
cp2 += 4;
|
|
|
|
|
rt += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
static void do_add_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 16:34:00 +01:00
|
|
|
float *rt1 = rect1;
|
|
|
|
|
float *rt2 = rect2;
|
|
|
|
|
float *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
|
|
|
|
const float temp_fac = (1.0f - (rt1[3] * (1.0f - fac))) * rt2[3];
|
|
|
|
|
rt[0] = rt1[0] + temp_fac * rt2[0];
|
|
|
|
|
rt[1] = rt1[1] + temp_fac * rt2[1];
|
|
|
|
|
rt[2] = rt1[2] + temp_fac * rt2[2];
|
2013-04-09 16:20:24 +00:00
|
|
|
rt[3] = rt1[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-31 13:52:13 +00:00
|
|
|
rt1 += 4;
|
|
|
|
|
rt2 += 4;
|
|
|
|
|
rt += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_add_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_add_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_add_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Color Subtract Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
static void do_sub_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *cp1 = rect1;
|
|
|
|
|
uchar *cp2 = rect2;
|
|
|
|
|
uchar *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
int temp_fac = int(256.0f * fac);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
2023-07-20 20:11:08 +10:00
|
|
|
const int temp_fac2 = temp_fac * int(cp2[3]);
|
2021-12-28 16:34:00 +01:00
|
|
|
rt[0] = max_ii(cp1[0] - ((temp_fac2 * cp2[0]) >> 16), 0);
|
|
|
|
|
rt[1] = max_ii(cp1[1] - ((temp_fac2 * cp2[1]) >> 16), 0);
|
|
|
|
|
rt[2] = max_ii(cp1[2] - ((temp_fac2 * cp2[2]) >> 16), 0);
|
2013-04-09 16:20:24 +00:00
|
|
|
rt[3] = cp1[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-12-31 13:52:13 +00:00
|
|
|
cp1 += 4;
|
|
|
|
|
cp2 += 4;
|
|
|
|
|
rt += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
static void do_sub_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 16:34:00 +01:00
|
|
|
float *rt1 = rect1;
|
|
|
|
|
float *rt2 = rect2;
|
|
|
|
|
float *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
float mfac = 1.0f - fac;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
|
|
|
|
const float temp_fac = (1.0f - (rt1[3] * mfac)) * rt2[3];
|
|
|
|
|
rt[0] = max_ff(rt1[0] - temp_fac * rt2[0], 0.0f);
|
|
|
|
|
rt[1] = max_ff(rt1[1] - temp_fac * rt2[1], 0.0f);
|
|
|
|
|
rt[2] = max_ff(rt1[2] - temp_fac * rt2[2], 0.0f);
|
2013-04-09 16:20:24 +00:00
|
|
|
rt[3] = rt1[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-28 08:21:29 +00:00
|
|
|
rt1 += 4;
|
|
|
|
|
rt2 += 4;
|
|
|
|
|
rt += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_sub_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_sub_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_sub_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Drop Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-05-24 11:21:18 +10:00
|
|
|
/* Must be > 0 or add pre-copy, etc to the function. */
|
2012-05-08 09:31:25 +00:00
|
|
|
#define XOFF 8
|
|
|
|
|
#define YOFF 8
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
static void do_drop_effect_byte(float fac, int x, int y, uchar *rect2i, uchar *rect1i, uchar *outi)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 16:34:00 +01:00
|
|
|
const int xoff = min_ii(XOFF, x);
|
|
|
|
|
const int yoff = min_ii(YOFF, y);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
int temp_fac = int(70.0f * fac);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *rt2 = rect2i + yoff * 4 * x;
|
|
|
|
|
uchar *rt1 = rect1i;
|
|
|
|
|
uchar *out = outi;
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y - yoff; i++) {
|
2016-12-07 14:53:57 +01:00
|
|
|
memcpy(out, rt1, sizeof(*out) * xoff * 4);
|
2016-12-07 14:16:07 +01:00
|
|
|
rt1 += xoff * 4;
|
|
|
|
|
out += xoff * 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int j = xoff; j < x; j++) {
|
2021-12-28 14:28:52 +01:00
|
|
|
int temp_fac2 = ((temp_fac * rt2[3]) >> 8);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
|
|
|
|
rt2 += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2016-12-07 14:16:07 +01:00
|
|
|
rt2 += xoff * 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2021-12-28 16:34:00 +01:00
|
|
|
memcpy(out, rt1, sizeof(*out) * yoff * 4 * x);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_drop_effect_float(
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac, int x, int y, float *rect2i, float *rect1i, float *outi)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 16:34:00 +01:00
|
|
|
const int xoff = min_ii(XOFF, x);
|
|
|
|
|
const int yoff = min_ii(YOFF, y);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
float temp_fac = 70.0f * fac;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
float *rt2 = rect2i + yoff * 4 * x;
|
|
|
|
|
float *rt1 = rect1i;
|
|
|
|
|
float *out = outi;
|
|
|
|
|
for (int i = 0; i < y - yoff; i++) {
|
2016-12-07 14:53:57 +01:00
|
|
|
memcpy(out, rt1, sizeof(*out) * xoff * 4);
|
2016-12-07 14:16:07 +01:00
|
|
|
rt1 += xoff * 4;
|
|
|
|
|
out += xoff * 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int j = xoff; j < x; j++) {
|
2021-12-28 14:28:52 +01:00
|
|
|
float temp_fac2 = temp_fac * rt2[3];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0.0f, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0.0f, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0.0f, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
2023-11-07 16:33:19 +11:00
|
|
|
*(out++) = std::max(0.0f, *rt1 - temp_fac2);
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1++;
|
|
|
|
|
rt2 += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2016-12-07 14:16:07 +01:00
|
|
|
rt2 += xoff * 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2021-12-28 16:34:00 +01:00
|
|
|
memcpy(out, rt1, sizeof(*out) * yoff * 4 * x);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Multiply Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
static void do_mul_effect_byte(float fac, int x, int y, uchar *rect1, uchar *rect2, uchar *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *rt1 = rect1;
|
|
|
|
|
uchar *rt2 = rect2;
|
|
|
|
|
uchar *rt = out;
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
int temp_fac = int(256.0f * fac);
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2021-07-23 16:56:00 +10:00
|
|
|
/* Formula:
|
|
|
|
|
* `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + axaux = c * px + py * s;` // + centx
|
|
|
|
|
* `yaux = -s * px + c * py;` // + centy */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
2021-12-28 14:28:52 +01:00
|
|
|
rt[0] = rt1[0] + ((temp_fac * rt1[0] * (rt2[0] - 255)) >> 16);
|
|
|
|
|
rt[1] = rt1[1] + ((temp_fac * rt1[1] * (rt2[1] - 255)) >> 16);
|
|
|
|
|
rt[2] = rt1[2] + ((temp_fac * rt1[2] * (rt2[2] - 255)) >> 16);
|
|
|
|
|
rt[3] = rt1[3] + ((temp_fac * rt1[3] * (rt2[3] - 255)) >> 16);
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1 += 4;
|
|
|
|
|
rt2 += 4;
|
|
|
|
|
rt += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
static void do_mul_effect_float(float fac, int x, int y, float *rect1, float *rect2, float *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 16:34:00 +01:00
|
|
|
float *rt1 = rect1;
|
|
|
|
|
float *rt2 = rect2;
|
|
|
|
|
float *rt = out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-07-23 16:56:00 +10:00
|
|
|
/* Formula:
|
|
|
|
|
* `fac * (a * b) + (1 - fac) * a => fac * a * (b - 1) + a`. */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 16:34:00 +01:00
|
|
|
for (int i = 0; i < y; i++) {
|
|
|
|
|
for (int j = 0; j < x; j++) {
|
2021-12-28 14:28:52 +01:00
|
|
|
rt[0] = rt1[0] + fac * rt1[0] * (rt2[0] - 1.0f);
|
|
|
|
|
rt[1] = rt1[1] + fac * rt1[1] * (rt2[1] - 1.0f);
|
|
|
|
|
rt[2] = rt1[2] + fac * rt1[2] * (rt2[2] - 1.0f);
|
|
|
|
|
rt[3] = rt1[3] + fac * rt1[3] * (rt2[3] - 1.0f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
rt1 += 4;
|
|
|
|
|
rt2 += 4;
|
|
|
|
|
rt += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_mul_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_mul_effect_float(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_mul_effect_byte(fac, context->rectx, total_lines, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Blend Mode Effect
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* blend_function has to be: void (T* dst, const T *src1, const T *src2) */
|
|
|
|
|
template<typename T, typename Func>
|
|
|
|
|
static void apply_blend_function(
|
|
|
|
|
float fac, int width, int height, const T *src1, T *src2, T *dst, Func blend_function)
|
|
|
|
|
{
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
T achannel = src2[3];
|
|
|
|
|
src2[3] = T(achannel * fac);
|
|
|
|
|
blend_function(dst, src1, src2);
|
|
|
|
|
src2[3] = achannel;
|
|
|
|
|
dst[3] = src1[3];
|
|
|
|
|
src1 += 4;
|
|
|
|
|
src2 += 4;
|
|
|
|
|
dst += 4;
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_blend_effect_float(
|
2024-03-28 20:57:47 +11:00
|
|
|
float fac, int x, int y, const float *rect1, float *rect2, int btype, float *out)
|
2017-11-27 23:33:08 +01:00
|
|
|
{
|
|
|
|
|
switch (btype) {
|
|
|
|
|
case SEQ_TYPE_ADD:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_add_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SUB:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_sub_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_MUL:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_mul_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_DARKEN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_darken_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
2019-08-23 13:13:59 +02:00
|
|
|
case SEQ_TYPE_COLOR_BURN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_burn_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_LINEAR_BURN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearburn_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SCREEN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_screen_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_LIGHTEN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_lighten_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_DODGE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_dodge_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_OVERLAY:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_overlay_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SOFT_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_softlight_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_HARD_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hardlight_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_PIN_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_pinlight_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_LIN_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearlight_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_VIVID_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_vividlight_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_BLEND_COLOR:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_color_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_HUE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hue_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SATURATION:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_saturation_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_VALUE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_luminosity_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_DIFFERENCE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_difference_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_EXCLUSION:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_exclusion_float);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-25 17:04:52 +10:00
|
|
|
static void do_blend_effect_byte(
|
2024-08-26 11:34:42 +10:00
|
|
|
float fac, int x, int y, const uchar *rect1, uchar *rect2, int btype, uchar *out)
|
2017-11-27 23:33:08 +01:00
|
|
|
{
|
|
|
|
|
switch (btype) {
|
|
|
|
|
case SEQ_TYPE_ADD:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_add_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SUB:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_sub_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_MUL:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_mul_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_DARKEN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_darken_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
2019-08-23 13:13:59 +02:00
|
|
|
case SEQ_TYPE_COLOR_BURN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_burn_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_LINEAR_BURN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearburn_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SCREEN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_screen_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_LIGHTEN:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_lighten_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_DODGE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_dodge_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_OVERLAY:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_overlay_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SOFT_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_softlight_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_HARD_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hardlight_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_PIN_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_pinlight_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_LIN_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_linearlight_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_VIVID_LIGHT:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_vividlight_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_BLEND_COLOR:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_color_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_HUE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_hue_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_SATURATION:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_saturation_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_VALUE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_luminosity_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_DIFFERENCE:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_difference_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_EXCLUSION:
|
2023-12-14 17:31:05 +01:00
|
|
|
apply_blend_function(fac, x, y, rect1, rect2, out, blend_color_exclusion_byte);
|
2017-11-27 23:33:08 +01:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_blend_mode_effect(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2017-11-27 23:33:08 +01:00
|
|
|
{
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
do_blend_effect_float(
|
2021-12-28 14:28:52 +01:00
|
|
|
fac, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
do_blend_effect_byte(
|
2021-12-28 14:28:52 +01:00
|
|
|
fac, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-07 10:51:22 +11:00
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Color Mix Effect
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2017-11-27 23:33:08 +01:00
|
|
|
static void init_colormix_effect(Sequence *seq)
|
|
|
|
|
{
|
2017-12-04 16:37:31 +11:00
|
|
|
if (seq->effectdata) {
|
2017-11-27 23:33:08 +01:00
|
|
|
MEM_freeN(seq->effectdata);
|
|
|
|
|
}
|
|
|
|
|
seq->effectdata = MEM_callocN(sizeof(ColorMixVars), "colormixvars");
|
2024-10-30 13:37:31 +02:00
|
|
|
ColorMixVars *data = (ColorMixVars *)seq->effectdata;
|
2017-11-27 23:33:08 +01:00
|
|
|
data->blend_effect = SEQ_TYPE_OVERLAY;
|
|
|
|
|
data->factor = 1.0f;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_colormix_effect(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
|
|
|
|
float /*fac*/,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2017-11-27 23:33:08 +01:00
|
|
|
{
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
ColorMixVars *data = static_cast<ColorMixVars *>(seq->effectdata);
|
2021-12-28 14:28:52 +01:00
|
|
|
fac = data->factor;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
do_blend_effect_float(
|
2021-12-28 14:28:52 +01:00
|
|
|
fac, context->rectx, total_lines, rect1, rect2, data->blend_effect, rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
do_blend_effect_byte(
|
2021-12-28 14:28:52 +01:00
|
|
|
fac, context->rectx, total_lines, rect1, rect2, data->blend_effect, rect_out);
|
2017-11-27 23:33:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Wipe Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
struct WipeZone {
|
2009-01-12 19:02:08 +00:00
|
|
|
float angle;
|
|
|
|
|
int flip;
|
|
|
|
|
int xo, yo;
|
|
|
|
|
int width;
|
|
|
|
|
float pythangle;
|
2023-12-06 19:42:59 +01:00
|
|
|
float clockWidth;
|
|
|
|
|
int type;
|
|
|
|
|
bool forward;
|
2023-07-20 09:46:24 +02:00
|
|
|
};
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
static WipeZone precalc_wipe_zone(const WipeVars *wipe, int xo, int yo)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:42:59 +01:00
|
|
|
WipeZone zone;
|
|
|
|
|
zone.flip = (wipe->angle < 0.0f);
|
|
|
|
|
zone.angle = tanf(fabsf(wipe->angle));
|
|
|
|
|
zone.xo = xo;
|
|
|
|
|
zone.yo = yo;
|
|
|
|
|
zone.width = int(wipe->edgeWidth * ((xo + yo) / 2.0f));
|
|
|
|
|
zone.pythangle = 1.0f / sqrtf(zone.angle * zone.angle + 1.0f);
|
|
|
|
|
zone.clockWidth = wipe->edgeWidth * float(M_PI);
|
|
|
|
|
zone.type = wipe->wipetype;
|
|
|
|
|
zone.forward = wipe->forward != 0;
|
|
|
|
|
return zone;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/**
|
|
|
|
|
* This function calculates the blur band for the wipe effects.
|
|
|
|
|
*/
|
2012-04-29 15:47:02 +00:00
|
|
|
static float in_band(float width, float dist, int side, int dir)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-01-06 10:08:46 +00:00
|
|
|
float alpha;
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (width == 0) {
|
2023-07-20 20:11:08 +10:00
|
|
|
return float(side);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-01-06 10:08:46 +00:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (width < dist) {
|
2023-07-20 20:11:08 +10:00
|
|
|
return float(side);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-01-06 10:08:46 +00:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (side == 1) {
|
2012-05-08 09:31:25 +00:00
|
|
|
alpha = (dist + 0.5f * width) / (width);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-05-08 09:31:25 +00:00
|
|
|
alpha = (0.5f * width - dist) / (width);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-01-06 10:08:46 +00:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (dir == 0) {
|
2012-05-08 09:31:25 +00:00
|
|
|
alpha = 1 - alpha;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
|
|
|
|
return alpha;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
static float check_zone(const WipeZone *wipezone, int x, int y, float fac)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-04-29 15:47:02 +00:00
|
|
|
float posx, posy, hyp, hyp2, angle, hwidth, b1, b2, b3, pointdist;
|
2012-08-08 11:15:36 +00:00
|
|
|
float temp1, temp2, temp3, temp4; /* some placeholder variables */
|
2009-01-12 19:02:08 +00:00
|
|
|
int xo = wipezone->xo;
|
|
|
|
|
int yo = wipezone->yo;
|
2012-05-08 09:31:25 +00:00
|
|
|
float halfx = xo * 0.5f;
|
|
|
|
|
float halfy = yo * 0.5f;
|
|
|
|
|
float widthf, output = 0;
|
2009-01-12 19:02:08 +00:00
|
|
|
int width;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (wipezone->flip) {
|
2012-03-24 06:18:31 +00:00
|
|
|
x = xo - x;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
angle = wipezone->angle;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
if (wipezone->forward) {
|
2021-12-28 14:28:52 +01:00
|
|
|
posx = fac * xo;
|
|
|
|
|
posy = fac * yo;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2021-12-28 14:28:52 +01:00
|
|
|
posx = xo - fac * xo;
|
|
|
|
|
posy = yo - fac * yo;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
switch (wipezone->type) {
|
2009-01-12 19:02:08 +00:00
|
|
|
case DO_SINGLE_WIPE:
|
2021-12-28 14:28:52 +01:00
|
|
|
width = min_ii(wipezone->width, fac * yo);
|
|
|
|
|
width = min_ii(width, yo - fac * yo);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (angle == 0.0f) {
|
2009-01-12 19:02:08 +00:00
|
|
|
b1 = posy;
|
|
|
|
|
b2 = y;
|
2014-09-17 14:11:37 +10:00
|
|
|
hyp = fabsf(y - posy);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-05-08 09:31:25 +00:00
|
|
|
b1 = posy - (-angle) * posx;
|
|
|
|
|
b2 = y - (-angle) * x;
|
|
|
|
|
hyp = fabsf(angle * x + y + (-posy - angle * posx)) * wipezone->pythangle;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (angle < 0) {
|
2009-01-12 19:02:08 +00:00
|
|
|
temp1 = b1;
|
|
|
|
|
b1 = b2;
|
|
|
|
|
b2 = temp1;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
if (wipezone->forward) {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (b1 < b2) {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(width, hyp, 1, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(width, hyp, 0, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
else {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (b1 < b2) {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(width, hyp, 0, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(width, hyp, 1, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2011-04-21 15:53:30 +00:00
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
case DO_DOUBLE_WIPE:
|
2023-12-06 19:42:59 +01:00
|
|
|
if (!wipezone->forward) {
|
2021-12-28 14:28:52 +01:00
|
|
|
fac = 1.0f - fac; /* Go the other direction */
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-08 11:15:36 +00:00
|
|
|
width = wipezone->width; /* calculate the blur width */
|
2012-05-08 09:31:25 +00:00
|
|
|
hwidth = width * 0.5f;
|
2009-01-12 19:02:08 +00:00
|
|
|
if (angle == 0) {
|
2012-05-08 09:31:25 +00:00
|
|
|
b1 = posy * 0.5f;
|
|
|
|
|
b3 = yo - posy * 0.5f;
|
2009-01-12 19:02:08 +00:00
|
|
|
b2 = y;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-03-01 13:39:36 +11:00
|
|
|
hyp = fabsf(y - posy * 0.5f);
|
|
|
|
|
hyp2 = fabsf(y - (yo - posy * 0.5f));
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-05-08 09:31:25 +00:00
|
|
|
b1 = posy * 0.5f - (-angle) * posx * 0.5f;
|
|
|
|
|
b3 = (yo - posy * 0.5f) - (-angle) * (xo - posx * 0.5f);
|
|
|
|
|
b2 = y - (-angle) * x;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
hyp = fabsf(angle * x + y + (-posy * 0.5f - angle * posx * 0.5f)) * wipezone->pythangle;
|
|
|
|
|
hyp2 = fabsf(angle * x + y + (-(yo - posy * 0.5f) - angle * (xo - posx * 0.5f))) *
|
|
|
|
|
wipezone->pythangle;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-10-23 13:28:22 +00:00
|
|
|
hwidth = min_ff(hwidth, fabsf(b3 - b1) / 2.0f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
if (b2 < b1 && b2 < b3) {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(hwidth, hyp, 0, 1);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
else if (b2 > b1 && b2 > b3) {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(hwidth, hyp2, 0, 1);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2019-04-22 09:39:35 +10:00
|
|
|
if (hyp < hwidth && hyp2 > hwidth) {
|
2012-04-29 15:47:02 +00:00
|
|
|
output = in_band(hwidth, hyp, 1, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else if (hyp > hwidth && hyp2 < hwidth) {
|
2012-05-08 09:31:25 +00:00
|
|
|
output = in_band(hwidth, hyp2, 1, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-05-08 09:31:25 +00:00
|
|
|
output = in_band(hwidth, hyp2, 1, 1) * in_band(hwidth, hyp, 1, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2023-12-06 19:42:59 +01:00
|
|
|
if (!wipezone->forward) {
|
2012-05-08 09:31:25 +00:00
|
|
|
output = 1 - output;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2011-04-21 15:53:30 +00:00
|
|
|
case DO_CLOCK_WIPE:
|
2012-03-09 18:28:30 +00:00
|
|
|
/*
|
2018-09-02 18:28:27 +10:00
|
|
|
* temp1: angle of effect center in rads
|
|
|
|
|
* temp2: angle of line through (halfx, halfy) and (x, y) in rads
|
|
|
|
|
* temp3: angle of low side of blur
|
|
|
|
|
* temp4: angle of high side of blur
|
2012-03-09 18:28:30 +00:00
|
|
|
*/
|
2021-12-28 14:28:52 +01:00
|
|
|
output = 1.0f - fac;
|
2023-12-06 19:42:59 +01:00
|
|
|
widthf = wipezone->clockWidth;
|
2023-07-20 20:11:08 +10:00
|
|
|
temp1 = 2.0f * float(M_PI) * fac;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
if (wipezone->forward) {
|
2023-07-20 20:11:08 +10:00
|
|
|
temp1 = 2.0f * float(M_PI) - temp1;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-04-21 15:53:30 +00:00
|
|
|
x = x - halfx;
|
2012-05-08 09:31:25 +00:00
|
|
|
y = y - halfy;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
temp2 = atan2f(y, x);
|
|
|
|
|
if (temp2 < 0.0f) {
|
|
|
|
|
temp2 += 2.0f * float(M_PI);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
if (wipezone->forward) {
|
|
|
|
|
temp3 = temp1 - widthf * fac;
|
|
|
|
|
temp4 = temp1 + widthf * (1 - fac);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-06 19:42:59 +01:00
|
|
|
temp3 = temp1 - widthf * (1 - fac);
|
|
|
|
|
temp4 = temp1 + widthf * fac;
|
2011-04-21 15:53:30 +00:00
|
|
|
}
|
2019-04-22 09:39:35 +10:00
|
|
|
if (temp3 < 0) {
|
2011-04-21 15:53:30 +00:00
|
|
|
temp3 = 0;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-07-20 20:11:08 +10:00
|
|
|
if (temp4 > 2.0f * float(M_PI)) {
|
|
|
|
|
temp4 = 2.0f * float(M_PI);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-04-22 09:39:35 +10:00
|
|
|
if (temp2 < temp3) {
|
2012-03-24 06:18:31 +00:00
|
|
|
output = 0;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else if (temp2 > temp4) {
|
2011-04-21 15:53:30 +00:00
|
|
|
output = 1;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2012-05-08 09:31:25 +00:00
|
|
|
output = (temp2 - temp3) / (temp4 - temp3);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
if (x == 0 && y == 0) {
|
2012-03-24 06:18:31 +00:00
|
|
|
output = 1;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
if (output != output) {
|
2012-03-24 06:18:31 +00:00
|
|
|
output = 1;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-12-06 19:42:59 +01:00
|
|
|
if (wipezone->forward) {
|
2012-03-24 06:18:31 +00:00
|
|
|
output = 1 - output;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2009-01-12 19:02:08 +00:00
|
|
|
case DO_IRIS_WIPE:
|
2019-04-22 09:39:35 +10:00
|
|
|
if (xo > yo) {
|
2012-03-24 06:18:31 +00:00
|
|
|
yo = xo;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2009-01-12 19:02:08 +00:00
|
|
|
xo = yo;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
if (!wipezone->forward) {
|
2021-12-28 14:28:52 +01:00
|
|
|
fac = 1 - fac;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
width = wipezone->width;
|
2012-05-08 09:31:25 +00:00
|
|
|
hwidth = width * 0.5f;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
temp1 = (halfx - (halfx)*fac);
|
2014-06-14 17:27:50 +10:00
|
|
|
pointdist = hypotf(temp1, temp1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-06-14 17:27:50 +10:00
|
|
|
temp2 = hypotf(halfx - x, halfy - y);
|
2019-04-22 09:39:35 +10:00
|
|
|
if (temp2 > pointdist) {
|
2014-03-01 14:20:54 +11:00
|
|
|
output = in_band(hwidth, fabsf(temp2 - pointdist), 0, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2014-03-01 14:20:54 +11:00
|
|
|
output = in_band(hwidth, fabsf(temp2 - pointdist), 1, 1);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:42:59 +01:00
|
|
|
if (!wipezone->forward) {
|
2012-05-08 09:31:25 +00:00
|
|
|
output = 1 - output;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-22 09:39:35 +10:00
|
|
|
if (output < 0) {
|
2009-01-12 19:02:08 +00:00
|
|
|
output = 0;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
|
|
|
|
else if (output > 1) {
|
2012-03-24 06:18:31 +00:00
|
|
|
output = 1;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
return output;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void init_wipe_effect(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2012-08-08 11:15:36 +00:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2012-08-08 11:15:38 +00:00
|
|
|
seq->effectdata = MEM_callocN(sizeof(WipeVars), "wipevars");
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_wipe()
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2016-03-30 06:33:35 +11:00
|
|
|
return 2;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_wipe_effect(Sequence *seq, const bool /*do_id_user*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_wipe_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
template<typename T>
|
|
|
|
|
static void do_wipe_effect(
|
|
|
|
|
const Sequence *seq, float fac, int width, int height, const T *rect1, const T *rect2, T *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:42:59 +01:00
|
|
|
using namespace blender;
|
|
|
|
|
const WipeVars *wipe = (const WipeVars *)seq->effectdata;
|
|
|
|
|
const WipeZone wipezone = precalc_wipe_zone(wipe, width, height);
|
|
|
|
|
|
|
|
|
|
threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
|
2023-12-14 17:31:05 +01:00
|
|
|
const T *cp1 = rect1 ? rect1 + y_range.first() * width * 4 : nullptr;
|
|
|
|
|
const T *cp2 = rect2 ? rect2 + y_range.first() * width * 4 : nullptr;
|
|
|
|
|
T *rt = out + y_range.first() * width * 4;
|
2023-12-06 19:42:59 +01:00
|
|
|
for (const int y : y_range) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float check = check_zone(&wipezone, x, y, fac);
|
|
|
|
|
if (check) {
|
|
|
|
|
if (cp1) {
|
2023-12-14 17:31:05 +01:00
|
|
|
float4 col1 = load_premul_pixel(cp1);
|
|
|
|
|
float4 col2 = load_premul_pixel(cp2);
|
|
|
|
|
float4 col = col1 * check + col2 * (1.0f - check);
|
|
|
|
|
store_premul_pixel(col, rt);
|
2023-12-06 19:42:59 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-14 17:31:05 +01:00
|
|
|
store_opaque_black_pixel(rt);
|
2023-12-06 19:42:59 +01:00
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-06 19:42:59 +01:00
|
|
|
if (cp2) {
|
2023-12-14 17:31:05 +01:00
|
|
|
memcpy(rt, cp2, sizeof(T) * 4);
|
2023-12-06 19:42:59 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-14 17:31:05 +01:00
|
|
|
store_opaque_black_pixel(rt);
|
2023-12-06 19:42:59 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2023-12-06 19:42:59 +01:00
|
|
|
|
|
|
|
|
rt += 4;
|
|
|
|
|
if (cp1 != nullptr) {
|
|
|
|
|
cp1 += 4;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2023-12-06 19:42:59 +01:00
|
|
|
if (cp2 != nullptr) {
|
|
|
|
|
cp2 += 4;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2023-12-06 19:42:59 +01:00
|
|
|
});
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static ImBuf *do_wipe_effect(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
ImBuf *ibuf1,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *ibuf2)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-12-14 17:31:05 +01:00
|
|
|
do_wipe_effect(seq,
|
|
|
|
|
fac,
|
|
|
|
|
context->rectx,
|
|
|
|
|
context->recty,
|
|
|
|
|
ibuf1->float_buffer.data,
|
|
|
|
|
ibuf2->float_buffer.data,
|
|
|
|
|
out->float_buffer.data);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-12-14 17:31:05 +01:00
|
|
|
do_wipe_effect(seq,
|
|
|
|
|
fac,
|
|
|
|
|
context->rectx,
|
|
|
|
|
context->recty,
|
|
|
|
|
ibuf1->byte_buffer.data,
|
|
|
|
|
ibuf2->byte_buffer.data,
|
|
|
|
|
out->byte_buffer.data);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-07-25 17:19:55 +00:00
|
|
|
return out;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Transform Effect
|
|
|
|
|
* \{ */
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
static void init_transform_effect(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2012-08-08 11:15:36 +00:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2012-08-08 11:15:38 +00:00
|
|
|
seq->effectdata = MEM_callocN(sizeof(TransformVars), "transformvars");
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2024-10-30 13:37:31 +02:00
|
|
|
TransformVars *transform = (TransformVars *)seq->effectdata;
|
2009-12-11 22:51:53 +00:00
|
|
|
|
|
|
|
|
transform->ScalexIni = 1.0f;
|
|
|
|
|
transform->ScaleyIni = 1.0f;
|
|
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
transform->xIni = 0.0f;
|
|
|
|
|
transform->yIni = 0.0f;
|
2009-12-11 22:51:53 +00:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
transform->rotIni = 0.0f;
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
transform->interpolation = 1;
|
|
|
|
|
transform->percent = 1;
|
|
|
|
|
transform->uniform_scale = 0;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_transform()
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_transform_effect(Sequence *seq, const bool /*do_id_user*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_transform_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void transform_image(int x,
|
|
|
|
|
int y,
|
2020-08-17 20:52:59 +02:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf,
|
2017-12-04 16:49:30 +11:00
|
|
|
ImBuf *out,
|
|
|
|
|
float scale_x,
|
|
|
|
|
float scale_y,
|
|
|
|
|
float translate_x,
|
|
|
|
|
float translate_y,
|
|
|
|
|
float rotate,
|
|
|
|
|
int interpolation)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-08-08 11:15:36 +00:00
|
|
|
/* Rotate */
|
2020-08-17 20:52:59 +02:00
|
|
|
float s = sinf(rotate);
|
|
|
|
|
float c = cosf(rotate);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
float4 *dst_fl = reinterpret_cast<float4 *>(out->float_buffer.data);
|
|
|
|
|
uchar4 *dst_ch = reinterpret_cast<uchar4 *>(out->byte_buffer.data);
|
|
|
|
|
|
|
|
|
|
size_t offset = size_t(x) * start_line;
|
2020-08-17 20:52:59 +02:00
|
|
|
for (int yi = start_line; yi < start_line + total_lines; yi++) {
|
|
|
|
|
for (int xi = 0; xi < x; xi++) {
|
2021-06-24 15:56:58 +10:00
|
|
|
/* Translate point. */
|
2020-08-17 20:52:59 +02:00
|
|
|
float xt = xi - translate_x;
|
|
|
|
|
float yt = yi - translate_y;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-24 15:56:58 +10:00
|
|
|
/* Rotate point with center ref. */
|
2020-08-17 20:52:59 +02:00
|
|
|
float xr = c * xt + s * yt;
|
|
|
|
|
float yr = -s * xt + c * yt;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-24 15:56:58 +10:00
|
|
|
/* Scale point with center ref. */
|
2009-12-11 22:51:53 +00:00
|
|
|
xt = xr / scale_x;
|
|
|
|
|
yt = yr / scale_y;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-06-24 15:56:58 +10:00
|
|
|
/* Undo reference center point. */
|
2020-08-17 20:52:59 +02:00
|
|
|
xt += (x / 2.0f);
|
|
|
|
|
yt += (y / 2.0f);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-08 11:15:36 +00:00
|
|
|
/* interpolate */
|
2012-04-28 06:31:57 +00:00
|
|
|
switch (interpolation) {
|
2012-05-08 09:31:25 +00:00
|
|
|
case 0:
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
if (dst_fl) {
|
2024-03-21 13:22:10 +01:00
|
|
|
dst_fl[offset] = imbuf::interpolate_nearest_border_fl(ibuf, xt, yt);
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2024-03-21 13:22:10 +01:00
|
|
|
dst_ch[offset] = imbuf::interpolate_nearest_border_byte(ibuf, xt, yt);
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
|
|
|
|
case 1:
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
if (dst_fl) {
|
2024-02-02 16:28:51 +01:00
|
|
|
dst_fl[offset] = imbuf::interpolate_bilinear_border_fl(ibuf, xt, yt);
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2024-02-02 16:28:51 +01:00
|
|
|
dst_ch[offset] = imbuf::interpolate_bilinear_border_byte(ibuf, xt, yt);
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
|
|
|
|
case 2:
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
if (dst_fl) {
|
|
|
|
|
dst_fl[offset] = imbuf::interpolate_cubic_bspline_fl(ibuf, xt, yt);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dst_ch[offset] = imbuf::interpolate_cubic_bspline_byte(ibuf, xt, yt);
|
|
|
|
|
}
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
ImBuf: Refactor pixel interpolation functions
There exist a bunch of "give me a (filtered) image pixel at this location"
functions, some with duplicated functionality, some with almost the same but
not quite, some that look similar but behave slightly differently, etc.
Some of them were in BLI, some were in ImBuf.
This commit tries to improve the situation by:
* Adding low level interpolation functions to `BLI_math_interp.hh`
- With documentation on their behavior,
- And with more unit tests.
* At `ImBuf` level, there are only convenience inline wrappers to the above BLI
functions (split off into a separate header `IMB_interp.hh`). However, since
these wrappers are inline, some things get a tiny bit faster as a side
effect. E.g. VSE image strip, scaling to 4K resolution (Windows/Ryzen5950X):
- Nearest filter: 2.33 -> 1.94ms
- Bilinear filter: 5.83 -> 5.69ms
- Subsampled3x3 filter: 28.6 -> 22.4ms
Details on the functions:
- All of them have `_byte` and `_fl` suffixes.
- They exist in 4-channel byte (uchar4) and float (float4), as well as
explicitly passed amount of channels for other float images.
- New functions in BLI `blender::math` namespace:
- `interpolate_nearest`
- `interpolate_bilinear`
- `interpolate_bilinear_wrap`. Note that unlike previous "wrap" function,
this one no longer requires the caller to do their own wrapping.
- `interpolate_cubic_bspline`. Previous similar function was called just
"bicubic" which could mean many different things.
- Same functions exist in `IMB_interp.hh`, they are just convenience that takes
ImBuf and uses data pointer, width, height from that.
Other bits:
- Renamed `mod_f_positive` to `floored_fmod` (better matches `safe_floored_modf`
and `floored_modulo` that exist elsewhere), made it branchless and added more
unit tests.
- `interpolate_bilinear_wrap_fl` no longer clamps result to 0..1 range. Instead,
moved the clamp to be outside of the call in `paint_image_proj.cc` and
`paint_utils.cc`. Though the need for clamping in there is also questionable.
Pull Request: https://projects.blender.org/blender/blender/pulls/117387
2024-01-25 11:45:24 +01:00
|
|
|
offset++;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
}
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-17 20:52:59 +02:00
|
|
|
static void do_transform_effect(const SeqRenderData *context,
|
2020-08-20 16:09:48 +10:00
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
|
|
|
|
float /*fac*/,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf * /*ibuf2*/,
|
2020-08-20 16:09:48 +10:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2009-12-11 22:51:53 +00:00
|
|
|
{
|
2012-08-08 11:15:36 +00:00
|
|
|
TransformVars *transform = (TransformVars *)seq->effectdata;
|
2009-12-11 22:51:53 +00:00
|
|
|
float scale_x, scale_y, translate_x, translate_y, rotate_radians;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-07 22:51:57 +00:00
|
|
|
/* Scale */
|
2009-12-11 22:51:53 +00:00
|
|
|
if (transform->uniform_scale) {
|
|
|
|
|
scale_x = scale_y = transform->ScalexIni;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2009-12-11 22:51:53 +00:00
|
|
|
scale_x = transform->ScalexIni;
|
|
|
|
|
scale_y = transform->ScaleyIni;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-08-17 20:52:59 +02:00
|
|
|
int x = context->rectx;
|
|
|
|
|
int y = context->recty;
|
|
|
|
|
|
2012-07-07 22:51:57 +00:00
|
|
|
/* Translate */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!transform->percent) {
|
2021-10-25 05:35:54 +02:00
|
|
|
/* Compensate text size for preview render size. */
|
|
|
|
|
double proxy_size_comp = context->scene->r.size / 100.0;
|
|
|
|
|
if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
|
|
|
|
|
proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-10-25 05:35:54 +02:00
|
|
|
translate_x = transform->xIni * proxy_size_comp + (x / 2.0f);
|
|
|
|
|
translate_y = transform->yIni * proxy_size_comp + (y / 2.0f);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2012-05-08 09:31:25 +00:00
|
|
|
translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f);
|
|
|
|
|
translate_y = y * (transform->yIni / 100.0f) + (y / 2.0f);
|
2009-12-11 22:51:53 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-07-07 22:51:57 +00:00
|
|
|
/* Rotate */
|
2011-09-17 09:43:51 +00:00
|
|
|
rotate_radians = DEG2RADF(transform->rotIni);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-04-29 15:47:02 +00:00
|
|
|
transform_image(x,
|
|
|
|
|
y,
|
2020-08-17 20:52:59 +02:00
|
|
|
start_line,
|
|
|
|
|
total_lines,
|
2012-04-29 15:47:02 +00:00
|
|
|
ibuf1,
|
|
|
|
|
out,
|
|
|
|
|
scale_x,
|
|
|
|
|
scale_y,
|
|
|
|
|
translate_x,
|
|
|
|
|
translate_y,
|
|
|
|
|
rotate_radians,
|
|
|
|
|
transform->interpolation);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Glow Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
static void glow_blur_bitmap(
|
|
|
|
|
const float4 *src, float4 *map, int width, int height, float blur, int quality)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:39:42 +01:00
|
|
|
using namespace blender;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-08-08 11:15:36 +00:00
|
|
|
/* If we're not really blurring, bail out */
|
2019-04-22 09:39:35 +10:00
|
|
|
if (blur <= 0) {
|
2009-01-12 19:02:08 +00:00
|
|
|
return;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-08-30 10:24:45 +02:00
|
|
|
/* If result would be no blurring, early out. */
|
2023-12-06 19:39:42 +01:00
|
|
|
const int halfWidth = ((quality + 1) * blur);
|
2023-08-30 10:24:45 +02:00
|
|
|
if (halfWidth == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-06 19:39:42 +01:00
|
|
|
Array<float4> temp(width * height);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:39:42 +01:00
|
|
|
/* Initialize the gaussian filter. @TODO: use code from RE_filter_value */
|
|
|
|
|
Array<float> filter(halfWidth * 2);
|
|
|
|
|
const float k = -1.0f / (2.0f * float(M_PI) * blur * blur);
|
|
|
|
|
float weight = 0;
|
|
|
|
|
for (int ix = 0; ix < halfWidth; ix++) {
|
2023-07-20 20:11:08 +10:00
|
|
|
weight = float(exp(k * (ix * ix)));
|
2009-01-12 19:02:08 +00:00
|
|
|
filter[halfWidth - ix] = weight;
|
|
|
|
|
filter[halfWidth + ix] = weight;
|
|
|
|
|
}
|
|
|
|
|
filter[0] = weight;
|
2012-08-08 11:15:36 +00:00
|
|
|
/* Normalize the array */
|
2023-12-06 19:39:42 +01:00
|
|
|
float fval = 0;
|
|
|
|
|
for (int ix = 0; ix < halfWidth * 2; ix++) {
|
2012-05-08 09:31:25 +00:00
|
|
|
fval += filter[ix];
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2023-12-06 19:39:42 +01:00
|
|
|
for (int ix = 0; ix < halfWidth * 2; ix++) {
|
2012-05-08 09:31:25 +00:00
|
|
|
filter[ix] /= fval;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:39:42 +01:00
|
|
|
/* Blur the rows: read map, write temp */
|
|
|
|
|
threading::parallel_for(IndexRange(height), 32, [&](const IndexRange y_range) {
|
|
|
|
|
for (const int y : y_range) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float4 curColor = float4(0.0f);
|
|
|
|
|
int xmin = math::max(x - halfWidth, 0);
|
|
|
|
|
int xmax = math::min(x + halfWidth, width);
|
|
|
|
|
for (int nx = xmin, index = (xmin - x) + halfWidth; nx < xmax; nx++, index++) {
|
|
|
|
|
curColor += map[nx + y * width] * filter[index];
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2023-12-06 19:39:42 +01:00
|
|
|
temp[x + y * width] = curColor;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2023-12-06 19:39:42 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Blur the columns: read temp, write map */
|
|
|
|
|
threading::parallel_for(IndexRange(width), 32, [&](const IndexRange x_range) {
|
|
|
|
|
const float4 one = float4(1.0f);
|
|
|
|
|
for (const int x : x_range) {
|
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
|
|
|
float4 curColor = float4(0.0f);
|
|
|
|
|
int ymin = math::max(y - halfWidth, 0);
|
|
|
|
|
int ymax = math::min(y + halfWidth, height);
|
|
|
|
|
for (int ny = ymin, index = (ymin - y) + halfWidth; ny < ymax; ny++, index++) {
|
|
|
|
|
curColor += temp[x + ny * width] * filter[index];
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2023-12-06 19:39:42 +01:00
|
|
|
if (src != nullptr) {
|
|
|
|
|
curColor = math::min(one, src[x + y * width] + curColor);
|
|
|
|
|
}
|
|
|
|
|
map[x + y * width] = curColor;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2023-12-06 19:39:42 +01:00
|
|
|
});
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
static void blur_isolate_highlights(const float4 *in,
|
|
|
|
|
float4 *out,
|
2023-12-06 19:39:42 +01:00
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
float threshold,
|
|
|
|
|
float boost,
|
|
|
|
|
float clamp)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:39:42 +01:00
|
|
|
using namespace blender;
|
|
|
|
|
threading::parallel_for(IndexRange(height), 64, [&](const IndexRange y_range) {
|
|
|
|
|
const float4 clampv = float4(clamp);
|
|
|
|
|
for (const int y : y_range) {
|
|
|
|
|
int index = y * width;
|
|
|
|
|
for (int x = 0; x < width; x++, index++) {
|
|
|
|
|
|
|
|
|
|
/* Isolate the intensity */
|
|
|
|
|
float intensity = (in[index].x + in[index].y + in[index].z - threshold);
|
|
|
|
|
float4 val;
|
|
|
|
|
if (intensity > 0) {
|
|
|
|
|
val = math::min(clampv, in[index] * (boost * intensity));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
val = float4(0.0f);
|
|
|
|
|
}
|
|
|
|
|
out[index] = val;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-12-06 19:39:42 +01:00
|
|
|
});
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void init_glow_effect(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2012-08-08 11:15:36 +00:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2012-08-08 11:15:38 +00:00
|
|
|
seq->effectdata = MEM_callocN(sizeof(GlowVars), "glowvars");
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2024-10-30 13:37:31 +02:00
|
|
|
GlowVars *glow = (GlowVars *)seq->effectdata;
|
2009-01-12 19:02:08 +00:00
|
|
|
glow->fMini = 0.25;
|
|
|
|
|
glow->fClamp = 1.0;
|
|
|
|
|
glow->fBoost = 0.5;
|
|
|
|
|
glow->dDist = 3.0;
|
|
|
|
|
glow->dQuality = 3;
|
|
|
|
|
glow->bNoComp = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_glow()
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_glow_effect(Sequence *seq, const bool /*do_id_user*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_glow_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_glow_effect_byte(Sequence *seq,
|
|
|
|
|
int render_size,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
int x,
|
|
|
|
|
int y,
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *rect1,
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar * /*rect2*/,
|
2022-09-25 17:04:52 +10:00
|
|
|
uchar *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:39:42 +01:00
|
|
|
using namespace blender;
|
2012-12-31 13:52:13 +00:00
|
|
|
GlowVars *glow = (GlowVars *)seq->effectdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:39:42 +01:00
|
|
|
Array<float4> inbuf(x * y);
|
|
|
|
|
Array<float4> outbuf(x * y);
|
|
|
|
|
|
|
|
|
|
using namespace blender;
|
|
|
|
|
IMB_colormanagement_transform_from_byte_threaded(*inbuf.data(), rect1, x, y, 4, "sRGB", "sRGB");
|
|
|
|
|
|
|
|
|
|
blur_isolate_highlights(
|
|
|
|
|
inbuf.data(), outbuf.data(), x, y, glow->fMini * 3.0f, glow->fBoost * fac, glow->fClamp);
|
|
|
|
|
glow_blur_bitmap(glow->bNoComp ? nullptr : inbuf.data(),
|
|
|
|
|
outbuf.data(),
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
glow->dDist * (render_size / 100.0f),
|
|
|
|
|
glow->dQuality);
|
|
|
|
|
|
|
|
|
|
threading::parallel_for(IndexRange(y), 64, [&](const IndexRange y_range) {
|
|
|
|
|
size_t offset = y_range.first() * x;
|
|
|
|
|
IMB_buffer_byte_from_float(out + offset * 4,
|
|
|
|
|
*(outbuf.data() + offset),
|
|
|
|
|
4,
|
|
|
|
|
0.0f,
|
|
|
|
|
IB_PROFILE_SRGB,
|
|
|
|
|
IB_PROFILE_SRGB,
|
|
|
|
|
true,
|
|
|
|
|
x,
|
|
|
|
|
y_range.size(),
|
|
|
|
|
x,
|
|
|
|
|
x);
|
|
|
|
|
});
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_glow_effect_float(Sequence *seq,
|
|
|
|
|
int render_size,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
int x,
|
|
|
|
|
int y,
|
|
|
|
|
float *rect1,
|
2023-07-20 09:46:24 +02:00
|
|
|
float * /*rect2*/,
|
2017-12-04 16:49:30 +11:00
|
|
|
float *out)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-12-06 19:39:42 +01:00
|
|
|
using namespace blender;
|
|
|
|
|
float4 *outbuf = reinterpret_cast<float4 *>(out);
|
|
|
|
|
float4 *inbuf = reinterpret_cast<float4 *>(rect1);
|
2009-01-12 19:02:08 +00:00
|
|
|
GlowVars *glow = (GlowVars *)seq->effectdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-12-06 19:39:42 +01:00
|
|
|
blur_isolate_highlights(
|
2021-12-28 14:28:52 +01:00
|
|
|
inbuf, outbuf, x, y, glow->fMini * 3.0f, glow->fBoost * fac, glow->fClamp);
|
2023-12-06 19:39:42 +01:00
|
|
|
glow_blur_bitmap(glow->bNoComp ? nullptr : inbuf,
|
|
|
|
|
outbuf,
|
|
|
|
|
x,
|
|
|
|
|
y,
|
|
|
|
|
glow->dDist * (render_size / 100.0f),
|
|
|
|
|
glow->dQuality);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static ImBuf *do_glow_effect(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
ImBuf *ibuf1,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *ibuf2)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-01-19 00:16:19 +06:00
|
|
|
int render_size = 100 * context->rectx / context->scene->r.xsch;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2017-12-04 16:49:30 +11:00
|
|
|
do_glow_effect_float(seq,
|
|
|
|
|
render_size,
|
2021-12-28 14:28:52 +01:00
|
|
|
fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
context->rectx,
|
|
|
|
|
context->recty,
|
2023-05-18 10:19:01 +02:00
|
|
|
ibuf1->float_buffer.data,
|
2023-07-20 09:46:24 +02:00
|
|
|
nullptr,
|
2023-05-18 10:19:01 +02:00
|
|
|
out->float_buffer.data);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2017-12-04 16:49:30 +11:00
|
|
|
do_glow_effect_byte(seq,
|
|
|
|
|
render_size,
|
2021-12-28 14:28:52 +01:00
|
|
|
fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
context->rectx,
|
|
|
|
|
context->recty,
|
2023-05-18 10:19:01 +02:00
|
|
|
ibuf1->byte_buffer.data,
|
2023-07-20 09:46:24 +02:00
|
|
|
nullptr,
|
2023-05-18 10:19:01 +02:00
|
|
|
out->byte_buffer.data);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-07-25 17:19:55 +00:00
|
|
|
return out;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Solid Color Effect
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
|
|
|
|
static void init_solid_color(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2012-08-08 11:15:36 +00:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2012-08-08 11:15:38 +00:00
|
|
|
seq->effectdata = MEM_callocN(sizeof(SolidColorVars), "solidcolor");
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2024-10-30 13:37:31 +02:00
|
|
|
SolidColorVars *cv = (SolidColorVars *)seq->effectdata;
|
2009-01-12 19:02:08 +00:00
|
|
|
cv->col[0] = cv->col[1] = cv->col[2] = 0.5;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_color()
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_solid_color(Sequence *seq, const bool /*do_id_user*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_solid_color(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_color(const Sequence * /*seq*/, float /*fac*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::NoInput;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static ImBuf *do_solid_color(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
|
|
|
|
float /*fac*/,
|
2017-12-04 16:49:30 +11:00
|
|
|
ImBuf *ibuf1,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *ibuf2)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2024-01-12 18:04:38 +01:00
|
|
|
using namespace blender;
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
SolidColorVars *cv = (SolidColorVars *)seq->effectdata;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-01-12 18:04:38 +01:00
|
|
|
threading::parallel_for(IndexRange(out->y), 64, [&](const IndexRange y_range) {
|
|
|
|
|
if (out->byte_buffer.data) {
|
|
|
|
|
/* Byte image. */
|
|
|
|
|
uchar color[4];
|
|
|
|
|
rgb_float_to_uchar(color, cv->col);
|
|
|
|
|
color[3] = 255;
|
|
|
|
|
|
|
|
|
|
uchar *dst = out->byte_buffer.data + y_range.first() * out->x * 4;
|
|
|
|
|
uchar *dst_end = dst + y_range.size() * out->x * 4;
|
|
|
|
|
while (dst < dst_end) {
|
|
|
|
|
memcpy(dst, color, sizeof(color));
|
|
|
|
|
dst += 4;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2024-01-12 18:04:38 +01:00
|
|
|
else {
|
|
|
|
|
/* Float image. */
|
|
|
|
|
float color[4];
|
|
|
|
|
color[0] = cv->col[0];
|
|
|
|
|
color[1] = cv->col[1];
|
|
|
|
|
color[2] = cv->col[2];
|
|
|
|
|
color[3] = 1.0f;
|
|
|
|
|
|
|
|
|
|
float *dst = out->float_buffer.data + y_range.first() * out->x * 4;
|
|
|
|
|
float *dst_end = dst + y_range.size() * out->x * 4;
|
|
|
|
|
while (dst < dst_end) {
|
|
|
|
|
memcpy(dst, color, sizeof(color));
|
|
|
|
|
dst += 4;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
}
|
2024-01-12 18:04:38 +01:00
|
|
|
});
|
2021-11-15 21:03:43 +01:00
|
|
|
|
|
|
|
|
out->planes = R_IMF_PLANES_RGB;
|
|
|
|
|
|
2010-07-25 17:19:55 +00:00
|
|
|
return out;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
2022-03-08 13:48:31 +11:00
|
|
|
/** \name Multi-Camera Effect
|
2022-03-07 10:51:22 +11:00
|
|
|
* \{ */
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** No effect inputs for multi-camera, we use #give_ibuf_seq. */
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_multicam()
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_multicam(const Sequence * /*seq*/, float /*fac*/)
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
{
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::NoInput;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static ImBuf *do_multicam(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2020-11-06 14:10:59 +01:00
|
|
|
float timeline_frame,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*fac*/,
|
|
|
|
|
ImBuf * /*ibuf1*/,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf * /*ibuf2*/)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2012-08-08 11:15:36 +00:00
|
|
|
ImBuf *out;
|
2012-05-08 09:31:25 +00:00
|
|
|
Editing *ed;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
|
|
|
|
|
if (seq->multicam_source == 0 || seq->multicam_source >= seq->machine) {
|
2023-07-20 09:46:24 +02:00
|
|
|
return nullptr;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
2014-01-19 00:16:19 +06:00
|
|
|
ed = context->scene->ed;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
if (!ed) {
|
2023-07-20 09:46:24 +02:00
|
|
|
return nullptr;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
}
|
2022-06-02 01:39:40 +02:00
|
|
|
ListBase *seqbasep = SEQ_get_seqbase_by_seq(context->scene, seq);
|
2022-05-02 18:20:56 +02:00
|
|
|
ListBase *channels = SEQ_get_channels_by_seq(&ed->seqbase, &ed->channels, seq);
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
if (!seqbasep) {
|
2023-07-20 09:46:24 +02:00
|
|
|
return nullptr;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
2022-04-04 12:52:48 +02:00
|
|
|
out = seq_render_give_ibuf_seqbase(
|
|
|
|
|
context, timeline_frame, seq->multicam_source, channels, seqbasep);
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2010-07-25 17:19:55 +00:00
|
|
|
return out;
|
== Sequencer ==
This adds MULTICAM-editing support for blender. (Well, the beginning of.)
There is now a new effect track, named MULTICAM, which just selects
one of the lower tracks.
Doesn't sound that exciting, but if you combine this with A/B-Trim (moving
split points of two directly connected tracks around, while magically
resizing both strips, something to be added), you just do:
* add several tracks for your camera angles
* (optionally) sync those tracks
* add one multicam track on top
Use that multicam-track to edit your movie. (Either using fcurves on the
multicam source selector or using knife-tool and A/B-Trim.)
Compare that to:
* add several tracks
* add cross fades between them
* do some python scripting to add several fcurves to make that beast
somewhat work.
* cry out loud, using it, if you have to move cut points around
Alternatively, even harder:
* just edit the old way and put strip after strip
You might think, that this isn't really helpfull for animators, but
consider using scene-strips (in OpenGL-mode) for input, that are set for
different camera angles and can now be intercut a lot more easily...
Also: small fix on the way: the speed effect can now be used in cascade.
(Don't know, if anyone used it that way, but now it works.)
2010-04-25 12:53:39 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
2011-05-16 17:14:47 +00:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Adjustment Effect
|
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
|
|
/** No effect inputs for adjustment, we use #give_ibuf_seq. */
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_adjustment()
|
2011-05-16 17:14:47 +00:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_adjustment(const Sequence * /*seq*/, float /*fac*/)
|
2011-05-16 17:14:47 +00:00
|
|
|
{
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::NoInput;
|
2011-05-16 17:14:47 +00:00
|
|
|
}
|
|
|
|
|
|
2020-11-06 14:10:59 +01:00
|
|
|
static ImBuf *do_adjustment_impl(const SeqRenderData *context, Sequence *seq, float timeline_frame)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2012-05-08 09:31:25 +00:00
|
|
|
Editing *ed;
|
2023-07-20 09:46:24 +02:00
|
|
|
ImBuf *i = nullptr;
|
2011-05-16 17:14:47 +00:00
|
|
|
|
2014-01-19 00:16:19 +06:00
|
|
|
ed = context->scene->ed;
|
2011-05-16 17:14:47 +00:00
|
|
|
|
2022-06-02 01:39:40 +02:00
|
|
|
ListBase *seqbasep = SEQ_get_seqbase_by_seq(context->scene, seq);
|
2022-05-02 18:20:56 +02:00
|
|
|
ListBase *channels = SEQ_get_channels_by_seq(&ed->seqbase, &ed->channels, seq);
|
2011-05-16 17:14:47 +00:00
|
|
|
|
2021-03-20 01:33:15 +01:00
|
|
|
/* Clamp timeline_frame to strip range so it behaves as if it had "still frame" offset (last
|
|
|
|
|
* frame is static after end of strip). This is how most strips behave. This way transition
|
|
|
|
|
* effects that doesn't overlap or speed effect can't fail rendering outside of strip range. */
|
2022-06-02 01:39:40 +02:00
|
|
|
timeline_frame = clamp_i(timeline_frame,
|
2022-06-29 12:45:59 +02:00
|
|
|
SEQ_time_left_handle_frame_get(context->scene, seq),
|
|
|
|
|
SEQ_time_right_handle_frame_get(context->scene, seq) - 1);
|
2021-03-20 01:33:15 +01:00
|
|
|
|
2012-08-10 15:30:38 +00:00
|
|
|
if (seq->machine > 1) {
|
2022-04-04 12:52:48 +02:00
|
|
|
i = seq_render_give_ibuf_seqbase(
|
|
|
|
|
context, timeline_frame, seq->machine - 1, channels, seqbasep);
|
2011-05-16 17:14:47 +00:00
|
|
|
}
|
|
|
|
|
|
2021-05-14 17:35:22 +10:00
|
|
|
/* Found nothing? so let's work the way up the meta-strip stack, so
|
2012-08-08 11:15:36 +00:00
|
|
|
* that it is possible to group a bunch of adjustment strips into
|
2021-05-14 17:35:22 +10:00
|
|
|
* a meta-strip and have that work on everything below the meta-strip. */
|
2012-03-09 18:28:30 +00:00
|
|
|
|
2011-05-16 17:14:47 +00:00
|
|
|
if (!i) {
|
2012-05-08 09:31:25 +00:00
|
|
|
Sequence *meta;
|
2011-05-16 17:14:47 +00:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
meta = SEQ_find_metastrip_by_sequence(&ed->seqbase, nullptr, seq);
|
2011-05-16 17:14:47 +00:00
|
|
|
|
|
|
|
|
if (meta) {
|
2020-11-06 14:10:59 +01:00
|
|
|
i = do_adjustment_impl(context, meta, timeline_frame);
|
2011-05-16 17:14:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static ImBuf *do_adjustment(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2020-11-06 14:10:59 +01:00
|
|
|
float timeline_frame,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*fac*/,
|
|
|
|
|
ImBuf * /*ibuf1*/,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf * /*ibuf2*/)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2012-08-08 11:15:36 +00:00
|
|
|
ImBuf *out;
|
2012-05-08 09:31:25 +00:00
|
|
|
Editing *ed;
|
2011-05-16 17:14:47 +00:00
|
|
|
|
2014-01-19 00:16:19 +06:00
|
|
|
ed = context->scene->ed;
|
2011-05-16 17:14:47 +00:00
|
|
|
|
|
|
|
|
if (!ed) {
|
2023-07-20 09:46:24 +02:00
|
|
|
return nullptr;
|
2011-05-16 17:14:47 +00:00
|
|
|
}
|
|
|
|
|
|
2020-11-06 14:10:59 +01:00
|
|
|
out = do_adjustment_impl(context, seq, timeline_frame);
|
2018-06-17 17:05:51 +02:00
|
|
|
|
2011-05-16 17:14:47 +00:00
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Speed Effect
|
|
|
|
|
* \{ */
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
static void init_speed_effect(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2012-08-08 11:15:36 +00:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2012-08-08 11:15:36 +00:00
|
|
|
|
2012-08-08 11:15:38 +00:00
|
|
|
seq->effectdata = MEM_callocN(sizeof(SpeedControlVars), "speedcontrolvars");
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2024-10-30 13:37:31 +02:00
|
|
|
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
|
2021-07-01 15:03:14 -03:00
|
|
|
v->speed_control_type = SEQ_SPEED_STRETCH;
|
|
|
|
|
v->speed_fader = 1.0f;
|
|
|
|
|
v->speed_fader_length = 0.0f;
|
|
|
|
|
v->speed_fader_frame_number = 0.0f;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-08 09:31:25 +00:00
|
|
|
static void load_speed_effect(Sequence *seq)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-05-08 09:31:25 +00:00
|
|
|
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
|
2023-07-20 09:46:24 +02:00
|
|
|
v->frameMap = nullptr;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_speed()
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_speed_effect(Sequence *seq, const bool /*do_id_user*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-05-08 09:31:25 +00:00
|
|
|
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
|
2019-04-22 09:39:35 +10:00
|
|
|
if (v->frameMap) {
|
2012-08-08 11:15:36 +00:00
|
|
|
MEM_freeN(v->frameMap);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_speed_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2012-05-08 09:31:25 +00:00
|
|
|
SpeedControlVars *v;
|
2009-01-12 19:02:08 +00:00
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
v = (SpeedControlVars *)dst->effectdata;
|
2023-07-20 09:46:24 +02:00
|
|
|
v->frameMap = nullptr;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_speed(const Sequence * /*seq*/, float /*fac*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::DoEffect;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
static FCurve *seq_effect_speed_speed_factor_curve_get(Scene *scene, Sequence *seq)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
return id_data_find_fcurve(&scene->id, seq, &RNA_Sequence, "speed_factor", 0, nullptr);
|
2021-08-24 01:01:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq)
|
|
|
|
|
{
|
2022-06-30 15:27:49 +02:00
|
|
|
const int effect_strip_length = SEQ_time_right_handle_frame_get(scene, seq) -
|
|
|
|
|
SEQ_time_left_handle_frame_get(scene, seq);
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
if ((seq->seq1 == nullptr) || (effect_strip_length < 1)) {
|
2023-07-16 15:50:02 +10:00
|
|
|
return; /* Make COVERITY happy and check for (CID 598) input strip. */
|
2009-08-19 09:35:22 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-21 13:13:09 +10:00
|
|
|
const FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq);
|
2023-07-20 09:46:24 +02:00
|
|
|
if (fcu == nullptr) {
|
2021-08-24 01:01:48 +02:00
|
|
|
return;
|
2021-07-01 15:03:14 -03:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
|
|
|
|
|
if (v->frameMap) {
|
|
|
|
|
MEM_freeN(v->frameMap);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
v->frameMap = static_cast<float *>(MEM_mallocN(sizeof(float) * effect_strip_length, __func__));
|
2021-08-24 01:01:48 +02:00
|
|
|
v->frameMap[0] = 0.0f;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
float target_frame = 0;
|
|
|
|
|
for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) {
|
2022-06-29 12:45:59 +02:00
|
|
|
target_frame += evaluate_fcurve(fcu, SEQ_time_left_handle_frame_get(scene, seq) + frame_index);
|
2022-06-30 23:44:13 +10:00
|
|
|
const int target_frame_max = SEQ_time_strip_length_get(scene, seq->seq1);
|
|
|
|
|
CLAMP(target_frame, 0, target_frame_max);
|
2021-08-24 01:01:48 +02:00
|
|
|
v->frameMap[frame_index] = target_frame;
|
2010-11-21 20:00:31 +00:00
|
|
|
}
|
2021-08-24 01:01:48 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 12:43:15 +10:00
|
|
|
static void seq_effect_speed_frame_map_ensure(Scene *scene, Sequence *seq)
|
2021-08-24 01:01:48 +02:00
|
|
|
{
|
2024-09-16 11:39:02 +10:00
|
|
|
const SpeedControlVars *v = (SpeedControlVars *)seq->effectdata;
|
2023-07-20 09:46:24 +02:00
|
|
|
if (v->frameMap != nullptr) {
|
2021-08-24 01:01:48 +02:00
|
|
|
return;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
seq_effect_speed_rebuild_map(scene, seq);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
float seq_speed_effect_target_frame_get(Scene *scene,
|
|
|
|
|
Sequence *seq_speed,
|
|
|
|
|
float timeline_frame,
|
|
|
|
|
int input)
|
|
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
if (seq_speed->seq1 == nullptr) {
|
2021-08-24 01:01:48 +02:00
|
|
|
return 0.0f;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
SEQ_effect_handle_get(seq_speed); /* Ensure, that data are initialized. */
|
2023-10-19 02:08:20 +02:00
|
|
|
int frame_index = round_fl_to_int(SEQ_give_frame_index(scene, seq_speed, timeline_frame));
|
2021-08-24 01:01:48 +02:00
|
|
|
SpeedControlVars *s = (SpeedControlVars *)seq_speed->effectdata;
|
|
|
|
|
const Sequence *source = seq_speed->seq1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
float target_frame = 0.0f;
|
|
|
|
|
switch (s->speed_control_type) {
|
2021-08-24 12:43:15 +10:00
|
|
|
case SEQ_SPEED_STRETCH: {
|
2021-08-31 03:12:00 +02:00
|
|
|
/* Only right handle controls effect speed! */
|
2022-07-08 17:44:24 +02:00
|
|
|
const float target_content_length = SEQ_time_strip_length_get(scene, source) -
|
2021-09-05 23:25:36 -04:00
|
|
|
source->startofs;
|
2022-06-29 12:45:59 +02:00
|
|
|
const float speed_effetct_length = SEQ_time_right_handle_frame_get(scene, seq_speed) -
|
|
|
|
|
SEQ_time_left_handle_frame_get(scene, seq_speed);
|
2021-08-31 03:12:00 +02:00
|
|
|
const float ratio = frame_index / speed_effetct_length;
|
|
|
|
|
target_frame = target_content_length * ratio;
|
2021-08-24 01:01:48 +02:00
|
|
|
break;
|
2021-08-24 12:43:15 +10:00
|
|
|
}
|
|
|
|
|
case SEQ_SPEED_MULTIPLY: {
|
2024-05-21 13:13:09 +10:00
|
|
|
const FCurve *fcu = seq_effect_speed_speed_factor_curve_get(scene, seq_speed);
|
2023-07-20 09:46:24 +02:00
|
|
|
if (fcu != nullptr) {
|
2021-08-24 12:43:15 +10:00
|
|
|
seq_effect_speed_frame_map_ensure(scene, seq_speed);
|
2021-08-24 01:01:48 +02:00
|
|
|
target_frame = s->frameMap[frame_index];
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2010-08-08 13:55:30 +00:00
|
|
|
else {
|
2021-08-24 01:01:48 +02:00
|
|
|
target_frame = frame_index * s->speed_fader;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2021-08-24 01:01:48 +02:00
|
|
|
break;
|
2021-08-24 12:43:15 +10:00
|
|
|
}
|
2021-08-24 01:01:48 +02:00
|
|
|
case SEQ_SPEED_LENGTH:
|
2022-07-08 17:44:24 +02:00
|
|
|
target_frame = SEQ_time_strip_length_get(scene, source) * (s->speed_fader_length / 100.0f);
|
2021-08-24 01:01:48 +02:00
|
|
|
break;
|
|
|
|
|
case SEQ_SPEED_FRAME_NUMBER:
|
|
|
|
|
target_frame = s->speed_fader_frame_number;
|
|
|
|
|
break;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-07-08 17:44:24 +02:00
|
|
|
CLAMP(target_frame, 0, SEQ_time_strip_length_get(scene, source));
|
2021-08-24 01:01:48 +02:00
|
|
|
target_frame += seq_speed->start;
|
2020-04-30 20:45:41 +02:00
|
|
|
|
|
|
|
|
/* No interpolation. */
|
|
|
|
|
if ((s->flags & SEQ_SPEED_USE_INTERPOLATION) == 0) {
|
2021-08-24 01:01:48 +02:00
|
|
|
return target_frame;
|
2020-04-30 20:45:41 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
/* Interpolation is used, switch between current and next frame based on which input is
|
|
|
|
|
* requested. */
|
|
|
|
|
return input == 0 ? target_frame : ceil(target_frame);
|
2020-04-30 20:45:41 +02:00
|
|
|
}
|
|
|
|
|
|
2021-08-24 01:01:48 +02:00
|
|
|
static float speed_effect_interpolation_ratio_get(Scene *scene,
|
|
|
|
|
Sequence *seq_speed,
|
2020-11-06 14:10:59 +01:00
|
|
|
float timeline_frame)
|
2020-04-30 20:45:41 +02:00
|
|
|
{
|
2021-08-24 01:01:48 +02:00
|
|
|
const float target_frame = seq_speed_effect_target_frame_get(
|
|
|
|
|
scene, seq_speed, timeline_frame, 0);
|
|
|
|
|
return target_frame - floor(target_frame);
|
2020-04-30 20:45:41 +02:00
|
|
|
}
|
|
|
|
|
|
2014-01-19 00:16:19 +06:00
|
|
|
static ImBuf *do_speed_effect(const SeqRenderData *context,
|
2020-04-30 20:45:41 +02:00
|
|
|
Sequence *seq,
|
2020-11-06 14:10:59 +01:00
|
|
|
float timeline_frame,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2017-12-04 16:49:30 +11:00
|
|
|
ImBuf *ibuf1,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *ibuf2)
|
2012-08-08 16:46:45 +00:00
|
|
|
{
|
2024-09-15 23:14:09 +10:00
|
|
|
const SpeedControlVars *s = (SpeedControlVars *)seq->effectdata;
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle cross_effect = get_sequence_effect_impl(SEQ_TYPE_CROSS);
|
2020-04-30 20:45:41 +02:00
|
|
|
ImBuf *out;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-30 20:45:41 +02:00
|
|
|
if (s->flags & SEQ_SPEED_USE_INTERPOLATION) {
|
2021-12-28 14:28:52 +01:00
|
|
|
fac = speed_effect_interpolation_ratio_get(context->scene, seq, timeline_frame);
|
2020-04-30 20:45:41 +02:00
|
|
|
/* Current frame is ibuf1, next frame is ibuf2. */
|
2020-11-05 13:33:27 +01:00
|
|
|
out = seq_render_effect_execute_threaded(
|
2024-09-09 18:02:59 +02:00
|
|
|
&cross_effect, context, nullptr, timeline_frame, fac, ibuf1, ibuf2);
|
2020-04-30 20:45:41 +02:00
|
|
|
return out;
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
2020-04-30 20:45:41 +02:00
|
|
|
|
|
|
|
|
/* No interpolation. */
|
|
|
|
|
return IMB_dupImBuf(ibuf1);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Over-Drop Effect
|
|
|
|
|
* \{ */
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static void do_overdrop_effect(const SeqRenderData *context,
|
2023-07-20 09:46:24 +02:00
|
|
|
Sequence * /*seq*/,
|
|
|
|
|
float /*timeline_frame*/,
|
2021-12-28 14:28:52 +01:00
|
|
|
float fac,
|
2024-02-05 11:37:42 +02:00
|
|
|
const ImBuf *ibuf1,
|
|
|
|
|
const ImBuf *ibuf2,
|
2017-12-04 16:49:30 +11:00
|
|
|
int start_line,
|
|
|
|
|
int total_lines,
|
|
|
|
|
ImBuf *out)
|
2012-08-08 16:46:45 +00:00
|
|
|
{
|
2014-01-19 00:16:19 +06:00
|
|
|
int x = context->rectx;
|
2012-08-08 16:46:45 +00:00
|
|
|
int y = total_lines;
|
|
|
|
|
|
2023-05-18 10:19:01 +02:00
|
|
|
if (out->float_buffer.data) {
|
2023-07-20 09:46:24 +02:00
|
|
|
float *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_float_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_drop_effect_float(fac, x, y, rect1, rect2, rect_out);
|
2023-12-14 17:31:05 +01:00
|
|
|
do_alphaover_effect(fac, x, y, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-20 09:46:24 +02:00
|
|
|
uchar *rect1 = nullptr, *rect2 = nullptr, *rect_out = nullptr;
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
slice_get_byte_buffers(context, ibuf1, ibuf2, out, start_line, &rect1, &rect2, &rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
|
2021-12-28 14:28:52 +01:00
|
|
|
do_drop_effect_byte(fac, x, y, rect1, rect2, rect_out);
|
2023-12-14 17:31:05 +01:00
|
|
|
do_alphaover_effect(fac, x, y, rect1, rect2, rect_out);
|
2012-08-08 16:46:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Gaussian Blur
|
|
|
|
|
* \{ */
|
2014-07-19 22:16:10 +06:00
|
|
|
|
|
|
|
|
static void init_gaussian_blur_effect(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2014-07-19 22:16:10 +06:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2014-07-19 22:16:10 +06:00
|
|
|
|
2024-10-30 13:36:42 +02:00
|
|
|
seq->effectdata = MEM_callocN(sizeof(GaussianBlurVars), "gaussianblurvars");
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_gaussian_blur()
|
2014-07-19 22:16:10 +06:00
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_gaussian_blur_effect(Sequence *seq, const bool /*do_id_user*/)
|
2014-07-19 22:16:10 +06:00
|
|
|
{
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_gaussian_blur_effect(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2014-07-19 22:16:10 +06:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_gaussian_blur(const Sequence *seq, float /*fac*/)
|
2014-07-19 22:16:10 +06:00
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
GaussianBlurVars *data = static_cast<GaussianBlurVars *>(seq->effectdata);
|
2014-07-20 14:32:09 +06:00
|
|
|
if (data->size_x == 0.0f && data->size_y == 0) {
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::UseInput1;
|
2014-07-20 14:32:09 +06:00
|
|
|
}
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::DoEffect;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
|
2024-05-10 19:07:53 +03:00
|
|
|
static Array<float> make_gaussian_blur_kernel(float rad, int size)
|
2014-07-19 22:16:10 +06:00
|
|
|
{
|
2023-12-14 17:31:05 +01:00
|
|
|
int n = 2 * size + 1;
|
2024-05-10 19:07:53 +03:00
|
|
|
Array<float> gaussian(n);
|
2014-07-19 22:16:10 +06:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
float sum = 0.0f;
|
|
|
|
|
float fac = (rad > 0.0f ? 1.0f / rad : 0.0f);
|
|
|
|
|
for (int i = -size; i <= size; i++) {
|
|
|
|
|
float val = RE_filter_value(R_FILTER_GAUSS, float(i) * fac);
|
2014-07-19 22:16:10 +06:00
|
|
|
sum += val;
|
2024-05-10 19:07:53 +03:00
|
|
|
gaussian[i + size] = val;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
float inv_sum = 1.0f / sum;
|
|
|
|
|
for (int i = 0; i < n; i++) {
|
2024-05-10 19:07:53 +03:00
|
|
|
gaussian[i] *= inv_sum;
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2014-07-19 22:16:10 +06:00
|
|
|
|
2024-05-10 19:07:53 +03:00
|
|
|
return gaussian;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
template<typename T>
|
2024-05-10 19:07:53 +03:00
|
|
|
static void gaussian_blur_x(const Span<float> gaussian,
|
2023-12-14 17:31:05 +01:00
|
|
|
int half_size,
|
|
|
|
|
int start_line,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
int /*frame_height*/,
|
|
|
|
|
const T *rect,
|
|
|
|
|
T *dst)
|
|
|
|
|
{
|
2024-05-10 17:57:20 +02:00
|
|
|
dst += int64_t(start_line) * width * 4;
|
2023-12-14 17:31:05 +01:00
|
|
|
for (int y = start_line; y < start_line + height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float4 accum(0.0f);
|
2014-07-19 22:16:10 +06:00
|
|
|
float accum_weight = 0.0f;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-10 19:07:53 +03:00
|
|
|
int xmin = math::max(x - half_size, 0);
|
|
|
|
|
int xmax = math::min(x + half_size, width - 1);
|
2023-12-14 17:31:05 +01:00
|
|
|
for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
|
2024-05-10 19:07:53 +03:00
|
|
|
float weight = gaussian[index];
|
2023-12-14 17:31:05 +01:00
|
|
|
int offset = (y * width + nx) * 4;
|
|
|
|
|
accum += float4(rect + offset) * weight;
|
2016-01-22 00:11:37 +05:00
|
|
|
accum_weight += weight;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
2023-12-14 17:31:05 +01:00
|
|
|
accum *= (1.0f / accum_weight);
|
2024-10-30 14:41:54 +02:00
|
|
|
if constexpr (math::is_math_float_type<T>) {
|
|
|
|
|
dst[0] = accum[0];
|
|
|
|
|
dst[1] = accum[1];
|
|
|
|
|
dst[2] = accum[2];
|
|
|
|
|
dst[3] = accum[3];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dst[0] = accum[0] + 0.5f;
|
|
|
|
|
dst[1] = accum[1] + 0.5f;
|
|
|
|
|
dst[2] = accum[2] + 0.5f;
|
|
|
|
|
dst[3] = accum[3] + 0.5f;
|
|
|
|
|
}
|
2023-12-14 17:31:05 +01:00
|
|
|
dst += 4;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
template<typename T>
|
2024-05-10 19:07:53 +03:00
|
|
|
static void gaussian_blur_y(const Span<float> gaussian,
|
2023-12-14 17:31:05 +01:00
|
|
|
int half_size,
|
|
|
|
|
int start_line,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
int frame_height,
|
|
|
|
|
const T *rect,
|
|
|
|
|
T *dst)
|
|
|
|
|
{
|
2024-05-10 17:57:20 +02:00
|
|
|
dst += int64_t(start_line) * width * 4;
|
2023-12-14 17:31:05 +01:00
|
|
|
for (int y = start_line; y < start_line + height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float4 accum(0.0f);
|
2014-07-19 22:16:10 +06:00
|
|
|
float accum_weight = 0.0f;
|
2024-05-10 19:07:53 +03:00
|
|
|
int ymin = math::max(y - half_size, 0);
|
|
|
|
|
int ymax = math::min(y + half_size, frame_height - 1);
|
2023-12-14 17:31:05 +01:00
|
|
|
for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
|
2024-05-10 19:07:53 +03:00
|
|
|
float weight = gaussian[index];
|
2023-12-14 17:31:05 +01:00
|
|
|
int offset = (ny * width + x) * 4;
|
|
|
|
|
accum += float4(rect + offset) * weight;
|
2016-01-22 00:11:37 +05:00
|
|
|
accum_weight += weight;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
2023-12-14 17:31:05 +01:00
|
|
|
accum *= (1.0f / accum_weight);
|
2024-10-30 14:41:54 +02:00
|
|
|
if constexpr (math::is_math_float_type<T>) {
|
|
|
|
|
dst[0] = accum[0];
|
|
|
|
|
dst[1] = accum[1];
|
|
|
|
|
dst[2] = accum[2];
|
|
|
|
|
dst[3] = accum[3];
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
dst[0] = accum[0] + 0.5f;
|
|
|
|
|
dst[1] = accum[1] + 0.5f;
|
|
|
|
|
dst[2] = accum[2] + 0.5f;
|
|
|
|
|
dst[3] = accum[3] + 0.5f;
|
|
|
|
|
}
|
2023-12-14 17:31:05 +01:00
|
|
|
dst += 4;
|
2014-07-19 22:16:10 +06:00
|
|
|
}
|
|
|
|
|
}
|
2016-01-22 00:11:37 +05:00
|
|
|
}
|
|
|
|
|
|
2017-12-04 16:49:30 +11:00
|
|
|
static ImBuf *do_gaussian_blur_effect(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
|
|
|
|
float /*fac*/,
|
2017-12-04 16:49:30 +11:00
|
|
|
ImBuf *ibuf1,
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf * /*ibuf2*/)
|
2016-01-22 00:11:37 +05:00
|
|
|
{
|
2023-12-14 17:31:05 +01:00
|
|
|
using namespace blender;
|
2016-01-22 00:11:37 +05:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* Create blur kernel weights. */
|
|
|
|
|
const GaussianBlurVars *data = static_cast<const GaussianBlurVars *>(seq->effectdata);
|
|
|
|
|
const int half_size_x = int(data->size_x + 0.5f);
|
|
|
|
|
const int half_size_y = int(data->size_y + 0.5f);
|
2024-05-10 19:07:53 +03:00
|
|
|
Array<float> gaussian_x = make_gaussian_blur_kernel(data->size_x, half_size_x);
|
|
|
|
|
Array<float> gaussian_y = make_gaussian_blur_kernel(data->size_y, half_size_y);
|
2016-01-22 00:11:37 +05:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
const int width = context->rectx;
|
|
|
|
|
const int height = context->recty;
|
|
|
|
|
const bool is_float = ibuf1->float_buffer.data;
|
2016-01-22 00:11:37 +05:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* Horizontal blur: create output, blur ibuf1 into it. */
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *out = prepare_effect_imbufs(context, ibuf1, nullptr);
|
2023-12-14 17:31:05 +01:00
|
|
|
threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
|
|
|
|
|
const int y_first = y_range.first();
|
|
|
|
|
const int y_size = y_range.size();
|
|
|
|
|
if (is_float) {
|
2024-05-10 19:07:53 +03:00
|
|
|
gaussian_blur_x(gaussian_x,
|
2023-12-14 17:31:05 +01:00
|
|
|
half_size_x,
|
|
|
|
|
y_first,
|
|
|
|
|
width,
|
|
|
|
|
y_size,
|
|
|
|
|
height,
|
|
|
|
|
ibuf1->float_buffer.data,
|
|
|
|
|
out->float_buffer.data);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-05-10 19:07:53 +03:00
|
|
|
gaussian_blur_x(gaussian_x,
|
2023-12-14 17:31:05 +01:00
|
|
|
half_size_x,
|
|
|
|
|
y_first,
|
|
|
|
|
width,
|
|
|
|
|
y_size,
|
|
|
|
|
height,
|
|
|
|
|
ibuf1->byte_buffer.data,
|
|
|
|
|
out->byte_buffer.data);
|
|
|
|
|
}
|
|
|
|
|
});
|
2016-01-22 00:11:37 +05:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* Vertical blur: create output, blur previous output into it. */
|
2016-01-22 00:11:37 +05:00
|
|
|
ibuf1 = out;
|
2024-09-09 18:02:59 +02:00
|
|
|
out = prepare_effect_imbufs(context, ibuf1, nullptr);
|
2023-12-14 17:31:05 +01:00
|
|
|
threading::parallel_for(IndexRange(context->recty), 32, [&](const IndexRange y_range) {
|
|
|
|
|
const int y_first = y_range.first();
|
|
|
|
|
const int y_size = y_range.size();
|
|
|
|
|
if (is_float) {
|
2024-05-10 19:07:53 +03:00
|
|
|
gaussian_blur_y(gaussian_y,
|
2023-12-14 17:31:05 +01:00
|
|
|
half_size_y,
|
|
|
|
|
y_first,
|
|
|
|
|
width,
|
|
|
|
|
y_size,
|
|
|
|
|
height,
|
|
|
|
|
ibuf1->float_buffer.data,
|
|
|
|
|
out->float_buffer.data);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2024-05-10 19:07:53 +03:00
|
|
|
gaussian_blur_y(gaussian_y,
|
2023-12-14 17:31:05 +01:00
|
|
|
half_size_y,
|
|
|
|
|
y_first,
|
|
|
|
|
width,
|
|
|
|
|
y_size,
|
|
|
|
|
height,
|
|
|
|
|
ibuf1->byte_buffer.data,
|
|
|
|
|
out->byte_buffer.data);
|
|
|
|
|
}
|
|
|
|
|
});
|
2016-01-22 00:11:37 +05:00
|
|
|
|
2023-12-14 17:31:05 +01:00
|
|
|
/* Free the first output. */
|
2016-01-22 00:11:37 +05:00
|
|
|
IMB_freeImBuf(ibuf1);
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Text Effect
|
|
|
|
|
* \{ */
|
2019-01-13 21:28:07 -08:00
|
|
|
|
2015-07-01 20:29:18 +02:00
|
|
|
static void init_text_effect(Sequence *seq)
|
|
|
|
|
{
|
2019-04-22 09:39:35 +10:00
|
|
|
if (seq->effectdata) {
|
2015-07-01 20:29:18 +02:00
|
|
|
MEM_freeN(seq->effectdata);
|
2019-04-22 09:39:35 +10:00
|
|
|
}
|
2015-07-01 20:29:18 +02:00
|
|
|
|
2024-10-30 13:37:31 +02:00
|
|
|
TextVars *data = static_cast<TextVars *>(
|
|
|
|
|
seq->effectdata = MEM_callocN(sizeof(TextVars), "textvars"));
|
2023-07-20 09:46:24 +02:00
|
|
|
data->text_font = nullptr;
|
2019-01-13 21:28:07 -08:00
|
|
|
data->text_blf_id = -1;
|
2021-11-13 09:39:18 -08:00
|
|
|
data->text_size = 60.0f;
|
2016-04-27 15:49:13 +10:00
|
|
|
|
|
|
|
|
copy_v4_fl(data->color, 1.0f);
|
2021-03-20 00:43:40 +01:00
|
|
|
data->shadow_color[3] = 0.7f;
|
2024-05-08 11:13:20 +02:00
|
|
|
data->shadow_angle = DEG2RADF(65.0f);
|
|
|
|
|
data->shadow_offset = 0.04f;
|
|
|
|
|
data->shadow_blur = 0.0f;
|
2021-03-20 00:43:40 +01:00
|
|
|
data->box_color[0] = 0.2f;
|
|
|
|
|
data->box_color[1] = 0.2f;
|
|
|
|
|
data->box_color[2] = 0.2f;
|
|
|
|
|
data->box_color[3] = 0.7f;
|
2020-11-06 15:42:52 +01:00
|
|
|
data->box_margin = 0.01f;
|
2024-05-08 11:13:20 +02:00
|
|
|
data->outline_color[3] = 0.7f;
|
|
|
|
|
data->outline_width = 0.05f;
|
2016-04-27 15:49:13 +10:00
|
|
|
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(data->text, "Text");
|
2015-07-11 02:17:06 +10:00
|
|
|
|
|
|
|
|
data->loc[0] = 0.5f;
|
2021-03-20 00:43:40 +01:00
|
|
|
data->loc[1] = 0.5f;
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
data->anchor_x = SEQ_TEXT_ALIGN_X_CENTER;
|
|
|
|
|
data->anchor_y = SEQ_TEXT_ALIGN_Y_CENTER;
|
2015-09-18 20:29:35 +10:00
|
|
|
data->align = SEQ_TEXT_ALIGN_X_CENTER;
|
2021-03-20 00:43:40 +01:00
|
|
|
data->wrap_width = 1.0f;
|
2015-07-01 20:29:18 +02:00
|
|
|
}
|
|
|
|
|
|
2020-12-19 05:57:27 +01:00
|
|
|
void SEQ_effect_text_font_unload(TextVars *data, const bool do_id_user)
|
2019-01-13 21:28:07 -08:00
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
if (data == nullptr) {
|
2021-08-27 13:02:59 +10:00
|
|
|
return;
|
|
|
|
|
}
|
2019-01-13 21:28:07 -08:00
|
|
|
|
2021-08-27 13:02:59 +10:00
|
|
|
/* Unlink the VFont */
|
2023-07-20 09:46:24 +02:00
|
|
|
if (do_id_user && data->text_font != nullptr) {
|
2021-08-27 13:02:59 +10:00
|
|
|
id_us_min(&data->text_font->id);
|
2023-07-20 09:46:24 +02:00
|
|
|
data->text_font = nullptr;
|
2021-08-27 13:02:59 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unload the BLF font. */
|
|
|
|
|
if (data->text_blf_id >= 0) {
|
|
|
|
|
BLF_unload_id(data->text_blf_id);
|
2019-01-13 21:28:07 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-19 05:57:27 +01:00
|
|
|
void SEQ_effect_text_font_load(TextVars *data, const bool do_id_user)
|
2019-01-13 21:28:07 -08:00
|
|
|
{
|
2021-08-27 13:02:59 +10:00
|
|
|
VFont *vfont = data->text_font;
|
2023-07-20 09:46:24 +02:00
|
|
|
if (vfont == nullptr) {
|
2021-08-27 13:02:59 +10:00
|
|
|
return;
|
|
|
|
|
}
|
2019-01-13 21:28:07 -08:00
|
|
|
|
2021-08-27 13:02:59 +10:00
|
|
|
if (do_id_user) {
|
|
|
|
|
id_us_plus(&vfont->id);
|
2019-01-13 21:28:07 -08:00
|
|
|
}
|
2021-08-27 13:02:59 +10:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
if (vfont->packedfile != nullptr) {
|
2021-08-27 13:09:33 +10:00
|
|
|
PackedFile *pf = vfont->packedfile;
|
|
|
|
|
/* Create a name that's unique between library data-blocks to avoid loading
|
2023-05-03 16:31:06 +10:00
|
|
|
* a font per strip which will load fonts many times.
|
|
|
|
|
*
|
|
|
|
|
* WARNING: this isn't fool proof!
|
|
|
|
|
* The #VFont may be renamed which will cause this to load multiple times,
|
|
|
|
|
* in practice this isn't so likely though. */
|
2021-08-27 13:09:33 +10:00
|
|
|
char name[MAX_ID_FULL_NAME];
|
|
|
|
|
BKE_id_full_name_get(name, &vfont->id, 0);
|
2021-08-27 13:02:59 +10:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
data->text_blf_id = BLF_load_mem(name, static_cast<const uchar *>(pf->data), pf->size);
|
2021-08-27 13:09:33 +10:00
|
|
|
}
|
|
|
|
|
else {
|
2023-06-05 10:47:37 +10:00
|
|
|
char filepath[FILE_MAX];
|
|
|
|
|
STRNCPY(filepath, vfont->filepath);
|
2023-11-07 11:04:03 +01:00
|
|
|
if (BLI_thread_is_main()) {
|
2024-03-26 17:54:30 +11:00
|
|
|
/* FIXME: This is a band-aid fix.
|
|
|
|
|
* A proper solution has to be worked on by the sequencer team.
|
2023-11-07 11:04:03 +01:00
|
|
|
*
|
|
|
|
|
* This code can be called from non-main thread, e.g. when copying sequences as part of
|
2024-02-19 15:54:08 +01:00
|
|
|
* depsgraph evaluated copy of the evaluated scene. Just skip font loading in that case, BLF
|
|
|
|
|
* code is not thread-safe, and if this happens from threaded context, it almost certainly
|
|
|
|
|
* means that a previous attempt to load the font already failed, e.g. because font file-path
|
|
|
|
|
* is invalid. Proposer fix would likely be to not attempt to reload a failed-to-load font
|
|
|
|
|
* every time. */
|
2023-11-07 11:04:03 +01:00
|
|
|
BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id));
|
|
|
|
|
|
|
|
|
|
data->text_blf_id = BLF_load(filepath);
|
|
|
|
|
}
|
2021-08-27 13:09:33 +10:00
|
|
|
}
|
2019-01-13 21:28:07 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_text_effect(Sequence *seq, const bool do_id_user)
|
|
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
2020-12-19 05:57:27 +01:00
|
|
|
SEQ_effect_text_font_unload(data, do_id_user);
|
2019-01-13 21:28:07 -08:00
|
|
|
|
|
|
|
|
if (data) {
|
|
|
|
|
MEM_freeN(data);
|
2023-07-20 09:46:24 +02:00
|
|
|
seq->effectdata = nullptr;
|
2019-01-13 21:28:07 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void load_text_effect(Sequence *seq)
|
|
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
2020-12-19 05:57:27 +01:00
|
|
|
SEQ_effect_text_font_load(data, false);
|
2019-01-13 21:28:07 -08:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_text_effect(Sequence *dst, const Sequence *src, const int flag)
|
2019-01-13 21:28:07 -08:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
2023-07-20 09:46:24 +02:00
|
|
|
TextVars *data = static_cast<TextVars *>(dst->effectdata);
|
2019-01-13 21:28:07 -08:00
|
|
|
|
|
|
|
|
data->text_blf_id = -1;
|
2020-12-19 05:57:27 +01:00
|
|
|
SEQ_effect_text_font_load(data, (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0);
|
2019-01-13 21:28:07 -08:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_text()
|
2015-07-01 20:29:18 +02:00
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_text(const Sequence *seq, float /*fac*/)
|
2015-07-01 20:29:18 +02:00
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
2021-11-13 09:39:18 -08:00
|
|
|
if (data->text[0] == 0 || data->text_size < 1.0f ||
|
2016-04-27 15:55:50 +10:00
|
|
|
((data->color[3] == 0.0f) &&
|
2024-05-08 11:13:20 +02:00
|
|
|
(data->shadow_color[3] == 0.0f || (data->flag & SEQ_TEXT_SHADOW) == 0) &&
|
|
|
|
|
(data->outline_color[3] == 0.0f || data->outline_width <= 0.0f ||
|
|
|
|
|
(data->flag & SEQ_TEXT_OUTLINE) == 0)))
|
2016-04-27 15:55:50 +10:00
|
|
|
{
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::UseInput1;
|
2015-07-01 20:29:18 +02:00
|
|
|
}
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::NoInput;
|
2015-07-01 20:29:18 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-05-10 17:57:20 +02:00
|
|
|
/* Simplified version of gaussian blur specifically for text shadow blurring:
|
2024-07-26 10:49:10 +02:00
|
|
|
* - Data is only the alpha channel,
|
2024-05-10 17:57:20 +02:00
|
|
|
* - Skips blur outside of shadow rectangle. */
|
2024-05-10 19:07:53 +03:00
|
|
|
static void text_gaussian_blur_x(const Span<float> gaussian,
|
2024-05-10 17:57:20 +02:00
|
|
|
int half_size,
|
|
|
|
|
int start_line,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
const uchar *rect,
|
|
|
|
|
uchar *dst,
|
|
|
|
|
const rcti &shadow_rect)
|
|
|
|
|
{
|
2024-07-26 10:49:10 +02:00
|
|
|
dst += int64_t(start_line) * width;
|
2024-05-10 17:57:20 +02:00
|
|
|
for (int y = start_line; y < start_line + height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float accum(0.0f);
|
|
|
|
|
if (x >= shadow_rect.xmin && x <= shadow_rect.xmax) {
|
|
|
|
|
float accum_weight = 0.0f;
|
2024-05-10 19:07:53 +03:00
|
|
|
int xmin = math::max(x - half_size, shadow_rect.xmin);
|
|
|
|
|
int xmax = math::min(x + half_size, shadow_rect.xmax);
|
2024-05-10 17:57:20 +02:00
|
|
|
for (int nx = xmin, index = (xmin - x) + half_size; nx <= xmax; nx++, index++) {
|
2024-05-10 19:07:53 +03:00
|
|
|
float weight = gaussian[index];
|
2024-07-26 10:49:10 +02:00
|
|
|
int offset = y * width + nx;
|
|
|
|
|
accum += rect[offset] * weight;
|
2024-05-10 17:57:20 +02:00
|
|
|
accum_weight += weight;
|
|
|
|
|
}
|
|
|
|
|
accum *= (1.0f / accum_weight);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 10:49:10 +02:00
|
|
|
*dst = accum;
|
|
|
|
|
dst++;
|
2024-05-10 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-10 19:07:53 +03:00
|
|
|
static void text_gaussian_blur_y(const Span<float> gaussian,
|
2024-05-10 17:57:20 +02:00
|
|
|
int half_size,
|
|
|
|
|
int start_line,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
const uchar *rect,
|
|
|
|
|
uchar *dst,
|
|
|
|
|
const rcti &shadow_rect)
|
|
|
|
|
{
|
2024-07-26 10:49:10 +02:00
|
|
|
dst += int64_t(start_line) * width;
|
2024-05-10 17:57:20 +02:00
|
|
|
for (int y = start_line; y < start_line + height; y++) {
|
|
|
|
|
for (int x = 0; x < width; x++) {
|
|
|
|
|
float accum(0.0f);
|
|
|
|
|
if (x >= shadow_rect.xmin && x <= shadow_rect.xmax) {
|
|
|
|
|
float accum_weight = 0.0f;
|
2024-05-10 19:07:53 +03:00
|
|
|
int ymin = math::max(y - half_size, shadow_rect.ymin);
|
|
|
|
|
int ymax = math::min(y + half_size, shadow_rect.ymax);
|
2024-05-10 17:57:20 +02:00
|
|
|
for (int ny = ymin, index = (ymin - y) + half_size; ny <= ymax; ny++, index++) {
|
2024-05-10 19:07:53 +03:00
|
|
|
float weight = gaussian[index];
|
2024-07-26 10:49:10 +02:00
|
|
|
int offset = ny * width + x;
|
|
|
|
|
accum += rect[offset] * weight;
|
2024-05-10 17:57:20 +02:00
|
|
|
accum_weight += weight;
|
|
|
|
|
}
|
|
|
|
|
accum *= (1.0f / accum_weight);
|
|
|
|
|
}
|
2024-07-26 10:49:10 +02:00
|
|
|
*dst = accum;
|
|
|
|
|
dst++;
|
2024-05-10 17:57:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 10:49:10 +02:00
|
|
|
static void clamp_rect(int width, int height, rcti &r_rect)
|
|
|
|
|
{
|
|
|
|
|
r_rect.xmin = math::clamp(r_rect.xmin, 0, width - 1);
|
|
|
|
|
r_rect.xmax = math::clamp(r_rect.xmax, 0, width - 1);
|
|
|
|
|
r_rect.ymin = math::clamp(r_rect.ymin, 0, height - 1);
|
|
|
|
|
r_rect.ymax = math::clamp(r_rect.ymax, 0, height - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void initialize_shadow_alpha(int width,
|
|
|
|
|
int height,
|
|
|
|
|
int2 offset,
|
|
|
|
|
const rcti &shadow_rect,
|
|
|
|
|
const uchar *input,
|
|
|
|
|
Array<uchar> &r_shadow_mask)
|
|
|
|
|
{
|
|
|
|
|
const IndexRange shadow_y_range(shadow_rect.ymin, shadow_rect.ymax - shadow_rect.ymin + 1);
|
|
|
|
|
threading::parallel_for(shadow_y_range, 8, [&](const IndexRange y_range) {
|
|
|
|
|
for (const int64_t y : y_range) {
|
|
|
|
|
const int64_t src_y = math::clamp<int64_t>(y + offset.y, 0, height - 1);
|
|
|
|
|
for (int x = shadow_rect.xmin; x <= shadow_rect.xmax; x++) {
|
|
|
|
|
int src_x = math::clamp(x - offset.x, 0, width - 1);
|
|
|
|
|
size_t src_offset = width * src_y + src_x;
|
|
|
|
|
size_t dst_offset = width * y + x;
|
|
|
|
|
r_shadow_mask[dst_offset] = input[src_offset * 4 + 3];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void composite_shadow(int width,
|
|
|
|
|
const rcti &shadow_rect,
|
|
|
|
|
const float4 &shadow_color,
|
|
|
|
|
const Array<uchar> &shadow_mask,
|
|
|
|
|
uchar *output)
|
|
|
|
|
{
|
|
|
|
|
const IndexRange shadow_y_range(shadow_rect.ymin, shadow_rect.ymax - shadow_rect.ymin + 1);
|
|
|
|
|
threading::parallel_for(shadow_y_range, 8, [&](const IndexRange y_range) {
|
|
|
|
|
for (const int64_t y : y_range) {
|
|
|
|
|
size_t offset = y * width + shadow_rect.xmin;
|
|
|
|
|
uchar *dst = output + offset * 4;
|
|
|
|
|
for (int x = shadow_rect.xmin; x <= shadow_rect.xmax; x++, offset++, dst += 4) {
|
|
|
|
|
uchar a = shadow_mask[offset];
|
|
|
|
|
if (a == 0) {
|
|
|
|
|
/* Fully transparent, leave output pixel as is. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
float4 col1 = load_premul_pixel(dst);
|
|
|
|
|
float4 col2 = shadow_color * (a * (1.0f / 255.0f));
|
|
|
|
|
/* Blend under the output. */
|
|
|
|
|
float fac = 1.0f - col1.w;
|
|
|
|
|
float4 col = col1 + fac * col2;
|
|
|
|
|
store_premul_pixel(col, dst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-08 11:13:20 +02:00
|
|
|
static void draw_text_shadow(const SeqRenderData *context,
|
|
|
|
|
const TextVars *data,
|
|
|
|
|
int line_height,
|
2024-05-10 17:57:20 +02:00
|
|
|
const rcti &rect,
|
2024-05-08 11:13:20 +02:00
|
|
|
ImBuf *out)
|
|
|
|
|
{
|
|
|
|
|
const int width = context->rectx;
|
|
|
|
|
const int height = context->recty;
|
|
|
|
|
/* Blur value of 1.0 applies blur kernel that is half of text line height. */
|
|
|
|
|
const float blur_amount = line_height * 0.5f * data->shadow_blur;
|
|
|
|
|
bool do_blur = blur_amount >= 1.0f;
|
2024-07-26 10:49:10 +02:00
|
|
|
|
|
|
|
|
Array<uchar> shadow_mask(size_t(width) * height, 0);
|
|
|
|
|
|
|
|
|
|
const int2 offset = int2(cosf(data->shadow_angle) * line_height * data->shadow_offset,
|
|
|
|
|
sinf(data->shadow_angle) * line_height * data->shadow_offset);
|
2024-05-08 11:13:20 +02:00
|
|
|
|
2024-05-10 17:57:20 +02:00
|
|
|
rcti shadow_rect = rect;
|
2024-07-26 10:49:10 +02:00
|
|
|
BLI_rcti_translate(&shadow_rect, offset.x, -offset.y);
|
2024-05-10 17:57:20 +02:00
|
|
|
BLI_rcti_pad(&shadow_rect, 1, 1);
|
2024-07-26 10:49:10 +02:00
|
|
|
clamp_rect(width, height, shadow_rect);
|
|
|
|
|
|
|
|
|
|
/* Initialize shadow by copying existing text/outline alpha. */
|
|
|
|
|
initialize_shadow_alpha(width, height, offset, shadow_rect, out->byte_buffer.data, shadow_mask);
|
|
|
|
|
|
2024-05-08 11:13:20 +02:00
|
|
|
if (do_blur) {
|
|
|
|
|
/* Create blur kernel weights. */
|
|
|
|
|
const int half_size = int(blur_amount + 0.5f);
|
2024-05-10 19:07:53 +03:00
|
|
|
Array<float> gaussian = make_gaussian_blur_kernel(blur_amount, half_size);
|
2024-05-08 11:13:20 +02:00
|
|
|
|
2024-05-10 17:57:20 +02:00
|
|
|
BLI_rcti_pad(&shadow_rect, half_size + 1, half_size + 1);
|
2024-07-26 10:49:10 +02:00
|
|
|
clamp_rect(width, height, shadow_rect);
|
|
|
|
|
|
|
|
|
|
/* Horizontal blur: blur shadow_mask into blur_buffer. */
|
|
|
|
|
Array<uchar> blur_buffer(size_t(width) * height, NoInitialization());
|
2024-05-10 17:57:20 +02:00
|
|
|
IndexRange blur_y_range(shadow_rect.ymin, shadow_rect.ymax - shadow_rect.ymin + 1);
|
|
|
|
|
threading::parallel_for(blur_y_range, 8, [&](const IndexRange y_range) {
|
2024-05-08 11:13:20 +02:00
|
|
|
const int y_first = y_range.first();
|
|
|
|
|
const int y_size = y_range.size();
|
2024-05-10 19:07:53 +03:00
|
|
|
text_gaussian_blur_x(gaussian,
|
2024-05-10 17:57:20 +02:00
|
|
|
half_size,
|
|
|
|
|
y_first,
|
|
|
|
|
width,
|
|
|
|
|
y_size,
|
2024-07-26 10:49:10 +02:00
|
|
|
shadow_mask.data(),
|
|
|
|
|
blur_buffer.data(),
|
2024-05-10 17:57:20 +02:00
|
|
|
shadow_rect);
|
2024-05-08 11:13:20 +02:00
|
|
|
});
|
|
|
|
|
|
2024-07-26 10:49:10 +02:00
|
|
|
/* Vertical blur: blur blur_buffer into shadow_mask. */
|
2024-05-10 17:57:20 +02:00
|
|
|
threading::parallel_for(blur_y_range, 8, [&](const IndexRange y_range) {
|
2024-05-08 11:13:20 +02:00
|
|
|
const int y_first = y_range.first();
|
|
|
|
|
const int y_size = y_range.size();
|
2024-05-10 19:07:53 +03:00
|
|
|
text_gaussian_blur_y(gaussian,
|
2024-05-10 17:57:20 +02:00
|
|
|
half_size,
|
|
|
|
|
y_first,
|
|
|
|
|
width,
|
|
|
|
|
y_size,
|
2024-07-26 10:49:10 +02:00
|
|
|
blur_buffer.data(),
|
|
|
|
|
shadow_mask.data(),
|
2024-05-10 17:57:20 +02:00
|
|
|
shadow_rect);
|
2024-05-08 11:13:20 +02:00
|
|
|
});
|
|
|
|
|
}
|
2024-07-26 10:49:10 +02:00
|
|
|
|
|
|
|
|
/* Composite shadow under regular output. */
|
|
|
|
|
float4 color = data->shadow_color;
|
|
|
|
|
color.x *= color.w;
|
|
|
|
|
color.y *= color.w;
|
|
|
|
|
color.z *= color.w;
|
|
|
|
|
composite_shadow(width, shadow_rect, color, shadow_mask, out->byte_buffer.data);
|
2024-05-08 11:13:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Text outline calculation is done by Jump Flooding Algorithm (JFA).
|
|
|
|
|
* This is similar to inpaint/jump_flooding in Compositor, also to
|
|
|
|
|
* "The Quest for Very Wide Outlines", Ben Golus 2020
|
|
|
|
|
* https://bgolus.medium.com/the-quest-for-very-wide-outlines-ba82ed442cd9 */
|
|
|
|
|
|
|
|
|
|
constexpr uint16_t JFA_INVALID = 0xFFFF;
|
|
|
|
|
|
|
|
|
|
struct JFACoord {
|
|
|
|
|
uint16_t x;
|
|
|
|
|
uint16_t y;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static void jump_flooding_pass(Span<JFACoord> input,
|
|
|
|
|
MutableSpan<JFACoord> output,
|
|
|
|
|
int2 size,
|
2024-05-10 17:57:20 +02:00
|
|
|
IndexRange x_range,
|
|
|
|
|
IndexRange y_range,
|
2024-05-08 11:13:20 +02:00
|
|
|
int step_size)
|
|
|
|
|
{
|
2024-05-10 17:57:20 +02:00
|
|
|
threading::parallel_for(y_range, 8, [&](const IndexRange sub_y_range) {
|
2024-05-08 11:13:20 +02:00
|
|
|
for (const int64_t y : sub_y_range) {
|
|
|
|
|
size_t index = y * size.x;
|
2024-05-10 17:57:20 +02:00
|
|
|
for (const int64_t x : x_range) {
|
2024-05-08 11:13:20 +02:00
|
|
|
float2 coord = float2(x, y);
|
|
|
|
|
|
|
|
|
|
/* For each pixel, sample 9 pixels at +/- step size pattern,
|
|
|
|
|
* and output coordinate of closest to the boundary. */
|
|
|
|
|
JFACoord closest_texel{JFA_INVALID, JFA_INVALID};
|
|
|
|
|
float minimum_squared_distance = std::numeric_limits<float>::max();
|
|
|
|
|
for (int dy = -step_size; dy <= step_size; dy += step_size) {
|
|
|
|
|
int yy = y + dy;
|
|
|
|
|
if (yy < 0 || yy >= size.y) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
for (int dx = -step_size; dx <= step_size; dx += step_size) {
|
|
|
|
|
int xx = x + dx;
|
|
|
|
|
if (xx < 0 || xx >= size.x) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
JFACoord val = input[size_t(yy) * size.x + xx];
|
|
|
|
|
if (val.x == JFA_INVALID) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float squared_distance = math::distance_squared(float2(val.x, val.y), coord);
|
|
|
|
|
if (squared_distance < minimum_squared_distance) {
|
|
|
|
|
minimum_squared_distance = squared_distance;
|
|
|
|
|
closest_texel = val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
output[index + x] = closest_texel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
namespace blender::seq {
|
|
|
|
|
|
|
|
|
|
static void text_draw(const TextVarsRuntime &runtime, float color[4])
|
|
|
|
|
{
|
|
|
|
|
for (const LineInfo &line : runtime.lines) {
|
|
|
|
|
for (const CharInfo &character : line.characters) {
|
|
|
|
|
BLF_position(runtime.font, character.position.x, character.position.y, 0.0f);
|
|
|
|
|
BLF_buffer_col(runtime.font, color);
|
|
|
|
|
BLF_draw_buffer(runtime.font, character.str_ptr, character.byte_length);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-08 11:13:20 +02:00
|
|
|
|
2024-07-26 10:49:10 +02:00
|
|
|
static rcti draw_text_outline(const SeqRenderData *context,
|
2024-05-08 11:13:20 +02:00
|
|
|
const TextVars *data,
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
const TextVarsRuntime &runtime,
|
2024-05-08 11:13:20 +02:00
|
|
|
ColorManagedDisplay *display,
|
|
|
|
|
ImBuf *out)
|
|
|
|
|
{
|
|
|
|
|
/* Outline width of 1.0 maps to half of text line height. */
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
const int outline_width = int(runtime.line_height * 0.5f * data->outline_width);
|
|
|
|
|
if (outline_width < 1 || data->outline_color[3] <= 0.0f ||
|
|
|
|
|
((data->flag & SEQ_TEXT_OUTLINE) == 0))
|
|
|
|
|
{
|
|
|
|
|
return runtime.text_boundbox;
|
2024-05-08 11:13:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const int2 size = int2(context->rectx, context->recty);
|
|
|
|
|
|
|
|
|
|
/* Draw white text into temporary buffer. */
|
|
|
|
|
const size_t pixel_count = size_t(size.x) * size.y;
|
|
|
|
|
Array<uchar4> tmp_buf(pixel_count, uchar4(0));
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
BLF_buffer(runtime.font, nullptr, (uchar *)tmp_buf.data(), size.x, size.y, display);
|
2024-05-08 11:13:20 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
text_draw(runtime, float4(1.0f));
|
|
|
|
|
|
|
|
|
|
rcti outline_rect = runtime.text_boundbox;
|
2024-05-10 17:57:20 +02:00
|
|
|
BLI_rcti_pad(&outline_rect, outline_width + 1, outline_width + 1);
|
|
|
|
|
outline_rect.xmin = clamp_i(outline_rect.xmin, 0, size.x - 1);
|
|
|
|
|
outline_rect.xmax = clamp_i(outline_rect.xmax, 0, size.x - 1);
|
|
|
|
|
outline_rect.ymin = clamp_i(outline_rect.ymin, 0, size.y - 1);
|
|
|
|
|
outline_rect.ymax = clamp_i(outline_rect.ymax, 0, size.y - 1);
|
|
|
|
|
const IndexRange rect_x_range(outline_rect.xmin, outline_rect.xmax - outline_rect.xmin + 1);
|
|
|
|
|
const IndexRange rect_y_range(outline_rect.ymin, outline_rect.ymax - outline_rect.ymin + 1);
|
|
|
|
|
|
2024-05-08 11:13:20 +02:00
|
|
|
/* Initialize JFA: invalid values for empty regions, pixel coordinates
|
|
|
|
|
* for opaque regions. */
|
2024-05-10 17:57:20 +02:00
|
|
|
Array<JFACoord> boundary(pixel_count, NoInitialization());
|
2024-05-08 11:13:20 +02:00
|
|
|
threading::parallel_for(IndexRange(size.y), 16, [&](const IndexRange y_range) {
|
|
|
|
|
for (const int y : y_range) {
|
|
|
|
|
size_t index = size_t(y) * size.x;
|
|
|
|
|
for (int x = 0; x < size.x; x++, index++) {
|
|
|
|
|
bool is_opaque = tmp_buf[index].w >= 128;
|
|
|
|
|
JFACoord coord;
|
|
|
|
|
coord.x = is_opaque ? x : JFA_INVALID;
|
|
|
|
|
coord.y = is_opaque ? y : JFA_INVALID;
|
|
|
|
|
boundary[index] = coord;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
/* Do jump flooding calculations. */
|
2024-05-18 18:00:58 +03:00
|
|
|
JFACoord invalid_coord{JFA_INVALID, JFA_INVALID};
|
|
|
|
|
Array<JFACoord> initial_flooded_result(pixel_count, invalid_coord);
|
2024-05-10 17:57:20 +02:00
|
|
|
jump_flooding_pass(boundary, initial_flooded_result, size, rect_x_range, rect_y_range, 1);
|
2024-05-08 11:13:20 +02:00
|
|
|
|
|
|
|
|
Array<JFACoord> *result_to_flood = &initial_flooded_result;
|
2024-05-18 18:00:58 +03:00
|
|
|
Array<JFACoord> intermediate_result(pixel_count, invalid_coord);
|
2024-05-08 11:13:20 +02:00
|
|
|
Array<JFACoord> *result_after_flooding = &intermediate_result;
|
|
|
|
|
|
|
|
|
|
int step_size = power_of_2_max_i(outline_width) / 2;
|
|
|
|
|
|
|
|
|
|
while (step_size != 0) {
|
2024-05-10 17:57:20 +02:00
|
|
|
jump_flooding_pass(
|
|
|
|
|
*result_to_flood, *result_after_flooding, size, rect_x_range, rect_y_range, step_size);
|
2024-05-08 11:13:20 +02:00
|
|
|
std::swap(result_to_flood, result_after_flooding);
|
|
|
|
|
step_size /= 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Premultiplied outline color. */
|
|
|
|
|
float4 color = data->outline_color;
|
|
|
|
|
color.x *= color.w;
|
|
|
|
|
color.y *= color.w;
|
|
|
|
|
color.z *= color.w;
|
|
|
|
|
|
2024-07-25 14:32:26 +02:00
|
|
|
const float text_color_alpha = data->color[3];
|
|
|
|
|
|
2024-05-08 11:13:20 +02:00
|
|
|
/* We have distances to the closest opaque parts of the image now. Composite the
|
|
|
|
|
* outline into the output image. */
|
|
|
|
|
|
2024-05-10 17:57:20 +02:00
|
|
|
threading::parallel_for(rect_y_range, 8, [&](const IndexRange y_range) {
|
2024-05-08 11:13:20 +02:00
|
|
|
for (const int y : y_range) {
|
2024-05-10 17:57:20 +02:00
|
|
|
size_t index = size_t(y) * size.x + rect_x_range.start();
|
2024-05-08 11:13:20 +02:00
|
|
|
uchar *dst = out->byte_buffer.data + index * 4;
|
2024-05-10 17:57:20 +02:00
|
|
|
for (int x = rect_x_range.start(); x < rect_x_range.one_after_last(); x++, index++, dst += 4)
|
|
|
|
|
{
|
2024-05-08 11:13:20 +02:00
|
|
|
JFACoord closest_texel = (*result_to_flood)[index];
|
|
|
|
|
if (closest_texel.x == JFA_INVALID) {
|
|
|
|
|
/* Outside of outline, leave output pixel as is. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Fade out / anti-alias the outline over one pixel towards outline distance. */
|
|
|
|
|
float distance = math::distance(float2(x, y), float2(closest_texel.x, closest_texel.y));
|
|
|
|
|
float alpha = math::clamp(outline_width - distance + 1.0f, 0.0f, 1.0f);
|
2024-07-25 14:32:26 +02:00
|
|
|
|
|
|
|
|
/* Do not put outline inside the text shape:
|
|
|
|
|
* - When overall text color is fully opaque, we want to make
|
|
|
|
|
* outline fully transparent only where text is fully opaque.
|
|
|
|
|
* This ensures that combined anti-aliased pixels at text boundary
|
|
|
|
|
* are properly fully opaque.
|
|
|
|
|
* - However when text color is fully transparent, we want to
|
|
|
|
|
* Use opposite alpha of text, to anti-alias the inner edge of
|
|
|
|
|
* the outline.
|
|
|
|
|
* In between those two, interpolate the alpha modulation factor. */
|
|
|
|
|
float text_alpha = tmp_buf[index].w * (1.0f / 255.0f);
|
|
|
|
|
float mul_opaque_text = text_alpha >= 1.0f ? 0.0f : 1.0f;
|
|
|
|
|
float mul_transparent_text = 1.0f - text_alpha;
|
|
|
|
|
float mul = math::interpolate(mul_transparent_text, mul_opaque_text, text_color_alpha);
|
|
|
|
|
alpha *= mul;
|
|
|
|
|
|
2024-05-08 11:13:20 +02:00
|
|
|
float4 col1 = color;
|
|
|
|
|
col1 *= alpha;
|
|
|
|
|
|
|
|
|
|
/* Blend over the output. */
|
|
|
|
|
float mfac = 1.0f - col1.w;
|
|
|
|
|
float4 col2 = load_premul_pixel(dst);
|
|
|
|
|
float4 col = col1 + mfac * col2;
|
|
|
|
|
store_premul_pixel(col, dst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
BLF_buffer(runtime.font, nullptr, out->byte_buffer.data, size.x, size.y, display);
|
2024-07-26 10:49:10 +02:00
|
|
|
|
|
|
|
|
return outline_rect;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Similar to #IMB_rectfill_area but blends the given color under the
|
|
|
|
|
* existing image. Also only works on byte buffers. */
|
|
|
|
|
static void fill_rect_alpha_under(
|
|
|
|
|
const ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2)
|
|
|
|
|
{
|
|
|
|
|
const int width = ibuf->x;
|
|
|
|
|
const int height = ibuf->y;
|
|
|
|
|
x1 = math::clamp(x1, 0, width);
|
|
|
|
|
x2 = math::clamp(x2, 0, width);
|
|
|
|
|
y1 = math::clamp(y1, 0, height);
|
|
|
|
|
y2 = math::clamp(y2, 0, height);
|
|
|
|
|
if (x1 > x2) {
|
|
|
|
|
std::swap(x1, x2);
|
|
|
|
|
}
|
|
|
|
|
if (y1 > y2) {
|
|
|
|
|
std::swap(y1, y2);
|
|
|
|
|
}
|
|
|
|
|
if (x1 == x2 || y1 == y2) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float4 premul_col = col;
|
|
|
|
|
straight_to_premul_v4(premul_col);
|
|
|
|
|
|
|
|
|
|
for (int y = y1; y < y2; y++) {
|
|
|
|
|
uchar *dst = ibuf->byte_buffer.data + (size_t(width) * y + x1) * 4;
|
|
|
|
|
for (int x = x1; x < x2; x++) {
|
|
|
|
|
float4 pix = load_premul_pixel(dst);
|
|
|
|
|
float fac = 1.0f - pix.w;
|
2024-07-27 13:26:56 +10:00
|
|
|
float4 dst_fl = fac * premul_col + pix;
|
|
|
|
|
store_premul_pixel(dst_fl, dst);
|
2024-07-26 10:49:10 +02:00
|
|
|
dst += 4;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-08 11:13:20 +02:00
|
|
|
}
|
|
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
static int text_effect_line_size_get(const SeqRenderData *context, const Sequence *seq)
|
|
|
|
|
{
|
|
|
|
|
TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
|
|
|
|
/* Compensate text size for preview render size. */
|
|
|
|
|
double proxy_size_comp = context->scene->r.size / 100.0;
|
|
|
|
|
if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) {
|
|
|
|
|
proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return proxy_size_comp * data->text_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int text_effect_font_init(const SeqRenderData *context, const Sequence *seq, int font_flags)
|
2015-07-01 20:29:18 +02:00
|
|
|
{
|
2023-07-20 09:46:24 +02:00
|
|
|
TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
2019-01-13 21:28:07 -08:00
|
|
|
int font = blf_mono_font_render;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-01-13 21:28:07 -08:00
|
|
|
if (data->text_blf_id == SEQ_FONT_NOT_LOADED) {
|
|
|
|
|
data->text_blf_id = -1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-08-27 12:59:55 +10:00
|
|
|
SEQ_effect_text_font_load(data, false);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-13 21:28:07 -08:00
|
|
|
if (data->text_blf_id >= 0) {
|
|
|
|
|
font = data->text_blf_id;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
BLF_size(font, text_effect_line_size_get(context, seq));
|
|
|
|
|
BLF_enable(font, font_flags);
|
|
|
|
|
return font;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
static blender::Vector<CharInfo> build_character_info(const TextVars *data, int font)
|
|
|
|
|
{
|
|
|
|
|
blender::Vector<CharInfo> characters;
|
|
|
|
|
const size_t len_max = BLI_strnlen(data->text, sizeof(data->text));
|
|
|
|
|
int byte_offset = 0;
|
|
|
|
|
while (byte_offset <= len_max) {
|
|
|
|
|
const char *str = data->text + byte_offset;
|
|
|
|
|
const int char_length = BLI_str_utf8_size_safe(str);
|
|
|
|
|
|
|
|
|
|
CharInfo char_info;
|
|
|
|
|
char_info.str_ptr = str;
|
|
|
|
|
char_info.byte_length = char_length;
|
|
|
|
|
char_info.advance_x = BLF_glyph_advance(font, str);
|
|
|
|
|
characters.append(char_info);
|
|
|
|
|
|
|
|
|
|
byte_offset += char_length;
|
2015-09-09 14:36:12 +02:00
|
|
|
}
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
return characters;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
static int wrap_width_get(const TextVars *data, const int2 image_size)
|
|
|
|
|
{
|
|
|
|
|
if (data->wrap_width == 0.0f) {
|
|
|
|
|
return std::numeric_limits<int>::max();
|
|
|
|
|
}
|
|
|
|
|
return data->wrap_width * image_size.x;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
/* Lines must contain CharInfo for newlines and \0, as UI must know where they begin. */
|
|
|
|
|
static void apply_word_wrapping(const TextVars *data,
|
|
|
|
|
TextVarsRuntime &runtime,
|
|
|
|
|
const int2 image_size,
|
|
|
|
|
blender::Vector<CharInfo> &characters)
|
|
|
|
|
{
|
|
|
|
|
const int wrap_width = wrap_width_get(data, image_size);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
float2 char_position{0.0f, 0.0f};
|
|
|
|
|
CharInfo *last_space = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
/* First pass: Find characters where line has to be broken. */
|
|
|
|
|
for (CharInfo &character : characters) {
|
|
|
|
|
if (character.str_ptr[0] == ' ') {
|
|
|
|
|
character.position = char_position;
|
|
|
|
|
last_space = &character;
|
|
|
|
|
}
|
|
|
|
|
if (character.str_ptr[0] == '\n') {
|
|
|
|
|
char_position.x = 0;
|
|
|
|
|
last_space = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (character.str_ptr[0] != '\0' && char_position.x > wrap_width && last_space != nullptr) {
|
|
|
|
|
last_space->do_wrap = true;
|
|
|
|
|
char_position -= last_space->position + last_space->advance_x;
|
|
|
|
|
}
|
|
|
|
|
char_position.x += character.advance_x;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
/* Second pass: Fill lines with characters. */
|
|
|
|
|
char_position = {0.0f, 0.0f};
|
|
|
|
|
runtime.lines.append(LineInfo());
|
|
|
|
|
for (CharInfo &character : characters) {
|
|
|
|
|
character.position = char_position;
|
|
|
|
|
runtime.lines.last().characters.append(character);
|
|
|
|
|
runtime.lines.last().width = char_position.x;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
char_position.x += character.advance_x;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
if (character.do_wrap || character.str_ptr[0] == '\n') {
|
|
|
|
|
runtime.lines.append(LineInfo());
|
|
|
|
|
char_position.x = 0;
|
|
|
|
|
char_position.y -= runtime.line_height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
static int text_box_width_get(const blender::Vector<LineInfo> &lines)
|
|
|
|
|
{
|
|
|
|
|
int width_max = 0;
|
2020-11-06 15:42:52 +01:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
for (const LineInfo &line : lines) {
|
|
|
|
|
width_max = std::max(width_max, line.width);
|
2015-07-11 02:17:06 +10:00
|
|
|
}
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
return width_max;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
static float2 horizontal_alignment_offset_get(const TextVars *data,
|
|
|
|
|
float line_width,
|
|
|
|
|
int width_max)
|
|
|
|
|
{
|
|
|
|
|
const float line_offset = (width_max - line_width);
|
|
|
|
|
|
|
|
|
|
if (data->align == SEQ_TEXT_ALIGN_X_RIGHT) {
|
|
|
|
|
return {line_offset, 0.0f};
|
|
|
|
|
}
|
|
|
|
|
else if (data->align == SEQ_TEXT_ALIGN_X_CENTER) {
|
|
|
|
|
return {line_offset / 2.0f, 0.0f};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {0.0f, 0.0f};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static float2 anchor_offset_get(const TextVars *data, int width_max, int text_height)
|
|
|
|
|
{
|
|
|
|
|
float2 anchor_offset;
|
|
|
|
|
|
|
|
|
|
switch (data->anchor_x) {
|
|
|
|
|
case SEQ_TEXT_ALIGN_X_LEFT:
|
|
|
|
|
anchor_offset.x = 0;
|
|
|
|
|
break;
|
|
|
|
|
case SEQ_TEXT_ALIGN_X_CENTER:
|
|
|
|
|
anchor_offset.x = -width_max / 2.0f;
|
|
|
|
|
break;
|
|
|
|
|
case SEQ_TEXT_ALIGN_X_RIGHT:
|
|
|
|
|
anchor_offset.x = -width_max;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
switch (data->anchor_y) {
|
|
|
|
|
case SEQ_TEXT_ALIGN_Y_TOP:
|
|
|
|
|
anchor_offset.y = 0;
|
|
|
|
|
break;
|
|
|
|
|
case SEQ_TEXT_ALIGN_Y_CENTER:
|
|
|
|
|
anchor_offset.y = text_height / 2.0f;
|
|
|
|
|
break;
|
|
|
|
|
case SEQ_TEXT_ALIGN_Y_BOTTOM:
|
|
|
|
|
anchor_offset.y = text_height;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return anchor_offset;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void apply_text_alignment(const TextVars *data,
|
|
|
|
|
TextVarsRuntime &runtime,
|
|
|
|
|
const int2 image_size)
|
|
|
|
|
{
|
|
|
|
|
const int width_max = text_box_width_get(runtime.lines);
|
|
|
|
|
const int text_height = runtime.lines.size() * runtime.line_height;
|
|
|
|
|
|
|
|
|
|
const float2 image_center{data->loc[0] * image_size.x, data->loc[1] * image_size.y};
|
|
|
|
|
const float2 line_height_offset{0.0f, float(-runtime.line_height - BLF_descender(runtime.font))};
|
|
|
|
|
const float2 anchor = anchor_offset_get(data, width_max, text_height);
|
|
|
|
|
|
|
|
|
|
Vector<rcti> line_boxes;
|
|
|
|
|
|
|
|
|
|
for (LineInfo &line : runtime.lines) {
|
|
|
|
|
const float2 alignment_x = horizontal_alignment_offset_get(data, line.width, width_max);
|
|
|
|
|
const float2 alignment = math::round(image_center + line_height_offset + alignment_x + anchor);
|
|
|
|
|
|
|
|
|
|
for (CharInfo &character : line.characters) {
|
|
|
|
|
character.position += alignment;
|
2015-07-11 02:17:06 +10:00
|
|
|
}
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
|
2024-10-08 09:54:04 +11:00
|
|
|
/* Get text box for line.
|
|
|
|
|
* This has to be done, because some fonts do not define a descender value,
|
|
|
|
|
* but define their height. In that case, box has unwanted offset in Y axis. */
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
rcti line_box;
|
|
|
|
|
size_t str_len = line.characters.last().str_ptr - line.characters.first().str_ptr;
|
|
|
|
|
BLF_boundbox(runtime.font, line.characters.first().str_ptr, str_len, &line_box, nullptr);
|
|
|
|
|
BLI_rcti_translate(
|
|
|
|
|
&line_box, line.characters.first().position.x, line.characters.first().position.y);
|
|
|
|
|
line_boxes.append(line_box);
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
runtime.text_boundbox = line_boxes.first();
|
|
|
|
|
for (const rcti &box : line_boxes) {
|
|
|
|
|
BLI_rcti_union(&runtime.text_boundbox, &box);
|
2015-07-01 20:29:18 +02:00
|
|
|
}
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void calc_text_runtime(const Sequence *seq,
|
|
|
|
|
int font,
|
|
|
|
|
const int2 image_size,
|
|
|
|
|
TextVarsRuntime &r_runtime)
|
|
|
|
|
{
|
|
|
|
|
const TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
|
|
|
|
|
|
|
|
|
r_runtime.font = font;
|
|
|
|
|
r_runtime.line_height = BLF_height_max(font);
|
|
|
|
|
r_runtime.font_descender = BLF_descender(font);
|
|
|
|
|
r_runtime.character_count = BLI_strlen_utf8(data->text);
|
|
|
|
|
|
|
|
|
|
blender::Vector<CharInfo> characters_temp = build_character_info(data, font);
|
|
|
|
|
apply_word_wrapping(data, r_runtime, image_size, characters_temp);
|
|
|
|
|
apply_text_alignment(data, r_runtime, image_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ImBuf *do_text_effect(const SeqRenderData *context,
|
|
|
|
|
Sequence *seq,
|
|
|
|
|
float /*timeline_frame*/,
|
|
|
|
|
float /*fac*/,
|
|
|
|
|
ImBuf * /*ibuf1*/,
|
|
|
|
|
ImBuf * /*ibuf2*/)
|
|
|
|
|
{
|
|
|
|
|
/* NOTE: text rasterization only fills in part of output image,
|
|
|
|
|
* need to clear it. */
|
|
|
|
|
ImBuf *out = prepare_effect_imbufs(context, nullptr, nullptr, false);
|
|
|
|
|
TextVars *data = static_cast<TextVars *>(seq->effectdata);
|
|
|
|
|
|
|
|
|
|
const char *display_device = context->scene->display_settings.display_device;
|
|
|
|
|
ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device);
|
|
|
|
|
const int font_flags = ((data->flag & SEQ_TEXT_BOLD) ? BLF_BOLD : 0) |
|
|
|
|
|
((data->flag & SEQ_TEXT_ITALIC) ? BLF_ITALIC : 0);
|
|
|
|
|
|
|
|
|
|
const int font = text_effect_font_init(context, seq, font_flags);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
TextVarsRuntime runtime;
|
|
|
|
|
calc_text_runtime(seq, font, {out->x, out->y}, runtime);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
rcti outline_rect = draw_text_outline(context, data, runtime, display, out);
|
|
|
|
|
BLF_buffer(font, nullptr, out->byte_buffer.data, out->x, out->y, display);
|
|
|
|
|
text_draw(runtime, data->color);
|
2024-05-10 18:54:53 +02:00
|
|
|
BLF_buffer(font, nullptr, nullptr, 0, 0, nullptr);
|
2021-03-23 16:03:28 +11:00
|
|
|
BLF_disable(font, font_flags);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2024-07-26 10:49:10 +02:00
|
|
|
/* Draw shadow. */
|
|
|
|
|
if (data->flag & SEQ_TEXT_SHADOW) {
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
draw_text_shadow(context, data, runtime.line_height, outline_rect, out);
|
2024-07-26 10:49:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Draw box under text. */
|
|
|
|
|
if (data->flag & SEQ_TEXT_BOX) {
|
|
|
|
|
if (out->byte_buffer.data) {
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
const int margin = data->box_margin * out->x;
|
|
|
|
|
const int minx = runtime.text_boundbox.xmin - margin;
|
|
|
|
|
const int maxx = runtime.text_boundbox.xmax + margin;
|
|
|
|
|
const int miny = runtime.text_boundbox.ymin - margin;
|
|
|
|
|
const int maxy = runtime.text_boundbox.ymax + margin;
|
2024-07-26 10:49:10 +02:00
|
|
|
fill_rect_alpha_under(out, data->box_color, minx, miny, maxx, maxy);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-01 20:29:18 +02:00
|
|
|
return out;
|
|
|
|
|
}
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
} // namespace blender::seq
|
2015-07-01 20:29:18 +02:00
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Sequence Effect Factory
|
|
|
|
|
* \{ */
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void init_noop(Sequence * /*seq*/) {}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void load_noop(Sequence * /*seq*/) {}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_noop(Sequence * /*seq*/, const bool /*do_id_user*/) {}
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static int num_inputs_default()
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
|
|
|
|
return 2;
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static void copy_effect_default(Sequence *dst, const Sequence *src, const int /*flag*/)
|
2015-07-01 20:29:18 +02:00
|
|
|
{
|
|
|
|
|
dst->effectdata = MEM_dupallocN(src->effectdata);
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void free_effect_default(Sequence *seq, const bool /*do_id_user*/)
|
2015-07-01 20:29:18 +02:00
|
|
|
{
|
2021-08-06 13:59:38 +10:00
|
|
|
MEM_SAFE_FREE(seq->effectdata);
|
2015-07-01 20:29:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_noop(const Sequence * /*seq*/, float /*fac*/)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::DoEffect;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_fade(const Sequence * /*seq*/, float fac)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 14:28:52 +01:00
|
|
|
if (fac == 0.0f) {
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::UseInput1;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2021-12-28 14:28:52 +01:00
|
|
|
if (fac == 1.0f) {
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::UseInput2;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::DoEffect;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_mul_input2(const Sequence * /*seq*/, float fac)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 14:28:52 +01:00
|
|
|
if (fac == 0.0f) {
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::UseInput1;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::DoEffect;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-02-05 11:37:42 +02:00
|
|
|
static StripEarlyOut early_out_mul_input1(const Sequence * /*seq*/, float fac)
|
2021-11-15 21:03:43 +01:00
|
|
|
{
|
2021-12-28 14:28:52 +01:00
|
|
|
if (fac == 0.0f) {
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::UseInput2;
|
2021-11-15 21:03:43 +01:00
|
|
|
}
|
2024-02-05 11:37:42 +02:00
|
|
|
return StripEarlyOut::DoEffect;
|
2021-11-15 21:03:43 +01:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 09:46:24 +02:00
|
|
|
static void get_default_fac_noop(const Scene * /*scene*/,
|
2024-02-05 11:37:42 +02:00
|
|
|
const Sequence * /*seq*/,
|
2023-07-20 09:46:24 +02:00
|
|
|
float /*timeline_frame*/,
|
2022-06-29 12:45:59 +02:00
|
|
|
float *fac)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2021-12-28 14:28:52 +01:00
|
|
|
*fac = 1.0f;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2022-06-29 12:45:59 +02:00
|
|
|
static void get_default_fac_fade(const Scene *scene,
|
2024-02-05 11:37:42 +02:00
|
|
|
const Sequence *seq,
|
2022-06-29 12:45:59 +02:00
|
|
|
float timeline_frame,
|
|
|
|
|
float *fac)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-07-20 20:11:08 +10:00
|
|
|
*fac = float(timeline_frame - SEQ_time_left_handle_frame_get(scene, seq));
|
2022-06-29 12:45:59 +02:00
|
|
|
*fac /= SEQ_time_strip_length_get(scene, seq);
|
2024-05-10 19:07:53 +03:00
|
|
|
*fac = math::clamp(*fac, 0.0f, 1.0f);
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-09 18:02:59 +02:00
|
|
|
static ImBuf *init_execution(const SeqRenderData *context, ImBuf *ibuf1, ImBuf *ibuf2)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2024-09-09 18:02:59 +02:00
|
|
|
ImBuf *out = prepare_effect_imbufs(context, ibuf1, ibuf2);
|
2010-07-25 17:19:55 +00:00
|
|
|
return out;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
static SeqEffectHandle get_sequence_effect_impl(int seq_type)
|
2012-05-17 23:21:11 +00:00
|
|
|
{
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle rval;
|
2009-01-12 19:02:08 +00:00
|
|
|
int sequence_type = seq_type;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = false;
|
|
|
|
|
rval.supports_mask = false;
|
2009-01-12 19:02:08 +00:00
|
|
|
rval.init = init_noop;
|
|
|
|
|
rval.num_inputs = num_inputs_default;
|
|
|
|
|
rval.load = load_noop;
|
|
|
|
|
rval.free = free_noop;
|
|
|
|
|
rval.early_out = early_out_noop;
|
|
|
|
|
rval.get_default_fac = get_default_fac_noop;
|
2023-07-20 09:46:24 +02:00
|
|
|
rval.execute = nullptr;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.init_execution = init_execution;
|
2023-07-20 09:46:24 +02:00
|
|
|
rval.execute_slice = nullptr;
|
|
|
|
|
rval.copy = nullptr;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
switch (sequence_type) {
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_CROSS:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_cross_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.early_out = early_out_fade;
|
|
|
|
|
rval.get_default_fac = get_default_fac_fade;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_GAMCROSS:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.early_out = early_out_fade;
|
|
|
|
|
rval.get_default_fac = get_default_fac_fade;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_gammacross_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_ADD:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_add_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.early_out = early_out_mul_input2;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_SUB:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_sub_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.early_out = early_out_mul_input2;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_MUL:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_mul_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.early_out = early_out_mul_input2;
|
|
|
|
|
break;
|
2017-11-27 23:33:08 +01:00
|
|
|
case SEQ_TYPE_SCREEN:
|
|
|
|
|
case SEQ_TYPE_OVERLAY:
|
2019-08-23 13:13:59 +02:00
|
|
|
case SEQ_TYPE_COLOR_BURN:
|
2017-11-27 23:33:08 +01:00
|
|
|
case SEQ_TYPE_LINEAR_BURN:
|
|
|
|
|
case SEQ_TYPE_DARKEN:
|
|
|
|
|
case SEQ_TYPE_LIGHTEN:
|
|
|
|
|
case SEQ_TYPE_DODGE:
|
|
|
|
|
case SEQ_TYPE_SOFT_LIGHT:
|
|
|
|
|
case SEQ_TYPE_HARD_LIGHT:
|
|
|
|
|
case SEQ_TYPE_PIN_LIGHT:
|
|
|
|
|
case SEQ_TYPE_LIN_LIGHT:
|
|
|
|
|
case SEQ_TYPE_VIVID_LIGHT:
|
|
|
|
|
case SEQ_TYPE_BLEND_COLOR:
|
|
|
|
|
case SEQ_TYPE_HUE:
|
|
|
|
|
case SEQ_TYPE_SATURATION:
|
|
|
|
|
case SEQ_TYPE_VALUE:
|
|
|
|
|
case SEQ_TYPE_DIFFERENCE:
|
|
|
|
|
case SEQ_TYPE_EXCLUSION:
|
|
|
|
|
rval.multithreaded = true;
|
|
|
|
|
rval.execute_slice = do_blend_mode_effect;
|
|
|
|
|
rval.early_out = early_out_mul_input2;
|
|
|
|
|
break;
|
|
|
|
|
case SEQ_TYPE_COLORMIX:
|
|
|
|
|
rval.multithreaded = true;
|
|
|
|
|
rval.init = init_colormix_effect;
|
|
|
|
|
rval.free = free_effect_default;
|
|
|
|
|
rval.copy = copy_effect_default;
|
|
|
|
|
rval.execute_slice = do_colormix_effect;
|
|
|
|
|
rval.early_out = early_out_mul_input2;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_ALPHAOVER:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_alpha_over_or_under;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_alphaover_effect;
|
2021-11-15 21:03:43 +01:00
|
|
|
rval.early_out = early_out_mul_input1;
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_OVERDROP:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_overdrop_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_ALPHAUNDER:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.multithreaded = true;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_alpha_over_or_under;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute_slice = do_alphaunder_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_WIPE:
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_wipe_effect;
|
|
|
|
|
rval.num_inputs = num_inputs_wipe;
|
|
|
|
|
rval.free = free_wipe_effect;
|
|
|
|
|
rval.copy = copy_wipe_effect;
|
|
|
|
|
rval.early_out = early_out_fade;
|
|
|
|
|
rval.get_default_fac = get_default_fac_fade;
|
|
|
|
|
rval.execute = do_wipe_effect;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_GLOW:
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_glow_effect;
|
|
|
|
|
rval.num_inputs = num_inputs_glow;
|
|
|
|
|
rval.free = free_glow_effect;
|
|
|
|
|
rval.copy = copy_glow_effect;
|
|
|
|
|
rval.execute = do_glow_effect;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_TRANSFORM:
|
2020-08-17 20:52:59 +02:00
|
|
|
rval.multithreaded = true;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_transform_effect;
|
|
|
|
|
rval.num_inputs = num_inputs_transform;
|
|
|
|
|
rval.free = free_transform_effect;
|
|
|
|
|
rval.copy = copy_transform_effect;
|
2020-08-17 20:52:59 +02:00
|
|
|
rval.execute_slice = do_transform_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_SPEED:
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_speed_effect;
|
|
|
|
|
rval.num_inputs = num_inputs_speed;
|
|
|
|
|
rval.load = load_speed_effect;
|
|
|
|
|
rval.free = free_speed_effect;
|
|
|
|
|
rval.copy = copy_speed_effect;
|
2012-08-08 16:46:45 +00:00
|
|
|
rval.execute = do_speed_effect;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.early_out = early_out_speed;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_COLOR:
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.init = init_solid_color;
|
|
|
|
|
rval.num_inputs = num_inputs_color;
|
|
|
|
|
rval.early_out = early_out_color;
|
|
|
|
|
rval.free = free_solid_color;
|
|
|
|
|
rval.copy = copy_solid_color;
|
|
|
|
|
rval.execute = do_solid_color;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_MULTICAM:
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.num_inputs = num_inputs_multicam;
|
|
|
|
|
rval.early_out = early_out_multicam;
|
|
|
|
|
rval.execute = do_multicam;
|
|
|
|
|
break;
|
2012-06-07 15:49:02 +00:00
|
|
|
case SEQ_TYPE_ADJUSTMENT:
|
2014-03-20 15:45:20 +06:00
|
|
|
rval.supports_mask = true;
|
2012-05-08 09:31:25 +00:00
|
|
|
rval.num_inputs = num_inputs_adjustment;
|
|
|
|
|
rval.early_out = early_out_adjustment;
|
|
|
|
|
rval.execute = do_adjustment;
|
|
|
|
|
break;
|
2014-07-19 22:16:10 +06:00
|
|
|
case SEQ_TYPE_GAUSSIAN_BLUR:
|
|
|
|
|
rval.init = init_gaussian_blur_effect;
|
|
|
|
|
rval.num_inputs = num_inputs_gaussian_blur;
|
|
|
|
|
rval.free = free_gaussian_blur_effect;
|
|
|
|
|
rval.copy = copy_gaussian_blur_effect;
|
|
|
|
|
rval.early_out = early_out_gaussian_blur;
|
2016-01-22 00:11:37 +05:00
|
|
|
rval.execute = do_gaussian_blur_effect;
|
2014-07-19 22:16:10 +06:00
|
|
|
break;
|
2015-07-01 20:29:18 +02:00
|
|
|
case SEQ_TYPE_TEXT:
|
|
|
|
|
rval.num_inputs = num_inputs_text;
|
|
|
|
|
rval.init = init_text_effect;
|
2019-01-13 21:28:07 -08:00
|
|
|
rval.free = free_text_effect;
|
|
|
|
|
rval.load = load_text_effect;
|
|
|
|
|
rval.copy = copy_text_effect;
|
2015-07-01 20:29:18 +02:00
|
|
|
rval.early_out = early_out_text;
|
VSE: Add text alignment feature
Previously, alignment did exist, but it only changed whole text block
position in relation to a fixed point. This was later renamed to "Anchor".
Now it correctly aligns each line of text. Alignment works with newline
character and word wrapping.
Currently newline characters can't be entered directly, but this should
be resolved soon.
To keep existing anchoring feature, new DNA fields are added and
values from old alignment are copied there.
This PR is part of bigger change [1], and originally I expected to
implement this feature at later stage. But the design called for drawing
text character by character, which would mean, that I would have to
rewrite text alignment anyway.
To render the text, a struct is built, where position and width of each
character is stored. In addition, width of each line is stored. This allows
to implement proper text alignment feature, instead of existing
anchoring. Text is then drawn character by character in a loop.
There are some small differences in text rendering, since this is only
approximation of how BLF library draws glyphs, but it is very close.
For text bounbox, `BLF_boundbox()` is used on per line basis,
because some fonts do not use their full height and this information
is not available on VSE side.
[1] https://projects.blender.org/blender/blender/issues/126547
Pull Request: https://projects.blender.org/blender/blender/pulls/126660
2024-10-04 12:20:33 +02:00
|
|
|
rval.execute = blender::seq::do_text_effect;
|
2015-07-01 20:29:18 +02:00
|
|
|
break;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-07 10:51:22 +11:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
|
/** \name Public Sequencer Effect API
|
|
|
|
|
* \{ */
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle SEQ_effect_handle_get(Sequence *seq)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle rval = {false, false, nullptr};
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2012-06-07 15:49:02 +00:00
|
|
|
if (seq->type & SEQ_TYPE_EFFECT) {
|
2009-01-12 19:02:08 +00:00
|
|
|
rval = get_sequence_effect_impl(seq->type);
|
|
|
|
|
if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
|
|
|
|
|
rval.load(seq);
|
|
|
|
|
seq->flag &= ~SEQ_EFFECT_NOT_LOADED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle seq_effect_get_sequence_blend(Sequence *seq)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle rval = {false, false, nullptr};
|
2009-01-12 19:02:08 +00:00
|
|
|
|
|
|
|
|
if (seq->blend_mode != 0) {
|
2019-01-13 21:28:07 -08:00
|
|
|
if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
|
|
|
|
|
/* load the effect first */
|
|
|
|
|
rval = get_sequence_effect_impl(seq->type);
|
|
|
|
|
rval.load(seq);
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-12 19:02:08 +00:00
|
|
|
rval = get_sequence_effect_impl(seq->blend_mode);
|
|
|
|
|
if ((seq->flag & SEQ_EFFECT_NOT_LOADED) != 0) {
|
2019-01-13 21:28:07 -08:00
|
|
|
/* now load the blend and unset unloaded flag */
|
2009-01-12 19:02:08 +00:00
|
|
|
rval.load(seq);
|
|
|
|
|
seq->flag &= ~SEQ_EFFECT_NOT_LOADED;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rval;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-19 05:57:27 +01:00
|
|
|
int SEQ_effect_get_num_inputs(int seq_type)
|
2009-01-12 19:02:08 +00:00
|
|
|
{
|
2023-07-20 20:11:08 +10:00
|
|
|
SeqEffectHandle rval = get_sequence_effect_impl(seq_type);
|
2009-01-12 19:02:08 +00:00
|
|
|
|
2022-03-16 11:58:22 +11:00
|
|
|
int count = rval.num_inputs();
|
2012-08-08 16:46:45 +00:00
|
|
|
if (rval.execute || (rval.execute_slice && rval.init_execution)) {
|
2022-03-16 11:58:22 +11:00
|
|
|
return count;
|
2009-01-12 19:02:08 +00:00
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2022-03-07 10:51:22 +11:00
|
|
|
|
|
|
|
|
/** \} */
|