diff --git a/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc b/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc index e8e2ed8bd09..93986675381 100644 --- a/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc +++ b/source/blender/editors/space_sequencer/sequencer_gizmo_retime.cc @@ -37,6 +37,7 @@ typedef struct GizmoGroup_retime { wmGizmo *add_handle_gizmo; wmGizmo *move_handle_gizmo; wmGizmo *remove_handle_gizmo; + wmGizmo *speed_set_gizmo; } GizmoGroup_retime; static bool gizmogroup_retime_poll(const bContext *C, wmGizmoGroupType *gzgt) @@ -81,6 +82,8 @@ static void gizmogroup_retime_setup(const bContext * /* C */, wmGizmoGroup *gzgr 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); + const wmGizmoType *gzt_speed_set = WM_gizmotype_find("GIZMO_GT_retime_speed_set", true); + ggd->speed_set_gizmo = WM_gizmo_new_ptr(gzt_speed_set, gzgroup, nullptr); gzgroup->customdata = ggd; /* Assign operators. */ @@ -90,6 +93,8 @@ static void gizmogroup_retime_setup(const bContext * /* C */, wmGizmoGroup *gzgr 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); + ot = WM_operatortype_find("SEQUENCER_OT_retiming_segment_speed_set", true); + WM_gizmo_operator_set(ggd->speed_set_gizmo, 0, ot, nullptr); } void SEQUENCER_GGT_gizmo_retime(wmGizmoGroupType *gzgt) diff --git a/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc b/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc index 91bd14b0d19..c10b0498f14 100644 --- a/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc +++ b/source/blender/editors/space_sequencer/sequencer_gizmo_retime_type.cc @@ -403,61 +403,6 @@ static void retime_handle_draw(const bContext *C, 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(scene, seq, next_handle) < start_frame || - handle_x_get(scene, seq, handle) > end_frame) - { - return; /* Label out of strip bounds. */ - } - - char label_str[40]; - size_t label_len; - - if (SEQ_retiming_handle_is_transition_type(handle)) { - const float prev_speed = SEQ_retiming_handle_speed_get(seq, handle - 1); - const float next_speed = SEQ_retiming_handle_speed_get(seq, next_handle + 1); - label_len = SNPRINTF_RLEN(label_str, - "%d%% - %d%%", - round_fl_to_int(prev_speed * 100.0f), - round_fl_to_int(next_speed * 100.0f)); - } - else { - const float speed = SEQ_retiming_handle_speed_get(seq, next_handle); - label_len = SNPRINTF_RLEN(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(scene, seq, handle)); - const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq), - handle_x_get(scene, 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) { RetimeHandleMoveGizmo *gizmo = (RetimeHandleMoveGizmo *)gz; @@ -488,8 +433,6 @@ static void gizmo_retime_handle_draw(const bContext *C, wmGizmo *gz) 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. */ } @@ -646,3 +589,183 @@ void GIZMO_GT_retime_remove(wmGizmoType *gzt) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Retiming Speed Set Gizmo + * \{ */ + +size_t label_str_get(const Sequence *seq, + const SeqRetimingHandle *handle, + size_t str_len, + char *r_label_str) +{ + const SeqRetimingHandle *next_handle = handle + 1; + if (SEQ_retiming_handle_is_transition_type(handle)) { + const float prev_speed = SEQ_retiming_handle_speed_get(seq, handle - 1); + const float next_speed = SEQ_retiming_handle_speed_get(seq, next_handle + 1); + return BLI_snprintf_rlen(r_label_str, + str_len, + "%d%% - %d%%", + round_fl_to_int(prev_speed * 100.0f), + round_fl_to_int(next_speed * 100.0f)); + } + const float speed = SEQ_retiming_handle_speed_get(seq, next_handle); + return BLI_snprintf_rlen(r_label_str, str_len, "%d%%", round_fl_to_int(speed * 100.0f)); +} + +static bool label_rect_get(const bContext *C, + const Sequence *seq, + const SeqRetimingHandle *handle, + char *label_str, + size_t label_len, + rctf *rect) +{ + const Scene *scene = CTX_data_scene(C); + const SeqRetimingHandle *next_handle = handle + 1; + const float width = pixels_to_view_width(C, BLF_width(BLF_default(), label_str, label_len)); + const float height = pixels_to_view_height(C, BLF_height(BLF_default(), label_str, label_len)); + + const float xmin = max_ff(SEQ_time_left_handle_frame_get(scene, seq), + handle_x_get(scene, seq, handle)); + const float xmax = min_ff(SEQ_time_right_handle_frame_get(scene, seq), + handle_x_get(scene, seq, next_handle)); + + rect->xmin = (xmin + xmax - width) / 2; + rect->xmax = rect->xmin + width; + rect->ymin = strip_y_rescale(seq, 0) + pixels_to_view_height(C, 5); + rect->ymax = rect->ymin + height; + + return width < xmax - xmin; +} + +static void label_rect_apply_mouseover_offset(const View2D *v2d, rctf *rect) +{ + float scale_x, scale_y; + UI_view2d_scale_get_inverse(v2d, &scale_x, &scale_y); + rect->xmin -= RETIME_HANDLE_MOUSEOVER_THRESHOLD * scale_x; + rect->xmax += RETIME_HANDLE_MOUSEOVER_THRESHOLD * scale_x; + rect->ymax += RETIME_HANDLE_MOUSEOVER_THRESHOLD * scale_y; +} + +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); + + const SeqRetimingHandle *next_handle = handle + 1; + if (handle_x_get(scene, seq, next_handle) < start_frame || + handle_x_get(scene, seq, handle) > end_frame) + { + return; /* Label out of strip bounds. */ + } + + char label_str[40]; + rctf label_rect; + size_t label_len = label_str_get(seq, handle, sizeof(label_str), label_str); + + if (!label_rect_get(C, seq, handle, label_str, label_len, &label_rect)) { + return; /* Not enough space to draw label. */ + } + + const uchar col[4] = {255, 255, 255, 255}; + UI_view2d_text_cache_add( + UI_view2d_fromcontext(C), label_rect.xmin, label_rect.ymin, label_str, label_len, col); +} + +static void gizmo_retime_speed_set_draw(const bContext *C, wmGizmo * /* gz */) +{ + const View2D *v2d = UI_view2d_fromcontext(C); + + wmOrtho2_region_pixelspace(CTX_wm_region(C)); + GPU_blend(GPU_BLEND_ALPHA); + 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(seq); + MutableSpan handles = SEQ_retiming_handles_get(seq); + + for (const SeqRetimingHandle &handle : handles) { + retime_speed_text_draw(C, 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_speed_set_test_select(bContext *C, wmGizmo *gz, const int mval[2]) +{ + Scene *scene = CTX_data_scene(C); + wmGizmoOpElem *op_elem = WM_gizmo_operator_get(gz, 0); + const View2D *v2d = UI_view2d_fromcontext(C); + + Sequence *seq = active_seq_from_context(C); + SEQ_retiming_data_ensure(seq); + + for (const SeqRetimingHandle &handle : SEQ_retiming_handles_get(seq)) { + if (SEQ_retiming_handle_is_transition_type(&handle)) { + continue; + } + + char label_str[40]; + rctf label_rect; + size_t label_len = label_str_get(seq, &handle, sizeof(label_str), label_str); + + if (!label_rect_get(C, seq, &handle, label_str, label_len, &label_rect)) { + continue; + } + + label_rect_apply_mouseover_offset(v2d, &label_rect); + + float mouse_view[2]; + UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouse_view[0], &mouse_view[1]); + + if (!BLI_rctf_isect_pt(&label_rect, mouse_view[0], mouse_view[1])) { + continue; + } + + /* Store next handle in RNA property, since label rect uses first handle as reference. */ + const int handle_index = SEQ_retiming_handle_index_get(seq, &handle) + 1; + RNA_int_set(&op_elem->ptr, "handle_index", handle_index); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return 0; + } + + return -1; +} + +static int gizmo_retime_speed_set_cursor_get(wmGizmo *gz) +{ + if (RNA_boolean_get(gz->ptr, "show_drag")) { + return WM_CURSOR_TEXT_EDIT; + } + return WM_CURSOR_DEFAULT; +} + +void GIZMO_GT_speed_set_remove(wmGizmoType *gzt) +{ + /* Identifiers. */ + gzt->idname = "GIZMO_GT_retime_speed_set"; + + /* Api callbacks. */ + gzt->draw = gizmo_retime_speed_set_draw; + gzt->test_select = gizmo_retime_speed_set_test_select; + gzt->cursor_get = gizmo_retime_speed_set_cursor_get; + gzt->struct_size = sizeof(wmGizmo); + + /* Currently only used for cursor display. */ + RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", ""); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index f23861a1353..77fb977c55a 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -312,6 +312,7 @@ 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); +void SEQUENCER_OT_retiming_segment_speed_set(struct wmOperatorType *ot); /* sequencer_gizmo_retime.c */ void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt); @@ -320,6 +321,7 @@ void SEQUENCER_GGT_gizmo_retime(struct wmGizmoGroupType *gzgt); 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); +void GIZMO_GT_speed_set_remove(struct wmGizmoType *gzt); #ifdef __cplusplus } diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index b808f13de1d..4745923a4da 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -74,6 +74,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_retiming_handle_move); WM_operatortype_append(SEQUENCER_OT_retiming_handle_add); WM_operatortype_append(SEQUENCER_OT_retiming_handle_remove); + WM_operatortype_append(SEQUENCER_OT_retiming_segment_speed_set); /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); diff --git a/source/blender/editors/space_sequencer/sequencer_retiming.cc b/source/blender/editors/space_sequencer/sequencer_retiming.cc index 26fe877b959..51ee8af7a08 100644 --- a/source/blender/editors/space_sequencer/sequencer_retiming.cc +++ b/source/blender/editors/space_sequencer/sequencer_retiming.cc @@ -461,3 +461,95 @@ void SEQUENCER_OT_retiming_handle_remove(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Retiming Set Segment Speed + * \{ */ + +static int sequencer_retiming_segment_speed_set_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_get(scene); + Sequence *seq = ed->act_seq; + MutableSpan handles = SEQ_retiming_handles_get(seq); + SeqRetimingHandle *handle = &handles[RNA_int_get(op->ptr, "handle_index")]; + + SEQ_retiming_handle_speed_set(scene, seq, handle, RNA_float_get(op->ptr, "speed")); + SEQ_relations_invalidate_cache_raw(scene, seq); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +static int sequencer_retiming_segment_speed_set_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; + } + + RNA_float_set(op->ptr, "speed", SEQ_retiming_handle_speed_get(seq, handle) * 100.0f); + RNA_int_set(op->ptr, "handle_index", SEQ_retiming_handle_index_get(seq, handle)); + return WM_operator_props_popup(C, op, event); +} + +void SEQUENCER_OT_retiming_segment_speed_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Speed"; + ot->description = "Set speed of retimed segment"; + ot->idname = "SEQUENCER_OT_retiming_segment_speed_set"; + + /* api callbacks */ + ot->invoke = sequencer_retiming_segment_speed_set_invoke; + ot->exec = sequencer_retiming_segment_speed_set_exec; + ot->poll = retiming_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + PropertyRNA *prop = RNA_def_int(ot->srna, + "handle_index", + 0, + 0, + INT_MAX, + "Handle Index", + "Index of handle to be removed", + 0, + INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + + prop = RNA_def_float(ot->srna, + "speed", + 100.0f, + 0.001f, + FLT_MAX, + "Speed", + "New speed of retimed segment", + 0.1f, + INT_MAX); +} + +/** \} */ diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 7806fb4f3eb..f95d364f9c1 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -430,6 +430,7 @@ static void sequencer_gizmos(void) WM_gizmotype_append(GIZMO_GT_retime_handle_add); WM_gizmotype_append(GIZMO_GT_retime_handle); WM_gizmotype_append(GIZMO_GT_retime_remove); + WM_gizmotype_append(GIZMO_GT_speed_set_remove); WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d); WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate); diff --git a/source/blender/sequencer/SEQ_retiming.h b/source/blender/sequencer/SEQ_retiming.h index b1879b68d95..16f70065823 100644 --- a/source/blender/sequencer/SEQ_retiming.h +++ b/source/blender/sequencer/SEQ_retiming.h @@ -48,6 +48,10 @@ void SEQ_retiming_offset_handle(const struct Scene *scene, const int offset); float SEQ_retiming_handle_speed_get(const struct Sequence *seq, const struct SeqRetimingHandle *handle); +void SEQ_retiming_handle_speed_set(const struct Scene *scene, + struct Sequence *seq, + struct SeqRetimingHandle *handle, + const float speed); int SEQ_retiming_handle_index_get(const struct Sequence *seq, const struct SeqRetimingHandle *handle); void SEQ_retiming_sound_animation_data_set(const struct Scene *scene, const struct Sequence *seq); diff --git a/source/blender/sequencer/intern/strip_retiming.cc b/source/blender/sequencer/intern/strip_retiming.cc index 89645192d3e..b9b85485f21 100644 --- a/source/blender/sequencer/intern/strip_retiming.cc +++ b/source/blender/sequencer/intern/strip_retiming.cc @@ -535,6 +535,29 @@ float SEQ_retiming_handle_speed_get(const Sequence *seq, const SeqRetimingHandle return speed; } +void SEQ_retiming_handle_speed_set(const Scene *scene, + Sequence *seq, + SeqRetimingHandle *handle, + const float speed) +{ + if (handle->strip_frame_index == 0) { + return; + } + + const SeqRetimingHandle *handle_prev = handle - 1; + const float speed_fac = 100.0f / speed; + + const int frame_index_max = seq->len; + const int frame_retimed_prev = round_fl_to_int(handle_prev->retiming_factor * frame_index_max); + const int frame_retimed = round_fl_to_int(handle->retiming_factor * frame_index_max); + + const int segment_duration = frame_retimed - frame_retimed_prev; + const int new_duration = segment_duration * speed_fac; + + const int offset = (handle_prev->strip_frame_index + new_duration) - handle->strip_frame_index; + SEQ_retiming_offset_handle(scene, seq, handle, offset); +} + enum eRangeType { LINEAR = 0, TRANSITION = 1,