VSE: Improve motion-picture workflow

This commit resolves problem introduced in e1665c3d31 - it was
difficult to import media at their original resolution.
This is done by using original resolution as reference for scale.

All crop and strip transform values and their animation is converted
form old files.

To make both workflows easy to use, sequencer tool settings have been
created with preset for preffered scaling method. This setting is in
sequencer timeline header and add image or movie strip operator
properties.

Two new operators have been added:
`sequencer.strip_transform_fit` operator with 3 options: Scale To Fit,
Scale to Fill and Stretch To Fill.
Operator can fail if strip image or video is not loaded currently, this
case should be either sanitized or data loaded on demand.

`sequencer.strip_transform_clear` operator with 4 options:
Clear position, scale, rotation and all (previous 3 options combined).

Reviewed By: sergey, fsiddi

Differential Revision: https://developer.blender.org/D9582
This commit is contained in:
Richard Antalik 2020-12-16 20:34:26 +01:00
parent 9d15226383
commit 5713626422
Notes: blender-bot 2023-02-14 08:25:14 +01:00
Referenced by commit 0f81dafe6c, Fix T87010: VSE: adding strips crashes in certain files
Referenced by commit d857892553, VSE: Fix proxy images have incorrect size
Referenced by commit 247b10e6a2, Fix sequencer transform test failing
Referenced by issue #87010, VSE: Crash When Trying to add Image Sequence / Movie [if based on file opened from pre 2.8]
Referenced by issue #82755, Sequencer: Improve motion-picture workflow
Referenced by issue #78988, VSE 2.0: Media transform redesign
17 changed files with 543 additions and 54 deletions

View File

@ -129,6 +129,8 @@ class SEQUENCER_HT_header(Header):
layout = self.layout
st = context.space_data
scene = context.scene
sequencer_tool_settings = context.tool_settings.sequencer_tool_settings
show_region_tool_header = st.show_region_tool_header
@ -139,9 +141,15 @@ class SEQUENCER_HT_header(Header):
SEQUENCER_MT_editor_menus.draw_collapsible(context, layout)
layout.separator_spacer()
if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}:
layout.separator_spacer()
row = layout.row(align=True)
row.prop(sequencer_tool_settings, "fit_method", text="")
layout.separator_spacer()
if st.view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}:
if st.view_type == 'PREVIEW':
layout.separator_spacer()
layout.prop(st, "display_mode", text="", icon_only=True)
layout.prop(st, "preview_channels", text="", icon_only=True)
@ -700,6 +708,22 @@ class SEQUENCER_MT_add_effect(Menu):
col.enabled = selected_sequences_len(context) != 0
class SEQUENCER_MT_strip_image_transform(Menu):
bl_label = "Image Transform"
def draw(self, _context):
layout = self.layout
layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT'
layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL'
layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH'
layout.separator()
layout.operator("sequencer.strip_transform_clear", text="Clear Position").property = 'POSITION'
layout.operator("sequencer.strip_transform_clear", text="Clear Scale").property = 'SCALE'
layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION'
layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL'
class SEQUENCER_MT_strip_transform(Menu):
bl_label = "Transform"
@ -794,6 +818,7 @@ class SEQUENCER_MT_strip(Menu):
layout.separator()
layout.menu("SEQUENCER_MT_strip_transform")
layout.menu("SEQUENCER_MT_strip_image_transform")
layout.separator()
layout.operator("sequencer.split", text="Split").type = 'SOFT'
@ -2285,6 +2310,7 @@ classes = (
SEQUENCER_MT_strip_effect,
SEQUENCER_MT_strip_movie,
SEQUENCER_MT_strip,
SEQUENCER_MT_strip_image_transform,
SEQUENCER_MT_strip_transform,
SEQUENCER_MT_strip_input,
SEQUENCER_MT_strip_lock_mute,

View File

@ -39,7 +39,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 7
#define BLENDER_FILE_SUBVERSION 8
/* 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

View File

@ -222,6 +222,7 @@ static void scene_init_data(ID *id)
/* Curve Profile */
scene->toolsettings->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE);
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
for (size_t i = 0; i < ARRAY_SIZE(scene->orientation_slots); i++) {
scene->orientation_slots[i].index_custom = -1;
@ -862,6 +863,9 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres
if (tos->custom_bevel_profile_preset) {
BKE_curveprofile_blend_write(writer, tos->custom_bevel_profile_preset);
}
if (tos->sequencer_tool_settings) {
BLO_write_struct(writer, SequencerToolSettings, tos->sequencer_tool_settings);
}
BKE_paint_blend_write(writer, &tos->imapaint.paint);
@ -1121,6 +1125,8 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id)
if (sce->toolsettings->custom_bevel_profile_preset) {
BKE_curveprofile_blend_read(reader, sce->toolsettings->custom_bevel_profile_preset);
}
BLO_read_data_address(reader, &sce->toolsettings->sequencer_tool_settings);
}
if (sce->ed) {
@ -1792,6 +1798,8 @@ ToolSettings *BKE_toolsettings_copy(ToolSettings *toolsettings, const int flag)
ts->gp_sculpt.cur_primitive = BKE_curvemapping_copy(ts->gp_sculpt.cur_primitive);
ts->custom_bevel_profile_preset = BKE_curveprofile_copy(ts->custom_bevel_profile_preset);
ts->sequencer_tool_settings = SEQ_tool_settings_copy(ts->sequencer_tool_settings);
return ts;
}
@ -1850,6 +1858,10 @@ void BKE_toolsettings_free(ToolSettings *toolsettings)
BKE_curveprofile_free(toolsettings->custom_bevel_profile_preset);
}
if (toolsettings->sequencer_tool_settings) {
SEQ_tool_settings_free(toolsettings->sequencer_tool_settings);
}
MEM_freeN(toolsettings);
}

