VSE: Add retiming tool #104544

Closed
Richard Antalik wants to merge 5 commits from iss:retiming-tool into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
25 changed files with 1696 additions and 30 deletions

Binary file not shown.

View File

@ -1872,7 +1872,7 @@ class SEQUENCER_PT_time(SequencerButtonsPanel, Panel):
split.label(text="Channel")
split.prop(strip, "channel", text="")
if not is_effect:
if strip.type == 'SOUND':
split = layout.split(factor=0.5 + max_factor)
split.alignment = 'RIGHT'
split.label(text="Speed Factor")

View File

@ -2620,6 +2620,18 @@ class _defs_sequencer_generic:
options={"KEYMAP_FALLBACK"},
)
@ToolDef.from_fn
def retime():
return dict(
idname="builtin.retime",
label="Retime",
icon="ops.sequencer.retime",
widget="SEQUENCER_GGT_gizmo_retime",
operator=None,
keymap=None,
options={'KEYMAP_FALLBACK'},
)
@ToolDef.from_fn
def sample():
return dict(
@ -3308,6 +3320,7 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel):
"SEQUENCER": [
*_tools_select,
_defs_sequencer_generic.blade,
_defs_sequencer_generic.retime,
],
"SEQUENCER_PREVIEW": [
*_tools_select,

View File

@ -77,6 +77,7 @@
#include "SEQ_channels.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
@ -685,6 +686,25 @@ static bool seq_speed_factor_set(Sequence *seq, void *user_data)
return true;
}
static bool do_versions_sequencer_init_retiming_tool_data(Sequence *seq, void *user_data)
{
const Scene *scene = static_cast<const Scene *>(user_data);
if (seq->speed_factor == 1 || !SEQ_retiming_is_allowed(seq)) {
return true;
}
const int content_length = SEQ_time_strip_length_get(scene, seq);
SEQ_retiming_data_ensure(scene, seq);
SeqRetimingHandle *handle = &seq->retiming_handles[seq->retiming_handle_num - 1];
handle->strip_frame_index = round_fl_to_int(content_length / seq->speed_factor);
seq->speed_factor = 0.0f;
return true;
}
static void version_geometry_nodes_replace_transfer_attribute_node(bNodeTree *ntree)
{
using namespace blender;
@ -1205,6 +1225,16 @@ void do_versions_after_linking_300(Main *bmain, ReportList * /*reports*/)
*/
{
/* Keep this block, even when empty. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
Editing *ed = SEQ_editing_get(scene);
if (ed == nullptr) {
continue;
}
SEQ_for_each_callback(
&scene->ed->seqbase, do_versions_sequencer_init_retiming_tool_data, scene);
}
}
}

View File

@ -867,6 +867,7 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES
ops.sculpt.mask_by_color
ops.sculpt.mesh_filter
ops.sequencer.blade
ops.sequencer.retime
ops.transform.bone_envelope
ops.transform.bone_size
ops.transform.edge_slide

View File

@ -35,10 +35,13 @@ set(SRC
sequencer_drag_drop.c
sequencer_draw.c
sequencer_edit.c
sequencer_gizmo_retime.cc
sequencer_gizmo_retime_type.cc
sequencer_modifier.c
sequencer_ops.c
sequencer_preview.c
sequencer_proxy.c
sequencer_retiming.cc
sequencer_scopes.c
sequencer_select.c
sequencer_thumbnails.c

View File

@ -0,0 +1,108 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_context.h"
#include "BLI_span.hh"
#include "RNA_access.h"
#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_gizmo_library.h"
#include "ED_gizmo_utils.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
/* Own include. */
#include "sequencer_intern.h"
typedef struct GizmoGroup_retime {
wmGizmo *add_handle_gizmo;
wmGizmo *move_handle_gizmo;
wmGizmo *remove_handle_gizmo;
} GizmoGroup_retime;
static bool gizmogroup_retime_poll(const bContext *C, wmGizmoGroupType *gzgt)
{
/* Needed to prevent drawing gizmos when retiming tool is not activated. */
if (!ED_gizmo_poll_or_unlink_delayed_from_tool(C, gzgt)) {
return false;
}
if ((U.gizmo_flag & USER_GIZMO_DRAW) == 0) {
return false;
}
ScrArea *area = CTX_wm_area(C);
if (area == nullptr && area->spacetype != SPACE_SEQ) {
return false;
}
const SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first;
if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) {
return false;
}
Editing *ed = SEQ_editing_get(CTX_data_scene(C));
Sequence *seq = ed->act_seq;
if (ed == nullptr || seq == nullptr || !SEQ_retiming_is_allowed(seq)) {
return false;
}
return true;
}
static void gizmogroup_retime_setup(const bContext * /* C */, wmGizmoGroup *gzgroup)
{
GizmoGroup_retime *ggd = (GizmoGroup_retime *)MEM_callocN(sizeof(GizmoGroup_retime), __func__);
/* Assign gizmos. */
const wmGizmoType *gzt_add_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_add", true);
ggd->add_handle_gizmo = WM_gizmo_new_ptr(gzt_add_handle, gzgroup, nullptr);
const wmGizmoType *gzt_remove_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_remove", true);
ggd->remove_handle_gizmo = WM_gizmo_new_ptr(gzt_remove_handle, gzgroup, nullptr);
const wmGizmoType *gzt_move_handle = WM_gizmotype_find("GIZMO_GT_retime_handle_move", true);
ggd->move_handle_gizmo = WM_gizmo_new_ptr(gzt_move_handle, gzgroup, nullptr);
gzgroup->customdata = ggd;
/* Assign operators. */
wmOperatorType *ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_move", true);
WM_gizmo_operator_set(ggd->move_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_add", true);
WM_gizmo_operator_set(ggd->add_handle_gizmo, 0, ot, nullptr);
ot = WM_operatortype_find("SEQUENCER_OT_retiming_handle_remove", true);
WM_gizmo_operator_set(ggd->remove_handle_gizmo, 0, ot, nullptr);
}
void SEQUENCER_GGT_gizmo_retime(wmGizmoGroupType *gzgt)
{
gzgt->name = "Sequencer Transform Gizmo Retime";
gzgt->idname = "SEQUENCER_GGT_gizmo_retime";
gzgt->flag = WM_GIZMOGROUPTYPE_DRAW_MODAL_ALL;
gzgt->gzmap_params.spaceid = SPACE_SEQ;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
gzgt->poll = gizmogroup_retime_poll;
gzgt->setup = gizmogroup_retime_setup;
}
/** \} */

View File

@ -0,0 +1,568 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_span.hh"
#include "DNA_anim_types.h"
#include "DNA_sequence_types.h"
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_scene.h"
#include "BLF_api.h"
#include "GPU_batch.h"
#include "GPU_batch_utils.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_matrix.h"
#include "GPU_select.h"
#include "GPU_state.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "ED_gizmo_library.h"
#include "ED_screen.h"
#include "ED_view3d.h"
#include "UI_interface.h"
#include "UI_interface_icons.h"
#include "UI_resources.h"
#include "UI_view2d.h"
#include "SEQ_iterator.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
/* Own include. */
#include "sequencer_intern.h"
using blender::MutableSpan;
#define REMOVE_GIZMO_HEIGHT 14.0f * U.dpi_fac /* Pixels from bottom of strip. */
#define RETIME_HANDLE_TRIANGLE_SIZE 14.0f * U.dpi_fac /* Size in pixels. */
#define RETIME_HANDLE_MOUSEOVER_THRESHOLD 16.0f * U.dpi_fac /* Size in pixels. */
#define RETIME_BUTTON_SIZE 0.6f /* Factor based on icon size. */
static float strip_y_rescale(const Sequence *seq, const float y_value)
{
const float y_range = SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM;
return (y_value * y_range) + seq->machine + SEQ_STRIP_OFSBOTTOM;
}
static float handle_x_get(const Sequence *seq, const SeqRetimingHandle *handle)
{
const SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
const bool is_last_handle = (handle == last_handle);
return SEQ_time_start_frame_get(seq) + handle->strip_frame_index + (is_last_handle ? 1 : 0);
}
static const SeqRetimingHandle *mouse_over_handle_get(const Sequence *seq,
const View2D *v2d,
const int mval[2])
{
int best_distance = INT_MAX;
const SeqRetimingHandle *best_handle = nullptr;
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
int distance = round_fl_to_int(
fabsf(UI_view2d_view_to_region_x(v2d, handle_x_get(seq, &handle)) - mval[0]));
if (distance < RETIME_HANDLE_MOUSEOVER_THRESHOLD && distance < best_distance) {
best_distance = distance;
best_handle = &handle;
}
}
return best_handle;
}
static float pixels_to_view_width(const bContext *C, const float width)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_x = UI_view2d_view_to_region_x(v2d, 1) - UI_view2d_view_to_region_x(v2d, 0.0f);
return width / scale_x;
}
static float pixels_to_view_height(const bContext *C, const float height)
{
const View2D *v2d = UI_view2d_fromcontext(C);
float scale_y = UI_view2d_view_to_region_y(v2d, 1) - UI_view2d_view_to_region_y(v2d, 0.0f);
return height / scale_y;
}
static float strip_start_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_left_handle_frame_get(scene, seq));
}
static float strip_end_screenspace_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
const Scene *scene = CTX_data_scene(C);
return UI_view2d_view_to_region_x(v2d, SEQ_time_right_handle_frame_get(scene, seq));
}
static Sequence *active_seq_from_context(const bContext *C)
{
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
return ed->act_seq;
}
static rctf strip_box_get(const bContext *C, const Sequence *seq)
{
const View2D *v2d = UI_view2d_fromcontext(C);
rctf rect;
rect.xmin = strip_start_screenspace_get(C, seq);
rect.xmax = strip_end_screenspace_get(C, seq);
rect.ymin = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0));
rect.ymax = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1));
return rect;
}
static rctf remove_box_get(const bContext *C, const Sequence *seq)
{
rctf rect = strip_box_get(C, seq);
rect.ymax = rect.ymin + REMOVE_GIZMO_HEIGHT;
return rect;
}
static bool mouse_is_inside_box(const rctf *box, const int mval[2])
{
return mval[0] >= box->xmin && mval[0] <= box->xmax && mval[1] >= box->ymin &&
mval[1] <= box->ymax;
}
/* -------------------------------------------------------------------- */
/** \name Retiming Add Handle Gizmo
* \{ */
typedef struct RetimeButtonGizmo {
wmGizmo gizmo;
int icon_id;
const Sequence *seq_under_mouse;
bool is_mouse_over_gizmo;
} RetimeButtonGizmo;
typedef struct ButtonDimensions {
float height;
float width;
float x;
float y;
} ButtonDimensions;
static ButtonDimensions button_dimensions_get(const bContext *C, const RetimeButtonGizmo *gizmo)
{
const Scene *scene = CTX_data_scene(C);
const View2D *v2d = UI_view2d_fromcontext(C);
const Sequence *seq = active_seq_from_context(C);
const float icon_height = UI_icon_get_height(gizmo->icon_id) * U.dpi_fac;
const float icon_width = UI_icon_get_width(gizmo->icon_id) * U.dpi_fac;
const float icon_x = UI_view2d_view_to_region_x(v2d, BKE_scene_frame_get(scene)) +
icon_width / 2;
const float icon_y = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.5)) -
icon_height / 2;
const ButtonDimensions dimensions = {icon_height, icon_width, icon_x, icon_y};
return dimensions;
}
static rctf button_box_get(const bContext *C, const RetimeButtonGizmo *gizmo)
{
ButtonDimensions button_dimensions = button_dimensions_get(C, gizmo);
float x_range = button_dimensions.width;
float y_range = button_dimensions.height;
rctf rect;
rect.xmin = button_dimensions.x;
rect.xmax = button_dimensions.x + x_range;
rect.ymin = button_dimensions.y;
rect.ymax = button_dimensions.y + y_range;
return rect;
}
static void gizmo_retime_handle_add_draw(const bContext *C, wmGizmo *gz)
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
return;
}
const ButtonDimensions button = button_dimensions_get(C, gizmo);
const rctf strip_box = strip_box_get(C, active_seq_from_context(C));
if (!BLI_rctf_isect_pt(&strip_box, button.x, button.y)) {
return;
}
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
const float alpha = gizmo->is_mouse_over_gizmo ? 1.0f : 0.6f;
immUniformColor4f(0.2f, 0.2f, 0.2f, alpha);
imm_draw_circle_fill_2d(pos,
button.x + button.width / 2,
button.y + button.height / 2,
button.width * RETIME_BUTTON_SIZE,
32);
immUnbindProgram();
GPU_polygon_smooth(false);
UI_icon_draw_alpha(button.x, button.y, gizmo->icon_id, alpha);
GPU_polygon_smooth(true);
GPU_blend(GPU_BLEND_NONE);
}
static int gizmo_retime_handle_add_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
Sequence *seq = active_seq_from_context(C);
Sequence *mouse_over_seq = nullptr;
gizmo->is_mouse_over_gizmo = false;
/* Store strip under mouse cursor. */
const rctf strip_box = strip_box_get(C, seq);
if (mouse_is_inside_box(&strip_box, mval)) {
mouse_over_seq = seq;
}
if (gizmo->seq_under_mouse != mouse_over_seq) {
gizmo->seq_under_mouse = mouse_over_seq;
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C));
}
if (gizmo->seq_under_mouse == nullptr) {
return -1;
}
const rctf button_box = button_box_get(C, gizmo);
if (!mouse_is_inside_box(&button_box, mval)) {
return -1;
}
gizmo->is_mouse_over_gizmo = true;
const Scene *scene = CTX_data_scene(C);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "timeline_frame", BKE_scene_frame_get(scene));
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, CTX_data_scene(C));
return 0;
}
static void gizmo_retime_handle_add_setup(wmGizmo *gz)
{
RetimeButtonGizmo *gizmo = (RetimeButtonGizmo *)gz;
gizmo->icon_id = ICON_ADD;
}
void GIZMO_GT_retime_handle_add(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_add";
/* Api callbacks. */
gzt->setup = gizmo_retime_handle_add_setup;
gzt->draw = gizmo_retime_handle_add_draw;
gzt->test_select = gizmo_retime_handle_add_test_select;
gzt->struct_size = sizeof(RetimeButtonGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Move Handle Gizmo
* \{ */
typedef struct RetimeHandleMoveGizmo {
wmGizmo gizmo;
const Sequence *mouse_over_seq;
int mouse_over_handle_x;
} RetimeHandleMoveGizmo;
static void retime_handle_draw(const bContext *C,
const RetimeHandleMoveGizmo *gizmo,
uint pos,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
const Scene *scene = CTX_data_scene(C);
const float handle_x = handle_x_get(seq, handle);
if (handle_x == SEQ_time_left_handle_frame_get(scene, seq)) {
return;
}
if (handle_x == SEQ_time_right_handle_frame_get(scene, seq)) {
return;
}
const View2D *v2d = UI_view2d_fromcontext(C);
const rctf strip_box = strip_box_get(C, seq);
if (!BLI_rctf_isect_x(&strip_box, UI_view2d_view_to_region_x(v2d, handle_x))) {
return; /* Handle out of strip bounds. */
}
const int ui_triangle_size = RETIME_HANDLE_TRIANGLE_SIZE;
const float bottom = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 0.0f)) + 2;
const float top = UI_view2d_view_to_region_y(v2d, strip_y_rescale(seq, 1.0f)) - 2;
const float handle_position = UI_view2d_view_to_region_x(v2d, handle_x);
if (seq == gizmo->mouse_over_seq && handle_x == gizmo->mouse_over_handle_x) {
immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}
else {
immUniformColor4f(0.65f, 0.65f, 0.65f, 1.0f);
}
immBegin(GPU_PRIM_TRI_FAN, 3);
immVertex2f(pos, handle_position - ui_triangle_size / 2, bottom);
immVertex2f(pos, handle_position + ui_triangle_size / 2, bottom);
immVertex2f(pos, handle_position, bottom + ui_triangle_size);
immEnd();
immBegin(GPU_PRIM_LINES, 2);
immVertex2f(pos, handle_position, bottom);
immVertex2f(pos, handle_position, top);
immEnd();
}
static void retime_speed_text_draw(const bContext *C,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle == last_handle) {
return;
}
const Scene *scene = CTX_data_scene(C);
const int start_frame = SEQ_time_left_handle_frame_get(scene, seq);
const int end_frame = SEQ_time_right_handle_frame_get(scene, seq);
int next_handle_index = SEQ_retiming_handle_index_get(seq, handle) + 1;
const SeqRetimingHandle *next_handle = &SEQ_retiming_handles_get(seq)[next_handle_index];
if (handle_x_get(seq, next_handle) < start_frame || handle_x_get(seq, handle) > end_frame) {
return; /* Label out of strip bounds. */
}
const float speed = SEQ_retiming_handle_speed_get(scene, seq, next_handle);
char label_str[20];
const size_t label_len = BLI_snprintf_rlen(
label_str, sizeof(label_str), "%d%%", round_fl_to_int(speed * 100.0f));
const float width = pixels_to_view_width(C, BLF_width(BLF_default(), label_str, label_len));
const float xmin = max_ff(SEQ_time_left_handle_frame_get(scene, seq), handle_x_get(seq, handle));
const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq),
handle_x_get(seq, next_handle));
const float text_x = (xmin + xmax - width) / 2;
const float text_y = strip_y_rescale(seq, 0) + pixels_to_view_height(C, 5);
if (width > xmax - xmin) {
return; /* Not enough space to draw label. */
}
const uchar col[4] = {255, 255, 255, 255};
UI_view2d_text_cache_add(UI_view2d_fromcontext(C), text_x, text_y, label_str, label_len, col);
}
static void gizmo_retime_handle_draw(const bContext *C, wmGizmo *gz)
{
const RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
const View2D *v2d = UI_view2d_fromcontext(C);
wmOrtho2_region_pixelspace(CTX_wm_region(C));
GPU_blend(GPU_BLEND_ALPHA);
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(CTX_data_scene(C), seq);
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (const SeqRetimingHandle &handle : handles) {
retime_speed_text_draw(C, seq, &handle);
if (&handle == handles.begin()) {
continue; /* Ignore first handle. */
}
retime_handle_draw(C, gizmo, pos, seq, &handle);
}
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
UI_view2d_text_cache_draw(CTX_wm_region(C));
UI_view2d_view_ortho(v2d); /* 'UI_view2d_text_cache_draw()' messes up current view. */
}
static int gizmo_retime_handle_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz;
Scene *scene = CTX_data_scene(C);
gizmo->mouse_over_seq = nullptr;
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(CTX_data_scene(C), seq);
const SeqRetimingHandle *handle = mouse_over_handle_get(seq, UI_view2d_fromcontext(C), mval);
const int handle_index = SEQ_retiming_handle_index_get(seq, handle);
if (handle == nullptr) {
return -1;
}
if (handle_x_get(seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
handle_index == 0) {
return -1;
}
rctf strip_box = strip_box_get(C, seq);
BLI_rctf_resize_x(&strip_box, BLI_rctf_size_x(&strip_box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE);
if (!mouse_is_inside_box(&strip_box, mval)) {
return -1;
}
gizmo->mouse_over_seq = seq;
gizmo->mouse_over_handle_x = handle_x_get(seq, handle);
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
static int gizmo_retime_handle_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_EW_SCROLL;
}
return WM_CURSOR_DEFAULT;
}
static void gizmo_retime_handle_setup(wmGizmo *gz)
{
gz->flag = WM_GIZMO_DRAW_MODAL;
}
void GIZMO_GT_retime_handle(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_move";
/* Api callbacks. */
gzt->setup = gizmo_retime_handle_setup;
gzt->draw = gizmo_retime_handle_draw;
gzt->test_select = gizmo_retime_handle_test_select;
gzt->cursor_get = gizmo_retime_handle_cursor_get;
gzt->struct_size = sizeof(RetimeHandleMoveGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Remove Handle Gizmo
* \{ */
static void gizmo_retime_remove_draw(const bContext * /* C */, wmGizmo * /* gz */)
{
/* Pass. */
}
static int gizmo_retime_remove_test_select(bContext *C, wmGizmo *gz, const int mval[2])
{
Scene *scene = CTX_data_scene(C);
Sequence *seq = active_seq_from_context(C);
SEQ_retiming_data_ensure(CTX_data_scene(C), seq);
const SeqRetimingHandle *handle = mouse_over_handle_get(seq, UI_view2d_fromcontext(C), mval);
const int handle_index = SEQ_retiming_handle_index_get(seq, handle);
if (handle == nullptr) {
return -1;
}
if (handle_x_get(seq, handle) == SEQ_time_left_handle_frame_get(scene, seq) ||
handle_index == 0) {
return -1; /* Ignore first handle. */
}
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle == last_handle) {
return -1; /* Last handle can not be removed. */
}
rctf box = remove_box_get(C, seq);
BLI_rctf_resize_x(&box, BLI_rctf_size_x(&box) + 2 * RETIME_HANDLE_TRIANGLE_SIZE);
if (!mouse_is_inside_box(&box, mval)) {
return -1;
}
wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0);
RNA_int_set(&op_elem->ptr, "handle_index", handle_index);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return 0;
}
static int gizmo_retime_remove_cursor_get(wmGizmo *gz)
{
if (RNA_boolean_get(gz->ptr, "show_drag")) {
return WM_CURSOR_ERASER;
}
return WM_CURSOR_DEFAULT;
}
void GIZMO_GT_retime_remove(wmGizmoType *gzt)
{
/* Identifiers. */
gzt->idname = "GIZMO_GT_retime_handle_remove";
/* Api callbacks. */
gzt->draw = gizmo_retime_remove_draw;
gzt->test_select = gizmo_retime_remove_test_select;
gzt->cursor_get = gizmo_retime_remove_cursor_get;
gzt->struct_size = sizeof(wmGizmo);
/* Currently only used for cursor display. */
RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
}
/** \} */

View File

@ -10,11 +10,17 @@
#include "DNA_sequence_types.h"
#include "RNA_access.h"
#ifdef __cplusplus
extern "C" {
#endif
/* Internal exports only. */
struct ARegion;
struct ARegionType;
struct Depsgraph;
struct wmGizmoGroupType;
struct wmGizmoType;
struct Main;
struct Scene;
struct SeqCollection;
@ -298,3 +304,21 @@ int sequencer_image_seq_get_minmax_frame(struct wmOperator *op,
int *r_numdigits);
void sequencer_image_seq_reserve_frames(
struct wmOperator *op, struct StripElem *se, int len, int minframe, int numdigits);
/* sequencer_retiming.c */
void SEQUENCER_OT_retiming_reset(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_move(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_add(struct wmOperatorType *ot);
void SEQUENCER_OT_retiming_handle_remove(struct wmOperatorType *ot);
/* sequencer_gizmo_retime.c */
void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt);
/* sequencer_gizmo_retime_type.c */
void GIZMO_GT_retime_handle_add(struct wmGizmoType *gzt);
void GIZMO_GT_retime_handle(struct wmGizmoType *gzt);
void GIZMO_GT_retime_remove(struct wmGizmoType *gzt);
#ifdef __cplusplus
}
#endif

View File

@ -68,6 +68,12 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_cursor_set);
WM_operatortype_append(SEQUENCER_OT_scene_frame_range_update);
/* sequencer_retiming.c */
WM_operatortype_append(SEQUENCER_OT_retiming_reset);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_move);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_add);
WM_operatortype_append(SEQUENCER_OT_retiming_handle_remove);
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);
WM_operatortype_append(SEQUENCER_OT_select);

