From fa2c1698b077f510175e79adf3dbf3e1602b1030 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 21 Sep 2021 09:38:30 +0200 Subject: VSE: Image transform tools Add tools for image manipulation in sequencer preview region. This includes: - Translate, rotate and resize operators, tools and gizmos - Origin for image transformation - Median point and individual origins pivot modes - Select and Box select operator works in preview - Image overlay drawing ref T90156 Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D12105 --- .../keyconfig/keymap_data/blender_default.py | 51 +++++- release/scripts/startup/bl_ui/space_sequencer.py | 13 +- .../startup/bl_ui/space_toolsystem_toolbar.py | 40 +++++ source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenloader/intern/versioning_300.c | 30 ++++ .../blenloader/intern/versioning_defaults.c | 1 + source/blender/editors/animation/anim_ops.c | 5 + .../editors/space_sequencer/sequencer_draw.c | 67 +++++++ .../editors/space_sequencer/sequencer_select.c | 123 +++++++++++-- .../editors/space_sequencer/space_sequencer.c | 73 +++++++- source/blender/editors/transform/CMakeLists.txt | 1 + source/blender/editors/transform/transform.c | 12 +- source/blender/editors/transform/transform.h | 12 +- .../blender/editors/transform/transform_convert.c | 18 +- .../blender/editors/transform/transform_convert.h | 4 + .../transform/transform_convert_sequencer_image.c | 195 +++++++++++++++++++++ .../editors/transform/transform_draw_cursors.c | 2 +- .../blender/editors/transform/transform_generics.c | 7 + .../blender/editors/transform/transform_gizmo_2d.c | 71 +++++++- source/blender/editors/transform/transform_mode.c | 2 +- .../editors/transform/transform_snap_sequencer.c | 4 + source/blender/makesdna/DNA_scene_types.h | 1 + source/blender/makesdna/DNA_sequence_types.h | 2 + source/blender/makesdna/DNA_space_types.h | 2 + source/blender/makesrna/intern/rna_scene.c | 14 ++ source/blender/makesrna/intern/rna_sequencer.c | 6 + source/blender/makesrna/intern/rna_space.c | 5 + source/blender/sequencer/SEQ_iterator.h | 8 +- source/blender/sequencer/SEQ_sequencer.h | 1 + source/blender/sequencer/SEQ_transform.h | 9 + source/blender/sequencer/intern/iterator.c | 120 +++++++++++++ source/blender/sequencer/intern/render.c | 99 +---------- source/blender/sequencer/intern/sequencer.c | 9 + source/blender/sequencer/intern/strip_transform.c | 98 +++++++++++ 34 files changed, 969 insertions(+), 138 deletions(-) create mode 100644 source/blender/editors/transform/transform_convert_sequencer_image.c diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 4120006edad..a2edf5e541d 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -2588,6 +2588,9 @@ def km_sequencercommon(params): ("wm.context_toggle_enum", {"type": 'TAB', "value": 'PRESS', "ctrl": True}, {"properties": [("data_path", 'space_data.view_type'), ("value_1", 'SEQUENCER'), ("value_2", 'PREVIEW')]}), ("sequencer.refresh_all", {"type": 'R', "value": 'PRESS', "ctrl": True}, None), + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), + ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, + {"properties": [("extend", True)]}), ]) if params.select_mouse == 'LEFTMOUSE' and not params.legacy: @@ -2670,9 +2673,6 @@ def km_sequencer(params): for i in range(10) ) ), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS'}, None), - ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True}, - {"properties": [("extend", True)]}), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "alt": True}, {"properties": [("linked_handle", True)]}), ("sequencer.select", {"type": params.select_mouse, "value": 'PRESS', "shift": True, "alt": True}, @@ -2749,6 +2749,15 @@ def km_sequencerpreview(params): ("sequencer.view_zoom_ratio", {"type": 'NUMPAD_8', "value": 'PRESS'}, {"properties": [("ratio", 0.125)]}), ("sequencer.sample", {"type": params.action_mouse, "value": 'PRESS'}, None), + ("transform.translate", {"type": 'G', "value": 'PRESS'}, None), + ("transform.resize", {"type": 'S', "value": 'PRESS'}, None), + ("transform.rotate", {"type": 'R', "value": 'PRESS'}, None), + ("sequencer.strip_transform_clear", {"type": 'G', "alt": True, "value": 'PRESS'}, + {"properties": [("property", 'POSITION')]}), + ("sequencer.strip_transform_clear", {"type": 'S', "alt": True, "value": 'PRESS'}, + {"properties": [("property", 'SCALE')]}), + ("sequencer.strip_transform_clear", {"type": 'R', "alt": True, "value": 'PRESS'}, + {"properties": [("property", 'ROTATION')]}), ]) return keymap @@ -7316,6 +7325,39 @@ def km_sequencer_editor_tool_blade(_params): ) +def km_sequencer_editor_tool_move(params): + return ( + "Sequencer Tool: Move", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("transform.translate", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("release_confirm", True)]}), + ]}, + ) + + +def km_sequencer_editor_tool_rotate(params): + return ( + "Sequencer Tool: Rotate", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("transform.rotate", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("release_confirm", True)]}), + ]}, + ) + + +def km_sequencer_editor_tool_scale(params): + return ( + "Sequencer Tool: Scale", + {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, + {"items": [ + ("transform.resize", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("release_confirm", True)]}), + ]}, + ) + + # ------------------------------------------------------------------------------ # Full Configuration @@ -7569,6 +7611,9 @@ def generate_keymaps(params=None): km_sequencer_editor_tool_select_box(params), km_sequencer_editor_tool_blade(params), km_sequencer_editor_tool_generic_sample(params), + km_sequencer_editor_tool_scale(params), + km_sequencer_editor_tool_rotate(params), + km_sequencer_editor_tool_move(params), ] # ------------------------------------------------------------------------------ diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 88cf8db686c..806b6100cc8 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -141,9 +141,14 @@ class SEQUENCER_HT_header(Header): layout.separator_spacer() + tool_settings = context.tool_settings + sequencer_tool_settings = tool_settings.sequencer_tool_settings + + if st.view_type == 'PREVIEW': + layout.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) + layout.separator_spacer() + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: - tool_settings = context.tool_settings - sequencer_tool_settings = tool_settings.sequencer_tool_settings row = layout.row(align=True) row.prop(sequencer_tool_settings, "overlap_mode", text="") row = layout.row(align=True) @@ -209,6 +214,7 @@ class SEQUENCER_PT_preview_overlay(Panel): layout = self.layout layout.active = st.show_strip_overlay + layout.prop(overlay_settings, "show_image_outline") layout.prop(ed, "show_overlay", text="Frame Overlay") layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") layout.prop(overlay_settings, "show_metadata", text="Metadata") @@ -1756,6 +1762,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): col = layout.column(align=True) col.prop(strip.transform, "rotation", text="Rotation") + col = layout.column(align=True) + col.prop(strip.transform, "origin") + row = layout.row(heading="Mirror") sub = row.row(align=True) sub.prop(strip, "use_flip_x", text="X", toggle=True) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index a513ab4b2d4..77a6ff79598 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -2454,6 +2454,39 @@ class _defs_sequencer_generic: keymap="Sequencer Tool: Sample", ) + @ToolDef.from_fn + def translate(): + return dict( + idname="builtin.move", + label="Move", + icon="ops.transform.translate", + widget="SEQUENCER_GGT_gizmo2d_translate", + operator="transform.translate", + keymap="Sequencer Tool: Move", + ) + + @ToolDef.from_fn + def rotate(): + return dict( + idname="builtin.rotate", + label="Rotate", + icon="ops.transform.rotate", + widget="SEQUENCER_GGT_gizmo2d_rotate", + operator="transform.rotate", + keymap="Sequencer Tool: Rotate", + ) + + @ToolDef.from_fn + def scale(): + return dict( + idname="builtin.scale", + label="Scale", + icon="ops.transform.resize", + widget="SEQUENCER_GGT_gizmo2d_resize", + operator="transform.resize", + keymap="Sequencer Tool: Scale", + ) + class _defs_sequencer_select: @ToolDef.from_fn @@ -3045,6 +3078,10 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ ], 'PREVIEW': [ + *_tools_select, + _defs_sequencer_generic.translate, + _defs_sequencer_generic.rotate, + _defs_sequencer_generic.scale, _defs_sequencer_generic.sample, *_tools_annotate, ], @@ -3054,6 +3091,9 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'SEQUENCER_PREVIEW': [ *_tools_select, + _defs_sequencer_generic.translate, + _defs_sequencer_generic.rotate, + _defs_sequencer_generic.scale, _defs_sequencer_generic.blade, _defs_sequencer_generic.sample, *_tools_annotate, diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 63d6b9121d2..b3ee2f411d7 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 23 +#define BLENDER_FILE_SUBVERSION 24 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 30e7c9bde4c..d0fa6c282f4 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -64,6 +64,7 @@ #include "MEM_guardedalloc.h" #include "readfile.h" +#include "SEQ_iterator.h" #include "SEQ_sequencer.h" #include "RNA_access.h" @@ -774,6 +775,14 @@ static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) } } } +static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) +{ + StripTransform *transform = seq->strip->transform; + if (seq->strip->transform != NULL) { + transform->origin[0] = transform->origin[1] = 0.5f; + } + return true; +} /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) @@ -1290,6 +1299,27 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 24)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene); + sequencer_tool_settings->pivot_point = V3D_AROUND_CENTER_MEDIAN; + + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_origin_set, NULL); + } + } + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index 074cae669af..f2d5896be03 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -160,6 +160,7 @@ static void blo_update_defaults_screen(bScreen *screen, seq->render_size = SEQ_RENDER_SIZE_PROXY_100; seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; + seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 450d7cd100e..b4ea33920b2 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -241,6 +241,11 @@ static bool use_sequencer_snapping(bContext *C) /* Modal Operator init */ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + ARegion *region = CTX_wm_region(C); + if (CTX_wm_space_seq(C) != NULL && region->regiontype == RGN_TYPE_PREVIEW) { + return OPERATOR_CANCELLED; + } + /* Change to frame that mouse is over before adding modal handler, * as user could click on a single frame (jump to frame) as well as * click-dragging over a range (modal scrubbing). diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 5b39feacfe3..e063e12a9a8 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -71,6 +71,7 @@ #include "BIF_glutil.h" #include "SEQ_effects.h" +#include "SEQ_iterator.h" #include "SEQ_prefetch.h" #include "SEQ_proxy.h" #include "SEQ_relations.h" @@ -2056,6 +2057,64 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene) return preview_frame; } +static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + if ((seq->flag & SELECT) == 0) { + return; + } + if (ED_screen_animation_no_scrub(CTX_wm_manager(C))) { + return; + } + if ((sseq->flag & SEQ_SHOW_OVERLAY) == 0 || + (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_OUTLINE_SELECTED) == 0) { + return; + } + if (ELEM(sseq->mainb, SEQ_DRAW_IMG_WAVEFORM, SEQ_DRAW_IMG_VECTORSCOPE, SEQ_DRAW_IMG_HISTOGRAM)) { + return; + } + + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(CTX_data_scene(C), seq, origin); + + /* Origin. */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); + immUniform1f("outlineWidth", 1.5f); + immUniformColor3f(1.0f, 1.0f, 1.0f); + immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 1.0f); + immUniform1f("size", 15.0f * U.pixelsize); + immBegin(GPU_PRIM_POINTS, 1); + immVertex2f(pos, origin[0], origin[1]); + immEnd(); + immUnbindProgram(); + + /* Outline. */ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(CTX_data_scene(C), seq, seq_image_quad); + + GPU_line_smooth(true); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_width(2); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + float col[3]; + UI_GetThemeColor3fv(TH_SEQ_SELECTED, col); + immUniformColor3fv(col); + immUniform1f("lineWidth", U.pixelsize); + immBegin(GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, seq_image_quad[0][0], seq_image_quad[0][1]); + immVertex2f(pos, seq_image_quad[1][0], seq_image_quad[1][1]); + immVertex2f(pos, seq_image_quad[2][0], seq_image_quad[2][1]); + immVertex2f(pos, seq_image_quad[3][0], seq_image_quad[3][1]); + immEnd(); + immUnbindProgram(); + GPU_line_width(1); + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); +} + void sequencer_draw_preview(const bContext *C, Scene *scene, ARegion *region, @@ -2132,9 +2191,17 @@ void sequencer_draw_preview(const bContext *C, sequencer_draw_borders_overlay(sseq, v2d, scene); } + SeqCollection *collection = SEQ_query_rendered_strips(&scene->ed->seqbase, timeline_frame, 0); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq_draw_image_origin_and_outline(C, seq); + } + SEQ_collection_free(collection); + if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_gpencil_overlay(C); } + #if 0 sequencer_draw_maskedit(C, scene, region, sseq); #endif diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 80d3e2cbdaa..aa6599a7c53 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -44,6 +44,7 @@ #include "SEQ_sequencer.h" #include "SEQ_time.h" #include "SEQ_transform.h" +#include "SEQ_utils.h" /* For menu, popup, icons, etc. */ @@ -385,6 +386,20 @@ void recurs_sel_seq(Sequence *seq_meta) } } +static bool seq_point_image_isect(const Scene *scene, const Sequence *seq, float point[2]) +{ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad); + return isect_point_quad_v2( + point, seq_image_quad[0], seq_image_quad[1], seq_image_quad[2], seq_image_quad[3]); +} + +static void sequencer_select_do_updates(bContext *C, Scene *scene) +{ + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -523,12 +538,6 @@ static void sequencer_select_set_active(Scene *scene, Sequence *seq) recurs_sel_seq(seq); } -static void sequencer_select_do_updates(bContext *C, Scene *scene) -{ - ED_outliner_select_sync_from_sequence_tag(C); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); -} - static void sequencer_select_side_of_frame(const bContext *C, const View2D *v2d, const int mval[2], @@ -626,6 +635,45 @@ static void sequencer_select_linked_handle(const bContext *C, } } +/* Check if click happened on image which belongs to strip. If multiple strips are found, loop + * through them in order. */ +static Sequence *seq_select_seq_from_preview(const bContext *C, const int mval[2]) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); + View2D *v2d = UI_view2d_fromcontext(C); + + float mouseco_view[2]; + UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]); + + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + ListBase strips_ordered = {NULL}; + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq_point_image_isect(scene, seq, mouseco_view)) { + BLI_remlink(seqbase, seq); + BLI_addtail(&strips_ordered, seq); + } + } + SEQ_collection_free(strips); + SEQ_sort(&strips_ordered); + + Sequence *seq_active = SEQ_select_active_get(scene); + Sequence *seq_select = strips_ordered.first; + LISTBASE_FOREACH (Sequence *, seq_iter, &strips_ordered) { + if (seq_iter == seq_active && seq_iter->next != NULL) { + seq_select = seq_iter->next; + break; + } + } + + BLI_movelisttolist(seqbase, &strips_ordered); + + return seq_select; +} + static bool element_already_selected(const Sequence *seq, const int handle_clicked) { const bool handle_already_selected = ((handle_clicked == SEQ_SIDE_LEFT) && @@ -680,8 +728,15 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) mval[0] = RNA_int_get(op->ptr, "mouse_x"); mval[1] = RNA_int_get(op->ptr, "mouse_y"); - int handle_clicked; - Sequence *seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); + ARegion *region = CTX_wm_region(C); + int handle_clicked = SEQ_SIDE_NONE; + Sequence *seq = NULL; + if (region->regiontype == RGN_TYPE_PREVIEW) { + seq = seq_select_seq_from_preview(C, mval); + } + else { + seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); + } /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, * therefore both properties can be true at the same time. */ @@ -1311,6 +1366,47 @@ void SEQUENCER_OT_select_side(wmOperatorType *ot) /** \name Box Select Operator * \{ */ +static bool seq_box_select_rect_image_isect(const Scene *scene, const Sequence *seq, rctf *rect) +{ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad); + float rect_quad[4][2] = {{rect->xmax, rect->ymax}, + {rect->xmax, rect->ymin}, + {rect->xmin, rect->ymin}, + {rect->xmin, rect->ymax}}; + + return seq_point_image_isect(scene, seq, rect_quad[0]) || + seq_point_image_isect(scene, seq, rect_quad[1]) || + seq_point_image_isect(scene, seq, rect_quad[2]) || + seq_point_image_isect(scene, seq, rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[0], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[1], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[2], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[3], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]); +} + +static void seq_box_select_seq_from_preview(const bContext *C, rctf *rect) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); + + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq_box_select_rect_image_isect(scene, seq, rect)) { + seq->flag |= SELECT; + } + } + + SEQ_collection_free(strips); +} + static int sequencer_box_select_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -1333,6 +1429,13 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf); + ARegion *region = CTX_wm_region(C); + if (region->regiontype == RGN_TYPE_PREVIEW) { + seq_box_select_seq_from_preview(C, &rectf); + sequencer_select_do_updates(C, scene); + return OPERATOR_FINISHED; + } + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { rctf rq; seq_rectf(seq, &rq); @@ -1378,9 +1481,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) } } - ED_outliner_select_sync_from_sequence_tag(C); - - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + sequencer_select_do_updates(C, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 0d09f2564e8..cb5fe6f0ef3 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -42,6 +42,7 @@ #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_transform.h" #include "ED_view3d.h" #include "ED_view3d_offscreen.h" /* Only for sequencer view3d drawing callback. */ @@ -98,10 +99,11 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->chanshown = 0; sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; - sseq->flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | - SEQ_TIMELINE_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_OVERLAY | - SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | - SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID; + sseq->flag = SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_SHOW_OVERLAY; + sseq->preview_overlay.flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; + sseq->timeline_overlay.flag = SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | + SEQ_TIMELINE_SHOW_FCURVES; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -481,11 +483,72 @@ static void SEQUENCER_GGT_navigate(wmGizmoGroupType *gzgt) VIEW2D_GGT_navigate_impl(gzgt, "SEQUENCER_GGT_navigate"); } +static void SEQUENCER_GGT_gizmo2d(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Translate Gizmo"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_translate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo Resize"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_resize"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_resize_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo Resize"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_rotate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_rotate_callbacks_set(gzgt); +} + static void sequencer_gizmos(void) { wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure( &(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW}); + 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_and_link(gzmap_type, SEQUENCER_GGT_navigate); } @@ -742,6 +805,8 @@ static void sequencer_preview_region_listener(const wmRegionListenerParams *para ARegion *region = params->region; wmNotifier *wmn = params->notifier; + WM_gizmomap_tag_refresh(region->gizmo_map); + /* Context changes. */ switch (wmn->category) { case NC_GPENCIL: diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index e9efed3cd61..64a720322c1 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRC transform_convert_particle.c transform_convert_sculpt.c transform_convert_sequencer.c + transform_convert_sequencer_image.c transform_convert_tracking.c transform_draw_cursors.c transform_generics.c diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 58491f8c2d3..e58e524e341 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1703,11 +1703,13 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->draw_handle_cursor = WM_paint_cursor_activate( SPACE_TYPE_ANY, RGN_TYPE_ANY, transform_draw_cursor_poll, transform_draw_cursor_draw, t); } - else if (t->spacetype == SPACE_SEQ) { - t->draw_handle_view = ED_region_draw_cb_activate( - t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - } - else if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP, SPACE_NODE, SPACE_GRAPH, SPACE_ACTION)) { + else if (ELEM(t->spacetype, + SPACE_IMAGE, + SPACE_CLIP, + SPACE_NODE, + SPACE_GRAPH, + SPACE_ACTION, + SPACE_SEQ)) { t->draw_handle_view = ED_region_draw_cb_activate( t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW); t->draw_handle_cursor = WM_paint_cursor_activate( diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index d1a1937cef1..7f4e533ccd7 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -87,15 +87,16 @@ typedef enum { CTX_PAINT_CURVE = (1 << 7), CTX_POSE_BONE = (1 << 8), CTX_TEXTURE_SPACE = (1 << 9), + CTX_SEQUENCER_IMAGE = (1 << 10), - CTX_NO_PET = (1 << 10), - CTX_AUTOCONFIRM = (1 << 11), + CTX_NO_PET = (1 << 11), + CTX_AUTOCONFIRM = (1 << 12), /** When transforming object's, adjust the object data so it stays in the same place. */ - CTX_OBMODE_XFORM_OBDATA = (1 << 12), + CTX_OBMODE_XFORM_OBDATA = (1 << 13), /** Transform object parents without moving their children. */ - CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13), + CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14), /** Enable edge scrolling in 2D views */ - CTX_VIEW2D_EDGE_PAN = (1 << 14), + CTX_VIEW2D_EDGE_PAN = (1 << 15), } eTContext; /** #TransInfo.flag */ @@ -240,6 +241,7 @@ typedef enum { TC_PARTICLE_VERTS, TC_SCULPT, TC_SEQ_DATA, + TC_SEQ_IMAGE_DATA, TC_TRACKING_DATA, } eTConvertType; diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d756e2c90a6..557fa79e7ac 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -955,6 +955,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_OBJECT_TEXSPACE: case TC_PAINT_CURVE_VERTS: case TC_PARTICLE_VERTS: + case TC_SEQ_IMAGE_DATA: case TC_NONE: default: break; @@ -1042,6 +1043,7 @@ static void init_proportional_edit(TransInfo *t) case TC_PAINT_CURVE_VERTS: case TC_SCULPT: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: case TC_NONE: default: @@ -1120,6 +1122,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_PARTICLE_VERTS: case TC_SCULPT: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: case TC_NONE: default: @@ -1204,6 +1207,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_NODE_DATA: case TC_PAINT_CURVE_VERTS: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: return T_POINTS | T_2D_EDIT; case TC_ARMATURE_VERTS: @@ -1282,7 +1286,12 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur convert_type = TC_NLA_DATA; } else if (t->spacetype == SPACE_SEQ) { - convert_type = TC_SEQ_DATA; + if (t->options & CTX_SEQUENCER_IMAGE) { + convert_type = TC_SEQ_IMAGE_DATA; + } + else { + convert_type = TC_SEQ_DATA; + } } else if (t->spacetype == SPACE_GRAPH) { convert_type = TC_GRAPH_EDIT_DATA; @@ -1470,6 +1479,10 @@ void createTransData(bContext *C, TransInfo *t) t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */ createTransSeqData(t); break; + case TC_SEQ_IMAGE_DATA: + t->obedit_type = -1; + createTransSeqImageData(t); + break; case TC_TRACKING_DATA: createTransTrackingData(C, t); break; @@ -1746,6 +1759,9 @@ void recalcData(TransInfo *t) case TC_SEQ_DATA: recalcData_sequencer(t); break; + case TC_SEQ_IMAGE_DATA: + recalcData_sequencer_image(t); + break; case TC_TRACKING_DATA: recalcData_tracking(t); break; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 9cb0400cad9..66d84bca2d2 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -218,6 +218,10 @@ void createTransSeqData(TransInfo *t); void recalcData_sequencer(TransInfo *t); void special_aftertrans_update__sequencer(bContext *C, TransInfo *t); +/* transform_convert_sequencer_image.c */ +void createTransSeqImageData(TransInfo *t); +void recalcData_sequencer_image(TransInfo *t); + /* transform_convert_tracking.c */ void createTransTrackingData(bContext *C, TransInfo *t); void recalcData_tracking(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c new file mode 100644 index 00000000000..465f8b9a694 --- /dev/null +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -0,0 +1,195 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edtransform + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_space_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_report.h" + +#include "SEQ_iterator.h" +#include "SEQ_relations.h" +#include "SEQ_sequencer.h" +#include "SEQ_time.h" +#include "SEQ_transform.h" +#include "SEQ_utils.h" + +#include "UI_view2d.h" + +#include "transform.h" +#include "transform_convert.h" + +/** Used for sequencer transform. */ +typedef struct TransDataSeq { + struct Sequence *seq; + float orig_origin_position[2]; + float orig_translation[2]; + float orig_scale[2]; + float orig_rotation; +} TransDataSeq; + +static TransData *SeqToTransData(const Scene *scene, + Sequence *seq, + TransData *td, + TransData2D *td2d, + TransDataSeq *tdseq, + int vert_index) +{ + const StripTransform *transform = seq->strip->transform; + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + float vertex[2] = {origin[0], origin[1]}; + + /* Add control vertex, so rotation and scale can be calculated. */ + if (vert_index == 1) { + vertex[0] += 1.0f; + } + else if (vert_index == 2) { + vertex[1] += 1.0f; + } + + td2d->loc[0] = vertex[0]; + td2d->loc[1] = vertex[1]; + td2d->loc2d = NULL; + td->loc = td2d->loc; + copy_v3_v3(td->iloc, td->loc); + + td->center[0] = origin[0]; + td->center[1] = origin[1]; + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + unit_m3(td->mtx); + unit_m3(td->smtx); + + tdseq->seq = seq; + copy_v2_v2(tdseq->orig_origin_position, origin); + tdseq->orig_translation[0] = transform->xofs; + tdseq->orig_translation[1] = transform->yofs; + tdseq->orig_scale[0] = transform->scale_x; + tdseq->orig_scale[1] = transform->scale_y; + tdseq->orig_rotation = transform->rotation; + + td->extra = (void *)tdseq; + td->ext = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + return td; +} + +static void freeSeqData(TransInfo *UNUSED(t), TransDataContainer *tc, TransCustomData *UNUSED(custom_data)) +{ + TransData *td = (TransData *)tc->data; + MEM_freeN(td->extra); +} + +void createTransSeqImageData(TransInfo *t) +{ + Editing *ed = SEQ_editing_get(t->scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, t->scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + const int count = SEQ_collection_len(strips); + if (ed == NULL || count == 0) { + SEQ_collection_free(strips); + return; + } + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + tc->custom.type.free_cb = freeSeqData; + + tc->data_len = count * 3; /* 3 vertices per sequence are needed. */ + TransData *td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData"); + TransData2D *td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), + "TransSeq TransData2D"); + TransDataSeq *tdseq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + /* One `Sequence` needs 3 `TransData` entries - center point placed in image origin, then 2 + * points offset by 1 in X and Y direction respectively, so rotation and scale can be + * calculated from these points. */ + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 0); + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 1); + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 2); + } + + SEQ_collection_free(strips); +} + +void recalcData_sequencer_image(TransInfo *t) +{ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td = NULL; + TransData2D *td2d = NULL; + int i; + + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + /* Origin. */ + float loc[2]; + copy_v2_v2(loc, td2d->loc); + i++, td++, td2d++; + + /* X and Y control points used to read scale and rotation. */ + float handle_x[2]; + copy_v2_v2(handle_x, td2d->loc); + sub_v2_v2(handle_x, loc); + i++, td++, td2d++; + float handle_y[2]; + copy_v2_v2(handle_y, td2d->loc); + sub_v2_v2(handle_y, loc); + + TransDataSeq *tdseq = td->extra; + Sequence *seq = tdseq->seq; + StripTransform *transform = seq->strip->transform; + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + + /* Calculate translation. */ + float translation[2]; + copy_v2_v2(translation, tdseq->orig_origin_position); + sub_v2_v2(translation, loc); + mul_v2_v2(translation, mirror); + transform->xofs = tdseq->orig_translation[0] - translation[0]; + transform->yofs = tdseq->orig_translation[1] - translation[1]; + + /* Scale. */ + transform->scale_x = tdseq->orig_scale[0] * fabs(len_v2(handle_x)); + transform->scale_y = tdseq->orig_scale[1] * fabs(len_v2(handle_y)); + + /* Rotation. Scaling can cause negative rotation. */ + if (t->mode == TFM_ROTATION) { + float rotation = angle_signed_v2v2(handle_x, (float[]){1, 0}) * mirror[0] * mirror[1]; + transform->rotation = tdseq->orig_rotation + rotation; + transform->rotation += DEG2RAD(360.0); + transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); + } + SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); + } +} diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c index ead8eae0997..af1f3cb72a4 100644 --- a/source/blender/editors/transform/transform_draw_cursors.c +++ b/source/blender/editors/transform/transform_draw_cursors.c @@ -95,7 +95,7 @@ static void drawArrow(const uint pos_id, const enum eArrowDirection dir) bool transform_draw_cursor_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - return (region && region->regiontype == RGN_TYPE_WINDOW) ? 1 : 0; + return (region && ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_PREVIEW)) ? 1 : 0; } /** diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index c493b9bd102..fa323f0c1f7 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -59,6 +59,8 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "SEQ_sequencer.h" + #include "transform.h" #include "transform_convert.h" #include "transform_mode.h" @@ -335,6 +337,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->options |= CTX_MASK; } } + else if (t->spacetype == SPACE_SEQ && region->regiontype == RGN_TYPE_PREVIEW) { + t->view = ®ion->v2d; + t->around = SEQ_tool_settings_pivot_point_get(t->scene); + t->options |= CTX_SEQUENCER_IMAGE; + } else { if (region) { /* XXX: For now, get View2D from the active region. */ diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 0b677e2560b..0d66db0d7e1 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -49,6 +49,11 @@ #include "ED_screen.h" #include "ED_uvedit.h" +#include "SEQ_iterator.h" +#include "SEQ_sequencer.h" +#include "SEQ_time.h" +#include "SEQ_transform.h" + #include "transform.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -234,17 +239,66 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min return changed; } +static float gizmo2d_calc_rotation(const bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_SEQ) { + return 0.0f; + } + + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq == ed->act_seq) { + StripTransform *transform = seq->strip->transform; + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + SEQ_collection_free(strips); + return transform->rotation * mirror[0] * mirror[1]; + } + } + + SEQ_collection_free(strips); + return 0.0f; +} + static bool gizmo2d_calc_center(const bContext *C, float r_center[2]) { ScrArea *area = CTX_wm_area(C); + Scene *scene = CTX_data_scene(C); bool has_select = false; zero_v2(r_center); if (area->spacetype == SPACE_IMAGE) { SpaceImage *sima = area->spacedata.first; - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, sima->around, &has_select); } + else if (area->spacetype == SPACE_SEQ) { + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + if (SEQ_collection_len(strips) <= 0) { + SEQ_collection_free(strips); + return false; + } + + has_select = true; + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + add_v2_v2(r_center, origin); + } + mul_v2_fl(r_center, 1.0f / SEQ_collection_len(strips)); + + SEQ_collection_free(strips); + } return has_select; } @@ -338,7 +392,7 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup } } - RNA_boolean_set(ptr, "release_confirm", 1); + RNA_boolean_set(ptr, "release_confirm", true); } { @@ -539,6 +593,7 @@ void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt) typedef struct GizmoGroup_Resize2D { wmGizmo *gizmo_xy[3]; float origin[2]; + float rotation; } GizmoGroup_Resize2D; static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup) @@ -571,6 +626,7 @@ static void gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup) ggd->gizmo_xy[i]->flag &= ~WM_GIZMO_HIDDEN; } copy_v2_v2(ggd->origin, origin); + ggd->rotation = gizmo2d_calc_rotation(C); } } @@ -595,6 +651,13 @@ static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { wmGizmo *gz = ggd->gizmo_xy[i]; WM_gizmo_set_matrix_location(gz, origin); + + if (i < 2) { + float axis[3] = {0.0f}, rotated_axis[3]; + axis[i] = 1.0f; + rotate_v3_v3v3fl(rotated_axis, axis, (float[3]){0, 0, 1}, ggd->rotation); + WM_gizmo_set_matrix_rotation_from_z_axis(gz, rotated_axis); + } } } @@ -617,10 +680,6 @@ static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgrou /* set up widget data */ RNA_float_set(gz->ptr, "length", 1.0f); - float axis[3] = {0.0f}; - axis[i] = 1.0f; - WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis); - RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX); WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index b9fb8a86752..b14d499cb66 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -75,7 +75,7 @@ bool transdata_check_local_center(const TransInfo *t, short around) /* implicit: (t->flag & T_EDIT) */ (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || (t->spacetype == SPACE_GRAPH) || - (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))); + (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE | CTX_SEQUENCER_IMAGE)))); } /* Informs if the mode can be switched during modal. */ diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index e82a00bcc77..2acdf5cfd9c 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -254,6 +254,10 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t) TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) { + if (t->data_type == TC_SEQ_IMAGE_DATA) { + return NULL; + } + TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__); ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 7800e7f9efe..f2244b4ae61 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1344,6 +1344,7 @@ typedef struct SequencerToolSettings { /** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all * possible snap points. */ int snap_distance; + int pivot_point; } SequencerToolSettings; typedef enum eSeqOverlapMode { diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 03c38eb71a0..782b4ac34aa 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -74,6 +74,8 @@ typedef struct StripTransform { float scale_x; float scale_y; float rotation; + /** 0-1 range, use SEQ_image_transform_origin_offset_pixelspace_get to convert to pixel space. */ + float origin[2]; } StripTransform; typedef struct StripColorBalance { diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 6505816256c..84dc58345e3 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -583,6 +583,7 @@ typedef struct SequencerPreviewOverlay { /* SequencerPreviewOverlay.flag */ typedef enum eSpaceSeq_SequencerPreviewOverlay_Flag { + SEQ_PREVIEW_SHOW_OUTLINE_SELECTED = (1 << 2), SEQ_PREVIEW_SHOW_SAFE_MARGINS = (1 << 3), SEQ_PREVIEW_SHOW_GPENCIL = (1 << 4), SEQ_PREVIEW_SHOW_SAFE_CENTER = (1 << 9), @@ -685,6 +686,7 @@ typedef enum eSpaceSeq_Flag { SPACE_SEQ_FLAG_UNUSED_15 = (1 << 15), SPACE_SEQ_FLAG_UNUSED_16 = (1 << 16), SEQ_USE_PROXIES = (1 << 17), + SEQ_SHOW_GRID = (1 << 18), } eSpaceSeq_Flag; /* SpaceSeq.view */ diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index badaaa14aa4..1762b964f8d 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3525,6 +3525,16 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem pivot_points[] = { + {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", ICON_PIVOT_MEDIAN, "Median Point", ""}, + {V3D_AROUND_LOCAL_ORIGINS, + "INDIVIDUAL_ORIGINS", + ICON_PIVOT_INDIVIDUAL, + "Individual Origins", + "Pivot around each selected island's own median point"}, + {0, NULL, 0, NULL, NULL}, + + }; srna = RNA_def_struct(brna, "SequencerToolSettings", NULL); RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); @@ -3568,6 +3578,10 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "overlap_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, scale_overlap_modes); RNA_def_property_ui_text(prop, "Overlap Mode", "How to resolve overlap after transformation"); + + prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, pivot_points); + RNA_def_property_ui_text(prop, "Pivot Point", "Rotation or scaling pivot point"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index cd87e4d10c1..b713ffb68b4 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -1442,6 +1442,12 @@ static void rna_def_strip_transform(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rotation", "Rotate around image center"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + prop = RNA_def_property(srna, "origin", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "origin"); + RNA_def_property_ui_text(prop, "Origin", "Origin of image for transformation"); + RNA_def_property_ui_range(prop, 0, 1, 1, 3); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + RNA_def_struct_path_func(srna, "rna_SequenceTransform_path"); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 8c331bd1911..70fe2dbc75a 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -5371,6 +5371,11 @@ static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_GPENCIL); RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_image_outline", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_OUTLINE_SELECTED); + RNA_def_property_ui_text(prop, "Image Outline", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index 4f7d603fd6a..d2a47a13db3 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -94,11 +94,15 @@ SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference, SeqCollection *collection)); SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase); SeqCollection *SEQ_query_unselected_strips(struct ListBase *seqbase); -SeqCollection *SEQ_query_all_strips(struct ListBase *seqbase); -SeqCollection *SEQ_query_all_strips_recursive(struct ListBase *seqbase); +SeqCollection *SEQ_query_all_strips(ListBase *seqbase); +SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase); +SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel); void SEQ_query_strip_effect_chain(struct Sequence *seq_reference, struct ListBase *seqbase, SeqCollection *collection); +void SEQ_filter_selected_strips(SeqCollection *collection); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index d7800d208a4..7e733817630 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -64,6 +64,7 @@ short SEQ_tool_settings_snap_flag_get(struct Scene *scene); short SEQ_tool_settings_snap_mode_get(struct Scene *scene); int SEQ_tool_settings_snap_distance_get(struct Scene *scene); eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene); +int SEQ_tool_settings_pivot_point_get(struct Scene *scene); struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *SEQ_editing_get(const struct Scene *scene); struct Editing *SEQ_editing_ensure(struct Scene *scene); diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 1977835f627..328efb9424a 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -61,6 +61,15 @@ void SEQ_transform_offset_after_frame(struct Scene *scene, const int delta, const int timeline_frame); +/* Image transformation. */ +void SEQ_image_transform_mirror_factor_get(const struct Sequence *seq, float r_mirror[2]); +void SEQ_image_transform_origin_offset_pixelspace_get(const struct Scene *scene, + const struct Sequence *seq, + float r_origin[2]); +void SEQ_image_transform_final_quad_get(const struct Scene *scene, + const struct Sequence *seq, + float r_quad[4][2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index 58f68205f51..2429405350b 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -37,6 +37,8 @@ #include "BKE_scene.h" #include "SEQ_iterator.h" +#include "SEQ_time.h" +#include "render.h" /* -------------------------------------------------------------------- */ /** \Iterator API @@ -340,6 +342,114 @@ SeqCollection *SEQ_query_selected_strips(ListBase *seqbase) return collection; } +static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame) +{ + SeqCollection *collection = SEQ_collection_create(__func__); + + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->machine <= channel) { + continue; + } + SEQ_collection_remove_strip(seq, collection); + } +} + +static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) +{ + if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || + seq_effect->seq3 == possibly_input) { + return true; + } + return false; +} + +/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. + * Order of applying these conditions is important. */ +static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) +{ + bool seq_have_effect_in_stack = false; + Sequence *seq_iter; + SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { + /* Strips is below another strip with replace blending are not rendered. */ + if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { + return false; + } + + if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { + /* Strips in same channel or higher than its effect are rendered. */ + if (seq->machine >= seq_iter->machine) { + return true; + } + /* Mark that this strip has effect in stack, that is above the strip. */ + seq_have_effect_in_stack = true; + } + } + + /* All effects are rendered (with respect to conditions above). */ + if ((seq->type & SEQ_TYPE_EFFECT) != 0) { + return true; + } + + /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ + if (seq_have_effect_in_stack) { + return false; + } + + return true; +} + +/* Remove strips we don't want to render from collection. */ +static void collection_filter_rendered_strips(SeqCollection *collection) +{ + Sequence *seq; + + /* Remove sound strips and muted strips from collection, because these are not rendered. + * Function #must_render_strip() don't have to check for these strips anymore. */ + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { + SEQ_collection_remove_strip(seq, collection); + } + } + + SEQ_ITERATOR_FOREACH (seq, collection) { + if (must_render_strip(seq, collection)) { + continue; + } + SEQ_collection_remove_strip(seq, collection); + } +} + +/** + * Query strips that are rendered at \a timeline_frame when \a displayed channel is viewed + * + * \param seqbase: ListBase in which strips are queried + * \param timeline_frame: viewed frame + * \param displayed_channel: viewed channel. when set to 0, no channel filter is applied + * \return strip collection + */ +SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel) +{ + SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame); + if (displayed_channel != 0) { + collection_filter_channel_up_to_incl(collection, displayed_channel); + } + collection_filter_rendered_strips(collection); + return collection; +} + /** * Query all unselected strips in seqbase. * @@ -396,3 +506,13 @@ void SEQ_query_strip_effect_chain(Sequence *seq_reference, } } } + +void SEQ_filter_selected_strips(SeqCollection *collection) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if ((seq->flag & SELECT) == 0) { + SEQ_collection_remove_strip(seq, collection); + } + } +} diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 6c4502a3608..e42a3f4d6e9 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -72,6 +72,7 @@ #include "SEQ_render.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" +#include "SEQ_transform.h" #include "SEQ_utils.h" #include "effects.h" @@ -262,94 +263,6 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame) return se; } -static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) -{ - if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || - seq_effect->seq3 == possibly_input) { - return true; - } - return false; -} - -/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. - * Order of applying these conditions is important. */ -static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) -{ - bool seq_have_effect_in_stack = false; - Sequence *seq_iter; - SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { - /* Strips is below another strip with replace blending are not rendered. */ - if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { - return false; - } - - if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { - /* Strips in same channel or higher than its effect are rendered. */ - if (seq->machine >= seq_iter->machine) { - return true; - } - /* Mark that this strip has effect in stack, that is above the strip. */ - seq_have_effect_in_stack = true; - } - } - - /* All effects are rendered (with respect to conditions above). */ - if ((seq->type & SEQ_TYPE_EFFECT) != 0) { - return true; - } - - /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ - if (seq_have_effect_in_stack) { - return false; - } - - return true; -} - -static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame) -{ - SeqCollection *collection = SEQ_collection_create(__func__); - - LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, collection) { - if (seq->machine <= channel) { - continue; - } - SEQ_collection_remove_strip(seq, collection); - } -} - -/* Remove strips we don't want to render from collection. */ -static void collection_filter_rendered_strips(SeqCollection *collection) -{ - Sequence *seq; - - /* Remove sound strips and muted strips from collection, because these are not rendered. - * Function #must_render_strip() don't have to check for these strips anymore. */ - SEQ_ITERATOR_FOREACH (seq, collection) { - if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { - SEQ_collection_remove_strip(seq, collection); - } - } - - SEQ_ITERATOR_FOREACH (seq, collection) { - if (must_render_strip(seq, collection)) { - continue; - } - SEQ_collection_remove_strip(seq, collection); - } -} - static int seq_channel_cmp_fn(const void *a, const void *b) { return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine; @@ -360,13 +273,7 @@ int seq_get_shown_sequences(ListBase *seqbase, const int chanshown, Sequence **r_seq_arr) { - SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame); - - if (chanshown != 0) { - collection_filter_channel_up_to_incl(collection, chanshown); - } - collection_filter_rendered_strips(collection); - + SeqCollection *collection = SEQ_query_rendered_strips(seqbase, timeline_frame, chanshown); const int strip_count = BLI_gset_len(collection->set); if (strip_count > MAXSEQ) { @@ -504,7 +411,7 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq, const float image_center_offs_y = (out->y - in->y) / 2; const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x; const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y; - const float pivot[2] = {in->x / 2, in->y / 2}; + const float pivot[2] = {in->x * transform->origin[0], in->y * transform->origin[1]}; loc_rot_size_to_mat3(r_transform_matrix, (const float[]){translate_x, translate_y}, transform->rotation, diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index bf5942090c9..d3f8411cf0a 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -79,6 +79,8 @@ static Strip *seq_strip_alloc(int type) strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform"); strip->transform->scale_x = 1; strip->transform->scale_y = 1; + strip->transform->origin[0] = 0.5f; + strip->transform->origin[1] = 0.5f; strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop"); } @@ -321,6 +323,7 @@ SequencerToolSettings *SEQ_tool_settings_init(void) SEQ_SNAP_TO_STRIP_HOLD; tool_settings->snap_distance = 15; tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE; + tool_settings->pivot_point = V3D_AROUND_LOCAL_ORIGINS; return tool_settings; } @@ -377,6 +380,12 @@ eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene) return tool_settings->overlap_mode; } +int SEQ_tool_settings_pivot_point_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->pivot_point; +} + /** * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase * diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 3a5f93a72b0..d5ff455c694 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -421,3 +421,101 @@ void SEQ_transform_offset_after_frame(Scene *scene, } } } + +void SEQ_image_transform_mirror_factor_get(const Sequence *seq, float r_mirror[2]) +{ + r_mirror[0] = 1.0f; + r_mirror[1] = 1.0f; + + if ((seq->flag & SEQ_FLIPX) != 0) { + r_mirror[0] = -1.0f; + } + if ((seq->flag & SEQ_FLIPY) != 0) { + r_mirror[1] = -1.0f; + } +} + +/** + * Get strip transform origin offset from image center + * Note: This function does not apply axis mirror. + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_origin: return value + */ +void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, + const Sequence *seq, + float r_origin[2]) +{ + float image_size[2]; + StripElem *strip_elem = seq->strip->stripdata; + if (strip_elem == NULL) { + image_size[0] = scene->r.xsch; + image_size[1] = scene->r.ysch; + } + else { + image_size[0] = strip_elem->orig_width; + image_size[1] = strip_elem->orig_height; + } + + const StripTransform *transform = seq->strip->transform; + r_origin[0] = (image_size[0] * transform->origin[0]) - (image_size[0] * 0.5f) + transform->xofs; + r_origin[1] = (image_size[1] * transform->origin[1]) - (image_size[1] * 0.5f) + transform->yofs; + + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + mul_v2_v2(r_origin, mirror); +} + +/** + * Get strip transform origin offset from image center + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_origin: return value + */ + +void SEQ_image_transform_final_quad_get(const Scene *scene, + const Sequence *seq, + float r_quad[4][2]) +{ + StripTransform *transform = seq->strip->transform; + StripCrop *crop = seq->strip->crop; + + int imgage_size[2] = {scene->r.xsch, scene->r.ysch}; + if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { + imgage_size[0] = seq->strip->stripdata->orig_width; + imgage_size[1] = seq->strip->stripdata->orig_height; + } + + float transform_matrix[3][3]; + loc_rot_size_to_mat3(transform_matrix, + (const float[]){transform->xofs, transform->yofs}, + transform->rotation, + (const float[]){transform->scale_x, transform->scale_y}); + const float origin[2] = {imgage_size[0] * transform->origin[0], + imgage_size[1] * transform->origin[1]}; + const float pivot[2] = {origin[0] - (imgage_size[0] / 2), origin[1] - (imgage_size[1] / 2)}; + transform_pivot_set_m3(transform_matrix, pivot); + + r_quad[0][0] = (imgage_size[0] / 2) - crop->right; + r_quad[0][1] = (imgage_size[1] / 2) - crop->top; + r_quad[1][0] = (imgage_size[0] / 2) - crop->right; + r_quad[1][1] = (-imgage_size[1] / 2) + crop->bottom; + r_quad[2][0] = (-imgage_size[0] / 2) + crop->left; + r_quad[2][1] = (-imgage_size[1] / 2) + crop->bottom; + r_quad[3][0] = (-imgage_size[0] / 2) + crop->left; + r_quad[3][1] = (imgage_size[1] / 2) - crop->top; + + mul_m3_v2(transform_matrix, r_quad[0]); + mul_m3_v2(transform_matrix, r_quad[1]); + mul_m3_v2(transform_matrix, r_quad[2]); + mul_m3_v2(transform_matrix, r_quad[3]); + + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + mul_v2_v2(r_quad[0], mirror); + mul_v2_v2(r_quad[1], mirror); + mul_v2_v2(r_quad[2], mirror); + mul_v2_v2(r_quad[3], mirror); +} -- cgit v1.2.3