View File

@ -74,6 +74,29 @@
/* Make preferences read-only, use versioning_userdef.c. */
#define U (*((const UserDef *)&U))
static eSpaceSeq_Proxy_RenderSize get_sequencer_render_size(Main *bmain)
{
eSpaceSeq_Proxy_RenderSize render_size = 100;
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
case SPACE_SEQ: {
SpaceSeq *sseq = (SpaceSeq *)sl;
if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) {
render_size = sseq->render_size;
break;
}
}
}
}
}
}
return render_size;
}
/* image_size is width or height depending what RNA property is converted - X or Y. */
static void seq_convert_transform_animation(const Scene *scene,
const char *path,
@ -212,6 +235,90 @@ static void seq_convert_transform_crop_lb(const Scene *scene,
}
}
static void seq_convert_transform_animation_2(const Scene *scene,
const char *path,
const float scale_to_fit_factor)
{
if (scene->adt == NULL || scene->adt->action == NULL) {
return;
}
FCurve *fcu = BKE_fcurve_find(&scene->adt->action->curves, path, 0);
if (fcu != NULL && !BKE_fcurve_is_empty(fcu)) {
BezTriple *bezt = fcu->bezt;
for (int i = 0; i < fcu->totvert; i++, bezt++) {
/* Same math as with old_image_center_*, but simplified. */
bezt->vec[1][1] *= scale_to_fit_factor;
}
}
}
static void seq_convert_transform_crop_2(const Scene *scene,
Sequence *seq,
const eSpaceSeq_Proxy_RenderSize render_size)
{
const StripElem *s_elem = SEQ_render_give_stripelem(seq, seq->start);
if (s_elem == NULL) {
return;
}
StripCrop *c = seq->strip->crop;
StripTransform *t = seq->strip->transform;
int image_size_x = s_elem->orig_width;
int image_size_y = s_elem->orig_height;
if (SEQ_can_use_proxy(seq, SEQ_rendersize_to_proxysize(render_size))) {
image_size_x /= SEQ_rendersize_to_scale_factor(render_size);
image_size_y /= SEQ_rendersize_to_scale_factor(render_size);
}
/* Calculate scale factor, so image fits in preview area with original aspect ratio. */
const float scale_to_fit_factor = MIN2((float)scene->r.xsch / (float)image_size_x,
(float)scene->r.ysch / (float)image_size_y);
t->scale_x *= scale_to_fit_factor;
t->scale_y *= scale_to_fit_factor;
c->top /= scale_to_fit_factor;
c->bottom /= scale_to_fit_factor;
c->left /= scale_to_fit_factor;
c->right /= scale_to_fit_factor;
char name_esc[(sizeof(seq->name) - 2) * 2], *path;
BLI_str_escape(name_esc, seq->name + 2, sizeof(name_esc));
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_x", name_esc);
seq_convert_transform_animation_2(scene, path, scale_to_fit_factor);
MEM_freeN(path);
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].transform.offset_y", name_esc);
seq_convert_transform_animation_2(scene, path, scale_to_fit_factor);
MEM_freeN(path);
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_x", name_esc);
seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
MEM_freeN(path);
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc);
seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
MEM_freeN(path);
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.min_y", name_esc);
seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
MEM_freeN(path);
path = BLI_sprintfN("sequence_editor.sequences_all[\"%s\"].crop.max_x", name_esc);
seq_convert_transform_animation_2(scene, path, 1 / scale_to_fit_factor);
MEM_freeN(path);
}
static void seq_convert_transform_crop_lb_2(const Scene *scene,
const ListBase *lb,
const eSpaceSeq_Proxy_RenderSize render_size)
{
LISTBASE_FOREACH (Sequence *, seq, lb) {
if (seq->type != SEQ_TYPE_SOUND_RAM) {
seq_convert_transform_crop_2(scene, seq, render_size);
}
if (seq->type == SEQ_TYPE_META) {
seq_convert_transform_crop_lb_2(scene, &seq->seqbase, render_size);
}
}
}
void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
{
if (!MAIN_VERSION_ATLEAST(bmain, 290, 1)) {
@ -441,21 +548,7 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
if (!MAIN_VERSION_ATLEAST(bmain, 292, 2)) {
eSpaceSeq_Proxy_RenderSize render_size = 100;
for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
switch (sl->spacetype) {
case SPACE_SEQ: {
SpaceSeq *sseq = (SpaceSeq *)sl;
render_size = sseq->render_size;
break;
}
}
}
}
}
eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain);
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
@ -464,6 +557,30 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) {
/* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in
* Armature obdata. */
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->type == OB_ARMATURE) {
BKE_pose_rebuild(bmain, ob, ob->data, true);
}
}
/* Wet Paint Radius Factor */
for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) {
br->wet_paint_radius_factor = 1.0f;
}
}
eSpaceSeq_Proxy_RenderSize render_size = get_sequencer_render_size(bmain);
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
seq_convert_transform_crop_lb_2(scene, &scene->ed->seqbase, render_size);
}
}
}
/**
* Versioning code until next subversion bump goes here.
*
@ -476,21 +593,6 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
/* Systematically rebuild posebones to ensure consistent ordering matching the one of bones in
* Armature obdata. */
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
if (ob->type == OB_ARMATURE) {
BKE_pose_rebuild(bmain, ob, ob->data, true);
}
}
}
/* Wet Paint Radius Factor */
for (Brush *br = bmain->brushes.first; br; br = br->id.next) {
if (br->ob_mode & OB_MODE_SCULPT && br->wet_paint_radius_factor == 0.0f) {
br->wet_paint_radius_factor = 1.0f;
}
}
}
@ -1305,6 +1407,22 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 292, 8)) {
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "GeometryNodeRandomAttribute")) {
STRNCPY(node->idname, "GeometryNodeAttributeRandomize");
}
}
}
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
if (scene->ed != NULL) {
scene->toolsettings->sequencer_tool_settings = SEQ_tool_settings_init();
}
}
}
/**
* Versioning code until next subversion bump goes here.
*
@ -1316,13 +1434,5 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
*/
{
/* Keep this block, even when empty. */
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (STREQ(node->idname, "GeometryNodeRandomAttribute")) {
STRNCPY(node->idname, "GeometryNodeAttributeRandomize");
}
}
}
}
}

