This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/editors/space_clip/clip_editor.c
Jeroen Bakker d49e7b82da Compositor: Redesign Cryptomatte node for better usability
In the current implementation, cryptomatte passes are connected to the node
and elements are picked by using the eyedropper tool on a special pick channel.

This design has two disadvantages - both connecting all passes individually
and always having to switch to the picker channel are tedious.

With the new design, the user selects the RenderLayer or Image from which the
Cryptomatte layers are directly loaded (the type of pass is determined by an
enum). This allows the node to automatically detect all relevant passes.

Then, when using the eyedropper tool, the operator looks up the selected
coordinates from the picked Image, Node backdrop or Clip and reads the picked
object directly from the Renderlayer/Image, therefore allowing to pick in any
context (e.g. by clicking on the Combined pass in the Image Viewer). The
sampled color is looked up in the metadata and the actual name is stored
in the cryptomatte node. This also allows to remove a hash by just removing
the name from the matte id.

Technically there is some loss of flexibility because the Cryptomatte pass
inputs can no longer be connected to other nodes, but since any compositing
done on them is likely to break the Cryptomatte system anyways, this isn't
really a concern in practise.

In the future, this would also allow to automatically translate values to names
by looking up the value in the associated metadata of the input, or to get a
better visualization of overlapping areas in the Pick output since we could
blend colors now that the output doesn't have to contain the exact value.

Idea + Original patch: Lucas Stockner
Reviewed By: Brecht van Lommel

Differential Revision: https://developer.blender.org/D3959
2021-03-16 07:43:17 +01:00

1161 lines
29 KiB
C