View File

@ -0,0 +1,377 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
/** \file
* \ingroup spseq
*/
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "BKE_context.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
#include "WM_api.h"
#include "RNA_define.h"
#include "UI_interface.h"
#include "UI_view2d.h"
#include "DEG_depsgraph.h"
/* Own include. */
#include "sequencer_intern.h"
using blender::MutableSpan;
static bool retiming_poll(bContext *C)
{
if (!sequencer_edit_poll(C)) {
return false;
}
const Editing *ed = SEQ_editing_get(CTX_data_scene(C));
Sequence *seq = ed->act_seq;
if (seq != nullptr && !SEQ_retiming_is_allowed(seq)) {
CTX_wm_operator_poll_msg_set(C, "This strip type can not be retimed");
return false;
}
return true;
}
static void retiming_handle_overlap(Scene *scene, Sequence *seq)
{
ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene));
SeqCollection *strips = SEQ_collection_create(__func__);
SEQ_collection_append_strip(seq, strips);
SeqCollection *dependant = SEQ_collection_create(__func__);
SEQ_collection_expand(scene, seqbase, strips, SEQ_query_strip_effect_chain);
SEQ_collection_remove_strip(seq, dependant);
SEQ_transform_handle_overlap(scene, seqbase, strips, dependant, true);
SEQ_collection_free(strips);
SEQ_collection_free(dependant);
}
/*-------------------------------------------------------------------- */
/** \name Retiming Reset
* \{ */
static int sequencer_retiming_reset_exec(bContext *C, wmOperator * /* op */)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
SEQ_retiming_data_clear(seq);
retiming_handle_overlap(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
void SEQUENCER_OT_retiming_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Retiming";
ot->description = "Reset strip retiming";
ot->idname = "SEQUENCER_OT_retiming_reset";
/* api callbacks */
ot->exec = sequencer_retiming_reset_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Move Handle
* \{ */
static SeqRetimingHandle *closest_retiming_handle_get(const bContext *C,
const Sequence *seq,
const float mouse_x)
{
const View2D *v2d = UI_view2d_fromcontext(C);
int best_distance = INT_MAX;
SeqRetimingHandle *closest_handle = nullptr;
const float distance_threshold = UI_view2d_region_to_view_x(v2d, 10);
const float mouse_x_view = UI_view2d_region_to_view_x(v2d, mouse_x);
for (int i = 0; i < SEQ_retiming_handles_count(seq); i++) {
SeqRetimingHandle *handle = seq->retiming_handles + i;
const int distance = round_fl_to_int(fabsf(handle->strip_frame_index - mouse_x_view));
if (distance < distance_threshold && distance < best_distance) {
best_distance = distance;
closest_handle = handle;
}
}
return closest_handle;
}
static int sequencer_retiming_handle_move_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
int handle_index = 0;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
handle_index = RNA_int_get(op->ptr, "handle_index");
}
/* Ensure retiming handle at left handle position. This way user gets more predictable result
* when strips have offsets. */
const int left_handle_frame = SEQ_time_left_handle_frame_get(scene, seq);
if (SEQ_retiming_add_handle(seq, left_handle_frame) != nullptr) {
handle_index++; /* Advance index, because new handle was created. */
}
MutableSpan handles = SEQ_retiming_handles_get(seq);
SeqRetimingHandle *handle = nullptr;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
BLI_assert(handle_index < handles.size());
handle = &handles[handle_index];
}
else {
handle = closest_retiming_handle_get(C, seq, event->mval[0]);
}
if (handle == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No handle available");
return OPERATOR_CANCELLED;
}
op->customdata = handle;
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
static int sequencer_retiming_handle_move_modal(bContext *C, wmOperator *op, const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
const ARegion *region = CTX_wm_region(C);
const View2D *v2d = &region->v2d;
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
switch (event->type) {
case MOUSEMOVE: {
float mouse_x = UI_view2d_region_to_view_x(v2d, event->mval[0]);
int offset = 0;
SeqRetimingHandle *handle = (SeqRetimingHandle *)op->customdata;
SeqRetimingHandle *handle_prev = handle - 1;
/* Limit retiming handle movement. */
int xmin = SEQ_time_start_frame_get(seq) + handle_prev->strip_frame_index + 1;
mouse_x = max_ff(xmin, mouse_x);
offset = mouse_x - (SEQ_time_start_frame_get(seq) + handle->strip_frame_index);
SEQ_retiming_offset_handle(scene, seq, handle, offset);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_RUNNING_MODAL;
}
case LEFTMOUSE:
case EVT_RETKEY:
case EVT_SPACEKEY: {
retiming_handle_overlap(scene, seq);
DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
case EVT_ESCKEY:
case RIGHTMOUSE: {
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_CANCELLED;
}
}
return OPERATOR_RUNNING_MODAL;
}
void SEQUENCER_OT_retiming_handle_move(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Move Retiming Handle";
ot->description = "Move retiming handle";
ot->idname = "SEQUENCER_OT_retiming_handle_move";
/* api callbacks */
ot->invoke = sequencer_retiming_handle_move_invoke;
ot->modal = sequencer_retiming_handle_move_modal;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int(ot->srna,
"handle_index",
0,
0,
INT_MAX,
"Handle Index",
"Index of handle to be moved",
0,
INT_MAX);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Add Handle
* \{ */
static int sequesequencer_retiming_handle_add_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
float timeline_frame;
if (RNA_struct_property_is_set(op->ptr, "timeline_frame")) {
timeline_frame = RNA_int_get(op->ptr, "timeline_frame");
}
else {
timeline_frame = BKE_scene_frame_get(scene);
}
bool inserted = false;
const float end_frame = seq->start + SEQ_time_strip_length_get(scene, seq);
if (seq->start < timeline_frame && end_frame > timeline_frame) {
SEQ_retiming_add_handle(seq, timeline_frame);
inserted = true;
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return inserted ? OPERATOR_FINISHED : OPERATOR_PASS_THROUGH;
}
void SEQUENCER_OT_retiming_handle_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Retiming Handle";
ot->description = "Add retiming Handle";
ot->idname = "SEQUENCER_OT_retiming_handle_add";
/* api callbacks */
ot->exec = sequesequencer_retiming_handle_add_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_int(ot->srna,
"timeline_frame",
0,
0,
INT_MAX,
"Timeline Frame",
"Frame where handle will be added",
0,
INT_MAX);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Retiming Remove Handle
* \{ */
static int sequencer_retiming_handle_remove_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
Sequence *seq = ed->act_seq;
SeqRetimingHandle *handle = (SeqRetimingHandle *)op->customdata;
SEQ_retiming_remove_handle(seq, handle);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
static int sequencer_retiming_handle_remove_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
const Scene *scene = CTX_data_scene(C);
const Editing *ed = SEQ_editing_get(scene);
const Sequence *seq = ed->act_seq;
if (seq == nullptr) {
return OPERATOR_CANCELLED;
}
MutableSpan handles = SEQ_retiming_handles_get(seq);
SeqRetimingHandle *handle = nullptr;
if (RNA_struct_property_is_set(op->ptr, "handle_index")) {
const int handle_index = RNA_int_get(op->ptr, "handle_index");
BLI_assert(handle_index < handles.size());
handle = &handles[handle_index];
}
else {
handle = closest_retiming_handle_get(C, seq, event->mval[0]);
}
if (handle == nullptr) {
BKE_report(op->reports, RPT_ERROR, "No handle available");
return OPERATOR_CANCELLED;
}
op->customdata = handle;
return sequencer_retiming_handle_remove_exec(C, op);
}
void SEQUENCER_OT_retiming_handle_remove(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove Retiming Handle";
ot->description = "Remove retiming handle";
ot->idname = "SEQUENCER_OT_retiming_handle_remove";
/* api callbacks */
ot->invoke = sequencer_retiming_handle_remove_invoke;
ot->exec = sequencer_retiming_handle_remove_exec;
ot->poll = retiming_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
RNA_def_int(ot->srna,
"handle_index",
0,
0,
INT_MAX,
"Handle Index",
"Index of handle to be removed",
0,
INT_MAX);
}
/** \} */

View File

@ -494,10 +494,15 @@ static void sequencer_gizmos(void)
wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(
&(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW});
WM_gizmotype_append(GIZMO_GT_retime_handle_add);
WM_gizmotype_append(GIZMO_GT_retime_handle);
WM_gizmotype_append(GIZMO_GT_retime_remove);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate);
WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo_retime);
WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate);
}
@ -645,6 +650,7 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
case ND_SEQUENCER:
case ND_RENDER_RESULT:
ED_region_tag_redraw(region);
WM_gizmomap_tag_refresh(region->gizmo_map);
break;
}
break;
@ -658,6 +664,7 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
case NC_SPACE:
if (wmn->data == ND_SPACE_SEQUENCER) {
ED_region_tag_redraw(region);
WM_gizmomap_tag_refresh(region->gizmo_map);
}
break;
case NC_ID:
@ -668,6 +675,7 @@ static void sequencer_main_region_listener(const wmRegionListenerParams *params)
case NC_SCREEN:
if (ELEM(wmn->data, ND_ANIMPLAY)) {
ED_region_tag_redraw(region);
WM_gizmomap_tag_refresh(region->gizmo_map);
}
break;
}
@ -1069,7 +1077,6 @@ void ED_spacetype_sequencer(void)
art->on_view2d_changed = sequencer_main_region_view2d_changed;
art->listener = sequencer_main_region_listener;
art->message_subscribe = sequencer_main_region_message_subscribe;
/* NOTE: inclusion of #ED_KEYMAP_GIZMO is currently for scripts and isn't used by default. */
art->keymapflag = ED_KEYMAP_TOOL | ED_KEYMAP_GIZMO | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES |
ED_KEYMAP_ANIMATION;
BLI_addhead(&st->regiontypes, art);

