mirror of
https://github.com/blender/blender
synced 2026-09-29 04:37:17 +03:00
VSE: Show thumbnails at strip ends by default
Implements design #151070: by default show thumbnails only at strip ends. Less clutter, and fewer thumbnails to load. There is an option to show thumbnails across the whole strip (i.e. previous behavior). Pull Request: https://projects.blender.org/blender/blender/pulls/151148
This commit is contained in:
parent
2ef6be31f1
commit
7d8fb0e00e
7 changed files with 275 additions and 55 deletions
|
|
@ -284,12 +284,33 @@ class SEQUENCER_PT_sequencer_overlay_strips(Panel):
|
|||
col.prop(overlay_settings, "show_fcurves", text="Animation Curves")
|
||||
|
||||
col = split.column()
|
||||
col.prop(overlay_settings, "show_thumbnails", text="Thumbnails")
|
||||
col.prop(overlay_settings, "show_strip_tag_color", text="Color Tags")
|
||||
col.prop(overlay_settings, "show_strip_offset", text="Offsets")
|
||||
col.prop(overlay_settings, "show_strip_retiming", text="Retiming")
|
||||
|
||||
|
||||
class SEQUENCER_PT_sequencer_overlay_thumbnails(Panel):
|
||||
bl_space_type = 'SEQUENCE_EDITOR'
|
||||
bl_region_type = 'HEADER'
|
||||
bl_parent_id = "SEQUENCER_PT_overlay"
|
||||
bl_label = "Thumbnails"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
st = context.space_data
|
||||
return st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}
|
||||
|
||||
def draw(self, context):
|
||||
st = context.space_data
|
||||
overlay_settings = st.timeline_overlay
|
||||
layout = self.layout
|
||||
|
||||
layout.active = st.show_overlays
|
||||
|
||||
row = layout.row()
|
||||
row.prop(overlay_settings, "thumbnail_display_style", expand=True)
|
||||
|
||||
|
||||
class SEQUENCER_PT_sequencer_overlay_waveforms(Panel):
|
||||
bl_space_type = 'SEQUENCE_EDITOR'
|
||||
bl_region_type = 'HEADER'
|
||||
|
|
@ -2076,6 +2097,7 @@ classes = (
|
|||
SEQUENCER_PT_preview_overlay,
|
||||
SEQUENCER_PT_sequencer_overlay,
|
||||
SEQUENCER_PT_sequencer_overlay_strips,
|
||||
SEQUENCER_PT_sequencer_overlay_thumbnails,
|
||||
SEQUENCER_PT_sequencer_overlay_waveforms,
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -185,7 +185,8 @@ static void blo_update_defaults_screen(bScreen *screen,
|
|||
SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
|
||||
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG |
|
||||
SEQ_TIMELINE_SHOW_STRIP_RETIMING |
|
||||
SEQ_TIMELINE_WAVEFORMS_HALF | SEQ_TIMELINE_SHOW_THUMBNAILS;
|
||||
SEQ_TIMELINE_WAVEFORMS_HALF |
|
||||
SEQ_TIMELINE_STRIP_END_THUMBNAILS;
|
||||
seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED;
|
||||
seq->cache_overlay.flag = SEQ_CACHE_SHOW | SEQ_CACHE_SHOW_FINAL_OUT;
|
||||
seq->draw_flag |= SEQ_DRAW_TRANSFORM_PREVIEW;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,160 @@ static void strip_get_thumb_image_dimensions(const Strip *strip,
|
|||
*r_image_height = image_height;
|
||||
}
|
||||
|
||||
static bool add_thumbnail_at_frame(float timeline_frame,
|
||||
const bContext *C,
|
||||
const View2D *v2d,
|
||||
const StripDrawContext &strip,
|
||||
Scene *scene,
|
||||
float thumb_width,
|
||||
float crop_left,
|
||||
float crop_right,
|
||||
float crop_x_multiplier,
|
||||
float upper_thumb_bound,
|
||||
float display_offset,
|
||||
bool is_muted,
|
||||
Vector<SeqThumbInfo> &r_thumbs)
|
||||
{
|
||||
/* Frame at which the thumb at `timeline_frame` will be drawn. */
|
||||
const float display_frame = timeline_frame + display_offset;
|
||||
|
||||
float thumb_x_end = display_frame + thumb_width;
|
||||
bool clipped = false;
|
||||
|
||||
/* Reached end of view, no more thumbnails needed. */
|
||||
if (display_frame > v2d->cur.xmax) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Clip if full thumbnail cannot be displayed. */
|
||||
if (thumb_x_end > upper_thumb_bound) {
|
||||
thumb_x_end = upper_thumb_bound;
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
crop_left = max_ff(crop_left, 0.0f);
|
||||
crop_right = max_ff(crop_right, 0.0f);
|
||||
|
||||
if (crop_left > 0.0f || crop_right > 0.0f) {
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
float cropx_min = crop_left * crop_x_multiplier;
|
||||
float cropx_max = min_ff(thumb_width - crop_right, (thumb_x_end - display_frame)) *
|
||||
crop_x_multiplier;
|
||||
if (cropx_max - cropx_min < 1.0f) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the thumbnail image. */
|
||||
ImBuf *ibuf = seq::thumbnail_cache_get(C, scene, strip.strip, timeline_frame);
|
||||
if (ibuf == nullptr) {
|
||||
/* Thumbnail is not in cache but still other frames have to request for thumbnails. */
|
||||
return true;
|
||||
}
|
||||
|
||||
SeqThumbInfo thumb = {};
|
||||
thumb.ibuf = ibuf;
|
||||
thumb.cropx_min = 0;
|
||||
thumb.cropx_max = ibuf->x - 1;
|
||||
if (clipped) {
|
||||
thumb.cropx_min = clamp_f(cropx_min, 0, ibuf->x - 1);
|
||||
thumb.cropx_max = clamp_f(cropx_max, 0, ibuf->x - 1);
|
||||
}
|
||||
thumb.left_handle = strip.left_handle;
|
||||
thumb.right_handle = strip.right_handle;
|
||||
thumb.is_muted = is_muted;
|
||||
thumb.bottom = strip.bottom;
|
||||
thumb.top = strip.top;
|
||||
thumb.x1 = display_frame + crop_left;
|
||||
thumb.x2 = min_ff(thumb_x_end, display_frame + thumb_width - crop_right);
|
||||
thumb.y1 = strip.bottom;
|
||||
thumb.y2 = strip.strip_content_top;
|
||||
r_thumbs.append(thumb);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
static bool is_thumbnail_in_view(const float timeline_frame,
|
||||
const float thumb_width,
|
||||
const View2D *v2d)
|
||||
{
|
||||
if (timeline_frame < v2d->cur.xmax && timeline_frame + thumb_width > v2d->cur.xmin) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void get_seq_strip_ends_thumbnails(const View2D *v2d,
|
||||
const bContext *C,
|
||||
const StripDrawContext &strip,
|
||||
Scene *scene,
|
||||
const float thumb_width,
|
||||
const float crop_x_multiplier,
|
||||
const float pixelx,
|
||||
const float upper_thumb_bound,
|
||||
bool is_muted,
|
||||
Vector<SeqThumbInfo> &r_thumbs)
|
||||
{
|
||||
const float left_frame = max_ff(strip.content_start, strip.left_handle);
|
||||
const float right_frame = strip.is_single_image ? left_frame :
|
||||
min_ff(strip.content_end, strip.right_handle);
|
||||
const float strip_width = strip.is_single_image ? (strip.right_handle - strip.left_handle) :
|
||||
(right_frame - left_frame);
|
||||
const float overlap = max_ff(0.0f, 2.0f * thumb_width - strip_width);
|
||||
const bool only_right_handle_selected = ((strip.strip->flag & SEQ_RIGHTSEL) &&
|
||||
!(strip.strip->flag & SEQ_LEFTSEL));
|
||||
/* Offset the start of last thumbnail. */
|
||||
const float display_offset = (strip.is_single_image ? strip_width : 0.0f) - thumb_width;
|
||||
const float gap = 1.5f * pixelx * UI_SCALE_FAC;
|
||||
|
||||
float crop_left = 0.0;
|
||||
float crop_right = 0.0;
|
||||
|
||||
if (overlap > 0.0f && only_right_handle_selected) {
|
||||
/* Crop left thumbnail from right. */
|
||||
crop_right = overlap + gap;
|
||||
}
|
||||
else if (overlap > 0.0f) {
|
||||
/* Crop right thumbnail from left. */
|
||||
crop_left = overlap + gap;
|
||||
}
|
||||
|
||||
if (is_thumbnail_in_view(left_frame, thumb_width, v2d)) {
|
||||
/* Draw left thumbnail. */
|
||||
add_thumbnail_at_frame(left_frame,
|
||||
C,
|
||||
v2d,
|
||||
strip,
|
||||
scene,
|
||||
thumb_width,
|
||||
0.0f,
|
||||
crop_right,
|
||||
crop_x_multiplier,
|
||||
upper_thumb_bound,
|
||||
0.0f,
|
||||
is_muted,
|
||||
r_thumbs);
|
||||
}
|
||||
|
||||
if (is_thumbnail_in_view(right_frame + display_offset, thumb_width, v2d)) {
|
||||
/* Draw right thumbnail. */
|
||||
add_thumbnail_at_frame(right_frame,
|
||||
C,
|
||||
v2d,
|
||||
strip,
|
||||
scene,
|
||||
thumb_width,
|
||||
crop_left,
|
||||
0.0f,
|
||||
crop_x_multiplier,
|
||||
upper_thumb_bound,
|
||||
display_offset,
|
||||
is_muted,
|
||||
r_thumbs);
|
||||
}
|
||||
}
|
||||
|
||||
static void get_seq_strip_thumbnails(const View2D *v2d,
|
||||
const bContext *C,
|
||||
Scene *scene,
|
||||
|
|
@ -88,6 +242,7 @@ static void get_seq_strip_thumbnails(const View2D *v2d,
|
|||
float pixelx,
|
||||
float pixely,
|
||||
bool is_muted,
|
||||
bool show_only_at_strip_ends,
|
||||
Vector<SeqThumbInfo> &r_thumbs)
|
||||
{
|
||||
if (!seq::strip_can_have_thumbnail(scene, strip.strip)) {
|
||||
|
|
@ -107,10 +262,24 @@ static void get_seq_strip_thumbnails(const View2D *v2d,
|
|||
const float crop_x_multiplier = 1.0f / pixelx / (thumb_height / image_height / pixely);
|
||||
|
||||
float upper_thumb_bound = min_ff(strip.right_handle, strip.content_end);
|
||||
if (strip.strip->type == STRIP_TYPE_IMAGE) {
|
||||
if (strip.is_single_image) {
|
||||
upper_thumb_bound = strip.right_handle;
|
||||
}
|
||||
|
||||
if (show_only_at_strip_ends) {
|
||||
get_seq_strip_ends_thumbnails(v2d,
|
||||
C,
|
||||
strip,
|
||||
scene,
|
||||
thumb_width,
|
||||
crop_x_multiplier,
|
||||
pixelx,
|
||||
upper_thumb_bound,
|
||||
is_muted,
|
||||
r_thumbs);
|
||||
return;
|
||||
}
|
||||
|
||||
int first_drawable_frame = max_iii(strip.left_handle, strip.strip->start, v2d->cur.xmin);
|
||||
/* Calculate how many thumbnails should we skip over to get to the first visible thumbnail. */
|
||||
float aligned_frame_offset = int((first_drawable_frame - strip.strip->start) / thumb_width) *
|
||||
|
|
@ -132,49 +301,25 @@ static void get_seq_strip_thumbnails(const View2D *v2d,
|
|||
|
||||
/* Start going over the strip length. */
|
||||
while (timeline_frame < upper_thumb_bound) {
|
||||
float thumb_x_end = timeline_frame + thumb_width;
|
||||
bool clipped = false;
|
||||
|
||||
/* Reached end of view, no more thumbnails needed. */
|
||||
if (timeline_frame > v2d->cur.xmax) {
|
||||
const bool should_add_next_thumbnail = add_thumbnail_at_frame(timeline_frame,
|
||||
C,
|
||||
v2d,
|
||||
strip,
|
||||
scene,
|
||||
thumb_width,
|
||||
0.0f,
|
||||
0.0f,
|
||||
crop_x_multiplier,
|
||||
upper_thumb_bound,
|
||||
0.0f,
|
||||
is_muted,
|
||||
r_thumbs);
|
||||
|
||||
if (!should_add_next_thumbnail) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Clip if full thumbnail cannot be displayed. */
|
||||
if (thumb_x_end > upper_thumb_bound) {
|
||||
thumb_x_end = upper_thumb_bound;
|
||||
clipped = true;
|
||||
}
|
||||
|
||||
float cropx_max = (thumb_x_end - timeline_frame) * crop_x_multiplier;
|
||||
if (cropx_max < 1.0f) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the thumbnail image. */
|
||||
ImBuf *ibuf = seq::thumbnail_cache_get(C, scene, strip.strip, timeline_frame);
|
||||
if (ibuf == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
SeqThumbInfo thumb = {};
|
||||
thumb.ibuf = ibuf;
|
||||
thumb.cropx_min = 0;
|
||||
thumb.cropx_max = ibuf->x - 1;
|
||||
if (clipped) {
|
||||
thumb.cropx_max = clamp_f(cropx_max - 1 * 0, 0, ibuf->x - 1);
|
||||
}
|
||||
thumb.left_handle = strip.left_handle;
|
||||
thumb.right_handle = strip.right_handle;
|
||||
thumb.is_muted = is_muted;
|
||||
thumb.bottom = strip.bottom;
|
||||
thumb.top = strip.top;
|
||||
thumb.x1 = timeline_frame;
|
||||
thumb.x2 = thumb_x_end;
|
||||
thumb.y1 = strip.bottom;
|
||||
thumb.y2 = strip.strip_content_top;
|
||||
r_thumbs.append(thumb);
|
||||
|
||||
if (draw_next_frame_ontop) {
|
||||
timeline_frame = strip.strip->start + aligned_frame_offset + thumb_width;
|
||||
draw_next_frame_ontop = false;
|
||||
|
|
@ -265,18 +410,31 @@ void draw_strip_thumbnails(const TimelineDrawContext &ctx,
|
|||
StripsDrawBatch &strips_batch,
|
||||
const Vector<StripDrawContext> &strips)
|
||||
{
|
||||
const bool show_thumbnails = (ctx.sseq->timeline_overlay.flag &
|
||||
SEQ_TIMELINE_STRIP_END_THUMBNAILS) ||
|
||||
(ctx.sseq->timeline_overlay.flag &
|
||||
SEQ_TIMELINE_CONTINUOUS_THUMBNAILS);
|
||||
/* Nothing to do if we're not showing thumbnails overall. */
|
||||
if ((ctx.sseq->flag & SEQ_SHOW_OVERLAY) == 0 ||
|
||||
(ctx.sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_THUMBNAILS) == 0)
|
||||
{
|
||||
if ((ctx.sseq->flag & SEQ_SHOW_OVERLAY) == 0 || !show_thumbnails) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Gather information for all thumbnails. */
|
||||
Vector<SeqThumbInfo> thumbs;
|
||||
/* Thumbnail display mode (Strip ends / Continuous). */
|
||||
const bool show_only_at_strip_ends = (ctx.sseq->timeline_overlay.flag &
|
||||
SEQ_TIMELINE_STRIP_END_THUMBNAILS);
|
||||
|
||||
for (const StripDrawContext &strip : strips) {
|
||||
get_seq_strip_thumbnails(
|
||||
ctx.v2d, ctx.C, ctx.scene, strip, ctx.pixelx, ctx.pixely, strip.is_muted, thumbs);
|
||||
get_seq_strip_thumbnails(ctx.v2d,
|
||||
ctx.C,
|
||||
ctx.scene,
|
||||
strip,
|
||||
ctx.pixelx,
|
||||
ctx.pixely,
|
||||
strip.is_muted,
|
||||
show_only_at_strip_ends,
|
||||
thumbs);
|
||||
}
|
||||
if (thumbs.is_empty()) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1292,6 +1292,27 @@ static void draw_strips_background(const TimelineDrawContext &ctx,
|
|||
}
|
||||
data.col_background = color_pack(col);
|
||||
|
||||
const bool show_thumbnails = (ctx.sseq->timeline_overlay.flag &
|
||||
SEQ_TIMELINE_STRIP_END_THUMBNAILS) ||
|
||||
(ctx.sseq->timeline_overlay.flag &
|
||||
SEQ_TIMELINE_CONTINUOUS_THUMBNAILS);
|
||||
/* Darker color band for thumbnail strips. */
|
||||
if (show_overlay && seq::strip_can_have_thumbnail(scene, strip.strip) && show_thumbnails) {
|
||||
/* The more negative the offset, darker the color. */
|
||||
const int color_offset = -20;
|
||||
uchar col_in[3] = {col[0], col[1], col[2]};
|
||||
uchar col_out[3];
|
||||
|
||||
ui::theme::get_color_shade_3ubv(col_in, color_offset, col_out);
|
||||
|
||||
col[0] = col_out[0];
|
||||
col[1] = col_out[1];
|
||||
col[2] = col_out[2];
|
||||
|
||||
data.flags |= GPU_SEQ_FLAG_COLOR_BAND;
|
||||
data.col_color_band = color_pack(col);
|
||||
}
|
||||
|
||||
/* Color band state. */
|
||||
if (show_overlay && (strip.strip->type == STRIP_TYPE_COLOR)) {
|
||||
data.flags |= GPU_SEQ_FLAG_COLOR_BAND;
|
||||
|
|
|
|||
|
|
@ -83,7 +83,8 @@ static SpaceLink *sequencer_create(const ScrArea * /*area*/, const Scene *scene)
|
|||
SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID |
|
||||
SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG |
|
||||
SEQ_TIMELINE_SHOW_STRIP_RETIMING | SEQ_TIMELINE_WAVEFORMS_HALF |
|
||||
SEQ_TIMELINE_SHOW_THUMBNAILS;
|
||||
SEQ_TIMELINE_STRIP_END_THUMBNAILS;
|
||||
|
||||
sseq->cache_overlay.flag = SEQ_CACHE_SHOW | SEQ_CACHE_SHOW_FINAL_OUT;
|
||||
sseq->draw_flag |= SEQ_DRAW_TRANSFORM_PREVIEW;
|
||||
|
||||
|
|
|
|||
|
|
@ -368,7 +368,8 @@ enum eSpaceSeq_SequencerPreviewOverlay_Flag {
|
|||
/** #SequencerTimelineOverlay.flag */
|
||||
enum eSpaceSeq_SequencerTimelineOverlay_Flag {
|
||||
SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1),
|
||||
SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2),
|
||||
/** Show thumbnails only at ends of a strip. */
|
||||
SEQ_TIMELINE_STRIP_END_THUMBNAILS = (1 << 2),
|
||||
/** Use #Strip::color_tag */
|
||||
SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG = (1 << 3),
|
||||
SEQ_TIMELINE_SHOW_STRIP_RETIMING = (1 << 4),
|
||||
|
|
@ -383,6 +384,8 @@ enum eSpaceSeq_SequencerTimelineOverlay_Flag {
|
|||
SEQ_TIMELINE_SHOW_STRIP_SOURCE = (1 << 15),
|
||||
SEQ_TIMELINE_SHOW_STRIP_DURATION = (1 << 16),
|
||||
SEQ_TIMELINE_SHOW_GRID = (1 << 18),
|
||||
/** Show continuous sequence of thumbnails. */
|
||||
SEQ_TIMELINE_CONTINUOUS_THUMBNAILS = (1 << 19),
|
||||
};
|
||||
|
||||
/** #SequencerCacheOverlay.flag */
|
||||
|
|
|
|||
|
|
@ -6473,6 +6473,26 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Waveform Style", "How Waveforms are displayed");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
|
||||
|
||||
static const EnumPropertyItem thumbnail_style_items[] = {
|
||||
{0, "NO_THUMBNAILS", 0, "None", "Do not show strip thumbnails"},
|
||||
{SEQ_TIMELINE_STRIP_END_THUMBNAILS,
|
||||
"STRIP_ENDS",
|
||||
0,
|
||||
"Strip Ends",
|
||||
"Show thumbnails only at the beginning and end of the strip"},
|
||||
{SEQ_TIMELINE_CONTINUOUS_THUMBNAILS,
|
||||
"CONTINUOUS",
|
||||
0,
|
||||
"Continuous",
|
||||
"Display thumbnails as a filmstrip"},
|
||||
{0, nullptr, 0, nullptr, nullptr}};
|
||||
|
||||
prop = RNA_def_property(srna, "thumbnail_display_style", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "timeline_overlay.flag");
|
||||
RNA_def_property_enum_items(prop, thumbnail_style_items);
|
||||
RNA_def_property_ui_text(prop, "Thumbnail style", "How thumbnails are displayed");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "show_fcurves", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "timeline_overlay.flag", SEQ_TIMELINE_SHOW_FCURVES);
|
||||
RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve");
|
||||
|
|
@ -6508,12 +6528,6 @@ static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Show Offsets", "Display strip in/out offsets");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "show_thumbnails", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "timeline_overlay.flag", SEQ_TIMELINE_SHOW_THUMBNAILS);
|
||||
RNA_def_property_ui_text(prop, "Show Thumbnails", "Show strip thumbnails");
|
||||
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "show_strip_tag_color", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "timeline_overlay.flag", SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue