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/sequencer')
-rw-r--r--source/blender/sequencer/SEQ_iterator.h8
-rw-r--r--source/blender/sequencer/SEQ_render.h21
-rw-r--r--source/blender/sequencer/SEQ_sequencer.h1
-rw-r--r--source/blender/sequencer/SEQ_transform.h9
-rw-r--r--source/blender/sequencer/SEQ_utils.h2
-rw-r--r--source/blender/sequencer/intern/image_cache.c81
-rw-r--r--source/blender/sequencer/intern/image_cache.h6
-rw-r--r--source/blender/sequencer/intern/iterator.c120
-rw-r--r--source/blender/sequencer/intern/render.c283
-rw-r--r--source/blender/sequencer/intern/sequencer.c11
-rw-r--r--source/blender/sequencer/intern/strip_transform.c98
-rw-r--r--source/blender/sequencer/intern/utils.c1
12 files changed, 538 insertions, 103 deletions
diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h
index 4f7d603fd6a..d2a47a13db3 100644
--- a/source/blender/sequencer/SEQ_iterator.h
+++ b/source/blender/sequencer/SEQ_iterator.h
@@ -94,11 +94,15 @@ SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference,
SeqCollection *collection));
SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase);
SeqCollection *SEQ_query_unselected_strips(struct ListBase *seqbase);
-SeqCollection *SEQ_query_all_strips(struct ListBase *seqbase);
-SeqCollection *SEQ_query_all_strips_recursive(struct ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips(ListBase *seqbase);
+SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase);
+SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase,
+ const int timeline_frame,
+ const int displayed_channel);
void SEQ_query_strip_effect_chain(struct Sequence *seq_reference,
struct ListBase *seqbase,
SeqCollection *collection);
+void SEQ_filter_selected_strips(SeqCollection *collection);
#ifdef __cplusplus
}
diff --git a/source/blender/sequencer/SEQ_render.h b/source/blender/sequencer/SEQ_render.h
index c138daf1318..e99dc6d344f 100644
--- a/source/blender/sequencer/SEQ_render.h
+++ b/source/blender/sequencer/SEQ_render.h
@@ -27,6 +27,8 @@
extern "C" {
#endif
+#define SEQ_RENDER_THUMB_SIZE 256
+
struct ListBase;
struct Main;
struct Scene;
@@ -67,6 +69,25 @@ struct ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context,
struct ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context,
float timeline_frame,
struct Sequence *seq);
+void SEQ_render_thumbnails(const struct SeqRenderData *context,
+ struct Sequence *seq,
+ struct Sequence *seq_orig,
+ float start_frame,
+ float frame_step,
+ rctf *view_area,
+ const short *stop);
+struct ImBuf *SEQ_get_thumbnail(const struct SeqRenderData *context,
+ struct Sequence *seq,
+ float timeline_frame,
+ rcti *crop,
+ bool clipped);
+int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const struct Sequence *seq);
+void SEQ_render_thumbnails_base_set(const struct SeqRenderData *context,
+ struct Sequence *seq,
+ struct Sequence *seq_orig,
+ rctf *view_area,
+ const short *stop);
+
void SEQ_render_init_colorspace(struct Sequence *seq);
void SEQ_render_new_render_data(struct Main *bmain,
struct Depsgraph *depsgraph,
diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h
index d7800d208a4..7e733817630 100644
--- a/source/blender/sequencer/SEQ_sequencer.h
+++ b/source/blender/sequencer/SEQ_sequencer.h
@@ -64,6 +64,7 @@ short SEQ_tool_settings_snap_flag_get(struct Scene *scene);
short SEQ_tool_settings_snap_mode_get(struct Scene *scene);
int SEQ_tool_settings_snap_distance_get(struct Scene *scene);
eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene);
+int SEQ_tool_settings_pivot_point_get(struct Scene *scene);
struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings);
struct Editing *SEQ_editing_get(const struct Scene *scene);
struct Editing *SEQ_editing_ensure(struct Scene *scene);
diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h
index 1977835f627..328efb9424a 100644
--- a/source/blender/sequencer/SEQ_transform.h
+++ b/source/blender/sequencer/SEQ_transform.h
@@ -61,6 +61,15 @@ void SEQ_transform_offset_after_frame(struct Scene *scene,
const int delta,
const int timeline_frame);
+/* Image transformation. */
+void SEQ_image_transform_mirror_factor_get(const struct Sequence *seq, float r_mirror[2]);
+void SEQ_image_transform_origin_offset_pixelspace_get(const struct Scene *scene,
+ const struct Sequence *seq,
+ float r_origin[2]);
+void SEQ_image_transform_final_quad_get(const struct Scene *scene,
+ const struct Sequence *seq,
+ float r_quad[4][2]);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h
index 09de7bc67e9..d30a1b2d7ae 100644
--- a/source/blender/sequencer/SEQ_utils.h
+++ b/source/blender/sequencer/SEQ_utils.h
@@ -34,6 +34,7 @@ struct Mask;
struct Scene;
struct Sequence;
struct StripElem;
+struct SeqRenderData;
void SEQ_sort(struct ListBase *seqbase);
void SEQ_sequence_base_unique_name_recursive(struct Scene *scene,
@@ -57,7 +58,6 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq,
const int preview_height,
const eSeqImageFitMethod fit_method);
void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene);
-
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c
index 86bd840ce31..86c198075e9 100644
--- a/source/blender/sequencer/intern/image_cache.c
+++ b/source/blender/sequencer/intern/image_cache.c
@@ -104,6 +104,7 @@
#define DCACHE_IMAGES_PER_FILE 100
#define DCACHE_CURRENT_VERSION 2
#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */
+#define THUMB_CACHE_LIMIT 5000
typedef struct DiskCacheHeaderEntry {
unsigned char encoding;
@@ -148,6 +149,7 @@ typedef struct SeqCache {
struct BLI_mempool *items_pool;
struct SeqCacheKey *last_key;
SeqDiskCache *disk_cache;
+ int thumbnail_count;
} SeqCache;
typedef struct SeqCacheItem {
@@ -776,7 +778,7 @@ static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, float timeli
/* With raw images, map timeline_frame to strip input media frame range. This means that static
* images or extended frame range of movies will only generate one cache entry. No special
* treatment in converting frame index to timeline_frame is needed. */
- if (type == SEQ_CACHE_STORE_RAW) {
+ if (type == SEQ_CACHE_STORE_RAW || type == SEQ_CACHE_STORE_THUMBNAIL) {
return seq_give_frame_index(seq, timeline_frame);
}
@@ -875,7 +877,7 @@ static void seq_cache_put_ex(Scene *scene, SeqCacheKey *key, ImBuf *ibuf)
if (BLI_ghash_reinsert(cache->hash, key, item, seq_cache_keyfree, seq_cache_valfree)) {
IMB_refImBuf(ibuf);
- if (!key->is_temp_cache) {
+ if (!key->is_temp_cache || key->type != SEQ_CACHE_STORE_THUMBNAIL) {
cache->last_key = key;
}
}
@@ -1161,6 +1163,7 @@ static void seq_cache_create(Main *bmain, Scene *scene)
cache->hash = BLI_ghash_new(seq_cache_hashhash, seq_cache_hashcmp, "SeqCache hash");
cache->last_key = NULL;
cache->bmain = bmain;
+ cache->thumbnail_count = 0;
BLI_mutex_init(&cache->iterator_mutex);
scene->ed->cache = cache;
@@ -1217,7 +1220,7 @@ void seq_cache_free_temp_cache(Scene *scene, short id, int timeline_frame)
SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter);
BLI_ghashIterator_step(&gh_iter);
- if (key->is_temp_cache && key->task_id == id) {
+ if (key->is_temp_cache && key->task_id == id && key->type != SEQ_CACHE_STORE_THUMBNAIL) {
/* Use frame_index here to avoid freeing raw images if they are used for multiple frames. */
float frame_index = seq_cache_timeline_frame_to_frame_index(
key->seq, timeline_frame, key->type);
@@ -1278,6 +1281,7 @@ void SEQ_cache_cleanup(Scene *scene)
BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree);
}
cache->last_key = NULL;
+ cache->thumbnail_count = 0;
seq_cache_unlock(scene);
}
@@ -1345,6 +1349,46 @@ void seq_cache_cleanup_sequence(Scene *scene,
seq_cache_unlock(scene);
}
+void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area_safe)
+{
+ /* Add offsets to the left and right end to keep some frames in cache. */
+ view_area_safe->xmax += 200;
+ view_area_safe->xmin -= 200;
+ view_area_safe->ymin -= 1;
+ view_area_safe->ymax += 1;
+
+ SeqCache *cache = seq_cache_get_from_scene(scene);
+ if (!cache) {
+ return;
+ }
+
+ GHashIterator gh_iter;
+ BLI_ghashIterator_init(&gh_iter, cache->hash);
+ while (!BLI_ghashIterator_done(&gh_iter)) {
+ SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter);
+ BLI_ghashIterator_step(&gh_iter);
+
+ const int frame_index = key->timeline_frame - key->seq->startdisp;
+ const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(key->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 + key->seq->startdisp;
+
+ if (nearest_guaranted_absolute_frame == key->timeline_frame) {
+ continue;
+ }
+
+ if ((key->type & SEQ_CACHE_STORE_THUMBNAIL) &&
+ (key->timeline_frame > view_area_safe->xmax ||
+ key->timeline_frame < view_area_safe->xmin || key->seq->machine > view_area_safe->ymax ||
+ key->seq->machine < view_area_safe->ymin)) {
+ BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree);
+ cache->thumbnail_count--;
+ }
+ }
+ cache->last_key = NULL;
+}
+
struct ImBuf *seq_cache_get(const SeqRenderData *context,
Sequence *seq,
float timeline_frame,
@@ -1436,6 +1480,37 @@ bool seq_cache_put_if_possible(
return false;
}
+void seq_cache_thumbnail_put(
+ const SeqRenderData *context, Sequence *seq, float timeline_frame, ImBuf *i, rctf *view_area)
+{
+ Scene *scene = context->scene;
+
+ if (!scene->ed->cache) {
+ seq_cache_create(context->bmain, scene);
+ }
+
+ seq_cache_lock(scene);
+ SeqCache *cache = seq_cache_get_from_scene(scene);
+ SeqCacheKey *key = seq_cache_allocate_key(
+ cache, context, seq, timeline_frame, SEQ_CACHE_STORE_THUMBNAIL);
+
+ /* Prevent reinserting, it breaks cache key linking. */
+ if (BLI_ghash_haskey(cache->hash, key)) {
+ seq_cache_unlock(scene);
+ return;
+ }
+
+ /* Limit cache to THUMB_CACHE_LIMIT (5000) images stored. */
+ if (cache->thumbnail_count >= THUMB_CACHE_LIMIT) {
+ rctf view_area_safe = *view_area;
+ seq_cache_thumbnail_cleanup(scene, &view_area_safe);
+ }
+
+ seq_cache_put_ex(scene, key, i);
+ cache->thumbnail_count++;
+ seq_cache_unlock(scene);
+}
+
void seq_cache_put(
const SeqRenderData *context, Sequence *seq, float timeline_frame, int type, ImBuf *i)
{
diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h
index 63c559caee9..60031311985 100644
--- a/source/blender/sequencer/intern/image_cache.h
+++ b/source/blender/sequencer/intern/image_cache.h
@@ -46,6 +46,11 @@ void seq_cache_put(const struct SeqRenderData *context,
float timeline_frame,
int type,
struct ImBuf *i);
+void seq_cache_thumbnail_put(const struct SeqRenderData *context,
+ struct Sequence *seq,
+ float timeline_frame,
+ struct ImBuf *i,
+ rctf *view_area);
bool seq_cache_put_if_possible(const struct SeqRenderData *context,
struct Sequence *seq,
float timeline_frame,
@@ -60,6 +65,7 @@ void seq_cache_cleanup_sequence(struct Scene *scene,
struct Sequence *seq_changed,
int invalidate_types,
bool force_seq_changed_range);
+void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area);
bool seq_cache_is_full(void);
#ifdef __cplusplus
diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c
index 58f68205f51..2429405350b 100644
--- a/source/blender/sequencer/intern/iterator.c
+++ b/source/blender/sequencer/intern/iterator.c
@@ -37,6 +37,8 @@
#include "BKE_scene.h"
#include "SEQ_iterator.h"
+#include "SEQ_time.h"
+#include "render.h"
/* -------------------------------------------------------------------- */
/** \Iterator API
@@ -340,6 +342,114 @@ SeqCollection *SEQ_query_selected_strips(ListBase *seqbase)
return collection;
}
+static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame)
+{
+ SeqCollection *collection = SEQ_collection_create(__func__);
+
+ LISTBASE_FOREACH (Sequence *, seq, seqbase) {
+ if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
+ SEQ_collection_append_strip(seq, collection);
+ }
+ }
+ return collection;
+}
+
+static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->machine <= channel) {
+ continue;
+ }
+ SEQ_collection_remove_strip(seq, collection);
+ }
+}
+
+static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input)
+{
+ if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input ||
+ seq_effect->seq3 == possibly_input) {
+ return true;
+ }
+ return false;
+}
+
+/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself.
+ * Order of applying these conditions is important. */
+static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame)
+{
+ bool seq_have_effect_in_stack = false;
+ Sequence *seq_iter;
+ SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) {
+ /* Strips is below another strip with replace blending are not rendered. */
+ if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) {
+ return false;
+ }
+
+ if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) {
+ /* Strips in same channel or higher than its effect are rendered. */
+ if (seq->machine >= seq_iter->machine) {
+ return true;
+ }
+ /* Mark that this strip has effect in stack, that is above the strip. */
+ seq_have_effect_in_stack = true;
+ }
+ }
+
+ /* All effects are rendered (with respect to conditions above). */
+ if ((seq->type & SEQ_TYPE_EFFECT) != 0) {
+ return true;
+ }
+
+ /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */
+ if (seq_have_effect_in_stack) {
+ return false;
+ }
+
+ return true;
+}
+
+/* Remove strips we don't want to render from collection. */
+static void collection_filter_rendered_strips(SeqCollection *collection)
+{
+ Sequence *seq;
+
+ /* Remove sound strips and muted strips from collection, because these are not rendered.
+ * Function #must_render_strip() don't have to check for these strips anymore. */
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
+ }
+
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if (must_render_strip(seq, collection)) {
+ continue;
+ }
+ SEQ_collection_remove_strip(seq, collection);
+ }
+}
+
+/**
+ * Query strips that are rendered at \a timeline_frame when \a displayed channel is viewed
+ *
+ * \param seqbase: ListBase in which strips are queried
+ * \param timeline_frame: viewed frame
+ * \param displayed_channel: viewed channel. when set to 0, no channel filter is applied
+ * \return strip collection
+ */
+SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase,
+ const int timeline_frame,
+ const int displayed_channel)
+{
+ SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame);
+ if (displayed_channel != 0) {
+ collection_filter_channel_up_to_incl(collection, displayed_channel);
+ }
+ collection_filter_rendered_strips(collection);
+ return collection;
+}
+
/**
* Query all unselected strips in seqbase.
*
@@ -396,3 +506,13 @@ void SEQ_query_strip_effect_chain(Sequence *seq_reference,
}
}
}
+
+void SEQ_filter_selected_strips(SeqCollection *collection)
+{
+ Sequence *seq;
+ SEQ_ITERATOR_FOREACH (seq, collection) {
+ if ((seq->flag & SELECT) == 0) {
+ SEQ_collection_remove_strip(seq, collection);
+ }
+ }
+}
diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c
index 6c4502a3608..2578a6d4223 100644
--- a/source/blender/sequencer/intern/render.c
+++ b/source/blender/sequencer/intern/render.c
@@ -72,6 +72,7 @@
#include "SEQ_render.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"
+#include "SEQ_transform.h"
#include "SEQ_utils.h"
#include "effects.h"
@@ -262,94 +263,6 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame)
return se;
}
-static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input)
-{
- if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input ||
- seq_effect->seq3 == possibly_input) {
- return true;
- }
- return false;
-}
-
-/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself.
- * Order of applying these conditions is important. */
-static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame)
-{
- bool seq_have_effect_in_stack = false;
- Sequence *seq_iter;
- SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) {
- /* Strips is below another strip with replace blending are not rendered. */
- if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) {
- return false;
- }
-
- if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) {
- /* Strips in same channel or higher than its effect are rendered. */
- if (seq->machine >= seq_iter->machine) {
- return true;
- }
- /* Mark that this strip has effect in stack, that is above the strip. */
- seq_have_effect_in_stack = true;
- }
- }
-
- /* All effects are rendered (with respect to conditions above). */
- if ((seq->type & SEQ_TYPE_EFFECT) != 0) {
- return true;
- }
-
- /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */
- if (seq_have_effect_in_stack) {
- return false;
- }
-
- return true;
-}
-
-static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame)
-{
- SeqCollection *collection = SEQ_collection_create(__func__);
-
- LISTBASE_FOREACH (Sequence *, seq, seqbase) {
- if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) {
- SEQ_collection_append_strip(seq, collection);
- }
- }
- return collection;
-}
-
-static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel)
-{
- Sequence *seq;
- SEQ_ITERATOR_FOREACH (seq, collection) {
- if (seq->machine <= channel) {
- continue;
- }
- SEQ_collection_remove_strip(seq, collection);
- }
-}
-
-/* Remove strips we don't want to render from collection. */
-static void collection_filter_rendered_strips(SeqCollection *collection)
-{
- Sequence *seq;
-
- /* Remove sound strips and muted strips from collection, because these are not rendered.
- * Function #must_render_strip() don't have to check for these strips anymore. */
- SEQ_ITERATOR_FOREACH (seq, collection) {
- if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) {
- SEQ_collection_remove_strip(seq, collection);
- }
- }
-
- SEQ_ITERATOR_FOREACH (seq, collection) {
- if (must_render_strip(seq, collection)) {
- continue;
- }
- SEQ_collection_remove_strip(seq, collection);
- }
-}
-
static int seq_channel_cmp_fn(const void *a, const void *b)
{
return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine;
@@ -360,13 +273,7 @@ int seq_get_shown_sequences(ListBase *seqbase,
const int chanshown,
Sequence **r_seq_arr)
{
- SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame);
-
- if (chanshown != 0) {
- collection_filter_channel_up_to_incl(collection, chanshown);
- }
- collection_filter_rendered_strips(collection);
-
+ SeqCollection *collection = SEQ_query_rendered_strips(seqbase, timeline_frame, chanshown);
const int strip_count = BLI_gset_len(collection->set);
if (strip_count > MAXSEQ) {
@@ -504,7 +411,7 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq,
const float image_center_offs_y = (out->y - in->y) / 2;
const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x;
const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y;
- const float pivot[2] = {in->x / 2, in->y / 2};
+ const float pivot[2] = {in->x * transform->origin[0], in->y * transform->origin[1]};
loc_rot_size_to_mat3(r_transform_matrix,
(const float[]){translate_x, translate_y},
transform->rotation,
@@ -527,6 +434,31 @@ static void sequencer_image_crop_init(const Sequence *seq,
BLI_rctf_init(r_crop, left, in->x - right, bottom, in->y - top);
}
+static void sequencer_thumbnail_transform(ImBuf *in, ImBuf *out)
+{
+ float image_scale_factor = (float)out->x / in->x;
+ float transform_matrix[3][3];
+
+ /* Set to keep same loc,scale,rot but change scale to thumb size limit. */
+ const float scale_x = 1 * image_scale_factor;
+ const float scale_y = 1 * image_scale_factor;
+ const float image_center_offs_x = (out->x - in->x) / 2;
+ const float image_center_offs_y = (out->y - in->y) / 2;
+ const float pivot[2] = {in->x / 2, in->y / 2};
+ loc_rot_size_to_mat3(transform_matrix,
+ (const float[]){image_center_offs_x, image_center_offs_y},
+ 0,
+ (const float[]){scale_x, scale_y});
+ transform_pivot_set_m3(transform_matrix, pivot);
+ invert_m3(transform_matrix);
+
+ /* No crop. */
+ rctf source_crop;
+ BLI_rctf_init(&source_crop, 0, in->x, 0, in->y);
+
+ IMB_transform(in, out, transform_matrix, &source_crop, IMB_FILTER_NEAREST);
+}
+
static void sequencer_preprocess_transform_crop(
ImBuf *in, ImBuf *out, const SeqRenderData *context, Sequence *seq, const bool is_proxy_image)
{
@@ -1989,7 +1921,164 @@ ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context,
seq_render_state_init(&state);
ImBuf *ibuf = seq_render_strip(context, &state, seq, timeline_frame);
-
return ibuf;
}
+
+/* Gets the direct image from source and scales to thumbnail size. */
+static ImBuf *seq_get_uncached_thumbnail(const SeqRenderData *context,
+ SeqRenderState *state,
+ Sequence *seq,
+ float timeline_frame)
+{
+ bool is_proxy_image = false;
+ ImBuf *ibuf = do_render_strip_uncached(context, state, seq, timeline_frame, &is_proxy_image);
+
+ if (ibuf == NULL) {
+ return NULL;
+ }
+
+ float aspect_ratio = (float)ibuf->x / ibuf->y;
+ int rectx, recty;
+ /* Calculate new dimensions - THUMB_SIZE (256) for x or y. */
+ if (ibuf->x > ibuf->y) {
+ rectx = SEQ_RENDER_THUMB_SIZE;
+ recty = round_fl_to_int(rectx / aspect_ratio);
+ }
+ else {
+ recty = SEQ_RENDER_THUMB_SIZE;
+ rectx = round_fl_to_int(recty * aspect_ratio);
+ }
+
+ /* Scale ibuf to thumbnail size. */
+ ImBuf *scaled_ibuf = IMB_allocImBuf(rectx, recty, 32, ibuf->rect_float ? IB_rectfloat : IB_rect);
+ sequencer_thumbnail_transform(ibuf, scaled_ibuf);
+ seq_imbuf_assign_spaces(context->scene, scaled_ibuf);
+ IMB_freeImBuf(ibuf);
+
+ return scaled_ibuf;
+}
+
+/* Get cached thumbnails. */
+ImBuf *SEQ_get_thumbnail(
+ const SeqRenderData *context, Sequence *seq, float timeline_frame, rcti *crop, bool clipped)
+{
+ ImBuf *ibuf = seq_cache_get(context, seq, roundf(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL);
+
+ if (!clipped || ibuf == NULL) {
+ return ibuf;
+ }
+
+ /* Do clipping. */
+ ImBuf *ibuf_cropped = IMB_dupImBuf(ibuf);
+ if (crop->xmin < 0 || crop->ymin < 0) {
+ crop->xmin = 0;
+ crop->ymin = 0;
+ }
+ if (crop->xmax >= ibuf->x || crop->ymax >= ibuf->y) {
+ crop->xmax = ibuf->x - 1;
+ crop->ymax = ibuf->y - 1;
+ }
+ IMB_rect_crop(ibuf_cropped, crop);
+ IMB_freeImBuf(ibuf);
+ return ibuf_cropped;
+}
+
+/* Render the series of thumbnails and store in cache. */
+void SEQ_render_thumbnails(const SeqRenderData *context,
+ Sequence *seq,
+ Sequence *seq_orig,
+ float start_frame,
+ float frame_step,
+ rctf *view_area,
+ const short *stop)
+{
+ SeqRenderState state;
+ seq_render_state_init(&state);
+
+ /* Adding the hold offset value (seq->anim_startofs) to the start frame. Position of image not
+ * affected, but frame loaded affected. */
+ start_frame = start_frame - frame_step;
+ float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp;
+ upper_thumb_bound = (upper_thumb_bound > view_area->xmax) ? view_area->xmax + frame_step :
+ upper_thumb_bound;
+
+ while ((start_frame < upper_thumb_bound) & !*stop) {
+ ImBuf *ibuf = seq_cache_get(
+ context, seq_orig, round_fl_to_int(start_frame), SEQ_CACHE_STORE_THUMBNAIL);
+ if (ibuf) {
+ IMB_freeImBuf(ibuf);
+ start_frame += frame_step;
+ continue;
+ }
+
+ ibuf = seq_get_uncached_thumbnail(context, &state, seq, round_fl_to_int(start_frame));
+
+ if (ibuf) {
+ seq_cache_thumbnail_put(context, seq_orig, round_fl_to_int(start_frame), ibuf, view_area);
+ IMB_freeImBuf(ibuf);
+ seq_orig->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS;
+ }
+ else {
+ /* Can not open source file. */
+ seq_orig->flag |= SEQ_FLAG_SKIP_THUMBNAILS;
+ return;
+ }
+
+ start_frame += frame_step;
+ }
+}
+
+/* Get frame step for equally spaced thumbnails. These thumbnails should always be present in
+ * memory, so they can be used when zooming.*/
+int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const Sequence *seq)
+{
+ const int content_len = (seq->enddisp - seq->startdisp - seq->startstill - seq->endstill);
+
+ /* Arbitrary, but due to performance reasons should be as low as possible. */
+ const int thumbnails_base_set_count = min_ii(content_len / 100, 30);
+ if (thumbnails_base_set_count <= 0) {
+ return 0;
+ }
+ return content_len / thumbnails_base_set_count;
+}
+
+/* Render set of evenly spaced thumbnails that are drawn when zooming. */
+void SEQ_render_thumbnails_base_set(
+ const SeqRenderData *context, Sequence *seq, Sequence *seq_orig, rctf *view_area, const short *stop)
+{
+ SeqRenderState state;
+ seq_render_state_init(&state);
+
+ int timeline_frame = seq->startdisp;
+ const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq);
+
+ while (timeline_frame < seq->enddisp && !*stop) {
+ ImBuf *ibuf = seq_cache_get(
+ context, seq_orig, roundf(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL);
+ if (ibuf) {
+ IMB_freeImBuf(ibuf);
+
+ if (frame_step == 0) {
+ return;
+ }
+
+ timeline_frame += frame_step;
+ continue;
+ }
+
+ ibuf = seq_get_uncached_thumbnail(context, &state, seq, timeline_frame);
+
+ if (ibuf) {
+ seq_cache_thumbnail_put(context, seq_orig, timeline_frame, ibuf, view_area);
+ IMB_freeImBuf(ibuf);
+ }
+
+ if (frame_step == 0) {
+ return;
+ }
+
+ timeline_frame += frame_step;
+ }
+}
+
/** \} */
diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c
index bf5942090c9..382bd51aae1 100644
--- a/source/blender/sequencer/intern/sequencer.c
+++ b/source/blender/sequencer/intern/sequencer.c
@@ -79,6 +79,8 @@ static Strip *seq_strip_alloc(int type)
strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform");
strip->transform->scale_x = 1;
strip->transform->scale_y = 1;
+ strip->transform->origin[0] = 0.5f;
+ strip->transform->origin[1] = 0.5f;
strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop");
}
@@ -321,6 +323,7 @@ SequencerToolSettings *SEQ_tool_settings_init(void)
SEQ_SNAP_TO_STRIP_HOLD;
tool_settings->snap_distance = 15;
tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE;
+ tool_settings->pivot_point = V3D_AROUND_LOCAL_ORIGINS;
return tool_settings;
}
@@ -377,6 +380,12 @@ eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene)
return tool_settings->overlap_mode;
}
+int SEQ_tool_settings_pivot_point_get(Scene *scene)
+{
+ const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene);
+ return tool_settings->pivot_point;
+}
+
/**
* Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase
*
@@ -948,6 +957,8 @@ static bool seq_read_lib_cb(Sequence *seq, void *user_data)
BLI_listbase_clear(&seq->anims);
SEQ_modifier_blend_read_lib(reader, sce, &seq->modifiers);
+
+ seq->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS;
return true;
}
diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c
index 3a5f93a72b0..d5ff455c694 100644
--- a/source/blender/sequencer/intern/strip_transform.c
+++ b/source/blender/sequencer/intern/strip_transform.c
@@ -421,3 +421,101 @@ void SEQ_transform_offset_after_frame(Scene *scene,
}
}
}
+
+void SEQ_image_transform_mirror_factor_get(const Sequence *seq, float r_mirror[2])
+{
+ r_mirror[0] = 1.0f;
+ r_mirror[1] = 1.0f;
+
+ if ((seq->flag & SEQ_FLIPX) != 0) {
+ r_mirror[0] = -1.0f;
+ }
+ if ((seq->flag & SEQ_FLIPY) != 0) {
+ r_mirror[1] = -1.0f;
+ }
+}
+
+/**
+ * Get strip transform origin offset from image center
+ * Note: This function does not apply axis mirror.
+ *
+ * \param scene: Scene in which strips are located
+ * \param seq: Sequence to calculate image transform origin
+ * \param r_origin: return value
+ */
+void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene,
+ const Sequence *seq,
+ float r_origin[2])
+{
+ float image_size[2];
+ StripElem *strip_elem = seq->strip->stripdata;
+ if (strip_elem == NULL) {
+ image_size[0] = scene->r.xsch;
+ image_size[1] = scene->r.ysch;
+ }
+ else {
+ image_size[0] = strip_elem->orig_width;
+ image_size[1] = strip_elem->orig_height;
+ }
+
+ const StripTransform *transform = seq->strip->transform;
+ r_origin[0] = (image_size[0] * transform->origin[0]) - (image_size[0] * 0.5f) + transform->xofs;
+ r_origin[1] = (image_size[1] * transform->origin[1]) - (image_size[1] * 0.5f) + transform->yofs;
+
+ float mirror[2];
+ SEQ_image_transform_mirror_factor_get(seq, mirror);
+ mul_v2_v2(r_origin, mirror);
+}
+
+/**
+ * Get strip transform origin offset from image center
+ *
+ * \param scene: Scene in which strips are located
+ * \param seq: Sequence to calculate image transform origin
+ * \param r_origin: return value
+ */
+
+void SEQ_image_transform_final_quad_get(const Scene *scene,
+ const Sequence *seq,
+ float r_quad[4][2])
+{
+ StripTransform *transform = seq->strip->transform;
+ StripCrop *crop = seq->strip->crop;
+
+ int imgage_size[2] = {scene->r.xsch, scene->r.ysch};
+ if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) {
+ imgage_size[0] = seq->strip->stripdata->orig_width;
+ imgage_size[1] = seq->strip->stripdata->orig_height;
+ }
+
+ float transform_matrix[3][3];
+ loc_rot_size_to_mat3(transform_matrix,
+ (const float[]){transform->xofs, transform->yofs},
+ transform->rotation,
+ (const float[]){transform->scale_x, transform->scale_y});
+ const float origin[2] = {imgage_size[0] * transform->origin[0],
+ imgage_size[1] * transform->origin[1]};
+ const float pivot[2] = {origin[0] - (imgage_size[0] / 2), origin[1] - (imgage_size[1] / 2)};
+ transform_pivot_set_m3(transform_matrix, pivot);
+
+ r_quad[0][0] = (imgage_size[0] / 2) - crop->right;
+ r_quad[0][1] = (imgage_size[1] / 2) - crop->top;
+ r_quad[1][0] = (imgage_size[0] / 2) - crop->right;
+ r_quad[1][1] = (-imgage_size[1] / 2) + crop->bottom;
+ r_quad[2][0] = (-imgage_size[0] / 2) + crop->left;
+ r_quad[2][1] = (-imgage_size[1] / 2) + crop->bottom;
+ r_quad[3][0] = (-imgage_size[0] / 2) + crop->left;
+ r_quad[3][1] = (imgage_size[1] / 2) - crop->top;
+
+ mul_m3_v2(transform_matrix, r_quad[0]);
+ mul_m3_v2(transform_matrix, r_quad[1]);
+ mul_m3_v2(transform_matrix, r_quad[2]);
+ mul_m3_v2(transform_matrix, r_quad[3]);
+
+ float mirror[2];
+ SEQ_image_transform_mirror_factor_get(seq, mirror);
+ mul_v2_v2(r_quad[0], mirror);
+ mul_v2_v2(r_quad[1], mirror);
+ mul_v2_v2(r_quad[2], mirror);
+ mul_v2_v2(r_quad[3], mirror);
+}
diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c
index 1d3e7e4a223..8421aab5217 100644
--- a/source/blender/sequencer/intern/utils.c
+++ b/source/blender/sequencer/intern/utils.c
@@ -42,6 +42,7 @@
#include "SEQ_edit.h"
#include "SEQ_iterator.h"
#include "SEQ_relations.h"
+#include "SEQ_render.h"
#include "SEQ_select.h"
#include "SEQ_sequencer.h"
#include "SEQ_time.h"