View File

@ -120,6 +120,12 @@ typedef struct Strip {
ColorManagedColorspaceSettings colorspace_settings;
} Strip;
typedef struct SeqRetimingHandle {
int strip_frame_index;
int _pad0[2];
float retiming_factor; /* Value between 0-1 mapped to original content range. */
} SeqRetimingHandle;
typedef struct SequenceRuntime {
SessionUUID session_uuid;
} SequenceRuntime;
@ -164,12 +170,12 @@ typedef struct Sequence {
float startstill, endstill;
/** Machine: the strip channel */
int machine;
int _pad3;
int _pad;
/** Starting and ending points of the effect strip. Undefined for other strip types. */
int startdisp, enddisp;
float sat;
float mul;
float _pad;
float _pad1;
short anim_preseek; /* UNUSED. */
/** Streamindex for movie or sound files with several streams. */
@ -231,7 +237,7 @@ typedef struct Sequence {
int8_t color_tag;
char alpha_mode;
char _pad4[2];
char _pad2[2];
int cache_flag;
@ -241,7 +247,7 @@ typedef struct Sequence {
/* Multiview */
char views_format;
char _pad1[3];
char _pad3[3];
struct Stereo3dFormat *stereo3d_format;
struct IDProperty *prop;
@ -251,9 +257,13 @@ typedef struct Sequence {
/* Playback rate of strip content in frames per second. */
float media_playback_rate;
/* Multiply strip playback speed. */
float speed_factor;
struct SeqRetimingHandle *retiming_handles;
void *_pad5;
int retiming_handle_num;
char _pad6[4];
SequenceRuntime runtime;
} Sequence;

View File

@ -468,6 +468,7 @@ void RNA_api_region_view3d(struct StructRNA *srna);
void RNA_api_texture(struct StructRNA *srna);
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, bool metastrip);
void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_api_sequence_retiming_handles(BlenderRNA *brna, PropertyRNA *cprop);
void RNA_api_sound(struct StructRNA *srna);
void RNA_api_vfont(struct StructRNA *srna);
void RNA_api_workspace(struct StructRNA *srna);

View File

@ -43,6 +43,7 @@
#include "SEQ_prefetch.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_sound.h"
@ -272,7 +273,7 @@ static int rna_SequenceEditor_elements_length(PointerRNA *ptr)
return (int)olen;
}
static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
static void rna_Sequence_elements_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Sequence *seq = (Sequence *)ptr->data;
rna_iterator_array_begin(iter,
@ -283,6 +284,85 @@ static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter,
NULL);
}
static int rna_Sequence_retiming_handles_length(PointerRNA *ptr)
{
return SEQ_retiming_handles_count((Sequence *)ptr->data);
}
static void rna_SequenceEditor_retiming_handles_begin(CollectionPropertyIterator *iter,
PointerRNA *ptr)
{
Sequence *seq = (Sequence *)ptr->data;
rna_iterator_array_begin(iter,
(void *)seq->retiming_handles,
sizeof(SeqRetimingHandle),
SEQ_retiming_handles_count(seq),
0,
NULL);
}
static Sequence *strip_by_handle_find(Scene *scene, SeqRetimingHandle *handle)
{
Editing *ed = SEQ_editing_get(scene);
SeqCollection *strips = SEQ_query_all_strips_recursive(&ed->seqbase);
Sequence *seq;
SEQ_ITERATOR_FOREACH (seq, strips) {
const int retiming_handle_count = SEQ_retiming_handles_count(seq);
SeqRetimingHandle *first = seq->retiming_handles;
SeqRetimingHandle *last = seq->retiming_handles + retiming_handle_count - 1;
if (handle >= first && handle <= last) {
return seq;
}
}
return NULL;
}
static void rna_Sequence_retiming_handle_remove(ID *id, SeqRetimingHandle *handle)
{
Scene *scene = (Scene *)id;
Sequence *seq = strip_by_handle_find(scene, handle);
if (seq == NULL) {
return;
}
SEQ_retiming_remove_handle(seq, handle);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
}
static int rna_Sequence_retiming_handle_frame_get(PointerRNA *ptr)
{
SeqRetimingHandle *handle = (SeqRetimingHandle *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
Sequence *seq = strip_by_handle_find(scene, handle);
if (seq == NULL) {
return 0;
}
return SEQ_time_start_frame_get(seq) + handle->strip_frame_index;
}
static void rna_Sequence_retiming_handle_frame_set(PointerRNA *ptr, int value)
{
SeqRetimingHandle *handle = (SeqRetimingHandle *)ptr->data;
Scene *scene = (Scene *)ptr->owner_id;
Sequence *seq = strip_by_handle_find(scene, handle);
if (seq == NULL) {
return;
}
const int offset = value - SEQ_time_start_frame_get(seq) + handle->strip_frame_index;
SEQ_retiming_offset_handle(scene, seq, handle, offset);
SEQ_relations_invalidate_cache_raw(scene, seq);
}
static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
rna_Sequence_invalidate_raw_update(bmain, scene, ptr);
@ -1493,6 +1573,31 @@ static void rna_def_strip_element(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Orig FPS", "Original frames per second");
}
static void rna_def_retiming_handle(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "RetimingHandle", NULL);
RNA_def_struct_ui_text(
srna,
"Retiming Handle",
"Handle mapped to particular frame that can be moved to change playback speed");
RNA_def_struct_sdna(srna, "SeqRetimingHandle");
prop = RNA_def_property(srna, "timeline_frame", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "strip_frame_index");
RNA_def_property_int_funcs(prop,
"rna_Sequence_retiming_handle_frame_get",
"rna_Sequence_retiming_handle_frame_set",
NULL);
RNA_def_property_ui_text(prop, "Timeline Frame", "Position of retiming handle in timeline");
FunctionRNA *func = RNA_def_function(srna, "remove", "rna_Sequence_retiming_handle_remove");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Remove retiming handle");
}
static void rna_def_strip_crop(BlenderRNA *brna)
{
StructRNA *srna;
@ -2564,7 +2669,7 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "SequenceElement");
RNA_def_property_ui_text(prop, "Elements", "");
RNA_def_property_collection_funcs(prop,
"rna_SequenceEditor_elements_begin",
"rna_Sequence_elements_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
@ -2596,7 +2701,6 @@ static void rna_def_image(BlenderRNA *brna)
rna_def_proxy(srna);
rna_def_input(srna);
rna_def_color_management(srna);
rna_def_speed_factor(srna);
}
static void rna_def_meta(BlenderRNA *brna)
@ -2628,7 +2732,6 @@ static void rna_def_meta(BlenderRNA *brna)
rna_def_filter_video(srna);
rna_def_proxy(srna);
rna_def_input(srna);
rna_def_speed_factor(srna);
}
static void rna_def_scene(BlenderRNA *brna)
@ -2677,7 +2780,6 @@ static void rna_def_scene(BlenderRNA *brna)
rna_def_proxy(srna);
rna_def_input(srna);
rna_def_movie_types(srna);
rna_def_speed_factor(srna);
}
static void rna_def_movie(BlenderRNA *brna)
@ -2705,7 +2807,7 @@ static void rna_def_movie(BlenderRNA *brna)
RNA_def_property_struct_type(prop, "SequenceElement");
RNA_def_property_ui_text(prop, "Elements", "");
RNA_def_property_collection_funcs(prop,
"rna_SequenceEditor_elements_begin",
"rna_Sequence_elements_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
@ -2714,6 +2816,21 @@ static void rna_def_movie(BlenderRNA *brna)
NULL,
NULL);
prop = RNA_def_property(srna, "retiming_handles", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "retiming_handles", NULL);
RNA_def_property_struct_type(prop, "RetimingHandle");
RNA_def_property_ui_text(prop, "Retiming Hndles", "");
RNA_def_property_collection_funcs(prop,
"rna_SequenceEditor_retiming_handles_begin",
"rna_iterator_array_next",
"rna_iterator_array_end",
"rna_iterator_array_get",
"rna_Sequence_retiming_handles_length",
NULL,
NULL,
NULL);
RNA_api_sequence_retiming_handles(brna, prop);
prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
RNA_def_property_ui_text(prop, "File", "");
RNA_def_property_string_funcs(prop,
@ -2761,7 +2878,6 @@ static void rna_def_movie(BlenderRNA *brna)
rna_def_input(srna);
rna_def_color_management(srna);
rna_def_movie_types(srna);
rna_def_speed_factor(srna);
}
static void rna_def_movieclip(BlenderRNA *brna)
@ -2789,7 +2905,6 @@ static void rna_def_movieclip(BlenderRNA *brna)
rna_def_filter_video(srna);
rna_def_input(srna);
rna_def_movie_types(srna);
rna_def_speed_factor(srna);
}
static void rna_def_mask(BlenderRNA *brna)
@ -2808,7 +2923,6 @@ static void rna_def_mask(BlenderRNA *brna)
rna_def_filter_video(srna);
rna_def_input(srna);
rna_def_speed_factor(srna);
}
static void rna_def_sound(BlenderRNA *brna)
@ -3613,6 +3727,7 @@ void RNA_def_sequencer(BlenderRNA *brna)
rna_def_color_balance(brna);
rna_def_strip_element(brna);
rna_def_retiming_handle(brna);
rna_def_strip_proxy(brna);
rna_def_strip_color_balance(brna);
rna_def_strip_crop(brna);

