122 for (
const auto &item :
map_.items()) {
123 for (
const auto &thumb : item.value.frames) {
135 if (entry ==
nullptr) {
138 for (
const auto &thumb : entry->
frames) {
141 map_.remove_contained(path);
148 if (*cache ==
nullptr) {
149 *cache = MEM_new<ThumbnailCache>(__func__);
156 if (scene ==
nullptr || scene->
ed ==
nullptr) {
164 if (scene ==
nullptr || scene->
ed ==
nullptr || strip ==
nullptr) {
181 switch (strip->
type) {
184 if (s_elem !=
nullptr) {
200 float aspect =
float(r_width) /
float(r_height);
201 if (r_width > r_height) {
215 if (ibuf ==
nullptr) {
230 if (ibuf ==
nullptr) {
234 int height = ibuf->
y;
241 Scene *scene_ =
nullptr;
251 static void end_fn(
void *customdata);
252 static void free_fn(
void *customdata);
263 "Generating strip thumbnails...",
267 ThumbGenerationJob *tj = MEM_new<ThumbGenerationJob>(
"ThumbGenerationJob", scene, cache);
276void ThumbGenerationJob::free_fn(
void *customdata)
282void ThumbGenerationJob::run_fn(
void *customdata,
wmJobWorkerStatus *worker_status)
284#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
285 clock_t t0 = clock();
286 std::atomic<int> total_thumbs = 0, total_images = 0, total_movies = 0;
291 while (!worker_status->
stop) {
299 requests.
reserve(job->cache_->requests_.size());
300 for (
const auto &request : job->cache_->requests_) {
310 std::sort(requests.
begin(),
312 [](
const ThumbnailCache::Request &a,
const ThumbnailCache::Request &
b) {
313 if (a.file_path != b.file_path) {
314 return a.file_path < b.file_path;
316 if (a.stream_index !=
b.stream_index) {
317 return a.stream_index < b.stream_index;
319 return a.frame_index <
b.frame_index;
330 MovieReader *cur_anim =
nullptr;
331 std::string cur_anim_path;
334 for (
const ThumbnailCache::Request &request : requests) {
335 if (worker_status->
stop) {
339#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
342 ImBuf *thumb =
nullptr;
345#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
352#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
357 if (request.file_path != cur_anim_path || request.stream_index != cur_stream) {
358 if (cur_anim !=
nullptr) {
363 cur_anim_path = request.file_path;
364 cur_stream = request.stream_index;
366 cur_anim_path.c_str(),
IB_byte_data, cur_stream,
true,
nullptr);
368 if (cur_anim !=
nullptr) {
377 if (cur_anim !=
nullptr) {
384 if (thumb !=
nullptr) {
398 ThumbnailCache::FileEntry *val = job->cache_->map_.lookup_ptr(request.file_path);
399 if (val !=
nullptr) {
400 val->used_at =
math::max(val->used_at, request.requested_at);
402 {request.frame_index, request.stream_index, thumb, request.requested_at});
408 job->cache_->requests_.remove(request);
415 if (cur_anim !=
nullptr) {
422#ifdef DEBUG_PRINT_THUMB_JOB_TIMES
423 clock_t t1 = clock();
424 printf(
"VSE thumb job: %i thumbs (%i img, %i movie) in %.3f sec\n",
428 double(t1 - t0) / CLOCKS_PER_SEC);
432void ThumbGenerationJob::end_fn(
void *customdata)
434 ThumbGenerationJob *job =
static_cast<ThumbGenerationJob *
>(customdata);
439 const std::string &key,
441 float timeline_frame,
446 ThumbnailCache::FileEntry *val = cache.
map_.lookup_ptr(key);
448 if (val ==
nullptr) {
450 ThumbnailCache::FileEntry value;
451 value.used_at = cur_time;
452 cache.
map_.add_new(key, value);
453 val = cache.
map_.lookup_ptr(key);
455 BLI_assert_msg(val !=
nullptr,
"Thumbnail cache value should never be null here");
459 int best_score = INT_MAX;
460 for (
int64_t index = 0; index < val->frames.size(); index++) {
461 if (strip->
streamindex != val->frames[index].stream_index) {
464 int score =
math::abs(frame_index - val->frames[index].frame_index);
465 if (score < best_score) {
474 if (best_score > 0) {
479 ThumbnailCache::Request request(key,
492 if (best_index < 0) {
497 val->used_at =
math::max(val->used_at, cur_time);
498 val->frames[best_index].used_at =
math::max(val->frames[best_index].used_at, cur_time);
499 return val->frames[best_index].thumb;
505 float timeline_frame)
519 ImBuf *res =
nullptr;
540 if (cache !=
nullptr) {
543 if (elem !=
nullptr) {
551 for (
int i = 0;
i < paths_count;
i++, elem++) {
565 if (cache !=
nullptr) {
570 std::string oldest_file;
574 for (
const auto &item : cache->
map_.items()) {
575 entries += item.value.frames.size();
576 if (item.value.used_at < oldest_time) {
577 oldest_file = item.key;
578 oldest_time = item.value.used_at;
579 oldest_entries = item.value.frames.size();
586 entries -= oldest_entries;
592 for (
const auto &item : cache->
map_.items()) {
593 for (
int64_t i = 0;
i < item.value.frames.size();
i++) {
596 item.value.frames.remove_and_reorder(
i);
608 if (cache !=
nullptr) {
609 cache->
requests_.remove_if([&](
const ThumbnailCache::Request &request) {
610 return request.timeline_frame < rect.
xmin || request.timeline_frame > rect.
xmax ||
611 request.channel < rect.
ymin || request.channel > rect.
ymax;
620 if (cache !=
nullptr) {
629 if (cache !=
nullptr) {
wmWindow * CTX_wm_window(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
Scene * CTX_data_sequencer_scene(const bContext *C)
#define BLI_assert_unreachable()
#define BLI_assert_msg(a, msg)
MINLINE int round_fl_to_int(float a)
bool BLI_path_abs(char path[FILE_MAX], const char *basepath) ATTR_NONNULL(1
#define BLI_path_join(...)
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
ImBuf * IMB_thumb_load_image(const char *filepath, const size_t max_thumb_size, char colorspace[IM_MAX_SPACE], const IMBThumbLoadFlags load_flags=IMBThumbLoadFlags::Zero)
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_refImBuf(ImBuf *ibuf)
void IMB_free_byte_pixels(ImBuf *ibuf)
bool IMB_scale(ImBuf *ibuf, unsigned int newx, unsigned int newy, IMBScaleFilter filter, bool threaded=true)
@ WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL
unsigned long long int uint64_t
void append(const T &value)
void reserve(const int64_t min_capacity)
static void ensure_job(const bContext *C, ThumbnailCache *cache)
ThumbGenerationJob(Scene *scene, ThumbnailCache *cache)
size_t(* MEM_allocN_len)(const void *vmemh)
int MOV_get_existing_proxies(const MovieReader *anim)
void MOV_close(MovieReader *anim)
MovieReader * MOV_open_file(const char *filepath, const int ib_flags, const int streamindex, const bool keep_original_colorspace, char colorspace[IM_MAX_SPACE])
ImBuf * MOV_decode_frame(MovieReader *anim, int position, IMB_Timecode_Type tc, IMB_Proxy_Size preview_size)
T max(const T &a, const T &b)
void seq_imbuf_to_sequencer_space(const Scene *scene, ImBuf *ibuf, bool make_float)
void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf)
void thumbnail_cache_invalidate_strip(Scene *scene, const Strip *strip)
float give_frame_index(const Scene *scene, const Strip *strip, float timeline_frame)
bool strip_can_have_thumbnail(const Scene *scene, const Strip *strip)
void thumbnail_cache_clear(Scene *scene)
static ImBuf * make_thumb_for_image(const Scene *scene, const ThumbnailCache::Request &request)
ImBuf * thumbnail_cache_get(const bContext *C, Scene *scene, const Strip *strip, float timeline_frame)
static Mutex thumb_cache_mutex
static ImBuf * query_thumbnail(ThumbnailCache &cache, const std::string &key, int frame_index, float timeline_frame, const bContext *C, const Strip *strip)
static constexpr int MAX_THUMBNAILS
static std::string get_path_from_strip(Scene *scene, const Strip *strip, float timeline_frame)
static void image_size_to_thumb_size(int &r_width, int &r_height)
static void scale_to_thumbnail_size(ImBuf *ibuf)
void thumbnail_cache_discard_requests_outside(Scene *scene, const rctf &rect)
static ThumbnailCache * query_thumbnail_cache(Scene *scene)
static ThumbnailCache * ensure_thumbnail_cache(Scene *scene)
void thumbnail_cache_destroy(Scene *scene)
StripElem * render_give_stripelem(const Scene *scene, const Strip *strip, int timeline_frame)
static constexpr int THUMB_SIZE
void thumbnail_cache_maintain_capacity(Scene *scene)
uint64_t get_default_hash(const T &v, const Args &...args)
ThumbnailCache * thumbnail_cache
ImBufFloatBuffer float_buffer
ImBufByteBuffer byte_buffer
Vector< FrameEntry > frames
bool operator==(const Request &o) const
Request(const std::string &path, int frame, int stream, StripType type, int64_t logical_time, float time_frame, int ch, int width, int height)
void remove_entry(const std::string &path)
Map< std::string, FileEntry > map_
void WM_main_add_notifier(uint type, void *reference)
bool WM_jobs_is_running(const wmJob *wm_job)
void WM_jobs_timer(wmJob *wm_job, double time_step, uint note, uint endnote)
void WM_jobs_start(wmWindowManager *wm, wmJob *wm_job)
wmJob * WM_jobs_get(wmWindowManager *wm, wmWindow *win, const void *owner, const char *name, const eWM_JobFlag flag, const eWM_JobType job_type)
void WM_jobs_callbacks(wmJob *wm_job, wm_jobs_start_callback startjob, void(*initjob)(void *), void(*update)(void *), void(*endjob)(void *))
void WM_jobs_customdata_set(wmJob *wm_job, void *customdata, void(*free)(void *customdata))