/*
* 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) 2011 Blender Foundation.
* All rights reserved.
*/
/** \file
* \ingroup spclip
*/
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
#include <sys/types.h>
#ifndef WIN32
# include <unistd.h>
#else
# include <io.h>
#endif
#include "MEM_guardedalloc.h"
#include "DNA_mask_types.h"
#include "BLI_fileops.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_rect.h"
#include "BLI_task.h"
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_mask.h"
#include "BKE_movieclip.h"
#include "BKE_tracking.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "ED_clip.h"
#include "ED_mask.h"
#include "ED_screen.h"
#include "ED_select_utils.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_view2d.h"
#include "clip_intern.h" /* own include */
/* -------------------------------------------------------------------- */
/** \name Operator Poll Functions
* \{ */
bool ED_space_clip_poll(bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
if (sc && sc->clip) {
return true;
}
return false;
}
bool ED_space_clip_view_clip_poll(bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
if (sc) {
return sc->view == SC_VIEW_CLIP;
}
return false;
}
bool ED_space_clip_tracking_poll(bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
if (sc && sc->clip) {
return ED_space_clip_check_show_trackedit(sc);
}
return false;
}
bool ED_space_clip_maskedit_poll(bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
if (sc && sc->clip) {
return ED_space_clip_check_show_maskedit(sc);
}
return false;
}
bool ED_space_clip_maskedit_mask_poll(bContext *C)
{
if (ED_space_clip_maskedit_poll(C)) {
MovieClip *clip = CTX_data_edit_movieclip(C);
if (clip) {
SpaceClip *sc = CTX_wm_space_clip(C);
return sc->mask_info.mask != NULL;
}
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Common Editing Functions
* \{ */
void ED_space_clip_get_size(SpaceClip *sc, int *width, int *height)
{
if (sc->clip) {
BKE_movieclip_get_size(sc->clip, &sc->user, width, height);
}
else {
*width = *height = IMG_SIZE_FALLBACK;
}
}
void ED_space_clip_get_size_fl(SpaceClip *sc, float size[2])
{
int size_i[2];
ED_space_clip_get_size(sc, &size_i[0], &size_i[1]);
size[0] = size_i[0];
size[1] = size_i[1];
}
void ED_space_clip_get_zoom(SpaceClip *sc, ARegion *region, float *zoomx, float *zoomy)
{
int width, height;
ED_space_clip_get_size(sc, &width, &height);
*zoomx = (float)(BLI_rcti_size_x(&region->winrct) + 1) /
(BLI_rctf_size_x(&region->v2d.cur) * width);
*zoomy = (float)(BLI_rcti_size_y(&region->winrct) + 1) /
(BLI_rctf_size_y(&region->v2d.cur) * height);
}
void ED_space_clip_get_aspect(SpaceClip *sc, float *aspx, float *aspy)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
if (clip) {
BKE_movieclip_get_aspect(clip, aspx, aspy);
}
else {
*aspx = *aspy = 1.0f;
}
if (*aspx < *aspy) {
*aspy = *aspy / *aspx;
*aspx = 1.0f;
}
else {
*aspx = *aspx / *aspy;
*aspy = 1.0f;
}
}
void ED_space_clip_get_aspect_dimension_aware(SpaceClip *sc, float *aspx, float *aspy)
{
int w, h;
/* most of tools does not require aspect to be returned with dimensions correction
* due to they're invariant to this stuff, but some transformation tools like rotation
* should be aware of aspect correction caused by different resolution in different
* directions.
* mainly this is used for transformation stuff
*/
if (!sc->clip) {
*aspx = 1.0f;
*aspy = 1.0f;
return;
}
ED_space_clip_get_aspect(sc, aspx, aspy);
BKE_movieclip_get_size(sc->clip, &sc->user, &w, &h);
*aspx *= (float)w;
*aspy *= (float)h;
if (*aspx < *aspy) {
*aspy = *aspy / *aspx;
*aspx = 1.0f;
}
else {
*aspx = *aspx / *aspy;
*aspy = 1.0f;
}
}
/* return current frame number in clip space */
int ED_space_clip_get_clip_frame_number(SpaceClip *sc)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
/* Caller must ensure space does have a valid clip, otherwise it will crash, see T45017. */
return BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr);
}
ImBuf *ED_space_clip_get_buffer(SpaceClip *sc)
{
if (sc->clip) {
ImBuf *ibuf;
ibuf = BKE_movieclip_get_postprocessed_ibuf(sc->clip, &sc->user, sc->postproc_flag);
if (ibuf && (ibuf->rect || ibuf->rect_float)) {
return ibuf;
}
if (ibuf) {
IMB_freeImBuf(ibuf);
}
}
return NULL;
}
ImBuf *ED_space_clip_get_stable_buffer(SpaceClip *sc, float loc[2], float *scale, float *angle)
{
if (sc->clip) {
ImBuf *ibuf;
ibuf = BKE_movieclip_get_stable_ibuf(
sc->clip, &sc->user, loc, scale, angle, sc->postproc_flag);
if (ibuf && (ibuf->rect || ibuf->rect_float)) {
return ibuf;
}
if (ibuf) {
IMB_freeImBuf(ibuf);
}
}
return NULL;
}
bool ED_space_clip_get_position(struct SpaceClip *sc,
struct ARegion *ar,
int mval[2],
float fpos[2])
{
ImBuf *ibuf = ED_space_clip_get_buffer(sc);
if (!ibuf) {
return false;
}
/* map the mouse coords to the backdrop image space */
ED_clip_mouse_pos(sc, ar, mval, fpos);
IMB_freeImBuf(ibuf);
return true;
}
/* Returns color in linear space, matching ED_space_image_color_sample(). */
bool ED_space_clip_color_sample(SpaceClip *sc, ARegion *region, int mval[2], float r_col[3])
{
ImBuf *ibuf;
float fx, fy, co[2];
bool ret = false;
ibuf = ED_space_clip_get_buffer(sc);
if (!ibuf) {
return false;
}
/* map the mouse coords to the backdrop image space */
ED_clip_mouse_pos(sc, region, mval, co);
fx = co[0];
fy = co[1];
if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) {
const float *fp;
uchar *cp;
int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y);
CLAMP(x, 0, ibuf->x - 1);
CLAMP(y, 0, ibuf->y - 1);
if (ibuf->rect_float) {
fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
copy_v3_v3(r_col, fp);
ret = true;
}
else if (ibuf->rect) {
cp = (uchar *)(ibuf->rect + y * ibuf->x + x);
rgb_uchar_to_float(r_col, cp);
IMB_colormanagement_colorspace_to_scene_linear_v3(r_col, ibuf->rect_colorspace);
ret = true;
}
}
IMB_freeImBuf(ibuf);
return ret;
}
void ED_clip_update_frame(const Main *mainp, int cfra)
{
/* image window, compo node users */
for (wmWindowManager *wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_CLIP) {
SpaceClip *sc = area->spacedata.first;
sc->scopes.ok = false;
BKE_movieclip_user_set_frame(&sc->user, cfra);
}
}
}
}
}
bool ED_clip_view_selection(const bContext *C, ARegion *UNUSED(region), bool fit)
{
float offset_x, offset_y;
float zoom;
if (!clip_view_calculate_view_selection(C, fit, &offset_x, &offset_y, &zoom)) {
return false;
}
SpaceClip *sc = CTX_wm_space_clip(C);
sc->xof = offset_x;
sc->yof = offset_y;
sc->zoom = zoom;
return true;
}
void ED_clip_select_all(SpaceClip *sc, int action, bool *r_has_selection)
{
MovieClip *clip = ED_space_clip_get_clip(sc);
const int framenr = ED_space_clip_get_clip_frame_number(sc);
MovieTracking *tracking = &clip->tracking;
MovieTrackingTrack *track = NULL; /* selected track */
MovieTrackingPlaneTrack *plane_track = NULL; /* selected plane track */
MovieTrackingMarker *marker;
ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking);
ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking);
bool has_selection = false;
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
for (track = tracksbase->first; track; track = track->next) {
if (TRACK_VIEW_SELECTED(sc, track)) {
marker = BKE_tracking_marker_get(track, framenr);
if (MARKER_VISIBLE(sc, track, marker)) {
action = SEL_DESELECT;
break;
}
}
}
for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
if (PLANE_TRACK_VIEW_SELECTED(plane_track)) {
action = SEL_DESELECT;
break;
}
}
}
for (track = tracksbase->first; track; track = track->next) {
if ((track->flag & TRACK_HIDDEN) == 0) {
marker = BKE_tracking_marker_get(track, framenr);
if (MARKER_VISIBLE(sc, track, marker)) {
switch (action) {
case SEL_SELECT:
track->flag |= SELECT;
track->pat_flag |= SELECT;
track->search_flag |= SELECT;
break;
case SEL_DESELECT:
track->flag &= ~SELECT;
track->pat_flag &= ~SELECT;
track->search_flag &= ~SELECT;
break;
case SEL_INVERT:
track->flag ^= SELECT;
track->pat_flag ^= SELECT;
track->search_flag ^= SELECT;
break;
}
}
}
if (TRACK_VIEW_SELECTED(sc, track)) {
has_selection = true;
}
}
for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) {
if ((plane_track->flag & PLANE_TRACK_HIDDEN) == 0) {
switch (action) {
case SEL_SELECT:
plane_track->flag |= SELECT;
break;
case SEL_DESELECT:
plane_track->flag &= ~SELECT;
break;
case SEL_INVERT:
plane_track->flag ^= SELECT;
break;
}
if (plane_track->flag & SELECT) {
has_selection = true;
}
}
}
if (r_has_selection) {
*r_has_selection = has_selection;
}
}
void ED_clip_point_undistorted_pos(SpaceClip *sc, const float co[2], float r_co[2])
{
copy_v2_v2(r_co, co);
if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
MovieClip *clip = ED_space_clip_get_clip(sc);
float aspy = 1.0f / clip->tracking.camera.pixel_aspect;
int width, height;
BKE_movieclip_get_size(sc->clip, &sc->user, &width, &height);
r_co[0] *= width;
r_co[1] *= height * aspy;
BKE_tracking_undistort_v2(&clip->tracking, width, height, r_co, r_co);
r_co[0] /= width;
r_co[1] /= height * aspy;
}
}
void ED_clip_point_stable_pos(
SpaceClip *sc, ARegion *region, float x, float y, float *xr, float *yr)
{
int sx, sy, width, height;
float zoomx, zoomy, pos[3], imat[4][4];
ED_space_clip_get_zoom(sc, region, &zoomx, &zoomy);
ED_space_clip_get_size(sc, &width, &height);
UI_view2d_view_to_region(&region->v2d, 0.0f, 0.0f, &sx, &sy);
pos[0] = (x - sx) / zoomx;
pos[1] = (y - sy) / zoomy;
pos[2] = 0.0f;
invert_m4_m4(imat, sc->stabmat);
mul_v3_m4v3(pos, imat, pos);
*xr = pos[0] / width;
*yr = pos[1] / height;
if (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) {
MovieClip *clip = ED_space_clip_get_clip(sc);
MovieTracking *tracking = &clip->tracking;
float aspy = 1.0f / tracking->camera.pixel_aspect;
float tmp[2] = {*xr * width, *yr * height * aspy};
BKE_tracking_distort_v2(tracking, width, height, tmp, tmp);
*xr = tmp[0] / width;
*yr = tmp[1] / (height * aspy);
}
}
/**
* \brief the reverse of #ED_clip_point_stable_pos(), gets the marker region coords.
* better name here? view_to_track / track_to_view or so?
*/
void ED_clip_point_stable_pos__reverse(SpaceClip *sc,
ARegion *region,
const float co[2],
float r_co[2])
{
float zoomx, zoomy;
float pos[3];
int width, height;
int sx, sy;
UI_view2d_view_to_region(&region->v2d, 0.0f, 0.0f, &sx, &sy);
ED_space_clip_get_size(sc, &width, &height);
ED_space_clip_get_zoom(sc, region, &zoomx, &zoomy);
ED_clip_point_undistorted_pos(sc, co, pos);
pos[2] = 0.0f;
/* untested */
mul_v3_m4v3(pos, sc->stabmat, pos);
r_co[0] = (pos[0] * width * zoomx) + (float)sx;
r_co[1] = (pos[1] * height * zoomy) + (float)sy;
}
/* takes event->mval */
void ED_clip_mouse_pos(SpaceClip *sc, ARegion *region, const int mval[2], float co[2])
{
ED_clip_point_stable_pos(sc, region, mval[0], mval[1], &co[0], &co[1]);
}
bool ED_space_clip_check_show_trackedit(SpaceClip *sc)
{
if (sc) {
return sc->mode == SC_MODE_TRACKING;
}
return false;
}
bool ED_space_clip_check_show_maskedit(SpaceClip *sc)
{
if (sc) {
return sc->mode == SC_MODE_MASKEDIT;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Clip Editing Functions
* \{ */
MovieClip *ED_space_clip_get_clip(SpaceClip *sc)
{
return sc->clip;
}
void ED_space_clip_set_clip(bContext *C, bScreen *screen, SpaceClip *sc, MovieClip *clip)
{
MovieClip *old_clip;
bool old_clip_visible = false;
if (!screen && C) {
screen = CTX_wm_screen(C);
}
old_clip = sc->clip;
sc->clip = clip;
id_us_ensure_real((ID *)sc->clip);
if (screen && sc->view == SC_VIEW_CLIP) {
ScrArea *area;
SpaceLink *sl;
for (area = screen->areabase.first; area; area = area->next) {
for (sl = area->spacedata.first; sl; sl = sl->next) {
if (sl->spacetype == SPACE_CLIP) {
SpaceClip *cur_sc = (SpaceClip *)sl;
if (cur_sc != sc) {
if (cur_sc->view == SC_VIEW_CLIP) {
if (cur_sc->clip == old_clip) {
old_clip_visible = true;
}
}
else {
if (ELEM(cur_sc->clip, old_clip, NULL)) {
cur_sc->clip = clip;
}
}
}
}
}
}
}
/* If clip is no longer visible on screen, free memory used by its cache */
if (old_clip && old_clip != clip && !old_clip_visible) {
BKE_movieclip_clear_cache(old_clip);
}
if (C) {
WM_event_add_notifier(C, NC_MOVIECLIP | NA_SELECTED, sc->clip);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Masking Editing Functions
* \{ */
Mask *ED_space_clip_get_mask(SpaceClip *sc)
{
return sc->mask_info.mask;
}
void ED_space_clip_set_mask(bContext *C, SpaceClip *sc, Mask *mask)
{
sc->mask_info.mask = mask;
id_us_ensure_real((ID *)sc->mask_info.mask);
if (C) {
WM_event_add_notifier(C, NC_MASK | NA_SELECTED, mask);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Pre-Fetching Functions
* \{ */
typedef struct PrefetchJob {
MovieClip *clip;
int start_frame, current_frame, end_frame;
short render_size, render_flag;
} PrefetchJob;
typedef struct PrefetchQueue {
int initial_frame, current_frame, start_frame, end_frame;
short render_size, render_flag;
/* If true pre-fetching goes forward in time,
* otherwise it goes backwards in time (starting from current frame).
*/
bool forward;
SpinLock spin;
short *stop;
short *do_update;
float *progress;
} PrefetchQueue;
/* check whether pre-fetching is allowed */
static bool check_prefetch_break(void)
{
return G.is_break;
}
/* read file for specified frame number to the memory */
static uchar *prefetch_read_file_to_memory(
MovieClip *clip, int current_frame, short render_size, short render_flag, size_t *r_size)
{
MovieClipUser user = {0};
user.framenr = current_frame;
user.render_size = render_size;
user.render_flag = render_flag;
char name[FILE_MAX];
BKE_movieclip_filename_for_frame(clip, &user, name);
int file = BLI_open(name, O_BINARY | O_RDONLY, 0);
if (file == -1) {
return NULL;
}
const size_t size = BLI_file_descriptor_size(file);
if (size < 1) {
close(file);
return NULL;
}
uchar *mem = MEM_mallocN(size, "movieclip prefetch memory file");
if (mem == NULL) {
close(file);
return NULL;
}
if (read(file, mem, size) != size) {
close(file);
MEM_freeN(mem);
return NULL;
}
*r_size = size;
close(file);
return mem;
}
/* find first uncached frame within prefetching frame range */
static int prefetch_find_uncached_frame(MovieClip *clip,
int from_frame,
int end_frame,
short render_size,
short render_flag,
short direction)
{
int current_frame;
MovieClipUser user = {0};
user.render_size = render_size;
user.render_flag = render_flag;
if (direction > 0) {
for (current_frame = from_frame; current_frame <= end_frame; current_frame++) {
user.framenr = current_frame;
if (!BKE_movieclip_has_cached_frame(clip, &user)) {
break;
}
}
}
else {
for (current_frame = from_frame; current_frame >= end_frame; current_frame--) {
user.framenr = current_frame;
if (!BKE_movieclip_has_cached_frame(clip, &user)) {
break;
}
}
}
return current_frame;
}
/* get memory buffer for first uncached frame within prefetch frame range */
static uchar *prefetch_thread_next_frame(PrefetchQueue *queue,
MovieClip *clip,
size_t *r_size,
int *r_current_frame)
{
uchar *mem = NULL;
BLI_spin_lock(&queue->spin);
if (!*queue->stop && !check_prefetch_break() &&
IN_RANGE_INCL(queue->current_frame, queue->start_frame, queue->end_frame)) {
int current_frame;
if (queue->forward) {
current_frame = prefetch_find_uncached_frame(clip,
queue->current_frame + 1,
queue->end_frame,
queue->render_size,
queue->render_flag,
1);
/* switch direction if read frames from current up to scene end frames */
if (current_frame > queue->end_frame) {
queue->current_frame = queue->initial_frame;
queue->forward = false;
}
}
if (!queue->forward) {
current_frame = prefetch_find_uncached_frame(clip,
queue->current_frame - 1,
queue->start_frame,
queue->render_size,
queue->render_flag,
-1);
}
if (IN_RANGE_INCL(current_frame, queue->start_frame, queue->end_frame)) {
int frames_processed;
mem = prefetch_read_file_to_memory(
clip, current_frame, queue->render_size, queue->render_flag, r_size);
*r_current_frame = current_frame;
queue->current_frame = current_frame;
if (queue->forward) {
frames_processed = queue->current_frame - queue->initial_frame;
}
else {
frames_processed = (queue->end_frame - queue->initial_frame) +
(queue->initial_frame - queue->current_frame);
}
*queue->do_update = 1;
*queue->progress = (float)frames_processed / (queue->end_frame - queue->start_frame);
}
}
BLI_spin_unlock(&queue->spin);
return mem;
}
static void prefetch_task_func(TaskPool *__restrict pool, void *task_data)
{
PrefetchQueue *queue = (PrefetchQueue *)BLI_task_pool_user_data(pool);
MovieClip *clip = (MovieClip *)task_data;
uchar *mem;
size_t size;
int current_frame;
while ((mem = prefetch_thread_next_frame(queue, clip, &size, &current_frame))) {
ImBuf *ibuf;
MovieClipUser user = {0};
int flag = IB_rect | IB_multilayer | IB_alphamode_detect | IB_metadata;
int result;
char *colorspace_name = NULL;
const bool use_proxy = (clip->flag & MCLIP_USE_PROXY) &&
(queue->render_size != MCLIP_PROXY_RENDER_SIZE_FULL);
user.framenr = current_frame;
user.render_size = queue->render_size;
user.render_flag = queue->render_flag;
/* Proxies are stored in the display space. */
if (!use_proxy) {
colorspace_name = clip->colorspace_settings.name;
}
ibuf = IMB_ibImageFromMemory(mem, size, flag, colorspace_name, "prefetch frame");
if (ibuf == NULL) {
continue;
}
BKE_movieclip_convert_multilayer_ibuf(ibuf);
result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf);
IMB_freeImBuf(ibuf);
MEM_freeN(mem);
if (!result) {
/* no more space in the cache, stop reading frames */
*queue->stop = 1;
break;
}
}
}
static void start_prefetch_threads(MovieClip *clip,
int start_frame,
int current_frame,
int end_frame,
short render_size,
short render_flag,
short *stop,
short *do_update,
float *progress)
{
int tot_thread = BLI_task_scheduler_num_threads();
/* initialize queue */
PrefetchQueue queue;
BLI_spin_init(&queue.spin);
queue.current_frame = current_frame;
queue.initial_frame = current_frame;
queue.start_frame = start_frame;
queue.end_frame = end_frame;
queue.render_size = render_size;
queue.render_flag = render_flag;
queue.forward = 1;
queue.stop = stop;
queue.do_update = do_update;
queue.progress = progress;
TaskPool *task_pool = BLI_task_pool_create(&queue, TASK_PRIORITY_LOW);
for (int i = 0; i < tot_thread; i++) {
BLI_task_pool_push(task_pool, prefetch_task_func, clip, false, NULL);
}
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
BLI_spin_end(&queue.spin);
}
static bool prefetch_movie_frame(
MovieClip *clip, int frame, short render_size, short render_flag, short *stop)
{
MovieClipUser user = {0};
ImBuf *ibuf;
if (check_prefetch_break() || *stop) {
return false;
}
user.framenr = frame;
user.render_size = render_size;
user.render_flag = render_flag;
if (!BKE_movieclip_has_cached_frame(clip, &user)) {
ibuf = BKE_movieclip_anim_ibuf_for_frame(clip, &user);
if (ibuf) {
int result;
result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf);
if (!result) {
/* no more space in the cache, we could stop prefetching here */
*stop = 1;
}
IMB_freeImBuf(ibuf);
}
else {
/* error reading frame, fair enough stop attempting further reading */
*stop = 1;
}
}
return true;
}
static void do_prefetch_movie(MovieClip *clip,
int start_frame,
int current_frame,
int end_frame,
short render_size,
short render_flag,
short *stop,
short *do_update,
float *progress)
{
int frame;
int frames_processed = 0;
/* read frames starting from current frame up to scene end frame */
for (frame = current_frame; frame <= end_frame; frame++) {
if (!prefetch_movie_frame(clip, frame, render_size, render_flag, stop)) {
return;
}
frames_processed++;
*do_update = 1;
*progress = (float)frames_processed / (end_frame - start_frame);
}
/* read frames starting from current frame up to scene start frame */
for (frame = current_frame; frame >= start_frame; frame--) {
if (!prefetch_movie_frame(clip, frame, render_size, render_flag, stop)) {
return;
}
frames_processed++;
*do_update = 1;
*progress = (float)frames_processed / (end_frame - start_frame);
}
}
static void prefetch_startjob(void *pjv, short *stop, short *do_update, float *progress)
{
PrefetchJob *pj = pjv;
if (pj->clip->source == MCLIP_SRC_SEQUENCE) {
/* read sequence files in multiple threads */
start_prefetch_threads(pj->clip,
pj->start_frame,
pj->current_frame,
pj->end_frame,
pj->render_size,
pj->render_flag,
stop,
do_update,
progress);
}
else if (pj->clip->source == MCLIP_SRC_MOVIE) {
/* read movie in a single thread */
do_prefetch_movie(pj->clip,
pj->start_frame,
pj->current_frame,
pj->end_frame,
pj->render_size,
pj->render_flag,
stop,
do_update,
progress);
}
else {
BLI_assert(!"Unknown movie clip source when prefetching frames");
}
}
static void prefetch_freejob(void *pjv)
{
PrefetchJob *pj = pjv;
MEM_freeN(pj);
}
static int prefetch_get_start_frame(const bContext *C)
{
Scene *scene = CTX_data_scene(C);
return SFRA;
}
static int prefetch_get_final_frame(const bContext *C)
{
Scene *scene = CTX_data_scene(C);
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
int end_frame;
/* check whether all the frames from prefetch range are cached */
end_frame = EFRA;
if (clip->len) {
end_frame = min_ii(end_frame, SFRA + clip->len - 1);
}
return end_frame;
}
/* returns true if early out is possible */
static bool prefetch_check_early_out(const bContext *C)
{
SpaceClip *sc = CTX_wm_space_clip(C);
MovieClip *clip = ED_space_clip_get_clip(sc);
int first_uncached_frame, end_frame;
int clip_len;
if (clip == NULL) {
return true;
}
clip_len = BKE_movieclip_get_duration(clip);
/* check whether all the frames from prefetch range are cached */
end_frame = prefetch_get_final_frame(C);
first_uncached_frame = prefetch_find_uncached_frame(
clip, sc->user.framenr, end_frame, sc->user.render_size, sc->user.render_flag, 1);
if (first_uncached_frame > end_frame || first_uncached_frame == clip_len) {
int start_frame = prefetch_get_start_frame(C);
first_uncached_frame = prefetch_find_uncached_frame(
clip, sc->user.framenr, start_frame, sc->user.render_size, sc->user.render_flag, -1);
if (first_uncached_frame < start_frame) {
return true;
}
}
return false;
}
void clip_start_prefetch_job(const bContext *C)
{
wmJob *wm_job;
PrefetchJob *pj;
SpaceClip *sc = CTX_wm_space_clip(C);
if (prefetch_check_early_out(C)) {
return;
}
wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_scene(C),
"Prefetching",
WM_JOB_PROGRESS,
WM_JOB_TYPE_CLIP_PREFETCH);
/* create new job */
pj = MEM_callocN(sizeof(PrefetchJob), "prefetch job");
pj->clip = ED_space_clip_get_clip(sc);
pj->start_frame = prefetch_get_start_frame(C);
pj->current_frame = sc->user.framenr;
pj->end_frame = prefetch_get_final_frame(C);
pj->render_size = sc->user.render_size;
pj->render_flag = sc->user.render_flag;
WM_jobs_customdata_set(wm_job, pj, prefetch_freejob);
WM_jobs_timer(wm_job, 0.2, NC_MOVIECLIP | ND_DISPLAY, 0);
WM_jobs_callbacks(wm_job, prefetch_startjob, NULL, NULL, NULL);
G.is_break = false;
/* and finally start the job */
WM_jobs_start(CTX_wm_manager(C), wm_job);
}
void ED_clip_view_lock_state_store(const bContext *C, ClipViewLockState *state)
{
SpaceClip *space_clip = CTX_wm_space_clip(C);
BLI_assert(space_clip != NULL);
state->offset_x = space_clip->xof;
state->offset_y = space_clip->yof;
state->zoom = space_clip->zoom;
state->lock_offset_x = 0.0f;
state->lock_offset_y = 0.0f;
if ((space_clip->flag & SC_LOCK_SELECTION) == 0) {
return;
}
if (!clip_view_calculate_view_selection(
C, false, &state->offset_x, &state->offset_y, &state->zoom)) {
return;
}
state->lock_offset_x = space_clip->xlockof;
state->lock_offset_y = space_clip->ylockof;
}
void ED_clip_view_lock_state_restore_no_jump(const bContext *C, const ClipViewLockState *state)
{
SpaceClip *space_clip = CTX_wm_space_clip(C);
BLI_assert(space_clip != NULL);
if ((space_clip->flag & SC_LOCK_SELECTION) == 0) {
return;
}
float offset_x, offset_y;
float zoom;
if (!clip_view_calculate_view_selection(C, false, &offset_x, &offset_y, &zoom)) {
return;
}
space_clip->xlockof = state->offset_x + state->lock_offset_x - offset_x;
space_clip->ylockof = state->offset_y + state->lock_offset_y - offset_y;
}
/** \} */