View File

@ -44,6 +44,7 @@
# include "SEQ_effects.h"
# include "SEQ_relations.h"
# include "SEQ_render.h"
# include "SEQ_retiming.h"
# include "SEQ_sequencer.h"
# include "SEQ_time.h"
@ -637,6 +638,29 @@ static void rna_Sequence_invalidate_cache_rnafunc(ID *id, Sequence *self, int ty
}
}
static SeqRetimingHandle *rna_Sequence_retiming_handles_add(ID *id,
Sequence *seq,
int timeline_frame)
{
Scene *scene = (Scene *)id;
SeqRetimingHandle *handle = SEQ_retiming_add_handle(seq, timeline_frame);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
return handle;
}
static void rna_Sequence_retiming_handles_reset(ID *id, Sequence *seq)
{
Scene *scene = (Scene *)id;
SEQ_retiming_data_clear(seq);
SEQ_relations_invalidate_cache_raw(scene, seq);
WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL);
}
#else
void RNA_api_sequence_strip(StructRNA *srna)
@ -744,6 +768,30 @@ void RNA_api_sequence_elements(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
void RNA_api_sequence_retiming_handles(BlenderRNA *brna, PropertyRNA *cprop)
{
StructRNA *srna;
RNA_def_property_srna(cprop, "RetimingHandles");
srna = RNA_def_struct(brna, "RetimingHandles", NULL);
RNA_def_struct_sdna(srna, "Sequence");
RNA_def_struct_ui_text(srna, "RetimingHandles", "Collection of RetimingHandle");
FunctionRNA *func = RNA_def_function(srna, "add", "rna_Sequence_retiming_handles_add");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_int(
func, "timeline_frame", 0, -MAXFRAME, MAXFRAME, "Timeline Frame", "", -MAXFRAME, MAXFRAME);
RNA_def_function_ui_description(func, "Add retiming handle");
/* return type */
PropertyRNA *parm = RNA_def_pointer(
func, "retiming_handle", "RetimingHandle", "", "New RetimingHandle");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "reset", "rna_Sequence_retiming_handles_reset");
RNA_def_function_flag(func, FUNC_USE_SELF_ID);
RNA_def_function_ui_description(func, "Remove all retiming handles");
}
void RNA_api_sequences(BlenderRNA *brna, PropertyRNA *cprop, const bool metastrip)
{
StructRNA *srna;

View File

@ -42,6 +42,8 @@ set(SRC
SEQ_proxy.h
SEQ_relations.h
SEQ_render.h
SEQ_retiming.h
SEQ_retiming.hh
SEQ_select.h
SEQ_sequencer.h
SEQ_sound.h
@ -76,6 +78,7 @@ set(SRC
intern/strip_add.c
intern/strip_edit.c
intern/strip_relations.c
intern/strip_retiming.cc
intern/strip_select.c
intern/strip_time.c
intern/strip_time.h

View File

@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2004 Blender Foundation. All rights reserved. */
#pragma once
/** \file
* \ingroup sequencer
*/
#ifdef __cplusplus
extern "C" {
#endif
struct Scene;
struct Sequence;
struct SeqRetimingHandle;
int SEQ_retiming_handles_count(const struct Sequence *seq);
bool SEQ_retiming_is_active(const struct Sequence *seq);
void SEQ_retiming_data_ensure(const struct Scene *scene, struct Sequence *seq);
void SEQ_retiming_data_clear(struct Sequence *seq);
bool SEQ_retiming_is_allowed(const struct Sequence *seq);
/**
* Add new retiming handle.
* This function always reallocates memory, so when function is used all stored pointers will
* become invalid.
*/
struct SeqRetimingHandle *SEQ_retiming_add_handle(struct Sequence *seq, const int timeline_frame);
struct SeqRetimingHandle *SEQ_retiming_last_handle_get(const struct Sequence *seq);
void SEQ_retiming_remove_handle(struct Sequence *seq, struct SeqRetimingHandle *handle);
void SEQ_retiming_offset_handle(const struct Scene *scene,
struct Sequence *seq,
struct SeqRetimingHandle *handle,
const int offset);
float SEQ_retiming_handle_speed_get(const struct Scene *scene,
const struct Sequence *seq,
const struct SeqRetimingHandle *handle);
int SEQ_retiming_handle_index_get(const struct Sequence *seq,
const struct SeqRetimingHandle *handle);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
#pragma once
/** \file
* \ingroup sequencer
*/
#include "BLI_span.hh"
struct Sequence;
struct SeqRetimingHandle;
blender::MutableSpan<SeqRetimingHandle> SEQ_retiming_handles_get(const Sequence *seq);

View File

@ -136,7 +136,6 @@ void SEQ_time_start_frame_set(const struct Scene *scene, struct Sequence *seq, i
* \note this function is currently only used internally and in versioning code.
*/
void SEQ_time_update_meta_strip_range(const struct Scene *scene, struct Sequence *seq_meta);
#ifdef __cplusplus
}
#endif

View File

@ -18,6 +18,7 @@
#include "BLI_listbase.h"
#include "BKE_fcurve.h"
#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_sound.h"
@ -34,6 +35,7 @@
#include "SEQ_modifier.h"
#include "SEQ_proxy.h"
#include "SEQ_relations.h"
#include "SEQ_retiming.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_sound.h"
@ -218,6 +220,12 @@ static void seq_sequence_free_ex(Scene *scene,
SEQ_channels_free(&seq->channels);
}
if (seq->retiming_handles != NULL) {
MEM_freeN(seq->retiming_handles);
seq->retiming_handles = NULL;
seq->retiming_handle_num = 0;
}
MEM_freeN(seq);
}
@ -576,6 +584,11 @@ static Sequence *seq_dupli(const Scene *scene_src,
}
}
if (seq->retiming_handles != NULL) {
seqn->retiming_handles = MEM_dupallocN(seq->retiming_handles);
seqn->retiming_handle_num = seq->retiming_handle_num;
}
return seqn;
}
@ -749,6 +762,12 @@ static bool seq_write_data_cb(Sequence *seq, void *userdata)
LISTBASE_FOREACH (SeqTimelineChannel *, channel, &seq->channels) {
BLO_write_struct(writer, SeqTimelineChannel, channel);
}
if (seq->retiming_handles != NULL) {
int size = SEQ_retiming_handles_count(seq);
BLO_write_struct_array(writer, SeqRetimingHandle, size, seq->retiming_handles);
}
return true;
}
@ -818,6 +837,11 @@ static bool seq_read_data_cb(Sequence *seq, void *user_data)
SEQ_modifier_blend_read_data(reader, &seq->modifiers);
BLO_read_list(reader, &seq->channels);
if (seq->retiming_handles != NULL) {
BLO_read_data_address(reader, &seq->retiming_handles);
}
return true;
}
void SEQ_blend_read(BlendDataReader *reader, ListBase *seqbase)