View File

@ -81,9 +81,18 @@ typedef struct SequencerAddData {
#define SEQPROP_ENDFRAME (1 << 1)
#define SEQPROP_NOPATHS (1 << 2)
#define SEQPROP_NOCHAN (1 << 3)
#define SEQPROP_FIT_METHOD (1 << 4)
#define SELECT 1
static const EnumPropertyItem scale_fit_methods[] = {
{SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
{SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"},
{SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"},
{SEQ_USE_ORIGINAL_SIZE, "ORIGINAL", 0, "Use Original Size", "Keep image at its original size"},
{0, NULL, 0, NULL, NULL},
};
static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
{
PropertyRNA *prop;
@ -123,6 +132,15 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag)
prop = RNA_def_boolean(
ot->srna, "overlap", 0, "Allow Overlap", "Don't correct overlap on new sequence strips");
RNA_def_property_flag(prop, PROP_HIDDEN);
if (flag & SEQPROP_FIT_METHOD) {
ot->prop = RNA_def_enum(ot->srna,
"fit_method",
scale_fit_methods,
SEQ_SCALE_TO_FIT,
"Fit Method",
"Scale fit method");
}
}
static void sequencer_generic_invoke_path__internal(bContext *C,
@ -206,6 +224,8 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato
seq_load->end_frame = seq_load->start_frame;
seq_load->channel = RNA_int_get(op->ptr, "channel");
seq_load->len = 1;
seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method");
SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method);
if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) {
/* Full path, file is set by the caller. */
@ -659,6 +679,7 @@ static int sequencer_add_movie_strip_invoke(bContext *C,
if (ed && ed->seqbasep && ed->seqbasep->first) {
RNA_boolean_set(op->ptr, "use_framerate", false);
}
RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene));
/* This is for drag and drop. */
if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) ||
@ -725,7 +746,7 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME);
sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD);
RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie");
RNA_def_boolean(ot->srna,
"use_framerate",
@ -928,6 +949,9 @@ static int sequencer_add_image_strip_invoke(bContext *C,
PropertyRNA *prop;
Scene *scene = CTX_data_scene(C);
const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method);
/* Name set already by drag and drop. */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
sequencer_generic_invoke_xy__internal(
@ -972,7 +996,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot)
WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME);
sequencer_generic_props__internal(ot,
SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD);
RNA_def_boolean(ot->srna,
"use_placeholders",

View File

@ -3414,3 +3414,148 @@ Sequence *find_nearest_seq(Scene *scene, View2D *v2d, int *hand, const int mval[
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Clear Strip Transform Operator
* \{ */
enum {
STRIP_TRANSFORM_POSITION,
STRIP_TRANSFORM_SCALE,
STRIP_TRANSFORM_ROTATION,
STRIP_TRANSFORM_ALL,
};
static const EnumPropertyItem transform_reset_properties[] = {
{STRIP_TRANSFORM_POSITION, "POSITION", 0, "Position", "Reset strip transform location"},
{STRIP_TRANSFORM_SCALE, "SCALE", 0, "Scale", "Reset strip transform scale"},
{STRIP_TRANSFORM_ROTATION, "ROTATION", 0, "Rotation", "Reset strip transform rotation"},
{STRIP_TRANSFORM_ALL, "ALL", 0, "All", "Reset strip transform location, scale and rotation"},
{0, NULL, 0, NULL, NULL},
};
static int sequencer_strip_transform_clear_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = BKE_sequencer_editing_get(scene, false);
Sequence *seq;
const int property = RNA_enum_get(op->ptr, "property");
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
StripTransform *transform = seq->strip->transform;
switch (property) {
case STRIP_TRANSFORM_POSITION:
transform->xofs = 0;
transform->yofs = 0;
break;
case STRIP_TRANSFORM_SCALE:
transform->scale_x = 1.0f;
transform->scale_y = 1.0f;
break;
case STRIP_TRANSFORM_ROTATION:
transform->rotation = 0.0f;
break;
case STRIP_TRANSFORM_ALL:
transform->xofs = 0;
transform->yofs = 0;
transform->scale_x = 1.0f;
transform->scale_y = 1.0f;
transform->rotation = 0.0f;
break;
}
BKE_sequence_invalidate_cache_preprocessed(scene, seq);
}
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Clear Strip Transform";
ot->idname = "SEQUENCER_OT_strip_transform_clear";
ot->description = "Reset image transformation to default value";
/* Api callbacks. */
ot->exec = sequencer_strip_transform_clear_exec;
ot->poll = sequencer_edit_poll;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(ot->srna,
"property",
transform_reset_properties,
STRIP_TRANSFORM_ALL,
"Property",
"Strip transform property to be reset");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Transform Set Fit Operator
* \{ */
static const EnumPropertyItem scale_fit_methods[] = {
{SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image so fits in preview"},
{SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image so it fills preview completely"},
{SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image so it fills preview"},
{0, NULL, 0, NULL, NULL},
};
static int sequencer_strip_transform_fit_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
const Editing *ed = BKE_sequencer_editing_get(scene, false);
Sequence *seq;
const eSeqImageFitMethod fit_method = RNA_enum_get(op->ptr, "fit_method");
for (seq = ed->seqbasep->first; seq; seq = seq->next) {
if (seq->flag & SELECT && seq->type != SEQ_TYPE_SOUND_RAM) {
const int timeline_frame = CFRA;
StripElem *strip_elem = SEQ_render_give_stripelem(seq, timeline_frame);
if (strip_elem == NULL) {
continue;
}
SEQ_set_scale_to_fit(seq,
strip_elem->orig_width,
strip_elem->orig_height,
scene->r.xsch,
scene->r.ysch,
fit_method);
BKE_sequence_invalidate_cache_preprocessed(scene, seq);
}
}
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
}
void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Strip Transform Set Fit";
ot->idname = "SEQUENCER_OT_strip_transform_fit";
/* Api callbacks. */
ot->exec = sequencer_strip_transform_fit_exec;
ot->poll = sequencer_edit_poll;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(ot->srna,
"fit_method",
scale_fit_methods,
SEQ_SCALE_TO_FIT,
"Fit Method",
"Scale fit fit_method");
}
/** \} */

View File

@ -144,6 +144,8 @@ void SEQUENCER_OT_enable_proxies(struct wmOperatorType *ot);
void SEQUENCER_OT_export_subtitles(struct wmOperatorType *ot);
void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot);
void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot);
void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot);
/* sequencer_select.c */
void SEQUENCER_OT_select_all(struct wmOperatorType *ot);

View File

@ -81,6 +81,8 @@ void sequencer_operatortypes(void)
WM_operatortype_append(SEQUENCER_OT_change_path);
WM_operatortype_append(SEQUENCER_OT_set_range_to_strips);
WM_operatortype_append(SEQUENCER_OT_strip_transform_clear);
WM_operatortype_append(SEQUENCER_OT_strip_transform_fit);
/* sequencer_select.c */
WM_operatortype_append(SEQUENCER_OT_select_all);

View File

@ -382,6 +382,8 @@ bool IMB_anim_can_produce_frames(const struct anim *anim);
int ismovie(const char *filepath);
void IMB_anim_set_preseek(struct anim *anim, int preseek);
int IMB_anim_get_preseek(struct anim *anim);
int IMB_anim_get_image_width(struct anim *anim);
int IMB_anim_get_image_height(struct anim *anim);
/**
*

View File

@ -1506,3 +1506,13 @@ int IMB_anim_get_preseek(struct anim *anim)
{
return anim->preseek;
}
int IMB_anim_get_image_width(struct anim *anim)
{
return anim->x;
}
int IMB_anim_get_image_height(struct anim *anim)
{
return anim->y;
}

View File

@ -1339,6 +1339,18 @@ typedef struct MeshStatVis {
float sharp_min, sharp_max;
} MeshStatVis;
typedef struct SequencerToolSettings {
/* eSeqImageFitMethod */
int fit_method;
} SequencerToolSettings;
typedef enum eSeqImageFitMethod {
SEQ_SCALE_TO_FIT,
SEQ_SCALE_TO_FILL,
SEQ_STRETCH_TO_FILL,
SEQ_USE_ORIGINAL_SIZE,
} eSeqImageFitMethod;
/* *************************************************************** */
/* Tool Settings */
@ -1513,6 +1525,9 @@ typedef struct ToolSettings {
* Temporary until there is a proper preset system that stores the profiles or maybe stores
* entire bevel configurations. */
struct CurveProfile *custom_bevel_profile_preset;
struct SequencerToolSettings *sequencer_tool_settings;
} ToolSettings;
/* *************************************************************** */

View File

@ -2200,6 +2200,11 @@ static char *rna_CurvePaintSettings_path(PointerRNA *UNUSED(ptr))
return BLI_strdup("tool_settings.curve_paint_settings");
}
static char *rna_SequencerToolSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_strdup("tool_settings.sequencer_tool_settings");
}
/* generic function to recalc geometry */
static void rna_EditMesh_update(bContext *C, PointerRNA *UNUSED(ptr))
{
@ -3584,6 +3589,38 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "custom_bevel_profile_preset");
RNA_def_property_struct_type(prop, "CurveProfile");
RNA_def_property_ui_text(prop, "Curve Profile Widget", "Used for defining a profile's path");
/* Sequencer tool settings */
prop = RNA_def_property(srna, "sequencer_tool_settings", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "SequencerToolSettings");
RNA_def_property_ui_text(prop, "Sequencer Tool Settings", NULL);
}
static void rna_def_sequencer_tool_settings(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
static const EnumPropertyItem scale_fit_methods[] = {
{SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"},
{SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"},
{SEQ_STRETCH_TO_FILL, "STRETCH", 0, "Stretch to Fill", "Stretch image to fill the canvas"},
{SEQ_USE_ORIGINAL_SIZE,
"ORIGINAL",
0,
"Use Original Size",
"Keep image at its original size"},
{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", "");
prop = RNA_def_property(srna, "fit_method", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, scale_fit_methods);
RNA_def_property_ui_text(prop, "Fit Method", "Scale fit method");
}
static void rna_def_unified_paint_settings(BlenderRNA *brna)
@ -7968,6 +8005,7 @@ void RNA_def_scene(BlenderRNA *brna)
rna_def_gpencil_interpolate(brna);
rna_def_unified_paint_settings(brna);
rna_def_curve_paint_settings(brna);
rna_def_sequencer_tool_settings(brna);
rna_def_statvis(brna);
rna_def_unit_settings(brna);
rna_def_scene_image_format_data(brna);

View File

@ -23,6 +23,8 @@
* \ingroup sequencer
*/
#include "DNA_scene_types.h"
#ifdef __cplusplus
extern "C" {
#endif
@ -47,6 +49,10 @@ struct StripElem;
struct TextVars;
struct bContext;
struct bSound;
struct BlendWriter;
struct BlendDataReader;
struct BlendLibReader;
struct SequencerToolSettings;
/* Wipe effect */
enum {
@ -179,6 +185,12 @@ void SEQ_render_pixel_from_sequencer_space_v4(struct Scene *scene, float pixel[4
* Sequencer scene functions
* ********************************************************************** */
struct SequencerToolSettings *SEQ_tool_settings_init(void);
void SEQ_tool_settings_free(struct SequencerToolSettings *tool_settings);
eSeqImageFitMethod SEQ_tool_settings_fit_method_get(struct Scene *scene);
void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fit_method);
struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings);
struct Editing *BKE_sequencer_editing_get(struct Scene *scene, bool alloc);
struct Editing *BKE_sequencer_editing_ensure(struct Scene *scene);
void BKE_sequencer_editing_free(struct Scene *scene, const bool do_id_user);
@ -360,6 +372,20 @@ void BKE_sequence_invalidate_cache_in_range(struct Scene *scene,
int invalidate_types);
void BKE_sequencer_all_free_anim_ibufs(struct Scene *scene, int timeline_frame);
/* **********************************************************************
* util.c
*
* Add strips
* **********************************************************************
*/
void SEQ_set_scale_to_fit(const struct Sequence *seq,
const int image_width,
const int image_height,
const int preview_width,
const int preview_height,
const eSeqImageFitMethod fit_method);
/* **********************************************************************
* sequencer.c
*
@ -376,6 +402,7 @@ typedef struct SeqLoadInfo {
int type;
int len; /* only for image strips */
char path[1024]; /* 1024 = FILE_MAX */
eSeqImageFitMethod fit_method;
/* multiview */
char views_format;

View File

@ -528,7 +528,6 @@ static void sequencer_image_transform_init(void *handle_v,
handle->ibuf_source = init_data->ibuf_source;
handle->ibuf_out = init_data->ibuf_out;
handle->transform = init_data->transform;
handle->scale_to_fit = init_data->scale_to_fit;
handle->image_scale_factor = init_data->image_scale_factor;
handle->for_render = init_data->for_render;
@ -540,8 +539,8 @@ static void *sequencer_image_transform_do_thread(void *data_v)
{
const ImageTransformThreadData *data = (ImageTransformThreadData *)data_v;
const StripTransform *transform = data->transform;
const float scale_x = transform->scale_x * data->scale_to_fit;
const float scale_y = transform->scale_y * data->scale_to_fit;
const float scale_x = transform->scale_x * data->image_scale_factor;
const float scale_y = transform->scale_y * data->image_scale_factor;
const float scale_to_fit_offs_x = (data->ibuf_out->x - data->ibuf_source->x) / 2;
const float scale_to_fit_offs_y = (data->ibuf_out->y - data->ibuf_source->y) / 2;
const float translate_x = transform->xofs * data->image_scale_factor + scale_to_fit_offs_x;
@ -626,10 +625,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
IMB_filtery(preprocessed_ibuf);
}
/* Calculate scale factor, so image fits in preview area with original aspect ratio. */
const float scale_to_fit_factor = MIN2((float)context->rectx / (float)ibuf->x,
(float)context->recty / (float)ibuf->y);
/* Get scale factor if preview resolution doesn't match project resolution. */
float preview_scale_factor;
if (context->preview_render_size == SEQ_RENDER_SIZE_SCENE) {
@ -648,10 +643,10 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
const int height = ibuf->y;
const StripCrop *c = seq->strip->crop;
const int left = c->left / scale_to_fit_factor * preview_scale_factor;
const int right = c->right / scale_to_fit_factor * preview_scale_factor;
const int top = c->top / scale_to_fit_factor * preview_scale_factor;
const int bottom = c->bottom / scale_to_fit_factor * preview_scale_factor;
const int left = c->left;
const int right = c->right;
const int top = c->top;
const int bottom = c->bottom;
const float col[4] = {0.0f, 0.0f, 0.0f, 0.0f};
/* Left. */
@ -673,7 +668,6 @@ static ImBuf *input_preprocess(const SeqRenderData *context,
init_data.ibuf_source = ibuf;
init_data.ibuf_out = preprocessed_ibuf;
init_data.transform = seq->strip->transform;
init_data.scale_to_fit = scale_to_fit_factor;
init_data.image_scale_factor = preview_scale_factor;
init_data.for_render = context->for_render;
IMB_processor_apply_threaded(context->recty,

View File

@ -301,6 +301,32 @@ static void seq_new_fix_links_recursive(Sequence *seq)
}
}
}
SequencerToolSettings *SEQ_tool_settings_init(void)
{
SequencerToolSettings *tool_settings = MEM_callocN(sizeof(SequencerToolSettings),
"Sequencer tool settings");
tool_settings->fit_method = SEQ_SCALE_TO_FIT;
return tool_settings;
}
void SEQ_tool_settings_free(SequencerToolSettings *tool_settings)
{
MEM_freeN(tool_settings);
}
eSeqImageFitMethod SEQ_tool_settings_fit_method_get(Scene *scene)
{
const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
return tool_settings->fit_method;
}
void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_method)
{
SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings;
tool_settings->fit_method = fit_method;
}
/** \} */
/* -------------------------------------------------------------------- */
@ -609,4 +635,11 @@ static void seq_free_animdata(Scene *scene, Sequence *seq)
}
#undef SEQ_RNAPATH_MAXSTR
SequencerToolSettings *SEQ_tool_settings_copy(SequencerToolSettings *tool_settings)
{
SequencerToolSettings *tool_settings_copy = MEM_dupallocN(tool_settings);
return tool_settings_copy;
}
/** \} */

View File

@ -118,6 +118,15 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad
seq->flag |= seq_load->flag & SEQ_USE_VIEWS;
seq_load_apply(CTX_data_main(C), scene, seq, seq_load);
char file_path[FILE_MAX];
BLI_join_dirfile(file_path, sizeof(file_path), seq_load->path, seq_load->name);
ImBuf *ibuf = IMB_loadiffname(file_path, IB_rect, seq->strip->colorspace_settings.name);
if (ibuf != NULL) {
SEQ_set_scale_to_fit(seq, ibuf->x, ibuf->y, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
IMB_freeImBuf(ibuf);
}
BKE_sequence_invalidate_cache_composite(scene, seq);
return seq;
@ -275,6 +284,11 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad
IMB_anim_load_metadata(anim_arr[0]);
seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
const float width = IMB_anim_get_image_width(anim_arr[0]);
const float height = IMB_anim_get_image_height(anim_arr[0]);
SEQ_set_scale_to_fit(seq, width, height, scene->r.xsch, scene->r.ysch, seq_load->fit_method);
BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2);
BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);

View File

@ -36,6 +36,7 @@
#include "BLI_listbase.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_image.h"
#include "BKE_main.h"
@ -547,3 +548,36 @@ bool sequencer_seq_generates_image(Sequence *seq)
}
return false;
}
void SEQ_set_scale_to_fit(const Sequence *seq,
const int image_width,
const int image_height,
const int preview_width,
const int preview_height,
const eSeqImageFitMethod fit_method)
{
StripTransform *transform = seq->strip->transform;
switch (fit_method) {
case SEQ_SCALE_TO_FIT:
transform->scale_x = transform->scale_y = MIN2((float)preview_width / (float)image_width,
(float)preview_height / (float)image_height);
break;
case SEQ_SCALE_TO_FILL:
transform->scale_x = transform->scale_y = MAX2((float)preview_width / (float)image_width,
(float)preview_height / (float)image_height);
break;
case SEQ_STRETCH_TO_FILL:
transform->scale_x = (float)preview_width / (float)image_width;
transform->scale_y = (float)preview_height / (float)image_height;
break;
case SEQ_USE_ORIGINAL_SIZE:
transform->scale_x = 1.0f;
transform->scale_y = 1.0f;
break;
}
return;
}