Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/anim_deps.c2
-rw-r--r--source/blender/editors/animation/anim_ops.c5
-rw-r--r--source/blender/editors/armature/armature_intern.h1
-rw-r--r--source/blender/editors/armature/armature_ops.c1
-rw-r--r--source/blender/editors/armature/pose_slide.c102
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c6
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c11
-rw-r--r--source/blender/editors/interface/interface_style.c7
-rw-r--r--source/blender/editors/interface/interface_template_search_menu.c40
-rw-r--r--source/blender/editors/interface/interface_templates.c5
-rw-r--r--source/blender/editors/interface/view2d_ops.c16
-rw-r--r--source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c4
-rw-r--r--source/blender/editors/object/object_bake_api.c18
-rw-r--r--source/blender/editors/render/render_preview.c9
-rw-r--r--source/blender/editors/screen/area.c12
-rw-r--r--source/blender/editors/space_file/filesel.c2
-rw-r--r--source/blender/editors/space_node/node_draw.cc4
-rw-r--r--source/blender/editors/space_node/node_intern.h1
-rw-r--r--source/blender/editors/space_node/node_ops.c1
-rw-r--r--source/blender/editors/space_node/node_view.cc90
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c5
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c13
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c595
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c6
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h1
-rw-r--r--source/blender/editors/space_sequencer/sequencer_select.c123
-rw-r--r--source/blender/editors/space_sequencer/space_sequencer.c88
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_preselect.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c4
-rw-r--r--source/blender/editors/transform/CMakeLists.txt1
-rw-r--r--source/blender/editors/transform/transform.c12
-rw-r--r--source/blender/editors/transform/transform.h12
-rw-r--r--source/blender/editors/transform/transform_convert.c18
-rw-r--r--source/blender/editors/transform/transform_convert.h4
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer_image.c195
-rw-r--r--source/blender/editors/transform/transform_draw_cursors.c2
-rw-r--r--source/blender/editors/transform/transform_generics.c7
-rw-r--r--source/blender/editors/transform/transform_gizmo_2d.c71
-rw-r--r--source/blender/editors/transform/transform_mode.c2
-rw-r--r--source/blender/editors/transform/transform_snap_sequencer.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c12
41 files changed, 1412 insertions, 104 deletions
diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c
index 97679723d84..088de80bb65 100644
--- a/source/blender/editors/animation/anim_deps.c
+++ b/source/blender/editors/animation/anim_deps.c
@@ -217,8 +217,6 @@ static void animchan_sync_fcurve_scene(bAnimListElem *ale)
/* Check if this strip is selected. */
Editing *ed = SEQ_editing_get(scene);
seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false);
- MEM_freeN(seq_name);
-
if (seq == NULL) {
return;
}
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/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
index f9950d27e97..696355324e6 100644
--- a/source/blender/editors/armature/armature_intern.h
+++ b/source/blender/editors/armature/armature_intern.h
@@ -216,6 +216,7 @@ void POSE_OT_relax(struct wmOperatorType *ot);
void POSE_OT_push_rest(struct wmOperatorType *ot);
void POSE_OT_relax_rest(struct wmOperatorType *ot);
void POSE_OT_breakdown(struct wmOperatorType *ot);
+void POSE_OT_blend_to_neighbours(struct wmOperatorType *ot);
void POSE_OT_propagate(struct wmOperatorType *ot);
diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c
index fbd89106de5..a1070a8823a 100644
--- a/source/blender/editors/armature/armature_ops.c
+++ b/source/blender/editors/armature/armature_ops.c
@@ -150,6 +150,7 @@ void ED_operatortypes_armature(void)
WM_operatortype_append(POSE_OT_push_rest);
WM_operatortype_append(POSE_OT_relax_rest);
WM_operatortype_append(POSE_OT_breakdown);
+ WM_operatortype_append(POSE_OT_blend_to_neighbours);
}
void ED_operatormacros_armature(void)
diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c
index f23376867af..b273d3aac76 100644
--- a/source/blender/editors/armature/pose_slide.c
+++ b/source/blender/editors/armature/pose_slide.c
@@ -117,6 +117,7 @@ typedef enum ePoseSlide_Modes {
POSESLIDE_BREAKDOWN,
POSESLIDE_PUSH_REST,
POSESLIDE_RELAX_REST,
+ POSESLIDE_BLEND,
} ePoseSlide_Modes;
/** Transforms/Channels to Affect. */
@@ -423,6 +424,25 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo
(*val) = ((sVal * w2) + (eVal * w1));
break;
}
+ case POSESLIDE_BLEND: /* Blend the current pose with the previous (<50%) or next key (>50%). */
+ {
+ /* FCurve value on current frame. */
+ const float cVal = evaluate_fcurve(fcu, cframe);
+ const float factor = ED_slider_factor_get(pso->slider);
+ /* Convert factor to absolute 0-1 range. */
+ const float blend_factor = fabs((factor - 0.5f) * 2);
+
+ if (factor < 0.5) {
+ /* Blend to previous key. */
+ (*val) = (cVal * (1 - blend_factor)) + (sVal * blend_factor);
+ }
+ else {
+ /* Blend to next key. */
+ (*val) = (cVal * (1 - blend_factor)) + (eVal * blend_factor);
+ }
+
+ break;
+ }
/* Those are handled in pose_slide_rest_pose_apply. */
case POSESLIDE_PUSH_REST:
case POSESLIDE_RELAX_REST: {
@@ -614,8 +634,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider));
}
- else {
- /* POSESLIDE_PUSH and POSESLIDE_RELAX. */
+ else if (pso->mode == POSESLIDE_PUSH || pso->mode == POSESLIDE_RELAX) {
float quat_breakdown[4];
float quat_curr[4];
@@ -638,6 +657,32 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl)
interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider));
}
}
+ else if (pso->mode == POSESLIDE_BLEND) {
+ float quat_blend[4];
+ float quat_curr[4];
+
+ copy_qt_qt(quat_curr, pchan->quat);
+
+ if (ED_slider_factor_get(pso->slider) < 0.5) {
+ quat_blend[0] = evaluate_fcurve(fcu_w, prevFrameF);
+ quat_blend[1] = evaluate_fcurve(fcu_x, prevFrameF);
+ quat_blend[2] = evaluate_fcurve(fcu_y, prevFrameF);
+ quat_blend[3] = evaluate_fcurve(fcu_z, prevFrameF);
+ }
+ else {
+ quat_blend[0] = evaluate_fcurve(fcu_w, nextFrameF);
+ quat_blend[1] = evaluate_fcurve(fcu_x, nextFrameF);
+ quat_blend[2] = evaluate_fcurve(fcu_y, nextFrameF);
+ quat_blend[3] = evaluate_fcurve(fcu_z, nextFrameF);
+ }
+
+ normalize_qt(quat_blend);
+ normalize_qt(quat_curr);
+
+ const float blend_factor = fabs((ED_slider_factor_get(pso->slider) - 0.5f) * 2);
+
+ interp_qt_qtqt(quat_final, quat_curr, quat_blend, blend_factor);
+ }
/* Apply final to the pose bone, keeping compatible for similar keyframe positions. */
quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat);
@@ -868,6 +913,9 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso)
case POSESLIDE_BREAKDOWN:
strcpy(mode_str, TIP_("Breakdown"));
break;
+ case POSESLIDE_BLEND:
+ strcpy(mode_str, TIP_("Blend To Neighbour"));
+ break;
default:
/* Unknown. */
@@ -1660,6 +1708,56 @@ void POSE_OT_breakdown(wmOperatorType *ot)
pose_slide_opdef_properties(ot);
}
+/* ........................ */
+static int pose_slide_blend_to_neighbours_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ /* Initialize data. */
+ if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) {
+ pose_slide_exit(C, op);
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Do common setup work. */
+ return pose_slide_invoke_common(C, op, event);
+}
+
+static int pose_slide_blend_to_neighbours_exec(bContext *C, wmOperator *op)
+{
+ tPoseSlideOp *pso;
+
+ /* Initialize data (from RNA-props). */
+ if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) {
+ pose_slide_exit(C, op);
+ return OPERATOR_CANCELLED;
+ }
+
+ pso = op->customdata;
+
+ /* Do common exec work. */
+ return pose_slide_exec_common(C, op, pso);
+}
+
+void POSE_OT_blend_to_neighbours(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Blend To Neighbour";
+ ot->idname = "POSE_OT_blend_to_neighbour";
+ ot->description = "Blend from current position to previous or next keyframe";
+
+ /* Callbacks. */
+ ot->exec = pose_slide_blend_to_neighbours_exec;
+ ot->invoke = pose_slide_blend_to_neighbours_invoke;
+ ot->modal = pose_slide_modal;
+ ot->cancel = pose_slide_cancel;
+ ot->poll = ED_operator_posemode;
+
+ /* Flags. */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X;
+
+ /* Properties. */
+ pose_slide_opdef_properties(ot);
+}
+
/* **************************************************** */
/* B) Pose Propagate */
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index fdd9f44605e..d7cab85abad 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -316,6 +316,9 @@ static void gpencil_stroke_pair_table(bContext *C,
if (ELEM(NULL, gps_from, gps_to)) {
continue;
}
+ if ((gps_from->totpoints == 0) || (gps_to->totpoints == 0)) {
+ continue;
+ }
/* Insert the pair entry in the hash table and the list of strokes to keep order. */
BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from));
BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to);
@@ -1333,6 +1336,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
if (ELEM(NULL, gps_from, gps_to)) {
continue;
}
+ if ((gps_from->totpoints == 0) || (gps_to->totpoints == 0)) {
+ continue;
+ }
/* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
if (gps_from->totpoints > gps_to->totpoints) {
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 2e7b0ce532c..58a9f362488 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -24,6 +24,8 @@
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
+#include "BLI_math_color.h"
+
#include "BKE_context.h"
#include "BKE_screen.h"
@@ -107,8 +109,13 @@ static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char
{
const uiFontStyle *fstyle = UI_FSTYLE_WIDGET;
- const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f};
- const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f};
+ /* Use the theme settings from tooltips. */
+ const bTheme *btheme = UI_GetTheme();
+ const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip;
+
+ float col_fg[4], col_bg[4];
+ rgba_uchar_to_float(col_fg, wcol->text);
+ rgba_uchar_to_float(col_bg, wcol->inner);
UI_fontstyle_draw_simple_backdrop(fstyle, x, y + U.widget_unit, name, col_fg, col_bg);
}
diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c
index 804156ba48c..6b1ff92a855 100644
--- a/source/blender/editors/interface/interface_style.c
+++ b/source/blender/editors/interface/interface_style.c
@@ -312,11 +312,8 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs,
const float decent = BLF_descender(fs->uifont_id);
const float margin = height / 4.0f;
- /* backdrop */
- const float color[4] = {col_bg[0], col_bg[1], col_bg[2], 0.5f};
-
UI_draw_roundbox_corner_set(UI_CNR_ALL);
- UI_draw_roundbox_aa(
+ UI_draw_roundbox_4fv(
&(const rctf){
.xmin = x - margin,
.xmax = x + width + margin,
@@ -325,7 +322,7 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs,
},
true,
margin,
- color);
+ col_bg);
}
BLF_position(fs->uifont_id, x, y, 0.0f);
diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c
index 672f1b64943..3a5d65475f7 100644
--- a/source/blender/editors/interface/interface_template_search_menu.c
+++ b/source/blender/editors/interface/interface_template_search_menu.c
@@ -350,24 +350,28 @@ static void menu_types_add_from_keymap_items(bContext *C,
if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) {
wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base;
- wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler);
- if (keymap && WM_keymap_poll(C, keymap)) {
- LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
- if (kmi->flag & KMI_INACTIVE) {
- continue;
- }
- if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) {
- char menu_idname[MAX_NAME];
- RNA_string_get(kmi->ptr, "name", menu_idname);
- MenuType *mt = WM_menutype_find(menu_idname, false);
-
- if (mt && BLI_gset_add(menu_tagged, mt)) {
- /* Unlikely, but possible this will be included twice. */
- BLI_linklist_prepend(menuid_stack_p, mt);
-
- void **kmi_p;
- if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) {
- *kmi_p = kmi;
+ wmEventHandler_KeymapResult km_result;
+ WM_event_get_keymaps_from_handler(wm, handler, &km_result);
+ for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) {
+ wmKeyMap *keymap = km_result.keymaps[km_index];
+ if (keymap && WM_keymap_poll(C, keymap)) {
+ LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) {
+ if (kmi->flag & KMI_INACTIVE) {
+ continue;
+ }
+ if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) {
+ char menu_idname[MAX_NAME];
+ RNA_string_get(kmi->ptr, "name", menu_idname);
+ MenuType *mt = WM_menutype_find(menu_idname, false);
+
+ if (mt && BLI_gset_add(menu_tagged, mt)) {
+ /* Unlikely, but possible this will be included twice. */
+ BLI_linklist_prepend(menuid_stack_p, mt);
+
+ void **kmi_p;
+ if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) {
+ *kmi_p = kmi;
+ }
}
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 0c9eb20af19..320371ad9ea 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -5823,6 +5823,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C)
icon = ICON_SEQUENCE;
break;
}
+ if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL)) {
+ handle_event = B_STOPSEQ;
+ icon = ICON_SEQUENCE;
+ break;
+ }
if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_BUILD_PROXY)) {
handle_event = B_STOPCLIP;
icon = ICON_TRACKER;
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index 1fd1b6c984d..4ef4c3dbc6d 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -147,6 +147,8 @@ static void view_pan_init(bContext *C, wmOperator *op)
const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1);
vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx;
vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy;
+
+ vpd->v2d->flag |= V2D_IS_NAVIGATING;
}
/* apply transform to view (i.e. adjust 'cur' rect) */
@@ -190,6 +192,8 @@ static void view_pan_apply(bContext *C, wmOperator *op)
/* Cleanup temp custom-data. */
static void view_pan_exit(wmOperator *op)
{
+ v2dViewPanData *vpd = op->customdata;
+ vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
MEM_SAFE_FREE(op->customdata);
}
@@ -358,6 +362,7 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event
View2DEdgePanData *vpd = op->customdata;
if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) {
+ vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
MEM_SAFE_FREE(op->customdata);
return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH);
}
@@ -371,6 +376,8 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event
static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op)
{
+ v2dViewPanData *vpd = op->customdata;
+ vpd->v2d->flag &= ~V2D_IS_NAVIGATING;
MEM_SAFE_FREE(op->customdata);
}
@@ -680,6 +687,8 @@ static void view_zoomdrag_init(bContext *C, wmOperator *op)
vzd->v2d = &vzd->region->v2d;
/* False by default. Interactive callbacks (ie invoke()) can set it to true. */
vzd->zoom_to_mouse_pos = false;
+
+ vzd->v2d->flag |= V2D_IS_NAVIGATING;
}
/* apply transform to view (i.e. adjust 'cur' rect) */
@@ -809,7 +818,8 @@ static void view_zoomstep_apply(bContext *C, wmOperator *op)
static void view_zoomstep_exit(wmOperator *op)
{
UI_view2d_zoom_cache_reset();
-
+ v2dViewZoomData *vzd = op->customdata;
+ vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
MEM_SAFE_FREE(op->customdata);
}
@@ -1041,6 +1051,7 @@ static void view_zoomdrag_exit(bContext *C, wmOperator *op)
if (op->customdata) {
v2dViewZoomData *vzd = op->customdata;
+ vzd->v2d->flag &= ~V2D_IS_NAVIGATING;
if (vzd->timer) {
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), vzd->timer);
@@ -1911,6 +1922,8 @@ static void scroller_activate_init(bContext *C,
vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin;
}
+ vsm->v2d->flag |= V2D_IS_NAVIGATING;
+
ED_region_tag_redraw_no_rebuild(region);
}
@@ -1921,6 +1934,7 @@ static void scroller_activate_exit(bContext *C, wmOperator *op)
v2dScrollerMove *vsm = op->customdata;
vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE | V2D_SCROLL_V_ACTIVE);
+ vsm->v2d->flag &= ~V2D_IS_NAVIGATING;
MEM_freeN(op->customdata);
op->customdata = NULL;
diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
index ae37d6c8deb..5faafa77bba 100644
--- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
+++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c
@@ -461,7 +461,7 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt)
gzgt->name = "Mesh Spin Init";
gzgt->idname = "MESH_GGT_spin";
- gzgt->flag = WM_GIZMOGROUPTYPE_3D;
+ gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@@ -1063,7 +1063,7 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt)
gzgt->name = "Mesh Spin Redo";
gzgt->idname = "MESH_GGT_spin_redo";
- gzgt->flag = WM_GIZMOGROUPTYPE_3D;
+ gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index 0a2df655395..26f5b21a311 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -412,6 +412,7 @@ static bool is_noncolor_pass(eScenePassType pass_type)
{
return ELEM(pass_type,
SCE_PASS_Z,
+ SCE_PASS_POSITION,
SCE_PASS_NORMAL,
SCE_PASS_VECTOR,
SCE_PASS_INDEXOB,
@@ -554,19 +555,10 @@ static bool bake_pass_filter_check(eScenePassType pass_type,
return true;
}
- if ((pass_filter & R_BAKE_PASS_FILTER_AO) != 0) {
- BKE_report(
- reports,
- RPT_ERROR,
- "Combined bake pass Ambient Occlusion contribution requires an enabled light pass "
- "(bake the Ambient Occlusion pass type instead)");
- }
- else {
- BKE_report(reports,
- RPT_ERROR,
- "Combined bake pass requires Emit, or a light pass with "
- "Direct or Indirect contributions enabled");
- }
+ BKE_report(reports,
+ RPT_ERROR,
+ "Combined bake pass requires Emit, or a light pass with "
+ "Direct or Indirect contributions enabled");
return false;
}
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 95351de45f0..81aecfdf788 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -479,15 +479,6 @@ static Scene *preview_prepare_scene(
BKE_color_managed_view_settings_free(&sce->view_settings);
BKE_color_managed_view_settings_copy(&sce->view_settings, &scene->view_settings);
- /* prevent overhead for small renders and icons (32) */
- if (id && sp->sizex < 40) {
- sce->r.tilex = sce->r.tiley = 64;
- }
- else {
- sce->r.tilex = sce->r.xsch / 4;
- sce->r.tiley = sce->r.ysch / 4;
- }
-
if ((id && sp->pr_method == PR_ICON_RENDER) && id_type != ID_WO) {
sce->r.alphamode = R_ALPHAPREMUL;
}
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 9546035375c..c71e68df2fd 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1735,10 +1735,14 @@ static void ed_default_handlers(
WM_event_add_keymap_handler(handlers, keymap);
}
if (flag & ED_KEYMAP_TOOL) {
- WM_event_add_keymap_handler_dynamic(
- &region->handlers, WM_event_get_keymap_from_toolsystem_fallback, area);
- WM_event_add_keymap_handler_dynamic(
- &region->handlers, WM_event_get_keymap_from_toolsystem, area);
+ if (flag & ED_KEYMAP_GIZMO) {
+ WM_event_add_keymap_handler_dynamic(
+ &region->handlers, WM_event_get_keymap_from_toolsystem_fallback, area);
+ }
+ else {
+ WM_event_add_keymap_handler_dynamic(
+ &region->handlers, WM_event_get_keymap_from_toolsystem, area);
+ }
}
if (flag & ED_KEYMAP_FRAMES) {
/* frame changing/jumping (for all spaces) */
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 11b06d2b414..f7bdb4326a5 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -1271,7 +1271,7 @@ void file_params_rename_end(wmWindowManager *wm,
/* Ensure smooth-scroll timer is active, even if not needed, because that way rename state is
* handled properly. */
file_params_invoke_rename_postscroll(wm, win, sfile);
- /* Also always activate the rename file, even if renaming was cancelled. */
+ /* Also always activate the rename file, even if renaming was canceled. */
file_params_renamefile_activate(sfile, params);
}
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index aa241071425..10a3285be8b 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -1446,6 +1446,8 @@ static int node_error_type_to_icon(const geo_log::NodeWarningType type)
return ICON_ERROR;
case geo_log::NodeWarningType::Info:
return ICON_INFO;
+ case geo_log::NodeWarningType::Legacy:
+ return ICON_ERROR;
}
BLI_assert(false);
@@ -1456,6 +1458,8 @@ static uint8_t node_error_type_priority(const geo_log::NodeWarningType type)
{
switch (type) {
case geo_log::NodeWarningType::Error:
+ return 4;
+ case geo_log::NodeWarningType::Legacy:
return 3;
case geo_log::NodeWarningType::Warning:
return 2;
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index d35fd729131..f069038cc09 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -175,6 +175,7 @@ int space_node_view_flag(struct bContext *C,
void NODE_OT_view_all(struct wmOperatorType *ot);
void NODE_OT_view_selected(struct wmOperatorType *ot);
+void NODE_OT_geometry_node_view_legacy(struct wmOperatorType *ot);
void NODE_OT_backimage_move(struct wmOperatorType *ot);
void NODE_OT_backimage_zoom(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c
index 610c2889e7a..df4f63af20b 100644
--- a/source/blender/editors/space_node/node_ops.c
+++ b/source/blender/editors/space_node/node_ops.c
@@ -51,6 +51,7 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_view_all);
WM_operatortype_append(NODE_OT_view_selected);
+ WM_operatortype_append(NODE_OT_geometry_node_view_legacy);
WM_operatortype_append(NODE_OT_mute_toggle);
WM_operatortype_append(NODE_OT_hide_toggle);
diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc
index f0db0539c4f..762b4b36a39 100644
--- a/source/blender/editors/space_node/node_view.cc
+++ b/source/blender/editors/space_node/node_view.cc
@@ -23,8 +23,10 @@
#include "DNA_node_types.h"
+#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
+#include "BLI_string_ref.hh"
#include "BLI_utildefines.h"
#include "BKE_context.h"
@@ -54,6 +56,8 @@
#include "node_intern.h" /* own include */
+using blender::StringRef;
+
/* -------------------------------------------------------------------- */
/** \name View All Operator
* \{ */
@@ -700,3 +704,89 @@ void NODE_OT_backimage_sample(wmOperatorType *ot)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name View Geometry Nodes Legacy Operator
+ *
+ * This operator should be removed when the 2.93 legacy nodes are removed.
+ * \{ */
+
+static int space_node_view_geometry_nodes_legacy(bContext *C, SpaceNode *snode, wmOperator *op)
+{
+ ARegion *region = CTX_wm_region(C);
+
+ /* Only use the node editor's active node tree. Otherwise this will be too complicated. */
+ bNodeTree *node_tree = snode->nodetree;
+ if (node_tree == nullptr || node_tree->type != NTREE_GEOMETRY) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bool found_legacy_node = false;
+ LISTBASE_FOREACH_BACKWARD (bNode *, node, &node_tree->nodes) {
+ StringRef idname{node->idname};
+ if (idname.find("Legacy") == StringRef::not_found) {
+ node->flag &= ~NODE_SELECT;
+ }
+ else {
+ found_legacy_node = true;
+ node->flag |= NODE_SELECT;
+ }
+ }
+
+ if (!found_legacy_node) {
+ WM_report(RPT_INFO, "Legacy node not found, may be in nested node group");
+ }
+
+ const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
+ if (space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx)) {
+ return OPERATOR_FINISHED;
+ }
+ return OPERATOR_CANCELLED;
+}
+
+static int geometry_node_view_legacy_exec(bContext *C, wmOperator *op)
+{
+ /* Allow running this operator directly in a specific node editor. */
+ if (SpaceNode *snode = CTX_wm_space_node(C)) {
+ return space_node_view_geometry_nodes_legacy(C, snode, op);
+ }
+
+ /* Since the operator is meant to be called from a button in the modifier panel, the node tree
+ * must be found from the screen, using the largest node editor if there is more than one. */
+ if (ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0)) {
+ if (SpaceNode *snode = static_cast<SpaceNode *>(area->spacedata.first)) {
+ ScrArea *old_area = CTX_wm_area(C);
+ ARegion *old_region = CTX_wm_region(C);
+
+ /* Override the context since it is used by the View2D panning code. */
+ CTX_wm_area_set(C, area);
+ CTX_wm_region_set(C, static_cast<ARegion *>(area->regionbase.last));
+ const int result = space_node_view_geometry_nodes_legacy(C, snode, op);
+ CTX_wm_area_set(C, old_area);
+ CTX_wm_region_set(C, old_region);
+ return result;
+ }
+ }
+
+ return OPERATOR_CANCELLED;
+}
+
+static bool geometry_node_view_legacy_poll(bContext *C)
+{
+ /* Allow direct execution in a node editor, but also affecting any visible node editor. */
+ return ED_operator_node_active(C) || BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0);
+}
+
+void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot)
+{
+ ot->name = "View Deprecated Geometry Nodes";
+ ot->idname = "NODE_OT_geometry_node_view_legacy";
+ ot->description = "Select and view legacy geometry nodes in the node editor";
+
+ ot->exec = geometry_node_view_legacy_exec;
+ ot->poll = geometry_node_view_legacy_poll;
+
+ ot->flag = OPTYPE_INTERNAL;
+}
+
+/** \} */
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index c06a1010168..7cdfb553da5 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -2358,7 +2358,10 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te)
case eGpencilModifierType_Texture:
data.icon = ICON_TEXTURE;
break;
- case eGpencilModifierType_Weight:
+ case eGpencilModifierType_WeightProximity:
+ data.icon = ICON_MOD_VERTEX_WEIGHT;
+ break;
+ case eGpencilModifierType_WeightAngle:
data.icon = ICON_MOD_VERTEX_WEIGHT;
break;
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index c5ec656080a..5427ae31ac3 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -1864,6 +1864,15 @@ static void outliner_filter_tree(SpaceOutliner *space_outliner, ViewLayer *view_
space_outliner, view_layer, &space_outliner->tree, search_string, exclude_filter);
}
+static void outliner_clear_newid_from_main(Main *bmain)
+{
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ id_iter->newid = NULL;
+ }
+ FOREACH_MAIN_ID_END;
+}
+
/* ======================================================= */
/* Main Tree Building API */
@@ -1926,5 +1935,7 @@ void outliner_build_tree(Main *mainvar,
outliner_filter_tree(space_outliner, view_layer);
outliner_restore_scrolling_position(space_outliner, region, &focus);
- BKE_main_id_newptr_and_tag_clear(mainvar);
+ /* `ID.newid` pointer is abused when building tree, DO NOT call #BKE_main_id_newptr_and_tag_clear
+ * as this expects valid IDs in this pointer, not random unknown data. */
+ outliner_clear_newid_from_main(mainvar);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index 5b39feacfe3..53f1c35776c 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -25,6 +25,7 @@
#include <string.h>
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_string_utils.h"
#include "BLI_threads.h"
@@ -44,6 +45,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
+#include "BKE_main.h"
#include "BKE_scene.h"
#include "BKE_sound.h"
@@ -71,6 +73,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"
@@ -1282,6 +1285,526 @@ static void draw_seq_fcurve_overlay(
}
}
+typedef struct ThumbnailDrawJob {
+ SeqRenderData context;
+ GHash *sequences_ghash;
+ Scene *scene;
+ rctf *view_area;
+ float pixelx;
+ float pixely;
+} ThumbnailDrawJob;
+
+typedef struct ThumbDataItem {
+ Sequence *seq_dupli;
+ Scene *scene;
+} ThumbDataItem;
+
+static void thumbnail_hash_data_free(void *val)
+{
+ ThumbDataItem *item = val;
+ SEQ_sequence_free(item->scene, item->seq_dupli, 0);
+ MEM_freeN(val);
+}
+
+static void thumbnail_freejob(void *data)
+{
+ ThumbnailDrawJob *tj = data;
+ BLI_ghash_free(tj->sequences_ghash, NULL, thumbnail_hash_data_free);
+ MEM_freeN(tj->view_area);
+ MEM_freeN(tj);
+}
+
+static void thumbnail_endjob(void *data)
+{
+ ThumbnailDrawJob *tj = data;
+ WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, tj->scene);
+}
+
+static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area)
+{
+ if (seq->type != SEQ_TYPE_MOVIE && seq->type != SEQ_TYPE_IMAGE) {
+ return false;
+ }
+ if (min_ii(seq->startdisp, seq->start) > view_area->xmax) {
+ return false;
+ }
+ if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) {
+ return false;
+ }
+ if (seq->machine + 1.0f < view_area->ymin) {
+ return false;
+ }
+ if (seq->machine > view_area->ymax) {
+ return false;
+ }
+
+ return true;
+}
+
+static void seq_get_thumb_image_dimensions(Sequence *seq,
+ float pixelx,
+ float pixely,
+ float *r_thumb_width,
+ float *r_thumb_height,
+ float *r_image_width,
+ float *r_image_height)
+{
+ float image_width = seq->strip->stripdata->orig_width;
+ float image_height = seq->strip->stripdata->orig_height;
+
+ /* Fix the dimensions to be max SEQ_RENDER_THUMB_SIZE (256) for x or y. */
+ float aspect_ratio = (float)image_width / image_height;
+ if (image_width > image_height) {
+ image_width = SEQ_RENDER_THUMB_SIZE;
+ image_height = round_fl_to_int(image_width / aspect_ratio);
+ }
+ else {
+ image_height = SEQ_RENDER_THUMB_SIZE;
+ image_width = round_fl_to_int(image_height * aspect_ratio);
+ }
+
+ /* Calculate thumb dimensions. */
+ float thumb_height = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) - (20 * U.dpi_fac * pixely);
+ aspect_ratio = ((float)image_width) / image_height;
+ float thumb_h_px = thumb_height / pixely;
+ float thumb_width = aspect_ratio * thumb_h_px * pixelx;
+
+ if (r_thumb_height == NULL) {
+ *r_thumb_width = thumb_width;
+ return;
+ }
+
+ *r_thumb_height = thumb_height;
+ *r_image_width = image_width;
+ *r_image_height = image_height;
+ *r_thumb_width = thumb_width;
+}
+
+static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf *view_area)
+{
+ if (seq->start > view_area->xmin && seq->start < view_area->xmax) {
+ return seq->start;
+ }
+
+ /* Drawing and caching both check to see if strip is in view area or not before calling this
+ * function so assuming strip/part of strip in view. */
+
+ int no_invisible_thumbs = (view_area->xmin - seq->start) / frame_step;
+ return ((no_invisible_thumbs - 1) * frame_step) + seq->start;
+}
+
+static void thumbnail_start_job(void *data,
+ short *stop,
+ short *UNUSED(do_update),
+ float *UNUSED(progress))
+{
+ ThumbnailDrawJob *tj = data;
+ float start_frame, frame_step;
+
+ GHashIterator gh_iter;
+ BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash);
+ while (!BLI_ghashIterator_done(&gh_iter) & !*stop) {
+ Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter);
+ ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig);
+
+ if (check_seq_need_thumbnails(seq_orig, tj->view_area)) {
+ seq_get_thumb_image_dimensions(
+ val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL);
+ start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area);
+ SEQ_render_thumbnails(
+ &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop);
+ SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop);
+ }
+ BLI_ghashIterator_step(&gh_iter);
+ }
+}
+
+static SeqRenderData sequencer_thumbnail_context_init(const bContext *C)
+{
+ struct Main *bmain = CTX_data_main(C);
+ struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Scene *scene = CTX_data_scene(C);
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ SeqRenderData context = {0};
+
+ /* Taking rectx and recty as 0 as dimensions not known here, and context is used to calculate
+ * hash key but not necessary as other variables of SeqRenderData are unique enough. */
+ SEQ_render_new_render_data(bmain, depsgraph, scene, 0, 0, sseq->render_size, false, &context);
+ context.view_id = BKE_scene_multiview_view_id_get(&scene->r, STEREO_LEFT_NAME);
+ context.use_proxies = false;
+
+ return context;
+}
+
+static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Editing *ed)
+{
+ Scene *scene = CTX_data_scene(C);
+
+ /* Set the data for thumbnail caching job. */
+ GHash *thumb_data_hash = BLI_ghash_ptr_new("seq_duplicates_and_origs");
+
+ LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) {
+ ThumbDataItem *val_need_update = BLI_ghash_lookup(thumb_data_hash, seq);
+ if (val_need_update == NULL && check_seq_need_thumbnails(seq, &v2d->cur)) {
+ ThumbDataItem *val = MEM_callocN(sizeof(ThumbDataItem), "Thumbnail Hash Values");
+ val->seq_dupli = SEQ_sequence_dupli_recursive(scene, scene, NULL, seq, 0);
+ val->scene = scene;
+ BLI_ghash_insert(thumb_data_hash, seq, val);
+ }
+ else {
+ if (val_need_update != NULL) {
+ val_need_update->seq_dupli->start = seq->start;
+ val_need_update->seq_dupli->startdisp = seq->startdisp;
+ }
+ }
+ }
+
+ return thumb_data_hash;
+}
+
+static void sequencer_thumbnail_init_job(const bContext *C, View2D *v2d, Editing *ed)
+{
+ wmJob *wm_job;
+ ThumbnailDrawJob *tj = NULL;
+ ScrArea *area = CTX_wm_area(C);
+ wm_job = WM_jobs_get(CTX_wm_manager(C),
+ CTX_wm_window(C),
+ CTX_data_scene(C),
+ "Draw Thumbnails",
+ 0,
+ WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL);
+
+ /* Get the thumbnail job if it exists. */
+ tj = WM_jobs_customdata_get(wm_job);
+ if (!tj) {
+ tj = MEM_callocN(sizeof(ThumbnailDrawJob), "Thumbnail cache job");
+
+ /* Duplicate value of v2d->cur and v2d->tot to have module separation. */
+ rctf *view_area = MEM_callocN(sizeof(struct rctf), "viewport area");
+ view_area->xmax = v2d->cur.xmax;
+ view_area->xmin = v2d->cur.xmin;
+ view_area->ymax = v2d->cur.ymax;
+ view_area->ymin = v2d->cur.ymin;
+
+ tj->scene = CTX_data_scene(C);
+ tj->view_area = view_area;
+ tj->context = sequencer_thumbnail_context_init(C);
+ tj->sequences_ghash = sequencer_thumbnail_ghash_init(C, v2d, ed);
+ tj->pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask);
+ tj->pixely = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask);
+ WM_jobs_customdata_set(wm_job, tj, thumbnail_freejob);
+ WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_SEQUENCER, NC_SCENE | ND_SEQUENCER);
+ WM_jobs_callbacks(wm_job, thumbnail_start_job, NULL, NULL, thumbnail_endjob);
+ }
+
+ if (!WM_jobs_is_running(wm_job)) {
+ G.is_break = false;
+ WM_jobs_start(CTX_wm_manager(C), wm_job);
+ }
+ else {
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
+ }
+
+ ED_area_tag_redraw(area);
+}
+
+static bool sequencer_thumbnail_v2d_is_navigating(const bContext *C)
+{
+ ARegion *region = CTX_wm_region(C);
+ View2D *v2d = &region->v2d;
+ return (v2d->flag & V2D_IS_NAVIGATING) != 0;
+}
+
+static void sequencer_thumbnail_start_job_if_necessary(const bContext *C,
+ Editing *ed,
+ View2D *v2d,
+ bool thumbnail_is_missing)
+{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+
+ if (sequencer_thumbnail_v2d_is_navigating(C)) {
+ WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL);
+ return;
+ }
+
+ /* `thumbnail_is_missing` should be set to true if missing image in strip. False when normal call
+ * to all strips done. */
+ if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax ||
+ v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax || thumbnail_is_missing) {
+
+ /* Stop the job first as view has changed. Pointless to continue old job. */
+ if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax ||
+ v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) {
+ WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job);
+ }
+
+ sequencer_thumbnail_init_job(C, v2d, ed);
+ sseq->runtime.last_thumbnail_area = v2d->cur;
+ }
+}
+
+void last_displayed_thumbnails_list_free(void *val)
+{
+ BLI_gset_free(val, NULL);
+}
+
+static GSet *last_displayed_thumbnails_list_ensure(const bContext *C, Sequence *seq)
+{
+ SpaceSeq *sseq = CTX_wm_space_seq(C);
+ if (sseq->runtime.last_displayed_thumbnails == NULL) {
+ sseq->runtime.last_displayed_thumbnails = BLI_ghash_ptr_new(__func__);
+ }
+
+ GSet *displayed_thumbnails = BLI_ghash_lookup(sseq->runtime.last_displayed_thumbnails, seq);
+ if (displayed_thumbnails == NULL) {
+ displayed_thumbnails = BLI_gset_int_new(__func__);
+ BLI_ghash_insert(sseq->runtime.last_displayed_thumbnails, seq, displayed_thumbnails);
+ }
+
+ return displayed_thumbnails;
+}
+
+static void last_displayed_thumbnails_list_cleanup(GSet *previously_displayed,
+ float range_start,
+ float range_end)
+{
+ GSetIterator gset_iter;
+ BLI_gsetIterator_init(&gset_iter, previously_displayed);
+ while (!BLI_gsetIterator_done(&gset_iter)) {
+ int frame = (float)POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter));
+ BLI_gsetIterator_step(&gset_iter);
+
+ if (frame > range_start && frame < range_end) {
+ BLI_gset_remove(previously_displayed, POINTER_FROM_INT(frame), NULL);
+ }
+ }
+}
+
+static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame,
+ GSet *previously_displayed)
+{
+ int best_diff = INT_MAX;
+ int best_frame = timeline_frame;
+
+ /* Previously displayed thumbnails. */
+ GSetIterator gset_iter;
+ BLI_gsetIterator_init(&gset_iter, previously_displayed);
+ while (!BLI_gsetIterator_done(&gset_iter)) {
+ int frame = POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter));
+ int diff = abs(frame - timeline_frame);
+ if (diff < best_diff) {
+ best_diff = diff;
+ best_frame = frame;
+ }
+ BLI_gsetIterator_step(&gset_iter);
+ }
+ return best_frame;
+}
+
+static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame)
+{
+ if (timeline_frame <= seq->startdisp) {
+ return seq->startdisp;
+ }
+
+ /* Set of "guaranteed" thumbnails. */
+ const int frame_index = timeline_frame - seq->startdisp;
+ const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq);
+ const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step;
+ const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp;
+ return nearest_guaranted_absolute_frame;
+}
+
+static ImBuf *sequencer_thumbnail_closest_from_memory(const SeqRenderData *context,
+ Sequence *seq,
+ int timeline_frame,
+ GSet *previously_displayed,
+ rcti *crop,
+ bool clipped)
+{
+ int frame_previous = sequencer_thumbnail_closest_previous_frame_get(timeline_frame,
+ previously_displayed);
+ ImBuf *ibuf_previous = SEQ_get_thumbnail(context, seq, frame_previous, crop, clipped);
+
+ int frame_guaranteed = sequencer_thumbnail_closest_guaranteed_frame_get(seq, timeline_frame);
+ ImBuf *ibuf_guaranteed = SEQ_get_thumbnail(context, seq, frame_guaranteed, crop, clipped);
+
+ ImBuf *closest_in_memory = NULL;
+
+ if (ibuf_previous && ibuf_guaranteed) {
+ if (abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) {
+ IMB_freeImBuf(ibuf_guaranteed);
+ closest_in_memory = ibuf_previous;
+ }
+ else {
+ IMB_freeImBuf(ibuf_previous);
+ closest_in_memory = ibuf_guaranteed;
+ }
+ }
+
+ if (ibuf_previous == NULL) {
+ closest_in_memory = ibuf_guaranteed;
+ }
+
+ if (ibuf_guaranteed == NULL) {
+ closest_in_memory = ibuf_previous;
+ }
+
+ return closest_in_memory;
+}
+
+static void draw_seq_strip_thumbnail(View2D *v2d,
+ const bContext *C,
+ Scene *scene,
+ Sequence *seq,
+ float y1,
+ float y2,
+ float pixelx,
+ float pixely)
+{
+ bool clipped = false;
+ float image_height, image_width, thumb_width, thumb_height;
+ rcti crop;
+
+ /* If width of the strip too small ignore drawing thumbnails. */
+ if ((y2 - y1) / pixely <= 40 * U.dpi_fac) {
+ return;
+ }
+
+ SeqRenderData context = sequencer_thumbnail_context_init(C);
+
+ if ((seq->flag & SEQ_FLAG_SKIP_THUMBNAILS) != 0) {
+ return;
+ }
+
+ seq_get_thumb_image_dimensions(
+ seq, pixelx, pixely, &thumb_width, &thumb_height, &image_width, &image_height);
+
+ float thumb_y_end = y1 + thumb_height - pixely;
+
+ float cut_off = 0;
+ float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
+ if (seq->type == SEQ_TYPE_IMAGE) {
+ upper_thumb_bound = seq->enddisp;
+ }
+
+ float thumb_x_start = seq_thumbnail_get_start_frame(seq, thumb_width, &v2d->cur);
+ float thumb_x_end;
+
+ while (thumb_x_start + thumb_width < v2d->cur.xmin) {
+ thumb_x_start += thumb_width;
+ }
+
+ /* Ignore thumbs to the left of strip. */
+ while (thumb_x_start + thumb_width < seq->startdisp) {
+ thumb_x_start += thumb_width;
+ }
+
+ GSet *last_displayed_thumbnails = last_displayed_thumbnails_list_ensure(C, seq);
+ /* Cleanup thumbnail list outside of rendered range, which is cleaned up one by one to prevent
+ * flickering after zooming. */
+ if (!sequencer_thumbnail_v2d_is_navigating(C)) {
+ last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, thumb_x_start);
+ }
+
+ /* Start drawing. */
+ while (thumb_x_start < upper_thumb_bound) {
+ thumb_x_end = thumb_x_start + thumb_width;
+ clipped = false;
+
+ /* Checks to make sure that thumbs are loaded only when in view and within the confines of the
+ * strip. Some may not be required but better to have conditions for safety as x1 here is
+ * point to start caching from and not drawing. */
+ if (thumb_x_start > v2d->cur.xmax) {
+ break;
+ }
+
+ /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */
+ if (IN_RANGE_INCL(seq->startdisp, thumb_x_start, thumb_x_end)) {
+ cut_off = seq->startdisp - thumb_x_start;
+ clipped = true;
+ }
+
+ /* Clip if full thumbnail cannot be displayed. */
+ if (thumb_x_end > (upper_thumb_bound)) {
+ thumb_x_end = upper_thumb_bound;
+ clipped = true;
+ if (thumb_x_end - thumb_x_start < 1) {
+ break;
+ }
+ }
+
+ float zoom_x = thumb_width / image_width;
+ float zoom_y = thumb_height / image_height;
+
+ float cropx_min = (cut_off / pixelx) / (zoom_y / pixely);
+ float cropx_max = ((thumb_x_end - thumb_x_start) / pixelx) / (zoom_y / pixely);
+ if (cropx_max == (thumb_x_end - thumb_x_start)) {
+ cropx_max = cropx_max + 1;
+ }
+ BLI_rcti_init(&crop, (int)(cropx_min), (int)cropx_max, 0, (int)(image_height)-1);
+
+ int timeline_frame = round_fl_to_int(thumb_x_start);
+
+ /* Get the image. */
+ ImBuf *ibuf = SEQ_get_thumbnail(&context, seq, timeline_frame, &crop, clipped);
+
+ if (!ibuf) {
+ sequencer_thumbnail_start_job_if_necessary(C, scene->ed, v2d, true);
+
+ ibuf = sequencer_thumbnail_closest_from_memory(
+ &context, seq, timeline_frame, last_displayed_thumbnails, &crop, clipped);
+ }
+ /* Store recently rendered frames, so they can be reused when zooming. */
+ else if (!sequencer_thumbnail_v2d_is_navigating(C)) {
+ /* Clear images in frame range occupied by new thumbnail. */
+ last_displayed_thumbnails_list_cleanup(
+ last_displayed_thumbnails, thumb_x_start, thumb_x_end);
+ /* Insert new thumbnail frame to list. */
+ BLI_gset_add(last_displayed_thumbnails, POINTER_FROM_INT(timeline_frame));
+ }
+
+ /* If there is no image still, abort. */
+ if (!ibuf) {
+ break;
+ }
+
+ /* Transparency on overlap. */
+ if (seq->flag & SEQ_OVERLAP) {
+ GPU_blend(GPU_BLEND_ALPHA);
+ if (ibuf->rect) {
+ unsigned char *buf = (unsigned char *)ibuf->rect;
+ for (int pixel = ibuf->x * ibuf->y; pixel--; buf += 4) {
+ buf[3] = OVERLAP_ALPHA;
+ }
+ }
+ else if (ibuf->rect_float) {
+ float *buf = (float *)ibuf->rect_float;
+ for (int pixel = ibuf->x * ibuf->y; pixel--; buf += ibuf->channels) {
+ buf[3] = (OVERLAP_ALPHA / 255.0f);
+ }
+ }
+ }
+
+ ED_draw_imbuf_ctx_clipping(C,
+ ibuf,
+ thumb_x_start + cut_off,
+ y1,
+ true,
+ thumb_x_start + cut_off,
+ y1,
+ thumb_x_end,
+ thumb_y_end,
+ zoom_x,
+ zoom_y);
+ IMB_freeImBuf(ibuf);
+ GPU_blend(GPU_BLEND_NONE);
+ cut_off = 0;
+ thumb_x_start += thumb_width;
+ }
+ last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, thumb_x_start, FLT_MAX);
+}
+
/* Draw visible strips. Bounds check are already made. */
static void draw_seq_strip(const bContext *C,
SpaceSeq *sseq,
@@ -1356,6 +1879,12 @@ static void draw_seq_strip(const bContext *C,
}
if ((sseq->flag & SEQ_SHOW_OVERLAY) &&
+ (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_THUMBNAILS) &&
+ (seq->type == SEQ_TYPE_MOVIE || seq->type == SEQ_TYPE_IMAGE)) {
+ draw_seq_strip_thumbnail(v2d, C, scene, seq, y1, y2, pixelx, pixely);
+ }
+
+ if ((sseq->flag & SEQ_SHOW_OVERLAY) &&
(sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_FCURVES)) {
draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx);
}
@@ -2056,6 +2585,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 +2719,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_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index b95b7fa0620..9f21fc0676c 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -579,7 +579,6 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve
static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
{
/* Only data types supported for now. */
- Editing *ed = SEQ_editing_get(scene);
bool changed = false;
/* Iterate in reverse so meta-strips are iterated after their children. */
@@ -633,7 +632,10 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset)
}
}
if (changed) {
- SEQ_relations_free_imbuf(scene, &ed->seqbase, false);
+ for (int i = data->num_seq - 1; i >= 0; i--) {
+ Sequence *seq = data->seq_array[i];
+ SEQ_relations_invalidate_cache_preprocessed(scene, seq);
+ }
}
return changed;
}
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index 767ac76efe6..5b5c381509f 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -67,6 +67,7 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain,
int timeline_frame,
int frame_ofs,
const char *viewname);
+void last_displayed_thumbnails_list_free(void *val);
/* sequencer_edit.c */
struct View2D;
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..99b75f82922 100644
--- a/source/blender/editors/space_sequencer/space_sequencer.c
+++ b/source/blender/editors/space_sequencer/space_sequencer.c
@@ -32,6 +32,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
#include "BLI_utildefines.h"
#include "BKE_context.h"
@@ -42,6 +43,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 +100,14 @@ 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;
+
+ BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f);
+ sseq->runtime.last_displayed_thumbnails = NULL;
/* Tool header. */
region = MEM_callocN(sizeof(ARegion), "tool header for sequencer");
@@ -172,7 +178,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
region->v2d.cur = region->v2d.tot;
region->v2d.min[0] = 10.0f;
- region->v2d.min[1] = 0.5f;
+ region->v2d.min[1] = 4.0f;
region->v2d.max[0] = MAXFRAMEF;
region->v2d.max[1] = MAXSEQ;
@@ -186,6 +192,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce
region->v2d.keeptot = 0;
region->v2d.align = V2D_ALIGN_NO_NEG_Y;
+ sseq->runtime.last_displayed_thumbnails = NULL;
+
return (SpaceLink *)sseq;
}
@@ -216,6 +224,12 @@ static void sequencer_free(SpaceLink *sl)
if (scopes->histogram_ibuf) {
IMB_freeImBuf(scopes->histogram_ibuf);
}
+
+ if (sseq->runtime.last_displayed_thumbnails) {
+ BLI_ghash_free(
+ sseq->runtime.last_displayed_thumbnails, NULL, last_displayed_thumbnails_list_free);
+ sseq->runtime.last_displayed_thumbnails = NULL;
+ }
}
/* Spacetype init callback. */
@@ -330,6 +344,7 @@ static SpaceLink *sequencer_duplicate(SpaceLink *sl)
/* XXX sseq->gpd = gpencil_data_duplicate(sseq->gpd, false); */
memset(&sseqn->scopes, 0, sizeof(sseqn->scopes));
+ memset(&sseqn->runtime, 0, sizeof(sseqn->runtime));
return (SpaceLink *)sseqn;
}
@@ -481,11 +496,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 +818,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/space_view3d/view3d_gizmo_preselect.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c
index 441182d7a5f..918ecb14752 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c
@@ -58,7 +58,7 @@ void VIEW3D_GGT_mesh_preselect_elem(wmGizmoGroupType *gzgt)
gzgt->name = "Mesh Preselect Element";
gzgt->idname = "VIEW3D_GGT_mesh_preselect_elem";
- gzgt->flag = WM_GIZMOGROUPTYPE_3D;
+ gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
@@ -95,7 +95,7 @@ void VIEW3D_GGT_mesh_preselect_edgering(wmGizmoGroupType *gzgt)
gzgt->name = "Mesh Preselect Edge Ring";
gzgt->idname = "VIEW3D_GGT_mesh_preselect_edgering";
- gzgt->flag = WM_GIZMOGROUPTYPE_3D;
+ gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D;
gzgt->gzmap_params.spaceid = SPACE_VIEW3D;
gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW;
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index 3f572bf9d5a..39aed131ea1 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -2813,7 +2813,9 @@ static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *even
{
RNA_int_set_array(op->ptr, "location", event->mval);
- return view3d_select_exec(C, op);
+ const int retval = view3d_select_exec(C, op);
+
+ return WM_operator_flag_only_pass_through_on_press(retval, event);
}
void VIEW3D_OT_select(wmOperatorType *ot)
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 = &region->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/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index c0ccf1b7095..86390882bed 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -2122,7 +2122,9 @@ static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
RNA_float_set_array(op->ptr, "location", co);
- return uv_select_exec(C, op);
+ const int retval = uv_select_exec(C, op);
+
+ return WM_operator_flag_only_pass_through_on_press(retval, event);
}
void UV_OT_select(wmOperatorType *ot)
@@ -2281,7 +2283,9 @@ static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *eve
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
RNA_float_set_array(op->ptr, "location", co);
- return uv_select_loop_exec(C, op);
+ const int retval = uv_select_loop_exec(C, op);
+
+ return WM_operator_flag_only_pass_through_on_press(retval, event);
}
void UV_OT_select_loop(wmOperatorType *ot)
@@ -2341,7 +2345,9 @@ static int uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
RNA_float_set_array(op->ptr, "location", co);
- return uv_select_edge_ring_exec(C, op);
+ const int retval = uv_select_edge_ring_exec(C, op);
+
+ return WM_operator_flag_only_pass_through_on_press(retval, event);
}
void UV_OT_select_edge_ring(wmOperatorType *ot)