View File

@ -275,9 +275,7 @@ static void seq_split_set_right_hold_offset(Main *bmain,
/* Adjust within range of strip contents. */
else if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
seq->endofs = 0;
float speed_factor = (seq->type == SEQ_TYPE_SOUND_RAM) ?
seq_time_media_playback_rate_factor_get(scene, seq) :
seq_time_playback_rate_factor_get(scene, seq);
float speed_factor = seq_time_media_playback_rate_factor_get(scene, seq);
seq->anim_endofs += round_fl_to_int((content_end - timeline_frame) * speed_factor);
}
@ -296,9 +294,7 @@ static void seq_split_set_left_hold_offset(Main *bmain,
/* Adjust within range of strip contents. */
if ((timeline_frame >= content_start) && (timeline_frame <= content_end)) {
float speed_factor = (seq->type == SEQ_TYPE_SOUND_RAM) ?
seq_time_media_playback_rate_factor_get(scene, seq) :
seq_time_playback_rate_factor_get(scene, seq);
float speed_factor = seq_time_media_playback_rate_factor_get(scene, seq);
seq->anim_startofs += round_fl_to_int((timeline_frame - content_start) * speed_factor);
seq->start = timeline_frame;
seq->startofs = 0;

View File

@ -0,0 +1,247 @@
/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. All rights reserved. */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_span.hh"
#include "BKE_fcurve.h"
#include "BKE_movieclip.h"
#include "BKE_scene.h"
#include "BKE_sound.h"
#include "DNA_anim_types.h"
#include "DNA_sound_types.h"
#include "IMB_imbuf.h"
#include "RNA_prototypes.h"
#include "SEQ_channels.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_retiming.h"
#include "SEQ_retiming.hh"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
#include "sequencer.h"
#include "strip_time.h"
#include "utils.h"
using blender::MutableSpan;
MutableSpan<SeqRetimingHandle> SEQ_retiming_handles_get(const Sequence *seq)
{
blender::MutableSpan<SeqRetimingHandle> handles(seq->retiming_handles, seq->retiming_handle_num);
return handles;
}
struct SeqRetimingHandle *SEQ_retiming_last_handle_get(const struct Sequence *seq)
{
return seq->retiming_handles + seq->retiming_handle_num - 1;
}
int SEQ_retiming_handle_index_get(const Sequence *seq, const SeqRetimingHandle *handle)
{
return handle - seq->retiming_handles;
}
static bool seq_retiming_is_last_handle(const Sequence *seq, const SeqRetimingHandle *handle)
{
return SEQ_retiming_handle_index_get(seq, handle) == seq->retiming_handle_num - 1;
}
static const SeqRetimingHandle *retiming_find_segment_start_handle(const Sequence *seq,
const int frame_index)
{
const SeqRetimingHandle *start_handle = nullptr;
for (auto const &handle : SEQ_retiming_handles_get(seq)) {
if (seq_retiming_is_last_handle(seq, &handle)) {
break;
}
if (handle.strip_frame_index > frame_index) {
break;
}
start_handle = &handle;
}
return start_handle;
}
int SEQ_retiming_handles_count(const Sequence *seq)
{
return seq->retiming_handle_num;
}
void SEQ_retiming_data_ensure(const Scene *scene, Sequence *seq)
{
if (!SEQ_retiming_is_allowed(seq)) {
return;
}
if (seq->retiming_handles != nullptr) {
return;
}
seq->retiming_handles = (SeqRetimingHandle *)MEM_calloc_arrayN(
2, sizeof(SeqRetimingHandle), __func__);
SeqRetimingHandle *handle = seq->retiming_handles + 1;
handle->strip_frame_index = seq_time_strip_original_content_length_get(scene, seq) - 1;
handle->retiming_factor = 1.0f;
seq->retiming_handle_num = 2;
}
void SEQ_retiming_data_clear(Sequence *seq)
{
seq->retiming_handles = nullptr;
seq->retiming_handle_num = 0;
}
bool SEQ_retiming_is_active(const Sequence *seq)
{
return seq->retiming_handle_num > 1;
}
bool SEQ_retiming_is_allowed(const Sequence *seq)
{
return ELEM(seq->type,
SEQ_TYPE_IMAGE,
SEQ_TYPE_META,
SEQ_TYPE_SCENE,
SEQ_TYPE_MOVIE,
SEQ_TYPE_MOVIECLIP,
SEQ_TYPE_MASK);
}
float seq_retiming_evaluate(const Sequence *seq, const int frame_index)
{
const SeqRetimingHandle *previous_handle = retiming_find_segment_start_handle(seq, frame_index);
const SeqRetimingHandle *next_handle = previous_handle + 1;
const int previous_handle_index = previous_handle - seq->retiming_handles;
BLI_assert(previous_handle_index < seq->retiming_handle_num);
Review

Add UNUSED_VARS_NDEBUG(previous_handle_index) to solve the

[947/1084] Building CXX object source/blender/sequencer/CMakeFiles/bf_sequencer.dir/intern/strip_retiming.cc.o
/Users/sergey/Developer/blender/blender/source/blender/sequencer/intern/strip_retiming.cc:133:13: warning: unused variable 'previous_handle_index' [-Wunused-variable]
  const int previous_handle_index = previous_handle - seq->retiming_handles;
Add `UNUSED_VARS_NDEBUG(previous_handle_index)` to solve the ``` [947/1084] Building CXX object source/blender/sequencer/CMakeFiles/bf_sequencer.dir/intern/strip_retiming.cc.o /Users/sergey/Developer/blender/blender/source/blender/sequencer/intern/strip_retiming.cc:133:13: warning: unused variable 'previous_handle_index' [-Wunused-variable] const int previous_handle_index = previous_handle - seq->retiming_handles; ```
UNUSED_VARS_NDEBUG(previous_handle_index);
if (next_handle == nullptr) {
return 1.0f;
}
const int segment_length = next_handle->strip_frame_index - previous_handle->strip_frame_index;
const int segment_frame_index = frame_index - previous_handle->strip_frame_index;
const float segment_fac = segment_frame_index / (float)segment_length;
const float target_diff = next_handle->retiming_factor - previous_handle->retiming_factor;
return previous_handle->retiming_factor + (target_diff * segment_fac);
}
SeqRetimingHandle *SEQ_retiming_add_handle(Sequence *seq, const int timeline_frame)
{
float frame_index = timeline_frame - SEQ_time_start_frame_get(seq);
float value = seq_retiming_evaluate(seq, frame_index);
const SeqRetimingHandle *closest_handle = retiming_find_segment_start_handle(seq, frame_index);
if (closest_handle->strip_frame_index == frame_index) {
return nullptr; /* Retiming handle already exists. */
}
SeqRetimingHandle *handles = seq->retiming_handles;
size_t handle_count = SEQ_retiming_handles_count(seq);
const int new_handle_index = closest_handle - handles + 1;
BLI_assert(new_handle_index >= 0);
BLI_assert(new_handle_index < handle_count);
SeqRetimingHandle *new_handles = (SeqRetimingHandle *)MEM_callocN(
(handle_count + 1) * sizeof(SeqRetimingHandle), __func__);
if (new_handle_index > 0) {
memcpy(new_handles, handles, new_handle_index * sizeof(SeqRetimingHandle));
}
if (new_handle_index < handle_count) {
memcpy(new_handles + new_handle_index + 1,
handles + new_handle_index,
(handle_count - new_handle_index) * sizeof(SeqRetimingHandle));
}
MEM_freeN(handles);
seq->retiming_handles = new_handles;
seq->retiming_handle_num++;
SeqRetimingHandle *added_handle = (new_handles + new_handle_index);
added_handle->strip_frame_index = frame_index;
added_handle->retiming_factor = value;
return added_handle;
}
void SEQ_retiming_offset_handle(const Scene *scene,
Sequence *seq,
SeqRetimingHandle *handle,
const int offset)
{
if (handle->strip_frame_index == 0) {
return; /* First handle can not be moved. */
}
MutableSpan handles = SEQ_retiming_handles_get(seq);
for (; handle < handles.end(); handle++) {
handle->strip_frame_index += offset;
}
SEQ_time_update_meta_strip_range(scene, seq_sequence_lookup_meta_by_seq(scene, seq));
seq_time_update_effects_strip_range(scene, seq_sequence_lookup_effects_by_seq(scene, seq));
}
void SEQ_retiming_remove_handle(Sequence *seq, SeqRetimingHandle *handle)
{
SeqRetimingHandle *last_handle = SEQ_retiming_last_handle_get(seq);
if (handle->strip_frame_index == 0 || handle == last_handle) {
return; /* First and last handle can not be removed. */
}
size_t handle_count = SEQ_retiming_handles_count(seq);
SeqRetimingHandle *handles = (SeqRetimingHandle *)MEM_callocN(
(handle_count - 1) * sizeof(SeqRetimingHandle), __func__);
const int handle_index = handle - seq->retiming_handles;
memcpy(handles, seq->retiming_handles, (handle_index) * sizeof(SeqRetimingHandle));
memcpy(handles + handle_index,
seq->retiming_handles + handle_index + 1,
(handle_count - handle_index - 1) * sizeof(SeqRetimingHandle));
MEM_freeN(seq->retiming_handles);
seq->retiming_handles = handles;
seq->retiming_handle_num--;
}
float SEQ_retiming_handle_speed_get(const Scene *scene,
const Sequence *seq,
const SeqRetimingHandle *handle)
{
if (handle->strip_frame_index == 0) {
return 1.0f;
}
const SeqRetimingHandle *handle_prev = handle - 1;
const int frame_index_max = seq_time_strip_original_content_length_get(scene, seq) - 1;
const int frame_retimed_prev = round_fl_to_int(handle_prev->retiming_factor * frame_index_max);
const int frame_index_prev = handle_prev->strip_frame_index;
const int frame_retimed = round_fl_to_int(handle->retiming_factor * frame_index_max);
const int frame_index = handle->strip_frame_index;
const int fragment_length_retimed = frame_retimed - frame_retimed_prev;
const int fragment_length_original = frame_index - frame_index_prev;
const float speed = (float)fragment_length_retimed / (float)fragment_length_original;
return speed;
}

View File

@ -7,22 +7,31 @@
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BKE_fcurve.h"
#include "BKE_movieclip.h"
#include "BKE_scene.h"
#include "BKE_sound.h"
#include "DNA_anim_types.h"
#include "DNA_sound_types.h"
#include "IMB_imbuf.h"
#include "RNA_prototypes.h"
#include "SEQ_channels.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
#include "SEQ_render.h"
#include "SEQ_retiming.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
#include "SEQ_transform.h"
@ -44,9 +53,13 @@ float seq_time_media_playback_rate_factor_get(const Scene *scene, const Sequence
return seq->media_playback_rate / scene_playback_rate;
}
float seq_time_playback_rate_factor_get(const Scene *scene, const Sequence *seq)
int seq_time_strip_original_content_length_get(const Scene *scene, const Sequence *seq)
{
return seq_time_media_playback_rate_factor_get(scene, seq) * seq->speed_factor;
if (seq->type == SEQ_TYPE_SOUND_RAM) {
return seq->len;
}
return seq->len / seq_time_media_playback_rate_factor_get(scene, seq);
}
float seq_give_frame_index(const Scene *scene, Sequence *seq, float timeline_frame)
@ -54,6 +67,7 @@ float seq_give_frame_index(const Scene *scene, Sequence *seq, float timeline_fra
float frame_index;
float sta = SEQ_time_start_frame_get(seq);
float end = SEQ_time_content_end_frame_get(scene, seq) - 1;
const float length = seq_time_strip_original_content_length_get(scene, seq);
if (seq->type & SEQ_TYPE_EFFECT) {
end = SEQ_time_right_handle_frame_get(scene, seq);
@ -70,9 +84,15 @@ float seq_give_frame_index(const Scene *scene, Sequence *seq, float timeline_fra
frame_index = timeline_frame - sta;
}
/* Clamp frame index to strip frame range. */
frame_index = clamp_f(frame_index, 0, end - sta);
frame_index *= seq_time_playback_rate_factor_get(scene, seq);
if (SEQ_retiming_is_active(seq)) {
const float retiming_factor = seq_retiming_evaluate(seq, frame_index);
frame_index = retiming_factor * (length - 1);
}
else {
frame_index *= seq_time_media_playback_rate_factor_get(scene, seq);
/* Clamp frame index to strip frame range. */
frame_index = clamp_f(frame_index, 0, end - sta);
}
if (seq->strobe < 1.0f) {
seq->strobe = 1.0f;
@ -484,7 +504,13 @@ int SEQ_time_strip_length_get(const Scene *scene, const Sequence *seq)
return seq->len;
}
return seq->len / seq_time_playback_rate_factor_get(scene, seq);
if (SEQ_retiming_is_active(seq)) {
SeqRetimingHandle *handle_start = seq->retiming_handles;
SeqRetimingHandle *handle_end = seq->retiming_handles + (SEQ_retiming_handles_count(seq) - 1);
return handle_end->strip_frame_index - handle_start->strip_frame_index + 1;
}
return seq_time_strip_original_content_length_get(scene, seq);
}
float SEQ_time_start_frame_get(const Sequence *seq)

View File

@ -43,7 +43,9 @@ void seq_time_update_effects_strip_range(const struct Scene *scene, struct SeqCo
void seq_time_translate_handles(const struct Scene *scene, struct Sequence *seq, const int offset);
float seq_time_media_playback_rate_factor_get(const struct Scene *scene,
const struct Sequence *seq);
float seq_time_playback_rate_factor_get(const struct Scene *scene, const struct Sequence *seq);
int seq_time_strip_original_content_length_get(const struct Scene *scene,
const struct Sequence *seq);
float seq_retiming_evaluate(const struct Sequence *seq, const int frame_index);
#ifdef __cplusplus
}