diff options
Diffstat (limited to 'source/blender/blenkernel')
21 files changed, 2845 insertions, 654 deletions
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index a790715ac98..86576f9a11b 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -42,7 +42,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 274 -#define BLENDER_SUBVERSION 3 +#define BLENDER_SUBVERSION 4 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 5 diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 4849c66df1b..aacb7a4066b 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -136,6 +136,14 @@ bool BKE_camera_view_frame_fit_to_coords( void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings); +/* Camera multi-view API */ + +struct Object *BKE_camera_multiview_render(struct Scene *scene, struct Object *camera, const char *viewname); +void BKE_camera_multiview_view_matrix(struct RenderData *rd, struct Object *camera, const bool is_left, float r_viewmat[4][4]); +void BKE_camera_multiview_model_matrix(struct RenderData *rd, struct Object *camera, const char *viewname, float r_modelmat[4][4]); +float BKE_camera_multiview_shift_x(struct RenderData *rd, struct Object *camera, const char *viewname); +void BKE_camera_multiview_params(struct RenderData *rd, struct CameraParams *params, struct Object *camera, const char *viewname); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 3b7ba24d20b..ad191968a40 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -45,34 +45,34 @@ struct Object; struct ImageFormatData; struct ImagePool; struct Main; +struct ReportList; #define IMA_MAX_SPACE 64 void BKE_images_init(void); void BKE_images_exit(void); +void BKE_image_free_packedfiles(struct Image *image); +void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); /* call from library */ void BKE_image_free(struct Image *image); void BKE_imbuf_stamp_info(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf); -void BKE_image_stamp_buf( - struct Scene *scene, struct Object *camera, - unsigned char *rect, float *rectf, int width, int height, int channels); +void BKE_image_stamp_buf(struct Scene *scene, struct Object *camera, unsigned char *rect, float *rectf, int width, int height, int channels); bool BKE_imbuf_alpha_test(struct ImBuf *ibuf); int BKE_imbuf_write_stamp(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); +void BKE_imbuf_write_prepare(struct ImBuf *ibuf, struct ImageFormatData *imf); int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf, const bool is_copy); - void BKE_image_path_from_imformat( char *string, const char *base, const char *relbase, int frame, - const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames); + const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix); void BKE_image_path_from_imtype( char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames); - -bool BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); -bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); + const char imtype, const bool use_ext, const bool use_frames, const char *suffix); +int BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); char BKE_image_ftype_to_imtype(const int ftype); int BKE_image_imtype_to_ftype(const char imtype); @@ -103,6 +103,7 @@ void BKE_image_tag_time(struct Image *ima); /* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */ /* should be used in conjunction with an ID * to Image. */ struct ImageUser; +struct RenderData; struct RenderPass; struct RenderResult; @@ -172,11 +173,12 @@ struct Image *BKE_image_load_exists(const char *filepath); /* adds image, adds ibuf, generates color or pattern */ struct Image *BKE_image_add_generated( - struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]); + struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d); /* adds image from imbuf, owns imbuf */ struct Image *BKE_image_add_from_imbuf(struct ImBuf *ibuf); /* for reload, refresh, pack */ +void BKE_image_init_imageuser(struct Image *ima, struct ImageUser *iuser); void BKE_image_signal(struct Image *ima, struct ImageUser *iuser, int signal); void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, @@ -184,6 +186,8 @@ void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, /* ensures an Image exists for viewing nodes or render */ struct Image *BKE_image_verify_viewer(int type, const char *name); +/* ensures the view node cache is compatible with the scene views */ +void BKE_image_verify_viewer_views(const struct RenderData *rd, struct Image *ima, struct ImageUser *iuser); /* called on frame change or before render */ void BKE_image_user_frame_calc(struct ImageUser *iuser, int cfra, int fieldnr); @@ -195,13 +199,23 @@ void BKE_image_update_frame(const struct Main *bmain, int cfra); /* sets index offset for multilayer files */ struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct ImageUser *iuser); +/* sets index offset for multiview files */ +void BKE_image_multiview_index(struct Image *ima, struct ImageUser *iuser); + /* for multilayer images as well as for render-viewer */ +bool BKE_image_is_multilayer(struct Image *ima); struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima); void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima); +/* for multilayer images as well as for singlelayer */ +bool BKE_image_is_openexr(struct Image *ima); + /* for multiple slot render, call this before render */ void BKE_image_backup_render(struct Scene *scene, struct Image *ima); - + +/* for singlelayer openexr saving */ +bool BKE_image_save_openexr_multiview(struct Image *ima, struct ImBuf *ibuf, const char *filepath, const int flags); + /* goes over all textures that use images */ void BKE_image_free_all_textures(void); @@ -212,6 +226,7 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame); void BKE_image_all_free_anim_ibufs(int except_frame); void BKE_image_memorypack(struct Image *ima); +void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath); /* prints memory statistics for images */ void BKE_image_print_memlist(void); @@ -243,7 +258,8 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame); /* Guess offset for the first frame in the sequence */ int BKE_image_sequence_guess_offset(struct Image *image); - +bool BKE_image_has_anim(struct Image *image); +bool BKE_image_has_packedfile(struct Image *image); bool BKE_image_is_animated(struct Image *image); bool BKE_image_is_dirty(struct Image *image); void BKE_image_file_format_set(struct Image *image, int ftype); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ee63771406e..eca15a62c70 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -941,6 +941,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria #define CMP_NODE_MAP_RANGE 319 #define CMP_NODE_PLANETRACKDEFORM 320 #define CMP_NODE_CORNERPIN 321 +#define CMP_NODE_SWITCH_VIEW 322 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -975,7 +976,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria /* API */ void ntreeCompositExecTree(struct Scene *scene, struct bNodeTree *ntree, struct RenderData *rd, int rendering, int do_previews, - const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); + const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings, + const char *view_name); void ntreeCompositTagRender(struct Scene *sce); int ntreeCompositTagAnimated(struct bNodeTree *ntree); void ntreeCompositTagGenerators(struct bNodeTree *ntree); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 27c89dd3a09..79778d504a5 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -70,6 +70,7 @@ void BKE_scene_free(struct Scene *sce); struct Scene *BKE_scene_add(struct Main *bmain, const char *name); /* base functions */ +struct Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name); struct Base *BKE_scene_base_find(struct Scene *scene, struct Object *ob); struct Base *BKE_scene_base_add(struct Scene *sce, struct Object *ob); void BKE_scene_base_unlink(struct Scene *sce, struct Base *base); @@ -122,6 +123,9 @@ void BKE_scene_update_for_newframe_ex(struct EvaluationContext *eval_ctx, struct struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const char *name); bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl); +struct SceneRenderView *BKE_scene_add_render_view(struct Scene *sce, const char *name); +bool BKE_scene_remove_render_view(struct Scene *scene, struct SceneRenderView *srv); + /* render profile */ int get_render_subsurf_level(const struct RenderData *r, int level); int get_render_child_particle_number(const struct RenderData *r, int num); @@ -142,6 +146,23 @@ int BKE_render_num_threads(const struct RenderData *r); double BKE_scene_unit_scale(const struct UnitSettings *unit, const int unit_type, double value); +/* multiview */ +bool BKE_scene_multiview_is_stereo3d(const struct RenderData *rd); +bool BKE_scene_multiview_is_render_view_active(const struct RenderData *rd, const struct SceneRenderView *srv); +bool BKE_scene_multiview_is_render_view_first(const struct RenderData *rd, const char *viewname); +bool BKE_scene_multiview_is_render_view_last(const struct RenderData *rd, const char *viewname); +size_t BKE_scene_multiview_num_views_get(const struct RenderData *rd); +struct SceneRenderView *BKE_scene_multiview_render_view_findindex(const struct RenderData *rd, const int view_id); +const char *BKE_scene_multiview_render_view_name_get(const struct RenderData *rd, const int view_id); +size_t BKE_scene_multiview_view_id_get(const struct RenderData *rd, const char *viewname); +void BKE_scene_multiview_filepath_get(struct SceneRenderView *srv, const char *filepath, char *r_filepath); +void BKE_scene_multiview_view_filepath_get(const struct RenderData *rd, const char *filepath, const char *view, char *r_filepath); +const char *BKE_scene_multiview_view_suffix_get(const struct RenderData *rd, const char *viewname); +const char *BKE_scene_multiview_view_id_suffix_get(const struct RenderData *rd, const size_t view_id); +void BKE_scene_multiview_view_prefix_get(struct Scene *scene, const char *name, char *rprefix, char **rext); +void BKE_scene_multiview_videos_dimensions_get(const struct RenderData *rd, const size_t width, const size_t height, size_t *r_width, size_t *r_height); +size_t BKE_scene_multiview_num_videos_get(const struct RenderData *rd); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 84818276e7b..4a45489933c 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -41,6 +41,7 @@ struct Mask; struct Scene; struct Sequence; struct SequenceModifierData; +struct Stereo3dFormat; struct StripElem; struct bSound; @@ -99,6 +100,7 @@ typedef struct SeqRenderData { float motion_blur_shutter; bool skip_cache; bool is_proxy_render; + size_t view_id; } SeqRenderData; void BKE_sequencer_new_render_data( @@ -223,6 +225,7 @@ void BKE_sequencer_base_clipboard_pointers_store(struct ListBase *seqbase); void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain); void BKE_sequence_free(struct Scene *scene, struct Sequence *seq); +void BKE_sequence_free_anim(struct Sequence *seq); const char *BKE_sequence_give_name(struct Sequence *seq); void BKE_sequence_calc(struct Scene *scene, struct Sequence *seq); void BKE_sequence_calc_disp(struct Scene *scene, struct Sequence *seq); @@ -235,7 +238,7 @@ struct StripElem *BKE_sequencer_give_stripelem(struct Sequence *seq, int cfra); void BKE_sequencer_update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_seq, int len_change, int ibuf_change); bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, struct Sequence *seq, float cfra); -struct SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list); +void BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list, ListBase *queue); void BKE_sequencer_proxy_rebuild(struct SeqIndexBuildContext *context, short *stop, short *do_update, float *progress); void BKE_sequencer_proxy_rebuild_finish(struct SeqIndexBuildContext *context, bool stop); @@ -355,6 +358,10 @@ typedef struct SeqLoadInfo { int len; /* only for image strips */ char path[1024]; /* 1024 = FILE_MAX */ + /* multiview */ + char views_format; + struct Stereo3dFormat *stereo3d_format; + /* return values */ char name[64]; struct Sequence *seq_sound; /* for movie's */ @@ -399,7 +406,7 @@ struct Sequence *BKE_sequencer_add_sound_strip(struct bContext *C, ListBase *seq struct Sequence *BKE_sequencer_add_movie_strip(struct bContext *C, ListBase *seqbasep, struct SeqLoadInfo *seq_load); /* view3d draw callback, run when not in background view */ -typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, char[256]); +typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, const char *, char[256]); extern SequencerDrawView sequencer_view3d_cb; /* copy/paste */ diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h index bc06be0295a..ca295c51f5d 100644 --- a/source/blender/blenkernel/BKE_writeavi.h +++ b/source/blender/blenkernel/BKE_writeavi.h @@ -43,16 +43,20 @@ struct ReportList; struct Scene; typedef struct bMovieHandle { - int (*start_movie)(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); - int (*append_movie)(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); - void (*end_movie)(void); - int (*get_next_frame)(struct RenderData *rd, struct ReportList *reports); /* optional */ - void (*get_movie_path)(char *string, struct RenderData *rd, bool preview); /* optional */ + int (*start_movie)(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, + struct ReportList *reports, bool preview, const char *suffix); + int (*append_movie)(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); + void (*end_movie)(void *context_v); + int (*get_next_frame)(void *context_v, struct RenderData *rd, struct ReportList *reports); /* optional */ + void (*get_movie_path)(char *string, struct RenderData *rd, bool preview, const char *suffix); /* optional */ + void *(*context_create)(void); + void (*context_free)(void *context_v); } bMovieHandle; bMovieHandle *BKE_movie_handle_get(const char imtype); -void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview); +void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix); +void BKE_context_create(bMovieHandle *mh); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 951fa501f9c..a40c31022e3 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -68,11 +68,11 @@ struct RenderData; struct ReportList; struct Scene; -int BKE_ffmpeg_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); -void BKE_ffmpeg_end(void); -int BKE_ffmpeg_append(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); -void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview); +int BKE_ffmpeg_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix); +void BKE_ffmpeg_end(void *context_v); +int BKE_ffmpeg_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); +void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix); void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); @@ -82,6 +82,9 @@ bool BKE_ffmpeg_alpha_channel_is_supported(struct RenderData *rd); int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str); void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_); +void *BKE_ffmpeg_context_create(void); +void BKE_ffmpeg_context_free(void *context_v); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_writeframeserver.h b/source/blender/blenkernel/BKE_writeframeserver.h index b7d601ea769..2f8bce31e41 100644 --- a/source/blender/blenkernel/BKE_writeframeserver.h +++ b/source/blender/blenkernel/BKE_writeframeserver.h @@ -40,11 +40,14 @@ struct RenderData; struct ReportList; struct Scene; -int BKE_frameserver_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); -void BKE_frameserver_end(void); -int BKE_frameserver_append(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); -int BKE_frameserver_loop(struct RenderData *rd, struct ReportList *reports); +int BKE_frameserver_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, + struct ReportList *reports, bool preview, const char *suffix); +void BKE_frameserver_end(void *context_v); +int BKE_frameserver_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char*suffix, struct ReportList *reports); +int BKE_frameserver_loop(void *context_v, struct RenderData *rd, struct ReportList *reports); +void *BKE_frameserver_context_create(void); +void BKE_frameserver_context_free(void *context_v); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index e94f30da0c5..bb3731218ed 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -423,10 +423,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int { Image *ima; ima = (Image *)id; - if (ima->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { + if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) { - if (!ima->packedfile) { + if (!BKE_image_has_packedfile(ima)) { BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); BKE_image_walk_all_users(bmain, ima, bpath_traverse_image_user_cb); } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 5b4d7ba8214..fec6542ac47 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -30,16 +30,19 @@ */ #include <stdlib.h> +#include <stddef.h> #include "DNA_camera_types.h" #include "DNA_lamp_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_view3d_types.h" +#include "DNA_ID.h" #include "BLI_math.h" -#include "BLI_utildefines.h" #include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" #include "BKE_animsys.h" #include "BKE_camera.h" @@ -47,6 +50,7 @@ #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "GPU_compositing.h" @@ -71,6 +75,10 @@ void *BKE_camera_add(Main *bmain, const char *name) GPU_fx_compositor_init_dof_settings(&cam->gpu_dof); + /* stereoscopy 3d */ + cam->stereo.interocular_distance = 0.065f; + cam->stereo.convergence_distance = 30.f * 0.065f; + return cam; } @@ -695,6 +703,250 @@ bool BKE_camera_view_frame_fit_to_coords( return camera_frame_fit_calc_from_data(¶ms, &data_cb, r_co, r_scale); } +/******************* multiview matrix functions ***********************/ + +static void camera_model_matrix(Object *camera, float r_modelmat[4][4]) +{ + copy_m4_m4(r_modelmat, camera->obmat); +} + +static void camera_stereo3d_model_matrix(Object *camera, const bool is_left, float r_modelmat[4][4]) +{ + Camera *data = (Camera *)camera->data; + float interocular_distance, convergence_distance; + short convergence_mode, pivot; + float sizemat[4][4]; + + float fac = 1.0f; + float fac_signed; + + interocular_distance = data->stereo.interocular_distance; + convergence_distance = data->stereo.convergence_distance; + convergence_mode = data->stereo.convergence_mode; + pivot = data->stereo.pivot; + + if (((pivot == CAM_S3D_PIVOT_LEFT) && is_left) || + ((pivot == CAM_S3D_PIVOT_RIGHT) && !is_left)) + { + return camera_model_matrix(camera, r_modelmat); + } + else { + float size[3]; + mat4_to_size(size, camera->obmat); + size_to_mat4(sizemat, size); + } + + if (pivot == CAM_S3D_PIVOT_CENTER) + fac = 0.5f; + + fac_signed = is_left ? fac : -fac; + + /* rotation */ + if (convergence_mode == CAM_S3D_TOE) { + float angle; + float angle_sin, angle_cos; + float toeinmat[4][4]; + float rotmat[4][4]; + + unit_m4(rotmat); + + if (pivot == CAM_S3D_PIVOT_CENTER) { + fac = -fac; + fac_signed = -fac_signed; + } + + angle = atanf((interocular_distance * 0.5f) / convergence_distance) / fac; + + angle_cos = cosf(angle * fac_signed); + angle_sin = sinf(angle * fac_signed); + + rotmat[0][0] = angle_cos; + rotmat[2][0] = -angle_sin; + rotmat[0][2] = angle_sin; + rotmat[2][2] = angle_cos; + + if (pivot == CAM_S3D_PIVOT_CENTER) { + /* set the rotation */ + copy_m4_m4(toeinmat, rotmat); + /* set the translation */ + toeinmat[3][0] = interocular_distance * fac_signed; + + /* transform */ + normalize_m4_m4(r_modelmat, camera->obmat); + mul_m4_m4m4(r_modelmat, r_modelmat, toeinmat); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } + else { /* CAM_S3D_PIVOT_LEFT, CAM_S3D_PIVOT_RIGHT */ + /* rotate perpendicular to the interocular line */ + normalize_m4_m4(r_modelmat, camera->obmat); + mul_m4_m4m4(r_modelmat, r_modelmat, rotmat); + + /* translate along the interocular line */ + unit_m4(toeinmat); + toeinmat[3][0] = -interocular_distance * fac_signed; + mul_m4_m4m4(r_modelmat, r_modelmat, toeinmat); + + /* rotate to toe-in angle */ + mul_m4_m4m4(r_modelmat, r_modelmat, rotmat); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } + } + else { + normalize_m4_m4(r_modelmat, camera->obmat); + + /* translate - no rotation in CAM_S3D_OFFAXIS, CAM_S3D_PARALLEL */ + translate_m4(r_modelmat, -interocular_distance * fac_signed, 0.0f, 0.0f); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } +} + +/* the view matrix is used by the viewport drawing, it is basically the inverted model matrix */ +void BKE_camera_multiview_view_matrix(RenderData *rd, Object *camera, const bool is_left, float r_viewmat[4][4]) +{ + BKE_camera_multiview_model_matrix(rd, camera, is_left ? STEREO_LEFT_NAME : STEREO_RIGHT_NAME, r_viewmat); + invert_m4(r_viewmat); +} + +/* left is the default */ +static bool camera_is_left(const char *viewname) +{ + if (viewname && viewname[0] != '\0') { + return !STREQ(viewname, STEREO_RIGHT_NAME); + } + return true; +} + +void BKE_camera_multiview_model_matrix(RenderData *rd, Object *camera, const char *viewname, float r_modelmat[4][4]) +{ + const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; + + if (!is_multiview) { + camera_model_matrix(camera, r_modelmat); + } + else if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + camera_model_matrix(camera, r_modelmat); + } + else { /* SCE_VIEWS_SETUP_BASIC */ + const bool is_left = camera_is_left(viewname); + camera_stereo3d_model_matrix(camera, is_left, r_modelmat); + } + normalize_m4(r_modelmat); +} + +static Object *camera_multiview_advanced(Scene *scene, Object *camera, const char *suffix) +{ + SceneRenderView *srv; + char name[MAX_NAME]; + const char *camera_name = camera->id.name + 2; + const int len_name = strlen(camera_name); + + name[0] = '\0'; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + const int len_suffix = strlen(srv->suffix); + + if (len_name < len_suffix) + continue; + + if (STREQ(camera_name + (len_name - len_suffix), srv->suffix)) { + BLI_snprintf(name, sizeof(name), "%.*s%s", (len_name - len_suffix), camera_name, suffix); + break; + } + } + + if (name[0] != '\0') { + Base *base = BKE_scene_base_find_by_name(scene, name); + if (base) { + return base->object; + } + } + + return camera; +} + +/* returns the camera to be used for render */ +Object *BKE_camera_multiview_render(Scene *scene, Object *camera, const char *viewname) +{ + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + + if (!is_multiview) { + return camera; + } + else if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + return camera; + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + const char *suffix = BKE_scene_multiview_view_suffix_get(&scene->r, viewname); + return camera_multiview_advanced(scene, camera, suffix); + } +} + +static float camera_stereo3d_shift_x(Object *camera, const char *viewname) +{ + Camera *data = camera->data; + float shift = data->shiftx; + float interocular_distance, convergence_distance; + short convergence_mode, pivot; + bool is_left = true; + + float fac = 1.0f; + float fac_signed; + + if (viewname && viewname[0]) { + is_left = STREQ(viewname, STEREO_LEFT_NAME); + } + + interocular_distance = data->stereo.interocular_distance; + convergence_distance = data->stereo.convergence_distance; + convergence_mode = data->stereo.convergence_mode; + pivot = data->stereo.pivot; + + if (((pivot == CAM_S3D_PIVOT_LEFT) && is_left) || + ((pivot == CAM_S3D_PIVOT_RIGHT) && !is_left)) + { + return shift; + } + + if (pivot == CAM_S3D_PIVOT_CENTER) + fac = 0.5f; + + fac_signed = is_left ? fac : -fac; + + /* Note: in viewport, parallel renders as offaxis, but in render it does parallel */ + if (ELEM(convergence_mode, CAM_S3D_OFFAXIS, CAM_S3D_PARALLEL)) { + shift += ((interocular_distance / data->sensor_x) * (data->lens / convergence_distance)) * fac_signed; + } + + return shift; +} + +float BKE_camera_multiview_shift_x(RenderData *rd, Object *camera, const char *viewname) +{ + const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; + Camera *data = camera->data; + + if (!is_multiview) { + return data->shiftx; + } + else if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + return data->shiftx; + } + else { /* SCE_VIEWS_SETUP_BASIC */ + return camera_stereo3d_shift_x(camera, viewname); + } +} + +void BKE_camera_multiview_params(RenderData *rd, CameraParams *params, Object *camera, const char *viewname) +{ + params->shiftx = BKE_camera_multiview_shift_x(rd, camera, viewname); +} + void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings) { if (camera->type == OB_CAMERA) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9b00a0150ad..f852d886ae9 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -99,6 +99,12 @@ static SpinLock image_spin; +/* prototypes */ +static size_t image_num_files(struct Image *ima); +static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r); +static void image_update_views_format(Image *ima, ImageUser *iuser); +static void image_add_view(Image *ima, const char *viewname, const char *filepath); + /* max int, to indicate we don't store sequences in ibuf */ #define IMA_NO_INDEX 0x7FEFEFEF @@ -257,6 +263,46 @@ static void image_free_cached_frames(Image *image) } } +static void image_free_packedfiles(Image *ima) +{ + while (ima->packedfiles.last) { + ImagePackedFile *imapf = ima->packedfiles.last; + if (imapf->packedfile) { + freePackedFile(imapf->packedfile); + } + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); + } +} + +void BKE_image_free_packedfiles(Image *ima) +{ + image_free_packedfiles(ima); +} + +static void image_free_views(Image *ima) +{ + BLI_freelistN(&ima->views); +} + +void BKE_image_free_views(Image *image) +{ + image_free_views(image); +} + +static void image_free_anims(Image *ima) +{ + while (ima->anims.last) { + ImageAnim *ia = ima->anims.last; + if (ia->anim) { + IMB_free_anim(ia->anim); + ia->anim = NULL; + } + BLI_remlink(&ima->anims, ia); + MEM_freeN(ia); + } +} + /** * Simply free the image data from memory, * on display the image can load again (except for render buffers). @@ -265,8 +311,7 @@ void BKE_image_free_buffers(Image *ima) { image_free_cached_frames(ima); - if (ima->anim) IMB_free_anim(ima->anim); - ima->anim = NULL; + image_free_anims(ima); if (ima->rr) { RE_FreeRenderResult(ima->rr); @@ -290,10 +335,9 @@ void BKE_image_free(Image *ima) int a; BKE_image_free_buffers(ima); - if (ima->packedfile) { - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - } + + image_free_packedfiles(ima); + BKE_icon_delete(&ima->id); ima->id.icon_id = 0; @@ -305,6 +349,9 @@ void BKE_image_free(Image *ima) ima->renders[a] = NULL; } } + + image_free_views(ima); + MEM_freeN(ima->stereo3d_format); } /* only image block itself */ @@ -328,7 +375,9 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ ima->flag |= IMA_VIEW_AS_RENDER; BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); + ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); } + return ima; } @@ -359,6 +408,22 @@ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame) } } +static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) +{ + const ImagePackedFile *imapf_src; + + BLI_listbase_clear(lb_dst); + for (imapf_src = lb_src->first; imapf_src; imapf_src = imapf_src->next) { + ImagePackedFile *imapf_dst = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)"); + BLI_strncpy(imapf_dst->filepath, imapf_src->filepath, sizeof(imapf_dst->filepath)); + + if (imapf_src->packedfile) + imapf_dst->packedfile = dupPackedFile(imapf_src->packedfile); + + BLI_addtail(lb_dst, imapf_dst); + } +} + /* empty image block, of similar type and filename */ Image *BKE_image_copy(Main *bmain, Image *ima) { @@ -381,8 +446,10 @@ Image *BKE_image_copy(Main *bmain, Image *ima) BKE_color_managed_colorspace_settings_copy(&nima->colorspace_settings, &ima->colorspace_settings); - if (ima->packedfile) - nima->packedfile = dupPackedFile(ima->packedfile); + copy_image_packedfiles(&nima->packedfiles, &ima->packedfiles); + + nima->stereo3d_format = MEM_dupallocN(ima->stereo3d_format); + BLI_duplicatelist(&nima->views, &ima->views); if (ima->id.lib) { BKE_id_lib_local_paths(bmain, ima->id.lib, &nima->id); @@ -686,7 +753,9 @@ Image *BKE_image_load_exists_ex(const char *filepath, bool *r_exists) BLI_path_abs(strtest, ID_BLEND_PATH(G.main, &ima->id)); if (BLI_path_cmp(strtest, str) == 0) { - if (ima->anim == NULL || ima->id.us == 0) { + if ((BKE_image_has_anim(ima) == false) || + (ima->id.us == 0)) + { ima->id.us++; /* officially should not, it doesn't link here! */ if (ima->ok == 0) ima->ok = IMA_OK; @@ -774,13 +843,14 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char } /* adds new image block, creates ImBuf and initializes color */ -Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]) +Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d) { /* on save, type is changed to FILE in editsima.c */ Image *ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); if (ima) { - ImBuf *ibuf; + size_t view_id; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; /* BLI_strncpy(ima->name, name, FILE_MAX); */ /* don't do this, this writes in ain invalid filepath! */ ima->gen_x = width; @@ -790,13 +860,21 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei ima->gen_depth = depth; copy_v4_v4(ima->gen_color, color); - ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + for (view_id = 0; view_id < 2; view_id++) { + ImBuf *ibuf; + ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); + image_assign_ibuf(ima, ibuf, stereo3d ? view_id : IMA_NO_INDEX, 0); - /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ - IMB_freeImBuf(ibuf); + /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ + IMB_freeImBuf(ibuf); + if (!stereo3d) break; + + image_add_view(ima, names[view_id], ""); + } ima->ok = IMA_OK_LOADED; + if (stereo3d) + ima->flag |= IMA_IS_STEREO | IMA_IS_MULTIVIEW; } return ima; @@ -821,17 +899,76 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf) return ima; } +/* packs rects from memory as PNG + * convert multiview images to R_IMF_VIEWS_INDIVIDUAL + */ +static void image_memorypack_multiview(Image *ima) +{ + ImageView *iv; + size_t i; + + image_free_packedfiles(ima); + + for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, i, 0); + + ibuf->ftype = PNG; + ibuf->planes = R_IMF_PLANES_RGBA; + + /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; + BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], ""); + } + + IMB_saveiff(ibuf, iv->filepath, IB_rect | IB_mem); + + if (ibuf->encodedbuffer == NULL) { + printf("memory save for pack error\n"); + IMB_freeImBuf(ibuf); + image_free_packedfiles(ima); + return; + } + else { + ImagePackedFile *imapf; + PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); + + pf->data = ibuf->encodedbuffer; + pf->size = ibuf->encodedsize; + + imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); + BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath)); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + + ibuf->encodedbuffer = NULL; + ibuf->encodedsize = 0; + ibuf->userflags &= ~IB_BITMAPDIRTY; + } + IMB_freeImBuf(ibuf); + } + + if (ima->source == IMA_SRC_GENERATED) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; +} + /* packs rect from memory as PNG */ void BKE_image_memorypack(Image *ima) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf; + + if ((ima->flag & IMA_IS_MULTIVIEW)) + return image_memorypack_multiview(ima); + + ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); if (ibuf == NULL) return; - if (ima->packedfile) { - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - } + + image_free_packedfiles(ima); ibuf->ftype = PNG; ibuf->planes = R_IMF_PLANES_RGBA; @@ -841,11 +978,17 @@ void BKE_image_memorypack(Image *ima) printf("memory save for pack error\n"); } else { + ImagePackedFile *imapf; PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); pf->data = ibuf->encodedbuffer; pf->size = ibuf->encodedsize; - ima->packedfile = pf; + + imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); + BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath)); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + ibuf->encodedbuffer = NULL; ibuf->encodedsize = 0; ibuf->userflags &= ~IB_BITMAPDIRTY; @@ -859,6 +1002,28 @@ void BKE_image_memorypack(Image *ima) IMB_freeImBuf(ibuf); } +void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) +{ + const size_t totfiles = image_num_files(ima); + + if (totfiles == 1) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); + BLI_addtail(&ima->packedfiles, imapf); + imapf->packedfile = newPackedFile(reports, ima->name, basepath); + BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath)); + } + else { + ImageView *iv; + for (iv = ima->views.first; iv; iv = iv->next) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); + BLI_addtail(&ima->packedfiles, imapf); + + imapf->packedfile = newPackedFile(reports, iv->filepath, basepath); + BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath)); + } + } +} + void BKE_image_tag_time(Image *ima) { ima->lastused = PIL_check_seconds_timer_i(); @@ -1259,7 +1424,7 @@ char BKE_imtype_from_arg(const char *imtype_arg) else return R_IMF_IMTYPE_INVALID; } -static bool image_path_ensure_ext(char *string, const char imtype, const ImageFormatData *im_format) +static bool do_add_image_extension(char *string, const char imtype, const ImageFormatData *im_format) { const char *extension = NULL; const char *extension_test; @@ -1311,7 +1476,7 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo } #endif #ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) { if (!BLI_testextensie(string, extension_test = ".exr")) extension = extension_test; } @@ -1369,14 +1534,14 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo } } -bool BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) +int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) { - return image_path_ensure_ext(string, im_format->imtype, im_format); + return do_add_image_extension(string, im_format->imtype, im_format); } -bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) { - return image_path_ensure_ext(string, imtype, NULL); + return do_add_image_extension(string, imtype, NULL); } void BKE_imformat_defaults(ImageFormatData *im_format) @@ -1908,14 +2073,12 @@ bool BKE_imbuf_alpha_test(ImBuf *ibuf) /* note: imf->planes is ignored here, its assumed the image channels * are already set */ -int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) +void BKE_imbuf_write_prepare(ImBuf *ibuf, ImageFormatData *imf) { char imtype = imf->imtype; char compress = imf->compress; char quality = imf->quality; - int ok; - if (imtype == R_IMF_IMTYPE_IRIS) { ibuf->ftype = IMAGIC; } @@ -1952,7 +2115,7 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) } #endif #ifdef WITH_OPENEXR - else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) { + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { ibuf->ftype = OPENEXR; if (imf->depth == R_IMF_CHAN_DEPTH_16) ibuf->ftype |= OPENEXR_HALF; @@ -2036,6 +2199,13 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) if (quality < 10) quality = 90; ibuf->ftype = JPG | quality; } +} + +int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) +{ + int ok; + + BKE_imbuf_write_prepare(ibuf, imf); BLI_make_existing_file(name); @@ -2079,9 +2249,10 @@ int BKE_imbuf_write_stamp(Scene *scene, struct Object *camera, ImBuf *ibuf, cons } -static void image_path_makepicstring( +static void do_makepicstring( char *string, const char *base, const char *relbase, int frame, const char imtype, - const ImageFormatData *im_format, const short use_ext, const short use_frames) + const ImageFormatData *im_format, const short use_ext, const short use_frames, + const char *suffix) { if (string == NULL) return; BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ @@ -2090,22 +2261,25 @@ static void image_path_makepicstring( if (use_frames) BLI_path_frame(string, frame, 4); + if (suffix) + BLI_path_suffix(string, FILE_MAX, suffix, ""); + if (use_ext) - image_path_ensure_ext(string, imtype, im_format); + do_add_image_extension(string, imtype, im_format); } void BKE_image_path_from_imformat( char *string, const char *base, const char *relbase, int frame, - const ImageFormatData *im_format, const bool use_ext, const bool use_frames) + const ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix) { - image_path_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames); + do_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames, suffix); } void BKE_image_path_from_imtype( char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames) + const char imtype, const bool use_ext, const bool use_frames, const char *view) { - image_path_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames); + do_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames, view); } struct anim *openanim_noload(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) @@ -2181,6 +2355,59 @@ Image *BKE_image_verify_viewer(int type, const char *name) return ima; } +static void image_viewer_create_views(const RenderData *rd, Image *ima) +{ + SceneRenderView *srv; + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + image_add_view(ima, srv->name, ""); + } +} + +/* Reset the image cache and views when the Viewer Nodes views don't match the scene views */ +void BKE_image_verify_viewer_views(const RenderData *rd, Image *ima, ImageUser *iuser) +{ + bool do_reset; + + BLI_lock_thread(LOCK_DRAW_IMAGE); + + if (BKE_scene_multiview_is_stereo3d(rd)) { + ima->flag |= IMA_IS_STEREO; + ima->flag |= IMA_IS_MULTIVIEW; + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + iuser->flag &= ~IMA_SHOW_STEREO; + } + + /* see if all scene render views are in the image view list */ + do_reset = (BKE_scene_multiview_num_views_get(rd) != BLI_listbase_count(&ima->views)); + if (!do_reset) { + SceneRenderView *srv; + ImageView *iv; + + for (iv = ima->views.first; iv; iv = iv->next) { + srv = BLI_findstring(&rd->views, iv->name, offsetof(SceneRenderView, name)); + if ((srv == NULL) || (BKE_scene_multiview_is_render_view_active(rd, srv) == false)) { + do_reset = true; + break; + } + } + } + + if (do_reset) { + image_free_cached_frames(ima); + BKE_image_free_views(ima); + + /* add new views */ + image_viewer_create_views(rd, ima); + } + + BLI_unlock_thread(LOCK_DRAW_IMAGE); +} + void BKE_image_walk_all_users(const Main *mainp, void *customdata, void callback(Image *ima, ImageUser *iuser, void *customdata)) { @@ -2238,6 +2465,33 @@ static void image_tag_frame_recalc(Image *ima, ImageUser *iuser, void *customdat } } +static void image_init_imageuser(Image *ima, ImageUser *iuser) +{ + RenderResult *rr = ima->rr; + + iuser->multi_index = 0; + iuser->layer = iuser->pass = iuser->view = 0; + iuser->passtype = SCE_PASS_COMBINED; + + if (rr) { + RenderLayer *rl = rr->layers.first; + + if (rl) { + RenderPass *rp = rl->passes.first; + + if (rp) + iuser->passtype = rp->passtype; + } + + BKE_image_multilayer_index(rr, iuser); + } +} + +void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) +{ + return image_init_imageuser(ima, iuser); +} + void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) { if (ima == NULL) @@ -2248,8 +2502,13 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) switch (signal) { case IMA_SIGNAL_FREE: BKE_image_free_buffers(ima); - if (iuser) + + if (iuser) { iuser->ok = 1; + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } break; case IMA_SIGNAL_SRC_CHANGE: if (ima->type == IMA_TYPE_UV_TEST) @@ -2301,23 +2560,41 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) case IMA_SIGNAL_RELOAD: /* try to repack file */ - if (ima->packedfile) { - PackedFile *pf; - pf = newPackedFile(NULL, ima->name, ID_BLEND_PATH(G.main, &ima->id)); - if (pf) { - freePackedFile(ima->packedfile); - ima->packedfile = pf; - BKE_image_free_buffers(ima); + if (BKE_image_has_packedfile(ima)) { + const size_t totfiles = image_num_files(ima); + + if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) { + /* in case there are new available files to be loaded */ + image_free_packedfiles(ima); + BKE_image_packfiles(NULL, ima, ID_BLEND_PATH(G.main, &ima->id)); } else { - printf("ERROR: Image not available. Keeping packed image\n"); + ImagePackedFile *imapf; + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + PackedFile *pf; + pf = newPackedFile(NULL, imapf->filepath, ID_BLEND_PATH(G.main, &ima->id)); + if (pf) { + freePackedFile(imapf->packedfile); + imapf->packedfile = pf; + } + else { + printf("ERROR: Image \"%s\" not available. Keeping packed image\n", imapf->filepath); + } + } } + + if (BKE_image_has_packedfile(ima)) + BKE_image_free_buffers(ima); } else BKE_image_free_buffers(ima); - if (iuser) + if (iuser) { iuser->ok = 1; + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } break; case IMA_SIGNAL_USER_NEW_IMAGE: @@ -2325,8 +2602,7 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) iuser->ok = 1; if (ima->source == IMA_SRC_FILE || ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_MULTILAYER) { - iuser->multi_index = 0; - iuser->layer = iuser->pass = 0; + image_init_imageuser(ima, iuser); } } } @@ -2368,21 +2644,34 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return NULL; if (iuser) { - short index = 0, rl_index = 0, rp_index; + short index = 0, rv_index, rl_index = 0, rp_index; + bool is_stereo = (iuser->flag & IMA_SHOW_STEREO) && RE_RenderResult_is_stereo(rr); + + rv_index = is_stereo ? iuser->multiview_eye : iuser->view; for (rl = rr->layers.first; rl; rl = rl->next, rl_index++) { rp_index = 0; - for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) - if (iuser->layer == rl_index && iuser->pass == rp_index) + + for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) { + if (iuser->layer == rl_index && + iuser->passtype == rpass->passtype && + rv_index == rpass->view_id) + { break; + } + } if (rpass) break; } - if (rpass) + if (rpass) { iuser->multi_index = index; - else + iuser->pass = rp_index; + } + else { iuser->multi_index = 0; + iuser->pass = 0; + } } if (rpass == NULL) { rl = rr->layers.first; @@ -2393,19 +2682,80 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return rpass; } +void BKE_image_multiview_index(Image *ima, ImageUser *iuser) +{ + if (iuser) { + bool is_stereo = (ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO); + if (is_stereo) { + iuser->multi_index = iuser->multiview_eye; + } + else { + if ((iuser->view < 0) || (iuser->view >= BLI_listbase_count_ex(&ima->views, iuser->view + 1))) { + iuser->multi_index = iuser->view = 0; + } + else { + iuser->multi_index = iuser->view; + } + } + } +} + +/* if layer or pass changes, we need an index for the imbufs list */ +/* note it is called for rendered results, but it doesnt use the index! */ +/* and because rendered results use fake layer/passes, don't correct for wrong indices here */ +bool BKE_image_is_multilayer(Image *ima) +{ + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + if (ima->type == IMA_TYPE_MULTILAYER) { + return true; + } + } + else if (ima->source == IMA_SRC_VIEWER) { + if (ima->type == IMA_TYPE_R_RESULT) { + return true; + } + } + return false; +} + +static void image_init_multilayer_multiview_flag(Image *ima, RenderResult *rr) +{ + if (rr) { + if (RE_RenderResult_is_stereo(rr)) { + ima->flag |= IMA_IS_STEREO; + ima->flag |= IMA_IS_MULTIVIEW; + } + else { + ima->flag &= ~IMA_IS_STEREO; + if (BLI_listbase_count_ex(&rr->views, 2) > 1) + ima->flag |= IMA_IS_MULTIVIEW; + else + ima->flag &= ~IMA_IS_MULTIVIEW; + } + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + } +} + RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima) { + RenderResult *rr = NULL; if (ima->rr) { - return ima->rr; + rr = ima->rr; } else if (ima->type == IMA_TYPE_R_RESULT) { if (ima->render_slot == ima->last_render_slot) - return RE_AcquireResultRead(RE_GetRender(scene->id.name)); + rr = RE_AcquireResultRead(RE_GetRender(scene->id.name)); else - return ima->renders[ima->render_slot]; + rr = ima->renders[ima->render_slot]; + + /* set proper multiview flag */ + image_init_multilayer_multiview_flag(ima, rr); } - else - return NULL; + + return rr; } void BKE_image_release_renderresult(Scene *scene, Image *ima) @@ -2419,6 +2769,18 @@ void BKE_image_release_renderresult(Scene *scene, Image *ima) } } +bool BKE_image_is_openexr(struct Image *ima) +{ +#ifdef WITH_OPENEXR + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + return BLI_testextensie(ima->name, ".exr"); + } +#else + UNUSED_VARS(ima); +#endif + return false; +} + void BKE_image_backup_render(Scene *scene, Image *ima) { /* called right before rendering, ima->renders contains render @@ -2439,8 +2801,155 @@ void BKE_image_backup_render(Scene *scene, Image *ima) ima->last_render_slot = slot; } +/**************************** multiview save openexr *********************************/ +#ifdef WITH_OPENEXR +static const char *image_get_view_cb(void *base, const size_t view_id) +{ + Image *ima = base; + ImageView *iv = BLI_findlink(&ima->views, view_id); + return iv ? iv->name : ""; +} +#endif /* WITH_OPENEXR */ + +#ifdef WITH_OPENEXR +static ImBuf *image_get_buffer_cb(void *base, const size_t view_id) +{ + Image *ima = base; + ImageUser iuser = {0}; + + iuser.view = view_id; + iuser.ok = 1; + + BKE_image_multiview_index(ima, &iuser); + + return image_acquire_ibuf(ima, &iuser, NULL); +} +#endif /* WITH_OPENEXR */ + +bool BKE_image_save_openexr_multiview(Image *ima, ImBuf *ibuf, const char *filepath, const int flags) +{ +#ifdef WITH_OPENEXR + char name[FILE_MAX]; + bool ok; + + BLI_strncpy(name, filepath, sizeof(name)); + BLI_path_abs(name, G.main->name); + + ibuf->userdata = ima; + ok = IMB_exr_multiview_save(ibuf, name, flags, BLI_listbase_count(&ima->views), image_get_view_cb, image_get_buffer_cb); + ibuf->userdata = NULL; + + return ok; +#else + UNUSED_VARS(ima, ibuf, filepath, flags); + return false; +#endif +} + +/**************************** multiview load openexr *********************************/ + +static void image_add_view(Image *ima, const char *viewname, const char *filepath) +{ + ImageView *iv; + + iv = MEM_mallocN(sizeof(ImageView), "Viewer Image View"); + BLI_strncpy(iv->name, viewname, sizeof(iv->name)); + BLI_strncpy(iv->filepath, filepath, sizeof(iv->filepath)); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(viewname, STEREO_LEFT_NAME)) { + BLI_addhead(&ima->views, iv); + } + else if (STREQ(viewname, STEREO_RIGHT_NAME)) { + ImageView *left_iv = BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)); + + if (left_iv == NULL) { + BLI_addhead(&ima->views, iv); + } + else { + BLI_insertlinkafter(&ima->views, left_iv, iv); + } + } + else { + BLI_addtail(&ima->views, iv); + } +} + +#ifdef WITH_OPENEXR +static void image_add_view_cb(void *base, const char *str) +{ + Image *ima = base; + image_add_view(ima, str, ima->name); +} + +static void image_add_buffer_cb(void *base, const char *str, ImBuf *ibuf, const int frame) +{ + Image *ima = base; + size_t id; + bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL); + const char *colorspace = ima->colorspace_settings.name; + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + + if (ibuf == NULL) + return; + + id = BLI_findstringindex(&ima->views, str, offsetof(ImageView, name)); + + if (id == -1) + return; + + if (ibuf->channels >= 3) + IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + colorspace, to_colorspace, predivide); + + image_assign_ibuf(ima, ibuf, id, frame); + IMB_freeImBuf(ibuf); +} +#endif /* WITH_OPENEXR */ + +#ifdef WITH_OPENEXR +static void image_update_multiview_flags(Image *ima) +{ + if (BLI_listbase_count_ex(&ima->views, 2) > 1) { + ima->flag |= IMA_IS_MULTIVIEW; + + if (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) && + BLI_findstring(&ima->views, STEREO_RIGHT_NAME, offsetof(ImageView, name))) + { + ima->flag |= IMA_IS_STEREO; + } + else { + ima->flag &= ~IMA_IS_STEREO; + } + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + } +} +#endif /* WITH_OPENEXR */ + /* after imbuf load, openexr type can return with a exrhandle open */ /* in that case we have to build a render-result */ +#ifdef WITH_OPENEXR +static void image_create_multiview(Image *ima, ImBuf *ibuf, const int frame) +{ + image_free_views(ima); + + IMB_exr_multiview_convert(ibuf->userdata, ima, image_add_view_cb, image_add_buffer_cb, frame); + + image_update_multiview_flags(ima); + + IMB_exr_close(ibuf->userdata); +} +#endif /* WITH_OPENEXR */ + +/* after imbuf load, openexr type can return with a exrhandle open */ +/* in that case we have to build a render-result */ +#ifdef WITH_OPENEXR static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) { const char *colorspace = ima->colorspace_settings.name; @@ -2448,14 +2957,16 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y); -#ifdef WITH_OPENEXR IMB_exr_close(ibuf->userdata); -#endif ibuf->userdata = NULL; if (ima->rr) ima->rr->framenr = framenr; + + /* set proper multiview flag */ + image_init_multilayer_multiview_flag(ima, ima->rr); } +#endif /* WITH_OPENEXR */ /* common stuff to do with images after loading */ static void image_initialize_after_load(Image *ima, ImBuf *ibuf) @@ -2488,18 +2999,41 @@ static int imbuf_alpha_flags_for_image(Image *ima) return flag; } -static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +/* the number of files will vary according to the stereo format */ +static size_t image_num_files(Image *ima) +{ + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + + if (!is_multiview) { + return 1; + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + else { + return BLI_listbase_count(&ima->views); + } +} + +static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id, bool *r_assign) { struct ImBuf *ibuf; char name[FILE_MAX]; int flag; + ImageUser iuser_t; /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; ima->lastframe = frame; - BKE_image_user_file_path(iuser, ima, name); + + if (iuser) + iuser_t = *iuser; + + iuser_t.view = view_id; + BKE_image_user_file_path(&iuser_t, ima, name); flag = IB_rect | IB_multilayer; flag |= imbuf_alpha_flags_for_image(ima); @@ -2520,25 +3054,78 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) #ifdef WITH_OPENEXR /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ if (ibuf->ftype == OPENEXR && ibuf->userdata) { - image_create_multilayer(ima, ibuf, frame); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; + /* handle singlelayer multiview case assign ibuf based on available views */ + if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) { + image_create_multiview(ima, ibuf, frame); + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else if (IMB_exr_has_multilayer(ibuf->userdata)) { + /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ + image_create_multilayer(ima, ibuf, frame); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } } else { image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); + *r_assign = true; } #else image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); + *r_assign = true; #endif } - else - ima->ok = 0; - if (iuser) - iuser->ok = ima->ok; + return ibuf; +} + +static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = NULL; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + bool assign = false; + + if (!is_multiview) { + ibuf = load_sequence_single(ima, iuser, frame, 0, &assign); + if (assign) { + image_assign_ibuf(ima, ibuf, 0, frame); + } + } + else { + size_t i; + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) + ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &assign); + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + if (assign) { + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } return ibuf; } @@ -2595,46 +3182,52 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f return ibuf; } - -static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id) { struct ImBuf *ibuf = NULL; + ImageAnim *ia; - ima->lastframe = frame; + ia = BLI_findlink(&ima->anims, view_id); - if (ima->anim == NULL) { + if (ia->anim == NULL) { char str[FILE_MAX]; int flags = IB_rect; + ImageUser iuser_t; + if (ima->flag & IMA_DEINTERLACE) { flags |= IB_animdeinterlace; } - BKE_image_user_file_path(iuser, ima, str); + if (iuser) + iuser_t = *iuser; + + iuser_t.view = view_id; + + BKE_image_user_file_path(&iuser_t, ima, str); /* FIXME: make several stream accessible in image editor, too*/ - ima->anim = openanim(str, flags, 0, ima->colorspace_settings.name); + ia->anim = openanim(str, flags, 0, ima->colorspace_settings.name); /* let's initialize this user */ - if (ima->anim && iuser && iuser->frames == 0) - iuser->frames = IMB_anim_get_duration(ima->anim, + if (ia->anim && iuser && iuser->frames == 0) + iuser->frames = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); } - if (ima->anim) { - int dur = IMB_anim_get_duration(ima->anim, + if (ia->anim) { + int dur = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); int fra = frame - 1; if (fra < 0) fra = 0; if (fra > (dur - 1)) fra = dur - 1; ibuf = IMB_makeSingleUser( - IMB_anim_absolute(ima->anim, fra, + IMB_anim_absolute(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); if (ibuf) { image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); } else ima->ok = 0; @@ -2642,67 +3235,223 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) else ima->ok = 0; + return ibuf; +} + +static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = NULL; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + size_t i; + + if (totfiles != BLI_listbase_count_ex(&ima->anims, totfiles + 1)) { + image_free_anims(ima); + + for (i = 0; i < totfiles; i++) { + /* allocate the ImageAnim */ + ImageAnim *ia = MEM_callocN(sizeof(ImageAnim), "Image Anim"); + BLI_addtail(&ima->anims, ia); + } + } + + if (!is_multiview) { + ibuf = load_movie_single(ima, iuser, frame, 0); + image_assign_ibuf(ima, ibuf, 0, frame); + } + else { + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views (movie) Imbufs"); + + for (i = 0; i < totfiles; i++) { + ibuf_arr[i] = load_movie_single(ima, iuser, frame, i); + } + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i]) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + else { + ima->ok = 0; + } + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } + if (iuser) iuser->ok = ima->ok; return ibuf; } -/* warning, 'iuser' can be NULL */ -static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) +static ImBuf *load_image_single( + Image *ima, ImageUser *iuser, int cfra, + const size_t view_id, + const bool has_packed, + bool *r_assign) { - struct ImBuf *ibuf; - char str[FILE_MAX]; - int assign = 0, flag; - - /* always ensure clean ima */ - BKE_image_free_buffers(ima); + char filepath[FILE_MAX]; + struct ImBuf *ibuf = NULL; + int flag; /* is there a PackedFile with this image ? */ - if (ima->packedfile) { + if (has_packed) { + ImagePackedFile *imapf; + flag = IB_rect | IB_multilayer; flag |= imbuf_alpha_flags_for_image(ima); - ibuf = IMB_ibImageFromMemory((unsigned char *)ima->packedfile->data, ima->packedfile->size, flag, - ima->colorspace_settings.name, "<packed data>"); + imapf = BLI_findlink(&ima->packedfiles, view_id); + ibuf = IMB_ibImageFromMemory( + (unsigned char *)imapf->packedfile->data, imapf->packedfile->size, flag, + ima->colorspace_settings.name, "<packed data>"); } else { + ImageUser iuser_t; + flag = IB_rect | IB_multilayer | IB_metadata; flag |= imbuf_alpha_flags_for_image(ima); - /* get the right string */ + /* get the correct filepath */ BKE_image_user_frame_calc(iuser, cfra, 0); - BKE_image_user_file_path(iuser, ima, str); + + if (iuser) + iuser_t = *iuser; + else + iuser_t.framenr = ima->lastframe; + + iuser_t.view = view_id; + + BKE_image_user_file_path(&iuser_t, ima, filepath); /* read ibuf */ - ibuf = IMB_loadiffname(str, flag, ima->colorspace_settings.name); + ibuf = IMB_loadiffname(filepath, flag, ima->colorspace_settings.name); } if (ibuf) { - /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ +#ifdef WITH_OPENEXR if (ibuf->ftype == OPENEXR && ibuf->userdata) { - image_create_multilayer(ima, ibuf, cfra); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; + if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) { + /* handle singlelayer multiview case assign ibuf based on available views */ + image_create_multiview(ima, ibuf, cfra); + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else if (IMB_exr_has_multilayer(ibuf->userdata)) { + /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ + image_create_multilayer(ima, ibuf, cfra); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } } else { image_initialize_after_load(ima, ibuf); - assign = 1; + *r_assign = true; /* check if the image is a font image... */ detectBitmapFont(ibuf); /* make packed file for autopack */ - if ((ima->packedfile == NULL) && (G.fileflags & G_AUTOPACK)) - ima->packedfile = newPackedFile(NULL, str, ID_BLEND_PATH(G.main, &ima->id)); + if ((has_packed == false) && (G.fileflags & G_AUTOPACK)) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile"); + BLI_addtail(&ima->packedfiles, imapf); + + BLI_strncpy(imapf->filepath, filepath, sizeof(imapf->filepath)); + imapf->packedfile = newPackedFile(NULL, filepath, ID_BLEND_PATH(G.main, &ima->id)); + } } +#else + image_initialize_after_load(ima, ibuf); + *r_assign = true; +#endif } - else + else { ima->ok = 0; + } - if (assign) - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + return ibuf; +} + +/* warning, 'iuser' can be NULL + * note: Image->views was already populated (in image_update_views_format) + */ +static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) +{ + struct ImBuf *ibuf = NULL; + bool assign = false; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + bool has_packed = BKE_image_has_packedfile(ima); + + /* always ensure clean ima */ + BKE_image_free_buffers(ima); + + /* this should never happen, but just playing safe */ + if (has_packed) { + if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) { + image_free_packedfiles(ima); + has_packed = false; + } + } + + if (!is_multiview) { + ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &assign); + if (assign) { + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + } + } + else { + size_t i; + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + BLI_assert(totviews > 0); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) + ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &assign); + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + /* return the original requested ImBuf */ + i = iuser && iuser->multi_index < totviews ? iuser->multi_index : 0; + ibuf = ibuf_arr[i]; + + if (assign) { + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, 0); + } + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } if (iuser) iuser->ok = ima->ok; @@ -2756,9 +3505,10 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ float *rectf, *rectz; unsigned int *rect; float dither; - int channels, layer, pass; + int channels, layer, passtype; ImBuf *ibuf; int from_render = (ima->render_slot == ima->last_render_slot); + int actview; bool byte_buffer_in_display_space = false; if (!(iuser && iuser->scene)) @@ -2772,14 +3522,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ channels = 4; layer = iuser->layer; - pass = iuser->pass; + passtype = iuser->passtype; + actview = iuser->view; + + if ((ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO)) + actview = iuser->multiview_eye; if (from_render) { - RE_AcquireResultImage(re, &rres); + RE_AcquireResultImage(re, &rres, actview); } else if (ima->renders[ima->render_slot]) { rres = *(ima->renders[ima->render_slot]); - rres.have_combined = rres.rectf != NULL; + rres.have_combined = RE_RenderViewGetRectf(&rres, actview) != NULL; } else memset(&rres, 0, sizeof(RenderResult)); @@ -2819,24 +3573,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ if (rl) { RenderPass *rpass; - /* there's no combined pass, is in renderlayer itself */ - if (pass == 0) { - rectf = rl->rectf; - if (rectf == NULL) { - /* Happens when Save Buffers is enabled. - * Use display buffer stored in the render layer. - */ - rect = (unsigned int *) rl->display_buffer; - byte_buffer_in_display_space = true; + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + if (passtype == rpass->passtype && + actview == rpass->view_id) + { + break; } } - else { - rpass = BLI_findlink(&rl->passes, pass - 1); - if (rpass) { - channels = rpass->channels; - rectf = rpass->rect; - dither = 0.0f; /* don't dither passes */ - } + + if (rpass) { + channels = rpass->channels; + rectf = rpass->rect; + dither = 0.0f; /* don't dither passes */ } for (rpass = rl->passes.first; rpass; rpass = rpass->next) @@ -2925,9 +3673,31 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ return ibuf; } +static size_t image_get_multiview_index(Image *ima, ImageUser *iuser) +{ + const bool is_multilayer = BKE_image_is_multilayer(ima); + const bool is_backdrop = (ima->source == IMA_SRC_VIEWER) && (ima->type == IMA_TYPE_COMPOSITE) && (iuser == NULL); + + if (is_multilayer) { + return iuser ? iuser->multi_index : IMA_NO_INDEX; + } + else if (is_backdrop) { + if ((ima->flag & IMA_IS_STEREO)) { + /* backdrop hackaround (since there is no iuser */ + return ima->eye; + } + } + else if ((ima->flag & IMA_IS_MULTIVIEW)) { + return iuser ? iuser->multi_index : 0; + } + + return IMA_NO_INDEX; +} + static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame, int *r_index) { int frame = 0, index = 0; + index = image_get_multiview_index(ima, iuser); /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { @@ -2939,7 +3709,6 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame } else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; - index = iuser ? iuser->multi_index : IMA_NO_INDEX; } } @@ -2958,10 +3727,12 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, ImBuf *ibuf = NULL; int frame = 0, index = 0; + index = image_get_multiview_index(ima, iuser); + /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; @@ -2970,7 +3741,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) { @@ -2990,18 +3761,17 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, } else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; - index = iuser ? iuser->multi_index : IMA_NO_INDEX; ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); else if (ima->type == IMA_TYPE_MULTILAYER) - ibuf = image_get_cached_ibuf_for_index_frame(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -3085,7 +3855,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) if (ima->gen_depth == 0) ima->gen_depth = 24; ibuf = add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_depth, (ima->gen_flag & IMA_GEN_FLOAT) != 0, ima->gen_type, ima->gen_color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + image_assign_ibuf(ima, ibuf, index, 0); ima->ok = IMA_OK_LOADED; } else if (ima->source == IMA_SRC_VIEWER) { @@ -3103,13 +3873,13 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) /* XXX anim play for viewer nodes not yet supported */ frame = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); if (!ibuf) { /* Composite Viewer, all handled in compositor */ /* fake ibuf, will be filled in compositor */ - ibuf = IMB_allocImBuf(256, 256, 32, IB_rect); - image_assign_ibuf(ima, ibuf, 0, frame); + ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat); + image_assign_ibuf(ima, ibuf, index, frame); } } } @@ -3409,7 +4179,13 @@ void BKE_image_update_frame(const Main *bmain, int cfra) void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) { - BLI_strncpy(filepath, ima->name, FILE_MAX); + if ((ima->flag & IMA_IS_MULTIVIEW) && (ima->rr == NULL)) { + ImageView *iv = BLI_findlink(&ima->views, iuser->view); + BLI_strncpy(filepath, iv->filepath, FILE_MAX); + } + else { + BLI_strncpy(filepath, ima->name, FILE_MAX); + } if (ima->source == IMA_SRC_SEQUENCE) { char head[FILE_MAX], tail[FILE_MAX]; @@ -3545,6 +4321,16 @@ int BKE_image_sequence_guess_offset(Image *image) return atoi(num); } +bool BKE_image_has_anim(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->anims) == false); +} + +bool BKE_image_has_packedfile(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->packedfiles) == false); +} + /** * Checks the image buffer changes (not keyframed values) * @@ -3675,3 +4461,88 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) return ibuf; } + +static void image_update_views_format(Image *ima, ImageUser *iuser) +{ + SceneRenderView *srv; + ImageView *iv; + Scene *scene = iuser->scene; + const bool is_multiview = ((scene->r.scemode & R_MULTIVIEW) != 0) && + ((ima->flag & IMA_USE_VIEWS) != 0); + + /* reset the image views */ + BKE_image_free_views(ima); + + if (!is_multiview) { + goto monoview; + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + size_t i; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + ima->flag |= IMA_IS_MULTIVIEW; + ima->flag |= IMA_IS_STEREO; + + for (i = 0; i < 2; i++) { + image_add_view(ima, names[i], ima->name); + } + return; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + char prefix[FILE_MAX] = {'\0'}; + char *name = ima->name; + char *ext = NULL; + + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] == '\0') { + goto monoview; + } + + /* create all the image views */ + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + char filepath[FILE_MAX]; + BLI_snprintf(filepath, sizeof(filepath), "%s%s%s", prefix, srv->suffix, ext); + image_add_view(ima, srv->name, filepath); + } + } + + /* check if the files are all available */ + iv = ima->views.last; + while (iv) { + int file; + char str[FILE_MAX]; + + BLI_strncpy(str, iv->filepath, sizeof(str)); + BLI_path_abs(str, G.main->name); + + /* exists? */ + file = BLI_open(str, O_BINARY | O_RDONLY, 0); + if (file == -1) { + ImageView *iv_del = iv; + iv = iv->prev; + BLI_remlink(&ima->views, iv_del); + MEM_freeN(iv_del); + } + else { + iv = iv->prev; + } + close(file); + } + + /* all good */ + if (BLI_listbase_count_ex(&ima->views, 2) > 1) { + ima->flag |= IMA_IS_MULTIVIEW; + if (BKE_scene_multiview_is_stereo3d(&scene->r)) + ima->flag |= IMA_IS_STEREO; + } + else { +monoview: + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + BKE_image_free_views(ima); + } + } +} diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 7f3db700534..0d375b0dbeb 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3475,6 +3475,7 @@ static void registerCompositNodes(void) register_node_type_cmp_bokehimage(); register_node_type_cmp_bokehblur(); register_node_type_cmp_switch(); + register_node_type_cmp_switch_view(); register_node_type_cmp_pixelate(); register_node_type_cmp_mask(); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 81460ecdcc9..b89b5ab4bbc 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1002,7 +1002,7 @@ static void cache_filename(char *string, const char *path, const char *relbase, BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname); - BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true); + BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true, ""); } /* silly functions but useful to inline when the args do a lot of indirections */ diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 9b429b356b4..61e39d0ad47 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -128,8 +128,8 @@ int countPackedFiles(Main *bmain) /* let's check if there are packed files... */ for (ima = bmain->image.first; ima; ima = ima->id.next) - if (ima->packedfile) - count++; + if (BKE_image_has_packedfile(ima)) + count ++; for (vf = bmain->vfont.first; vf; vf = vf->id.next) if (vf->packedfile) @@ -232,9 +232,9 @@ void packAll(Main *bmain, ReportList *reports) int tot = 0; for (ima = bmain->image.first; ima; ima = ima->id.next) { - if (ima->packedfile == NULL && ima->id.lib == NULL) { + if (BKE_image_has_packedfile(ima) == false && ima->id.lib == NULL) { if (ima->source == IMA_SRC_FILE) { - ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id)); + BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot ++; } else if (BKE_image_is_animated(ima)) { @@ -564,23 +564,47 @@ int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how) int unpackImage(ReportList *reports, Image *ima, int how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; - + if (ima != NULL && ima->name[0]) { - unpack_generate_paths(ima->name, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); - newname = unpackFile(reports, absname, localname, ima->packedfile, how); - if (newname != NULL) { - ret_value = RET_OK; - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - BLI_strncpy(ima->name, newname, sizeof(ima->name)); - MEM_freeN(newname); - BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + while (ima->packedfiles.last) { + char localname[FILE_MAX], absname[FILE_MAX]; + char *newname; + ImagePackedFile *imapf = ima->packedfiles.last; + + unpack_generate_paths(imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); + newname = unpackFile(reports, absname, localname, imapf->packedfile, how); + + if (newname != NULL) { + ImageView *iv; + + ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK; + freePackedFile(imapf->packedfile); + imapf->packedfile = NULL; + + /* update the new corresponding view filepath */ + iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath)); + if (iv) { + BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath)); + } + + /* keep the new name in the image for non-pack specific reasons */ + BLI_strncpy(ima->name, newname, sizeof(imapf->filepath)); + MEM_freeN(newname); + } + else { + ret_value = RET_ERROR; + } + + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); } } - + + if (ret_value == RET_OK) { + BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + } + return(ret_value); } @@ -636,7 +660,7 @@ void unpackAll(Main *bmain, ReportList *reports, int how) bSound *sound; for (ima = bmain->image.first; ima; ima = ima->id.next) - if (ima->packedfile) + if (BKE_image_has_packedfile(ima)) unpackImage(reports, ima, how); for (vf = bmain->vfont.first; vf; vf = vf->id.next) @@ -655,7 +679,7 @@ bool BKE_pack_check(ID *id) case ID_IM: { Image *ima = (Image *)id; - return ima->packedfile != NULL; + return BKE_image_has_packedfile(ima); } case ID_VF: { @@ -683,7 +707,7 @@ void BKE_unpack_id(Main *bmain, ID *id, ReportList *reports, int how) case ID_IM: { Image *ima = (Image *)id; - if (ima->packedfile) { + if (BKE_image_has_packedfile(ima)) { unpackImage(reports, ima, how); } break; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3b48de13553..443671f5a61 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -92,6 +92,7 @@ #include "PIL_time.h" #include "IMB_colormanagement.h" +#include "IMB_imbuf.h" #include "bmesh.h" @@ -157,14 +158,16 @@ Scene *BKE_scene_copy(Scene *sce, int type) Base *base, *obase; if (type == SCE_COPY_EMPTY) { - ListBase lb; + ListBase rl, rv; /* XXX. main should become an arg */ scen = BKE_scene_add(G.main, sce->id.name + 2); - lb = scen->r.layers; + rl = scen->r.layers; + rv = scen->r.views; scen->r = sce->r; - scen->r.layers = lb; + scen->r.layers = rl; scen->r.actlay = 0; + scen->r.views = rv; scen->unit = sce->unit; scen->physics_settings = sce->physics_settings; scen->gm = sce->gm; @@ -197,6 +200,7 @@ Scene *BKE_scene_copy(Scene *sce, int type) BLI_duplicatelist(&(scen->markers), &(sce->markers)); BLI_duplicatelist(&(scen->transform_spaces), &(sce->transform_spaces)); BLI_duplicatelist(&(scen->r.layers), &(sce->r.layers)); + BLI_duplicatelist(&(scen->r.views), &(sce->r.views)); BKE_keyingsets_copy(&(scen->keyingsets), &(sce->keyingsets)); if (sce->nodetree) { @@ -390,6 +394,7 @@ void BKE_scene_free(Scene *sce) BLI_freelistN(&sce->markers); BLI_freelistN(&sce->transform_spaces); BLI_freelistN(&sce->r.layers); + BLI_freelistN(&sce->r.views); if (sce->toolsettings) { if (sce->toolsettings->vpaint) { @@ -437,6 +442,7 @@ Scene *BKE_scene_add(Main *bmain, const char *name) ParticleEditSettings *pset; int a; const char *colorspace_name; + SceneRenderView *srv; sce = BKE_libblock_alloc(bmain, ID_SCE, name); sce->lay = sce->layact = 1; @@ -628,7 +634,16 @@ Scene *BKE_scene_add(Main *bmain, const char *name) /* note; in header_info.c the scene copy happens..., if you add more to renderdata it has to be checked there */ BKE_scene_add_render_layer(sce, NULL); - + + /* multiview - stereo */ + BKE_scene_add_render_view(sce, STEREO_LEFT_NAME); + srv = sce->r.views.first; + BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix)); + + BKE_scene_add_render_view(sce, STEREO_RIGHT_NAME); + srv = sce->r.views.last; + BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); + /* game data */ sce->gm.stereoflag = STEREO_NOSTEREO; sce->gm.stereomode = STEREO_ANAGLYPH; @@ -701,6 +716,19 @@ Scene *BKE_scene_add(Main *bmain, const char *name) return sce; } +Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name) +{ + Base *base; + + for (base = scene->base.first; base; base = base->next) { + if (STREQ(base->object->id.name + 2, name)) { + break; + } + } + + return base; +} + Base *BKE_scene_base_find(Scene *scene, Object *ob) { return BLI_findptr(&scene->base, ob, offsetof(Base, object)); @@ -1892,6 +1920,42 @@ bool BKE_scene_remove_render_layer(Main *bmain, Scene *scene, SceneRenderLayer * return true; } +/* return default view */ +SceneRenderView *BKE_scene_add_render_view(Scene *sce, const char *name) +{ + SceneRenderView *srv; + + if (!name) + name = DATA_("RenderView"); + + srv = MEM_callocN(sizeof(SceneRenderView), "new render view"); + BLI_strncpy(srv->name, name, sizeof(srv->name)); + BLI_uniquename(&sce->r.views, srv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(srv->name)); + BLI_addtail(&sce->r.views, srv); + + return srv; +} + +bool BKE_scene_remove_render_view(Scene *scene, SceneRenderView *srv) +{ + const int act = BLI_findindex(&scene->r.views, srv); + + if (act == -1) { + return false; + } + else if (scene->r.views.first == scene->r.views.last) { + /* ensure 1 view is kept */ + return false; + } + + BLI_remlink(&scene->r.views, srv); + MEM_freeN(srv); + + scene->r.actview = 0; + + return true; +} + /* render simplification */ int get_render_subsurf_level(const RenderData *r, int lvl) @@ -2062,3 +2126,281 @@ double BKE_scene_unit_scale(const UnitSettings *unit, const int unit_type, doubl return value; } } + +/******************** multiview *************************/ + +size_t BKE_scene_multiview_num_views_get(const RenderData *rd) +{ + SceneRenderView *srv; + size_t totviews = 0; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return 1; + + if (rd->views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + if (BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name))) { + totviews++; + } + + if (BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name))) { + totviews++; + } + } + else { + for (srv = rd->views.first; srv; srv = srv->next) { + if ((srv->viewflag & SCE_VIEW_DISABLE) == 0) { + totviews++; + } + } + } + return totviews; +} + +bool BKE_scene_multiview_is_stereo3d(const RenderData *rd) +{ + SceneRenderView *srv[2]; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return false; + + srv[0] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name)); + srv[1] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name)); + + return (srv[0] && ((srv[0]->viewflag & SCE_VIEW_DISABLE) == 0) && + srv[1] && ((srv[1]->viewflag & SCE_VIEW_DISABLE) == 0)); +} + +/* return whether to render this SceneRenderView */ +bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const SceneRenderView *srv) +{ + if (srv == NULL) + return false; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return false; + + if ((srv->viewflag & SCE_VIEW_DISABLE)) + return false; + + if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) + return true; + + /* SCE_VIEWS_SETUP_BASIC */ + if (STREQ(srv->name, STEREO_LEFT_NAME) || + STREQ(srv->name, STEREO_RIGHT_NAME)) + { + return true; + } + + return false; +} + +/* return true if viewname is the first or if the name is NULL or not found */ +bool BKE_scene_multiview_is_render_view_first(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return true; + + if ((!viewname) || (!viewname[0])) + return true; + + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + return STREQ(viewname, srv->name); + } + } + + return true; +} + +/* return true if viewname is the last or if the name is NULL or not found */ +bool BKE_scene_multiview_is_render_view_last(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return true; + + if ((!viewname) || (!viewname[0])) + return true; + + for (srv = rd->views.last; srv; srv = srv->prev) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + return STREQ(viewname, srv->name); + } + } + + return true; +} + +SceneRenderView *BKE_scene_multiview_render_view_findindex(const RenderData *rd, const int view_id) +{ + SceneRenderView *srv; + size_t nr; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return NULL; + + nr = 0; + for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (nr++ == view_id) + return srv; + } + } + return srv; +} + +const char *BKE_scene_multiview_render_view_name_get(const RenderData *rd, const int view_id) +{ + SceneRenderView *srv = BKE_scene_multiview_render_view_findindex(rd, view_id); + + if (srv) + return srv->name; + else + return ""; +} + +size_t BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + size_t nr; + + if ((!rd) || ((rd->scemode & R_MULTIVIEW) == 0)) + return 0; + + if ((!viewname) || (!viewname[0])) + return 0; + + nr = 0; + for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (STREQ(viewname, srv->name)) { + return nr; + } + else { + nr += 1; + } + } + } + + return 0; +} + +void BKE_scene_multiview_filepath_get( + SceneRenderView *srv, const char *filepath, + char *r_filepath) +{ + BLI_strncpy(r_filepath, filepath, FILE_MAX); + BLI_path_suffix(r_filepath, FILE_MAX, srv->suffix, ""); +} + +/** + * When multiview is not used the filepath is as usual (e.g., ``Image.jpg``). + * When multiview is on, even if only one view is enabled the view is incorporated + * into the file name (e.g., ``Image_L.jpg``). That allows for the user to re-render + * individual views. + */ +void BKE_scene_multiview_view_filepath_get( + const RenderData *rd, const char *filepath, const char *viewname, + char *r_filepath) +{ + SceneRenderView *srv; + char suffix[FILE_MAX]; + + srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name)); + if (srv) + BLI_strncpy(suffix, srv->suffix, sizeof(suffix)); + else + BLI_strncpy(suffix, viewname, sizeof(suffix)); + + BLI_strncpy(r_filepath, filepath, FILE_MAX); + BLI_path_suffix(r_filepath, FILE_MAX, suffix, ""); +} + +const char *BKE_scene_multiview_view_suffix_get(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((viewname == NULL) || (viewname[0] == '\0')) + return viewname; + + srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name)); + if (srv) + return srv->suffix; + else + return viewname; +} + +const char *BKE_scene_multiview_view_id_suffix_get(const RenderData *rd, const size_t view_id) +{ + if ((rd->scemode & R_MULTIVIEW) == 0) { + return ""; + } + else { + const char *viewname = BKE_scene_multiview_render_view_name_get(rd, view_id); + return BKE_scene_multiview_view_suffix_get(rd, viewname); + } +} + +void BKE_scene_multiview_view_prefix_get(Scene *scene, const char *name, char *rprefix, char **rext) +{ + SceneRenderView *srv; + size_t index_act; + char *suf_act; + const char delims[] = {'.', '\0'}; + + rprefix[0] = '\0'; + + /* begin of extension */ + index_act = BLI_str_rpartition(name, delims, rext, &suf_act); + BLI_assert(index_act > 0); + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + size_t len = strlen(srv->suffix); + if (STREQLEN(*rext - len, srv->suffix, len)) { + BLI_strncpy(rprefix, name, strlen(name) - strlen(*rext) - len + 1); + break; + } + } + } +} + +void BKE_scene_multiview_videos_dimensions_get( + const RenderData *rd, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + if ((rd->scemode & R_MULTIVIEW) && + rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) + { + IMB_stereo3d_write_dimensions( + rd->im_format.stereo3d_format.display_mode, + (rd->im_format.stereo3d_format.flag & S3D_SQUEEZED_FRAME) != 0, + width, height, + r_width, r_height); + } + else { + *r_width = width; + *r_height = height; + } +} + +size_t BKE_scene_multiview_num_videos_get(const RenderData *rd) +{ + if (BKE_imtype_is_movie(rd->im_format.imtype) == false) + return 0; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return 1; + + if (rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + return BKE_scene_multiview_num_views_get(rd); + } +} diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 41f17665065..abfc858fd03 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" #include "DNA_sequence_types.h" +#include "DNA_scene_types.h" #include "IMB_moviecache.h" #include "IMB_imbuf.h" @@ -41,6 +42,7 @@ #include "BLI_listbase.h" #include "BKE_sequencer.h" +#include "BKE_scene.h" typedef struct SeqCacheKey { struct Sequence *seq; @@ -77,7 +79,9 @@ static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) (a->bmain != b->bmain) || (a->scene != b->scene) || (a->motion_blur_shutter != b->motion_blur_shutter) || - (a->motion_blur_samples != b->motion_blur_samples)); + (a->motion_blur_samples != b->motion_blur_samples) || + (a->scene->r.views_format != b->scene->r.views_format) || + (a->view_id != b->view_id)); } static unsigned int seq_hash_render_data(const SeqRenderData *a) @@ -89,6 +93,7 @@ static unsigned int seq_hash_render_data(const SeqRenderData *a) rval ^= ((intptr_t) a->scene) << 6; rval ^= (int)(a->motion_blur_shutter * 100.0f) << 10; rval ^= a->motion_blur_samples << 24; + rval ^= ((a->scene->r.views_format * 2) + a->view_id) << 32; return rval; } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 721555097d7..942426febdd 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -53,6 +53,12 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#ifdef WIN32 +# include "BLI_winstuff.h" +#else +# include <unistd.h> +#endif + #include "BLF_translation.h" #include "BKE_animsys.h" @@ -89,6 +95,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, float cfra); static void seq_free_animdata(Scene *scene, Sequence *seq); static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr, bool make_float); +static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview); +static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id); /* **** XXX ******** */ #define SELECT 1 @@ -181,10 +189,7 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach if (seq->strip) seq_free_strip(seq->strip); - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); if (seq->type & SEQ_TYPE_EFFECT) { struct SeqEffectHandle sh = BKE_sequence_get_effect(seq); @@ -196,6 +201,10 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach ((ID *)seq->sound)->us--; } + if (seq->stereo3d_format) { + MEM_freeN(seq->stereo3d_format); + } + /* clipboard has no scene and will never have a sound handle or be active * same goes to sequences copy for proxy rebuild job */ @@ -240,6 +249,22 @@ void BKE_sequence_free(Scene *scene, Sequence *seq) BKE_sequence_free_ex(scene, seq, true); } +/* Function to free imbuf and anim data on changes */ +void BKE_sequence_free_anim(Sequence *seq) +{ + while (seq->anims.last) { + StripAnim *sanim = seq->anims.last; + BLI_remlink(&seq->anims, sanim); + + if (sanim->anim) { + IMB_free_anim(sanim->anim); + sanim->anim = NULL; + } + + MEM_freeN(sanim); + } +} + /* cache must be freed before calling this function * since it leaves the seqbase in an invalid state */ static void seq_free_sequence_recurse(Scene *scene, Sequence *seq) @@ -537,6 +562,7 @@ void BKE_sequencer_new_render_data( r_context->motion_blur_shutter = 0; r_context->skip_cache = false; r_context->is_proxy_render = false; + r_context->view_id = 0; } /* ************************* iterator ************************** */ @@ -771,10 +797,17 @@ void BKE_sequence_calc(Scene *scene, Sequence *seq) } } +static void seq_multiview_name(Scene *scene, const size_t view_id, const char *prefix, + const char *ext, char *r_path, size_t r_size) +{ + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + BLI_snprintf(r_path, r_size, "%s%s%s", prefix, suffix, ext); +} + /* note: caller should run BKE_sequence_calc(scene, seq) after */ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_range) { - char str[FILE_MAX]; + char path[FILE_MAX]; int prev_startdisp = 0, prev_enddisp = 0; /* note: don't rename the strip, will break animation curves */ @@ -807,22 +840,67 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r break; } case SEQ_TYPE_MOVIE: - BLI_join_dirfile(str, sizeof(str), seq->strip->dir, + { + StripAnim *sanim; + bool is_multiview_loaded = false; + const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (scene->r.scemode & R_MULTIVIEW) != 0; + + BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); - BLI_path_abs(str, G.main->name); + BLI_path_abs(path, G.main->name); + + BKE_sequence_free_anim(seq); + + if (is_multiview && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + char prefix[FILE_MAX]; + char *ext = NULL; + size_t totfiles = seq_num_files(scene, seq->views_format, true); + int i = 0; + + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); - if (seq->anim) IMB_free_anim(seq->anim); + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + struct anim *anim; + char str[FILE_MAX]; - seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); + seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX); + anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + seq_anim_add_suffix(scene, anim, i); - if (!seq->anim) { + if (anim) { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim; + } + } + is_multiview_loaded = true; + } + } + + if (is_multiview_loaded == false) { + struct anim *anim; + anim = openanim(path, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + if (anim) { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim; + } + } + + /* use the first video as reference for everything */ + sanim = seq->anims.first; + + if ((!sanim) || (!sanim->anim)) { return; } - seq->len = IMB_anim_get_duration(seq->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); - - seq->anim_preseek = IMB_anim_get_preseek(seq->anim); + seq->len = IMB_anim_get_duration(sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); + + seq->anim_preseek = IMB_anim_get_preseek(sanim->anim); seq->len -= seq->anim_startofs; seq->len -= seq->anim_endofs; @@ -830,6 +908,7 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r seq->len = 0; } break; + } case SEQ_TYPE_MOVIECLIP: if (seq->clip == NULL) return; @@ -1297,6 +1376,7 @@ typedef struct SeqIndexBuildContext { int size_flags; int quality; bool overwrite; + size_t view_id; Main *bmain; Scene *scene; @@ -1336,44 +1416,59 @@ static double seq_rendersize_to_scale_factor(int size) return 0.25; } -static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile) +/* the number of files will vary according to the stereo format */ +static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview) +{ + if (!is_multiview) { + return 1; + } + else if (views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + else { + return BKE_scene_multiview_num_views_get(&scene->r); + } +} + +static void seq_proxy_index_dir_set(struct anim *anim, const char *base_dir) +{ + char dir[FILE_MAX]; + char fname[FILE_MAXFILE]; + + IMB_anim_get_fname(anim, fname, FILE_MAXFILE); + BLI_strncpy(dir, base_dir, sizeof(dir)); + BLI_path_append(dir, sizeof(dir), fname); + IMB_anim_set_index_dir(anim, dir); +} + +static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) { + char dir[FILE_MAX]; char name[FILE_MAX]; StripProxy *proxy; + bool use_proxy; + bool is_multiview_loaded = false; + Editing *ed = scene->ed; + const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0; - if (seq->anim != NULL) { + if ((seq->anims.first != NULL) && (((StripAnim *)seq->anims.first)->anim != NULL)) { return; } + /* reset all the previously created anims */ + BKE_sequence_free_anim(seq); + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, G.main->name); - - if (openfile) { - seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); - } - else { - seq->anim = openanim_noload(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); - } - - if (seq->anim == NULL) { - return; - } proxy = seq->strip->proxy; - if (proxy == NULL) { - return; - } - - if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) || - (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE)) - { - char dir[FILE_MAX]; - char fname[FILE_MAXFILE]; + use_proxy = proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 || + (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE)); + if (use_proxy) { if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { if (ed->proxy_dir[0] == 0) BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); @@ -1383,21 +1478,93 @@ static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile) else { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } + BLI_path_abs(dir, G.main->name); + } - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); - BLI_path_append(dir, sizeof(dir), fname); + if (is_multiview && seq->views_format == R_IMF_VIEWS_INDIVIDUAL) { + size_t totfiles = seq_num_files(scene, seq->views_format, true); + char prefix[FILE_MAX]; + char *ext = NULL; + int i; - BLI_path_abs(dir, G.main->name); + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i); + char str[FILE_MAX]; + StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); - IMB_anim_set_index_dir(seq->anim, dir); + BLI_addtail(&seq->anims, sanim); + + BLI_snprintf(str, sizeof(str), "%s%s%s", prefix, suffix, ext); + + if (openfile) { + sanim->anim = openanim( + str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + seq_anim_add_suffix(scene, sanim->anim, i); + + if (sanim->anim == NULL) { + if (openfile) { + sanim->anim = openanim( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + /* no individual view files - monoscopic, stereo 3d or exr multiview */ + totfiles = 1; + } + + if (sanim->anim && use_proxy) { + seq_proxy_index_dir_set(sanim->anim, dir); + } + } + is_multiview_loaded = true; + } } -} + if (is_multiview_loaded == false) { + StripAnim *sanim; + + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); -static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name) + if (openfile) { + sanim->anim = openanim( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + if (sanim->anim && use_proxy) { + seq_proxy_index_dir_set(sanim->anim, dir); + } + } +} + +static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name, const size_t view_id) { int frameno; char dir[PROXY_MAXFILE]; + StripAnim *sanim; + char suffix[24] = {'\0'}; StripProxy *proxy = seq->strip->proxy; if (!proxy) { @@ -1412,23 +1579,25 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render * have both, a directory full of jpeg files and proxy avis, so * sorry folks, please rebuild your proxies... */ - if (seq->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { + sanim = BLI_findlink(&seq->anims, view_id); + + if (sanim && sanim->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { char fname[FILE_MAXFILE]; if (ed->proxy_dir[0] == 0) BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); else BLI_strncpy(dir, ed->proxy_dir, sizeof(dir)); - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); + IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); BLI_path_append(dir, sizeof(dir), fname); BLI_path_abs(name, G.main->name); } else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } - else if (seq->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { + else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { char fname[FILE_MAXFILE]; BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); + IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); BLI_path_append(dir, sizeof(dir), fname); } else if (seq->type == SEQ_TYPE_IMAGE) { @@ -1438,12 +1607,16 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render return false; } - if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && seq->anim && + if (view_id > 0) + BLI_snprintf(suffix, sizeof(suffix), "_%zu", view_id); + + if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim && ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { BLI_join_dirfile(name, PROXY_MAXFILE, dir, proxy->file); BLI_path_abs(name, G.main->name); + BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix); return true; } @@ -1451,13 +1624,13 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render /* generate a separate proxy directory for each preview size */ if (seq->type == SEQ_TYPE_IMAGE) { - BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir, render_size, - BKE_sequencer_give_stripelem(seq, cfra)->name); + BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy%s", dir, render_size, + BKE_sequencer_give_stripelem(seq, cfra)->name, suffix); frameno = 1; } else { frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; - BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir, render_size); + BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, render_size, suffix); } BLI_path_abs(name, G.main->name); @@ -1476,6 +1649,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c int render_size = context->preview_render_size; StripProxy *proxy = seq->strip->proxy; Editing *ed = context->scene->ed; + StripAnim *sanim; if (!(seq->flag & SEQ_USE_PROXY)) { return NULL; @@ -1496,7 +1670,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; if (proxy->anim == NULL) { - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { return NULL; } @@ -1506,14 +1680,15 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c return NULL; } - seq_open_anim_file(context->scene->ed, seq, true); + seq_open_anim_file(context->scene, seq, true); + sanim = seq->anims.first; - frameno = IMB_anim_index_get_frame_index(seq->anim, proxy->tc, frameno); + frameno = IMB_anim_index_get_frame_index(sanim ? sanim->anim : NULL, seq->strip->proxy->tc, frameno); return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE); } - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { return NULL; } @@ -1540,7 +1715,7 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i ImBuf *ibuf_tmp, *ibuf; Editing *ed = context->scene->ed; - if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name)) { + if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name, context->view_id)) { return; } @@ -1581,44 +1756,130 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i IMB_freeImBuf(ibuf); } -SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list) +/* returns whether the file this context would read from even exist, if not, don't create the context +*/ +static bool seq_proxy_multiview_context_invalid(Sequence *seq, Scene *scene, const size_t view_id) +{ + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return false; + + if ((seq->type == SEQ_TYPE_IMAGE) && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + static char prefix[FILE_MAX]; + static char *ext = NULL; + char str[FILE_MAX]; + + if (view_id == 0) { + char path[FILE_MAX]; + BLI_join_dirfile(path, sizeof(path), seq->strip->dir, + seq->strip->stripdata->name); + BLI_path_abs(path, G.main->name); + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); + } + + if (prefix[0] == '\0') + return view_id != 0; + + seq_multiview_name(scene, view_id, prefix, ext, str, FILE_MAX); + + if (BLI_access(str, R_OK) == 0) + return false; + else + return view_id != 0; + } + return false; +} + +/** This returns the maximum possible number of required contexts +*/ +static size_t seq_proxy_context_count(Sequence *seq, Scene *scene) +{ + size_t num_views = 1; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return 1; + + switch (seq->type) { + case SEQ_TYPE_MOVIE: + { + num_views = BLI_listbase_count(&seq->anims); + break; + } + case SEQ_TYPE_IMAGE: + { + switch (seq->views_format) { + case R_IMF_VIEWS_INDIVIDUAL: + num_views = BKE_scene_multiview_num_views_get(&scene->r); + break; + case R_IMF_VIEWS_STEREO_3D: + num_views = 2; + break; + case R_IMF_VIEWS_MULTIVIEW: + /* not supported at the moment */ + /* pass through */ + default: + num_views = 1; + } + break; + } + } + + return num_views; +} + +void BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list, ListBase *queue) { SeqIndexBuildContext *context; Sequence *nseq; + LinkData *link; + size_t i; + size_t num_files; if (!seq->strip || !seq->strip->proxy) { - return NULL; + return; } if (!(seq->flag & SEQ_USE_PROXY)) { - return NULL; + return; } - context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"); + num_files = seq_proxy_context_count(seq, scene); + + for (i = 0; i < num_files; i++) { + if (seq_proxy_multiview_context_invalid(seq, scene, i)) + continue; + + context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"); + + nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0); - nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0); + context->tc_flags = nseq->strip->proxy->build_tc_flags; + context->size_flags = nseq->strip->proxy->build_size_flags; + context->quality = nseq->strip->proxy->quality; + context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; - context->tc_flags = nseq->strip->proxy->build_tc_flags; - context->size_flags = nseq->strip->proxy->build_size_flags; - context->quality = nseq->strip->proxy->quality; - context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; + context->bmain = bmain; + context->scene = scene; + context->orig_seq = seq; + context->seq = nseq; - context->bmain = bmain; - context->scene = scene; - context->orig_seq = seq; - context->seq = nseq; + context->view_id = i; /* only for images */ - if (nseq->type == SEQ_TYPE_MOVIE) { - seq_open_anim_file(scene->ed, nseq, true); + link = BLI_genericNodeN(context); + BLI_addtail(queue, link); - if (nseq->anim) { - context->index_context = IMB_anim_index_rebuild_context(nseq->anim, - context->tc_flags, context->size_flags, context->quality, - context->overwrite, file_list); + if (nseq->type == SEQ_TYPE_MOVIE) { + StripAnim *sanim; + + seq_open_anim_file(scene, nseq, true); + sanim = BLI_findlink(&nseq->anims, i); + + if (sanim->anim) { + context->index_context = IMB_anim_index_rebuild_context(sanim->anim, + context->tc_flags, context->size_flags, context->quality, + context->overwrite, file_list); + } } } - - return context; } void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, short *do_update, float *progress) @@ -1657,6 +1918,7 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho render_context.skip_cache = true; render_context.is_proxy_render = true; + render_context.view_id = context->view_id; for (cfra = seq->startdisp + seq->startstill; cfra < seq->enddisp - seq->endstill; cfra++) { if (context->size_flags & IMB_PROXY_25) { @@ -1683,8 +1945,14 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho void BKE_sequencer_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) { if (context->index_context) { - IMB_close_anim_proxies(context->seq->anim); - IMB_close_anim_proxies(context->orig_seq->anim); + StripAnim *sanim; + + for (sanim = context->seq->anims.first; sanim; sanim = sanim->next) + IMB_close_anim_proxies(sanim->anim); + + for (sanim = context->orig_seq->anims.first; sanim; sanim = sanim->next) + IMB_close_anim_proxies(sanim->anim); + IMB_anim_index_rebuild_finish(context->index_context, stop); } @@ -2429,6 +2697,234 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, Sequenc return out; } +static ImBuf *seq_render_image_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) +{ + ImBuf *ibuf = NULL; + char name[FILE_MAX]; + bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0; + StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); + int flag; + + if (s_elem) { + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); + BLI_path_abs(name, G.main->name); + } + + flag = IB_rect; + if (seq->alpha_mode == SEQ_ALPHA_PREMUL) + flag |= IB_alphamode_premul; + + if (!s_elem) { + /* don't do anything */ + } + else if (is_multiview) { + size_t totfiles = seq_num_files(context->scene, seq->views_format, true); + size_t totviews; + struct ImBuf **ibufs_arr; + char prefix[FILE_MAX]; + char *ext = NULL; + int i; + + if (totfiles > 1) { + BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext); + if (prefix[0] == '\0') { + goto monoview_image; + } + } + + totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) { + + if (prefix[0] == '\0') { + ibufs_arr[i] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); + } + else { + char str[FILE_MAX]; + seq_multiview_name(context->scene, i, prefix, ext, str, FILE_MAX); + ibufs_arr[i] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); + } + + if (ibufs_arr[i]) { + /* we don't need both (speed reasons)! */ + if (ibufs_arr[i]->rect_float && ibufs_arr[i]->rect) + imb_freerectImBuf(ibufs_arr[i]); + } + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D && ibufs_arr[0]) + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]); + + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i]) { + SeqRenderData localcontext = *context; + localcontext.view_id = i; + + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + } + } + } + + /* return the original requested ImBuf */ + ibuf = ibufs_arr[context->view_id]; + if (ibuf) { + s_elem->orig_width = ibufs_arr[0]->x; + s_elem->orig_height = ibufs_arr[0]->y; + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i] != ibuf) { + IMB_freeImBuf(ibufs_arr[i]); + } + } + + MEM_freeN(ibufs_arr); + } + else { +monoview_image: + if ((ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { + /* we don't need both (speed reasons)! */ + if (ibuf->rect_float && ibuf->rect) + imb_freerectImBuf(ibuf); + + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + s_elem->orig_width = ibuf->x; + s_elem->orig_height = ibuf->y; + } + } + + return ibuf; +} + +static ImBuf *seq_render_movie_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) +{ + ImBuf *ibuf = NULL; + StripAnim *sanim; + bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0; + + /* load all the videos */ + seq_open_anim_file(context->scene, seq, false); + + if (is_multiview) { + ImBuf **ibuf_arr; + size_t totviews; + size_t totfiles = seq_num_files(context->scene, seq->views_format, true); + int i; + + if (totfiles != BLI_listbase_count_ex(&seq->anims, totfiles + 1)) + goto monoview_movie; + + totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (i = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, i++) { + if (sanim->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); + + ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + proxy_size); + + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf_arr[i] && proxy_size != IMB_PROXY_NONE) { + ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf_arr[i]) { + /* we don't need both (speed reasons)! */ + if (ibuf_arr[i]->rect_float && ibuf_arr[i]->rect) + imb_freerectImBuf(ibuf_arr[i]); + } + } + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D) { + if (ibuf_arr[0]) { + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + } + else { + /* probably proxy hasn't been created yet */ + MEM_freeN(ibuf_arr); + return NULL; + } + } + + for (i = 0; i < totviews; i++) { + SeqRenderData localcontext = *context; + localcontext.view_id = i; + + if (ibuf_arr[i]) { + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false); + } + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibuf_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf_arr[i]); + } + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[context->view_id]; + if (ibuf) { + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + MEM_freeN(ibuf_arr); + } + else { +monoview_movie: + sanim = seq->anims.first; + if (sanim && sanim->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); + + ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + proxy_size); + + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf && proxy_size != IMB_PROXY_NONE) { + ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf) { + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + /* we don't need both (speed reasons)! */ + if (ibuf->rect_float && ibuf->rect) { + imb_freerectImBuf(ibuf); + } + + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + } + } + } + return ibuf; +} + static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence *seq, float nr) { ImBuf *ibuf = NULL; @@ -2560,7 +3056,7 @@ static ImBuf *seq_render_mask_strip(const SeqRenderData *context, Sequence *seq, return seq_render_mask(context, seq->mask, nr, make_float); } -static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr) +static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) { ImBuf *ibuf = NULL; float frame; @@ -2658,6 +3154,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq char err_out[256] = "unknown"; int width = (scene->r.xsch * scene->r.size) / 100; int height = (scene->r.ysch * scene->r.size) / 100; + const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id); /* for old scened this can be uninitialized, * should probably be added to do_versions at some point if the functionality stays */ @@ -2669,14 +3166,18 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq ibuf = sequencer_view3d_cb(scene, camera, width, height, IB_rect, context->scene->r.seq_prev_type, (context->scene->r.seq_flag & R_SEQ_SOLID_TEX) != 0, - use_gpencil, true, scene->r.alphamode, err_out); + use_gpencil, true, scene->r.alphamode, viewname, err_out); if (ibuf == NULL) { fprintf(stderr, "seq_render_scene_strip failed to get opengl buffer: %s\n", err_out); } } else { Render *re = RE_GetRender(scene->id.name); - RenderResult rres; + size_t totviews = BKE_scene_multiview_num_views_get(&scene->r); + int i; + ImBuf **ibufs_arr; + + ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); /* XXX: this if can be removed when sequence preview rendering uses the job system * @@ -2696,27 +3197,51 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq /* restore previous state after it was toggled on & off by RE_BlenderFrame */ G.is_rendering = is_rendering; } - - RE_AcquireResultImage(re, &rres); - - if (rres.rectf) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); - memcpy(ibuf->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); - if (rres.rectz) { - addzbuffloatImBuf(ibuf); - memcpy(ibuf->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + + for (i = 0; i < totviews; i++) { + SeqRenderData localcontext = *context; + RenderResult rres; + + localcontext.view_id = i; + + RE_AcquireResultImage(re, &rres, i); + + if (rres.rectf) { + ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); + memcpy(ibufs_arr[i]->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); + + if (rres.rectz) { + addzbuffloatImBuf(ibufs_arr[i]); + memcpy(ibufs_arr[i]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + } + + /* float buffers in the sequencer are not linear */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + } + else if (rres.rect32) { + ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); + memcpy(ibufs_arr[i]->rect, rres.rect32, 4 * rres.rectx * rres.recty); } - /* float buffers in the sequencer are not linear */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + } + + RE_ReleaseResultImage(re); } - else if (rres.rect32) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); - memcpy(ibuf->rect, rres.rect32, 4 * rres.rectx * rres.recty); + + /* return the original requested ImBuf */ + ibuf = ibufs_arr[context->view_id]; + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i] != ibuf) { + IMB_freeImBuf(ibufs_arr[i]); + } } - - RE_ReleaseResultImage(re); - + MEM_freeN(ibufs_arr); + // BIF_end_render_callbacks(); } @@ -2743,7 +3268,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s float nr = give_stripelem_index(seq, cfra); int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : seq->type; bool use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra); - char name[FILE_MAX]; switch (type) { case SEQ_TYPE_META: @@ -2802,64 +3326,14 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s case SEQ_TYPE_IMAGE: { - StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); - int flag; - - if (s_elem) { - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); - BLI_path_abs(name, G.main->name); - } - - flag = IB_rect; - if (seq->alpha_mode == SEQ_ALPHA_PREMUL) - flag |= IB_alphamode_premul; - - if (s_elem && (ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) - imb_freerectImBuf(ibuf); - - /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - copy_to_ibuf_still(context, seq, nr, ibuf); - - s_elem->orig_width = ibuf->x; - s_elem->orig_height = ibuf->y; - } + ibuf = seq_render_image_strip(context, seq, nr, cfra); + copy_to_ibuf_still(context, seq, nr, ibuf); break; } case SEQ_TYPE_MOVIE: { - seq_open_anim_file(context->scene->ed, seq, false); - - if (seq->anim) { - IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); - IMB_anim_set_preseek(seq->anim, seq->anim_preseek); - - ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - proxy_size); - - /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf && proxy_size != IMB_PROXY_NONE) { - ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - IMB_PROXY_NONE); - } - if (ibuf) { - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) { - imb_freerectImBuf(ibuf); - } - - seq->strip->stripdata->orig_width = ibuf->x; - seq->strip->stripdata->orig_height = ibuf->y; - } - } + ibuf = seq_render_movie_strip(context, seq, nr, cfra); copy_to_ibuf_still(context, seq, nr, ibuf); break; } @@ -2867,7 +3341,7 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s case SEQ_TYPE_SCENE: { /* scene can be NULL after deletions */ - ibuf = seq_render_scene_strip(context, seq, nr); + ibuf = seq_render_scene_strip(context, seq, nr, cfra); /* Scene strips update all animation, so we need to restore original state.*/ BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra); @@ -3364,16 +3838,6 @@ ImBuf *BKE_sequencer_give_ibuf_threaded(const SeqRenderData *context, float cfra return e ? e->ibuf : NULL; } -/* Functions to free imbuf and anim data on changes */ - -static void free_anim_seq(Sequence *seq) -{ - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } -} - /* check whether sequence cur depends on seq */ bool BKE_sequence_check_depend(Sequence *seq, Sequence *cur) { @@ -3425,15 +3889,11 @@ static void sequence_invalidate_cache(Scene *scene, Sequence *seq, bool invalida /* invalidate cache for current sequence */ if (invalidate_self) { - if (seq->anim) { - /* Animation structure holds some buffers inside, - * so for proper cache invalidation we need to - * re-open the animation. - */ - IMB_free_anim(seq->anim); - seq->anim = NULL; - } - + /* Animation structure holds some buffers inside, + * so for proper cache invalidation we need to + * re-open the animation. + */ + BKE_sequence_free_anim(seq); BKE_sequencer_cache_cleanup_sequence(seq); } @@ -3480,7 +3940,7 @@ void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render) if (seq->strip) { if (seq->type == SEQ_TYPE_MOVIE) { - free_anim_seq(seq); + BKE_sequence_free_anim(seq); } if (seq->type == SEQ_TYPE_SPEED) { BKE_sequence_effect_speed_rebuild_map(scene, seq, true); @@ -3527,7 +3987,7 @@ static bool update_changed_seq_recurs(Scene *scene, Sequence *seq, Sequence *cha if (free_imbuf) { if (ibuf_change) { if (seq->type == SEQ_TYPE_MOVIE) - free_anim_seq(seq); + BKE_sequence_free_anim(seq); if (seq->type == SEQ_TYPE_SPEED) { BKE_sequence_effect_speed_rebuild_map(scene, seq, true); } @@ -4415,6 +4875,8 @@ Sequence *BKE_sequence_alloc(ListBase *lb, int cfra, int machine) seq->pitch = 1.0f; seq->scene_sound = NULL; + seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); + return seq; } @@ -4472,6 +4934,12 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad strip->stripdata = MEM_callocN(seq->len * sizeof(StripElem), "stripelem"); BLI_strncpy(strip->dir, seq_load->path, sizeof(strip->dir)); + if (seq_load->stereo3d_format) + *seq->stereo3d_format = *seq_load->stereo3d_format; + + seq->views_format = seq_load->views_format; + seq->flag |= seq_load->flag & SEQ_USE_VIEWS; + seq_load_apply(scene, seq, seq_load); return seq; @@ -4551,6 +5019,12 @@ Sequence *BKE_sequencer_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoad } #endif // WITH_AUDASPACE +static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id) +{ + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + IMB_suffix_anim(anim, suffix); +} + Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load) { Scene *scene = CTX_data_scene(C); /* only for sound */ @@ -4560,29 +5034,84 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad Strip *strip; StripElem *se; char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */ - - struct anim *an; + bool is_multiview_loaded = false; + const bool is_multiview = (seq_load->flag & SEQ_USE_VIEWS) != 0; + size_t totfiles = seq_num_files(scene, seq_load->views_format, is_multiview); + struct anim **anim_arr; + int i; BLI_strncpy(path, seq_load->path, sizeof(path)); BLI_path_abs(path, G.main->name); - an = openanim(path, IB_rect, 0, colorspace); + anim_arr = MEM_callocN(sizeof(struct anim *) * totfiles, "Video files"); - if (an == NULL) - return NULL; + if (is_multiview && (seq_load->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + char prefix[FILE_MAX]; + char *ext = NULL; + size_t j = 0; + + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); + + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + char str[FILE_MAX]; + + seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX); + anim_arr[j] = openanim(str, IB_rect, 0, colorspace); + seq_anim_add_suffix(scene, anim_arr[j], i); + + if (anim_arr[j]) { + j++; + } + } + + if (j == 0) { + MEM_freeN(anim_arr); + return NULL; + } + is_multiview_loaded = true; + } + } + + if (is_multiview_loaded == false) { + anim_arr[0] = openanim(path, IB_rect, 0, colorspace); + + if (anim_arr[0] == NULL) { + MEM_freeN(anim_arr); + return NULL; + } + } seq = BKE_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel); + + /* multiview settings */ + if (seq_load->stereo3d_format) { + *seq->stereo3d_format = *seq_load->stereo3d_format; + seq->views_format = seq_load->views_format; + } + seq->flag |= seq_load->flag & SEQ_USE_VIEWS; + seq->type = SEQ_TYPE_MOVIE; seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ - seq->anim = an; - seq->anim_preseek = IMB_anim_get_preseek(an); + for (i = 0; i < totfiles; i++) { + if (anim_arr[i]) { + StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim_arr[i]; + } + else { + break; + } + } + + seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2); BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); /* basic defaults */ seq->strip = strip = MEM_callocN(sizeof(Strip), "strip"); - seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); + seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); strip->us = 1; BLI_strncpy(seq->strip->colorspace_settings.name, colorspace, sizeof(seq->strip->colorspace_settings.name)); @@ -4610,6 +5139,7 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad /* can be NULL */ seq_load_apply(scene, seq, seq_load); + MEM_freeN(anim_arr); return seq; } @@ -4621,6 +5151,8 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup seq->tmp = seqn; seqn->strip = MEM_dupallocN(seq->strip); + seqn->stereo3d_format = MEM_dupallocN(seq->stereo3d_format); + /* XXX: add F-Curve duplication stuff? */ if (seq->strip->crop) { @@ -4667,7 +5199,7 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup else if (seq->type == SEQ_TYPE_MOVIE) { seqn->strip->stripdata = MEM_dupallocN(seq->strip->stripdata); - seqn->anim = NULL; + BLI_listbase_clear(&seqn->anims); } else if (seq->type == SEQ_TYPE_SOUND_RAM) { seqn->strip->stripdata = diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index a86ffacfdbd..cec455e01b9 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -50,26 +50,34 @@ /* ********************** general blender movie support ***************************** */ -static int start_stub(Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), - ReportList *UNUSED(reports), bool UNUSED(preview)) +static int start_stub(void *UNUSED(context_v), Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), + ReportList *UNUSED(reports), bool UNUSED(preview), const char *UNUSED(suffix)) { return 0; } -static void end_stub(void) +static void end_stub(void *UNUSED(context_v)) {} -static int append_stub(RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels), - int UNUSED(rectx), int UNUSED(recty), ReportList *UNUSED(reports)) +static int append_stub(void *UNUSED(context_v), RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels), + int UNUSED(rectx), int UNUSED(recty), const char *UNUSED(suffix), ReportList *UNUSED(reports)) { return 0; } +static void *context_create_stub(void) +{ return NULL; } + +static void context_free_stub(void *UNUSED(context_v)) +{} + #ifdef WITH_AVI # include "AVI_avi.h" /* callbacks */ -static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview); -static void end_avi(void); -static int append_avi(RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, ReportList *reports); -static void filepath_avi(char *string, RenderData *rd, bool preview); +static int start_avi(void *context_v, Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *suffix); +static void end_avi(void *context_v); +static int append_avi(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, ReportList *reports); +static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix); +static void *context_create_avi(void); +static void context_free_avi(void *context_v); #endif /* WITH_AVI */ #ifdef WITH_QUICKTIME @@ -93,13 +101,17 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.end_movie = end_stub; mh.get_next_frame = NULL; mh.get_movie_path = NULL; - + mh.context_create = context_create_stub; + mh.context_free = context_free_stub; + /* set the default handle, as builtin */ #ifdef WITH_AVI mh.start_movie = start_avi; mh.append_movie = append_avi; mh.end_movie = end_avi; mh.get_movie_path = filepath_avi; + mh.context_create = context_create_avi; + mh.context_free = context_free_avi; #endif /* do the platform specific handles */ @@ -109,6 +121,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = append_qt; mh.end_movie = end_qt; mh.get_movie_path = filepath_qt; + mh.context_create = context_create_qt; + mh.context_free = context_free_qt; } #endif #ifdef WITH_FFMPEG @@ -117,6 +131,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = BKE_ffmpeg_append; mh.end_movie = BKE_ffmpeg_end; mh.get_movie_path = BKE_ffmpeg_filepath_get; + mh.context_create = BKE_ffmpeg_context_create; + mh.context_free = BKE_ffmpeg_context_free; } #endif #ifdef WITH_FRAMESERVER @@ -125,6 +141,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = BKE_frameserver_append; mh.end_movie = BKE_frameserver_end; mh.get_next_frame = BKE_frameserver_loop; + mh.context_create = BKE_frameserver_context_create; + mh.context_free = BKE_frameserver_context_free; } #endif @@ -137,9 +155,7 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) #ifdef WITH_AVI -static AviMovie *avi = NULL; - -static void filepath_avi(char *string, RenderData *rd, bool preview) +static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix) { int sfra, efra; @@ -170,27 +186,27 @@ static void filepath_avi(char *string, RenderData *rd, bool preview) BLI_path_frame_range(string, sfra, efra, 4); } } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } -static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +static int start_avi(void *context_v, Scene *UNUSED(scene), RenderData *rd, int rectx, int recty, + ReportList *reports, bool preview, const char *suffix) { int x, y; char name[256]; AviFormat format; int quality; double framerate; - - (void)scene; /* unused */ - - filepath_avi(name, rd, preview); + AviMovie *avi = context_v; + + filepath_avi(name, rd, preview, suffix); x = rectx; y = recty; quality = rd->im_format.quality; framerate = (double) rd->frs_sec / (double) rd->frs_sec_base; - - avi = MEM_mallocN(sizeof(AviMovie), "avimovie"); if (rd->im_format.imtype != R_IMF_IMTYPE_AVIJPEG) format = AVI_FORMAT_AVI_RGB; else format = AVI_FORMAT_MJPEG; @@ -216,12 +232,13 @@ static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportL return 1; } -static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *pixels, - int rectx, int recty, ReportList *UNUSED(reports)) +static int append_avi(void *context_v, RenderData *UNUSED(rd), int start_frame, int frame, int *pixels, + int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports)) { unsigned int *rt1, *rt2, *rectot; int x, y; char *cp, rt; + AviMovie *avi = context_v; if (avi == NULL) return 0; @@ -252,22 +269,37 @@ static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *p return 1; } -static void end_avi(void) +static void end_avi(void *context_v) { + AviMovie *avi = context_v; + if (avi == NULL) return; AVI_close_compress(avi); - MEM_freeN(avi); - avi = NULL; } + +static void *context_create_avi(void) +{ + AviMovie *avi = MEM_mallocN(sizeof(AviMovie), "avimovie"); + return avi; +} + +static void context_free_avi(void *context_v) +{ + AviMovie *avi = context_v; + if (avi) { + MEM_freeN(avi); + } +} + #endif /* WITH_AVI */ /* similar to BKE_image_path_from_imformat() */ -void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview) +void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) { bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype); if (mh->get_movie_path) - mh->get_movie_path(string, rd, preview); + mh->get_movie_path(string, rd, preview, suffix); else string[0] = '\0'; } diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 0717369cde8..af71f19c226 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -61,36 +61,38 @@ #include "ffmpeg_compat.h" -static int ffmpeg_type = 0; -static int ffmpeg_codec = AV_CODEC_ID_MPEG4; -static int ffmpeg_audio_codec = AV_CODEC_ID_NONE; -static int ffmpeg_video_bitrate = 1150; -static int ffmpeg_audio_bitrate = 128; -static int ffmpeg_gop_size = 12; -static int ffmpeg_autosplit = 0; -static int ffmpeg_autosplit_count = 0; -static bool ffmpeg_preview = false; - -static AVFormatContext *outfile = 0; -static AVStream *video_stream = 0; -static AVStream *audio_stream = 0; -static AVFrame *current_frame = 0; -static struct SwsContext *img_convert_ctx = 0; - -static uint8_t *audio_input_buffer = 0; -static uint8_t *audio_deinterleave_buffer = 0; -static int audio_input_samples = 0; +typedef struct FFMpegContext { + int ffmpeg_type; + int ffmpeg_codec; + int ffmpeg_audio_codec; + int ffmpeg_video_bitrate; + int ffmpeg_audio_bitrate; + int ffmpeg_gop_size; + int ffmpeg_autosplit; + int ffmpeg_autosplit_count; + bool ffmpeg_preview; + + AVFormatContext *outfile; + AVStream *video_stream; + AVStream *audio_stream; + AVFrame *current_frame; + struct SwsContext *img_convert_ctx; + + uint8_t *audio_input_buffer; + uint8_t *audio_deinterleave_buffer; + int audio_input_samples; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 -static uint8_t *audio_output_buffer = 0; -static int audio_outbuf_size = 0; + uint8_t *audio_output_buffer; + int audio_outbuf_size; #endif -static double audio_time = 0.0f; -static bool audio_deinterleave = false; -static int audio_sample_size = 0; + double audio_time; + bool audio_deinterleave; + int audio_sample_size; #ifdef WITH_AUDASPACE -static AUD_Device *audio_mixdown_device = 0; + AUD_Device *audio_mixdown_device; #endif +} FFMpegContext; #define FFMPEG_AUTOSPLIT_SIZE 2000000000 @@ -99,6 +101,7 @@ static AUD_Device *audio_mixdown_device = 0; static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value); static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value); static void ffmpeg_set_expert_options(RenderData *rd); +static void ffmpeg_filepath_get(FFMpegContext *context, char *string, struct RenderData *rd, bool preview, const char *suffix); /* Delete a picture buffer */ @@ -117,50 +120,50 @@ static int request_float_audio_buffer(int codec_id) } #ifdef WITH_AUDASPACE -static int write_audio_frame(void) +static int write_audio_frame(FFMpegContext *context) { AVCodecContext *c = NULL; AVPacket pkt; AVFrame *frame = NULL; int got_output = 0; - c = audio_stream->codec; + c = context->audio_stream->codec; av_init_packet(&pkt); pkt.size = 0; pkt.data = NULL; - AUD_readDevice(audio_mixdown_device, audio_input_buffer, audio_input_samples); - audio_time += (double) audio_input_samples / (double) c->sample_rate; + AUD_readDevice(context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples); + context->audio_time += (double) context->audio_input_samples / (double) c->sample_rate; #ifdef FFMPEG_HAVE_ENCODE_AUDIO2 frame = avcodec_alloc_frame(); avcodec_get_frame_defaults(frame); - frame->pts = audio_time / av_q2d(c->time_base); - frame->nb_samples = audio_input_samples; + frame->pts = context->audio_time / av_q2d(c->time_base); + frame->nb_samples = context->audio_input_samples; frame->format = c->sample_fmt; #ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT frame->channel_layout = c->channel_layout; #endif - if (audio_deinterleave) { + if (context->audio_deinterleave) { int channel, i; uint8_t *temp; for (channel = 0; channel < c->channels; channel++) { for (i = 0; i < frame->nb_samples; i++) { - memcpy(audio_deinterleave_buffer + (i + channel * frame->nb_samples) * audio_sample_size, - audio_input_buffer + (c->channels * i + channel) * audio_sample_size, audio_sample_size); + memcpy(context->audio_deinterleave_buffer + (i + channel * frame->nb_samples) * context->audio_sample_size, + context->audio_input_buffer + (c->channels * i + channel) * context->audio_sample_size, context->audio_sample_size); } } - temp = audio_deinterleave_buffer; - audio_deinterleave_buffer = audio_input_buffer; - audio_input_buffer = temp; + temp = context->audio_deinterleave_buffer; + context->audio_deinterleave_buffer = context->audio_input_buffer; + context->audio_input_buffer = temp; } - avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, audio_input_buffer, - audio_input_samples * c->channels * audio_sample_size, 1); + avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, context->audio_input_buffer, + context->audio_input_samples * c->channels * context->audio_sample_size, 1); if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) { // XXX error("Error writing audio packet"); @@ -172,30 +175,30 @@ static int write_audio_frame(void) return 0; } #else - pkt.size = avcodec_encode_audio(c, audio_output_buffer, audio_outbuf_size, (short *) audio_input_buffer); + pkt.size = avcodec_encode_audio(c, context->audio_output_buffer, context->audio_outbuf_size, (short *) context->audio_input_buffer); if (pkt.size < 0) { // XXX error("Error writing audio packet"); return -1; } - pkt.data = audio_output_buffer; + pkt.data = context->audio_output_buffer; got_output = 1; #endif if (got_output) { if (pkt.pts != AV_NOPTS_VALUE) - pkt.pts = av_rescale_q(pkt.pts, c->time_base, audio_stream->time_base); + pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base); if (pkt.dts != AV_NOPTS_VALUE) - pkt.dts = av_rescale_q(pkt.dts, c->time_base, audio_stream->time_base); + pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base); if (pkt.duration > 0) - pkt.duration = av_rescale_q(pkt.duration, c->time_base, audio_stream->time_base); + pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base); - pkt.stream_index = audio_stream->index; + pkt.stream_index = context->audio_stream->index; pkt.flags |= AV_PKT_FLAG_KEY; - if (av_interleaved_write_frame(outfile, &pkt) != 0) { + if (av_interleaved_write_frame(context->outfile, &pkt) != 0) { fprintf(stderr, "Error writing audio packet!\n"); if (frame) avcodec_free_frame(&frame); @@ -302,11 +305,11 @@ static const char **get_file_extensions(int format) } /* Write a frame to the output file */ -static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) +static int write_video_frame(FFMpegContext *context, RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) { int got_output; int ret, success = 1; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; AVPacket packet = { 0 }; av_init_packet(&packet); @@ -321,22 +324,22 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis if (ret >= 0 && got_output) { if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base); + packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); PRINT("Video Frame PTS: %d\n", (int)packet.pts); } else { PRINT("Video Frame PTS: not set\n"); } if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base); + packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); PRINT("Video Frame DTS: %d\n", (int)packet.dts); } else { PRINT("Video Frame DTS: not set\n"); } - packet.stream_index = video_stream->index; - ret = av_interleaved_write_frame(outfile, &packet); + packet.stream_index = context->video_stream->index; + ret = av_interleaved_write_frame(context->outfile, &packet); success = (ret == 0); } else if (ret < 0) { @@ -350,11 +353,11 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis } /* read and encode a frame of audio from the buffer */ -static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) +static AVFrame *generate_video_frame(FFMpegContext *context, uint8_t *pixels, ReportList *reports) { uint8_t *rendered_frame; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; int width = c->width; int height = c->height; AVFrame *rgb_frame; @@ -367,7 +370,7 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) } } else { - rgb_frame = current_frame; + rgb_frame = context->current_frame; } rendered_frame = pixels; @@ -411,17 +414,17 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) } if (c->pix_fmt != PIX_FMT_BGR32) { - sws_scale(img_convert_ctx, (const uint8_t *const *) rgb_frame->data, + sws_scale(context->img_convert_ctx, (const uint8_t *const *) rgb_frame->data, rgb_frame->linesize, 0, c->height, - current_frame->data, current_frame->linesize); + context->current_frame->data, context->current_frame->linesize); delete_picture(rgb_frame); } - current_frame->format = PIX_FMT_BGR32; - current_frame->width = width; - current_frame->height = height; + context->current_frame->format = PIX_FMT_BGR32; + context->current_frame->width = width; + context->current_frame->height = height; - return current_frame; + return context->current_frame; } static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop, AVDictionary **dictionary) @@ -516,7 +519,7 @@ static void set_ffmpeg_properties(RenderData *rd, AVCodecContext *c, const char /* prepare a video stream for the output file */ -static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContext *of, +static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, int rectx, int recty, char *error, int error_size) { AVStream *st; @@ -542,7 +545,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->height = recty; /* FIXME: Really bad hack (tm) for NTSC support */ - if (ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { c->time_base.den = 2997; c->time_base.num = 100; } @@ -555,8 +558,8 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->time_base.num = ((double) rd->frs_sec_base) * 100000; } - c->gop_size = ffmpeg_gop_size; - c->bit_rate = ffmpeg_video_bitrate * 1000; + c->gop_size = context->ffmpeg_gop_size; + c->bit_rate = context->ffmpeg_video_bitrate * 1000; c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000; c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000; c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024; @@ -585,7 +588,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->pix_fmt = PIX_FMT_YUV422P; } - if (ffmpeg_type == FFMPEG_XVID) { + if (context->ffmpeg_type == FFMPEG_XVID) { /* arghhhh ... */ c->pix_fmt = PIX_FMT_YUV420P; c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); @@ -655,14 +658,14 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex } av_dict_free(&opts); - current_frame = alloc_picture(c->pix_fmt, c->width, c->height); + context->current_frame = alloc_picture(c->pix_fmt, c->width, c->height); - img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC, + context->img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); return st; } -static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size) +static AVStream *alloc_audio_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size) { AVStream *st; AVCodecContext *c; @@ -680,7 +683,7 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_rate = rd->ffcodecdata.audio_mixrate; - c->bit_rate = ffmpeg_audio_bitrate * 1000; + c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; c->channels = rd->ffcodecdata.audio_channels; @@ -747,35 +750,35 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex st->codec->time_base.den = st->codec->sample_rate; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - audio_outbuf_size = FF_MIN_BUFFER_SIZE; + context->audio_outbuf_size = FF_MIN_BUFFER_SIZE; #endif if (c->frame_size == 0) // used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD)) // not sure if that is needed anymore, so let's try out if there are any // complaints regarding some ffmpeg versions users might have - audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; + context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; else { - audio_input_samples = c->frame_size; + context->audio_input_samples = c->frame_size; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (c->frame_size * c->channels * sizeof(int16_t) * 4 > audio_outbuf_size) - audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; + if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) + context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; #endif } - audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); + context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); - audio_sample_size = av_get_bytes_per_sample(c->sample_fmt); + context->audio_sample_size = av_get_bytes_per_sample(c->sample_fmt); - audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size); + context->audio_input_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - audio_output_buffer = (uint8_t *) av_malloc(audio_outbuf_size); + context->audio_output_buffer = (uint8_t *) av_malloc(context->audio_outbuf_size); #endif - if (audio_deinterleave) - audio_deinterleave_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size); + if (context->audio_deinterleave) + context->audio_deinterleave_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); - audio_time = 0.0f; + context->audio_time = 0.0f; return st; } @@ -799,7 +802,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va av_dict_set(dict, key, buffer, 0); } -static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, ReportList *reports) +static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int rectx, int recty, const char *suffix, ReportList *reports) { /* Handle to the output file */ AVFormatContext *of; @@ -808,26 +811,26 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report char name[256], error[1024]; const char **exts; - ffmpeg_type = rd->ffcodecdata.type; - ffmpeg_codec = rd->ffcodecdata.codec; - ffmpeg_audio_codec = rd->ffcodecdata.audio_codec; - ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate; - ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate; - ffmpeg_gop_size = rd->ffcodecdata.gop_size; - ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT; - + context->ffmpeg_type = rd->ffcodecdata.type; + context->ffmpeg_codec = rd->ffcodecdata.codec; + context->ffmpeg_audio_codec = rd->ffcodecdata.audio_codec; + context->ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate; + context->ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate; + context->ffmpeg_gop_size = rd->ffcodecdata.gop_size; + context->ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT; + /* Determine the correct filename */ - BKE_ffmpeg_filepath_get(name, rd, ffmpeg_preview); + ffmpeg_filepath_get(context, name, rd, context->ffmpeg_preview, suffix); PRINT("Starting output to %s(ffmpeg)...\n" " Using type=%d, codec=%d, audio_codec=%d,\n" " video_bitrate=%d, audio_bitrate=%d,\n" " gop_size=%d, autosplit=%d\n" " render width=%d, render height=%d\n", - name, ffmpeg_type, ffmpeg_codec, ffmpeg_audio_codec, - ffmpeg_video_bitrate, ffmpeg_audio_bitrate, - ffmpeg_gop_size, ffmpeg_autosplit, rectx, recty); + name, context->ffmpeg_type, context->ffmpeg_codec, context->ffmpeg_audio_codec, + context->ffmpeg_video_bitrate, context->ffmpeg_audio_bitrate, + context->ffmpeg_gop_size, context->ffmpeg_autosplit, rectx, recty); - exts = get_file_extensions(ffmpeg_type); + exts = get_file_extensions(context->ffmpeg_type); if (!exts) { BKE_report(reports, RPT_ERROR, "No valid formats found"); return 0; @@ -846,7 +849,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report of->oformat = fmt; of->packet_size = rd->ffcodecdata.mux_packet_size; - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate); } else { @@ -857,15 +860,15 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report of->max_delay = (int)(0.7 * AV_TIME_BASE); - fmt->audio_codec = ffmpeg_audio_codec; + fmt->audio_codec = context->ffmpeg_audio_codec; BLI_strncpy(of->filename, name, sizeof(of->filename)); /* set the codec to the user's selection */ - switch (ffmpeg_type) { + switch (context->ffmpeg_type) { case FFMPEG_AVI: case FFMPEG_MOV: case FFMPEG_MKV: - fmt->video_codec = ffmpeg_codec; + fmt->video_codec = context->ffmpeg_codec; break; case FFMPEG_OGG: fmt->video_codec = AV_CODEC_ID_THEORA; @@ -908,9 +911,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } } - if (ffmpeg_type == FFMPEG_DV) { + if (context->ffmpeg_type == FFMPEG_DV) { fmt->audio_codec = AV_CODEC_ID_PCM_S16LE; - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!"); av_dict_free(&opts); return 0; @@ -918,9 +921,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } if (fmt->video_codec != AV_CODEC_ID_NONE) { - video_stream = alloc_video_stream(rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); - PRINT("alloc video stream %p\n", video_stream); - if (!video_stream) { + context->video_stream = alloc_video_stream(context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); + PRINT("alloc video stream %p\n", context->video_stream); + if (!context->video_stream) { if (error[0]) BKE_report(reports, RPT_ERROR, error); else @@ -931,9 +934,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } } - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of, error, sizeof(error)); - if (!audio_stream) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { + context->audio_stream = alloc_audio_stream(context, rd, fmt->audio_codec, of, error, sizeof(error)); + if (!context->audio_stream) { if (error[0]) BKE_report(reports, RPT_ERROR, error); else @@ -956,7 +959,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report return 0; } - outfile = of; + context->outfile = of; av_dump_format(of, 0, name, 1); av_dict_free(&opts); @@ -980,11 +983,11 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report * parameter. * </p> */ -static void flush_ffmpeg(void) +static void flush_ffmpeg(FFMpegContext *context) { int ret = 0; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; /* get the delayed frames */ while (1) { int got_output; @@ -1000,28 +1003,28 @@ static void flush_ffmpeg(void) break; } if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base); + packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); PRINT("Video Frame PTS: %d\n", (int) packet.pts); } else { PRINT("Video Frame PTS: not set\n"); } if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base); + packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); PRINT("Video Frame DTS: %d\n", (int) packet.dts); } else { PRINT("Video Frame DTS: not set\n"); } - packet.stream_index = video_stream->index; - ret = av_interleaved_write_frame(outfile, &packet); + packet.stream_index = context->video_stream->index; + ret = av_interleaved_write_frame(context->outfile, &packet); if (ret != 0) { fprintf(stderr, "Error writing delayed frame %d\n", ret); break; } } - avcodec_flush_buffers(video_stream->codec); + avcodec_flush_buffers(context->video_stream->codec); } /* ********************************************************************** @@ -1029,7 +1032,7 @@ static void flush_ffmpeg(void) * ********************************************************************** */ /* Get the output filename-- similar to the other output formats */ -void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) +static void ffmpeg_filepath_get(FFMpegContext *context, char *string, RenderData *rd, bool preview, const char *suffix) { char autosplit[20]; @@ -1056,7 +1059,9 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) autosplit[0] = 0; if ((rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT) != 0) { - sprintf(autosplit, "_%03d", ffmpeg_autosplit_count); + if (context) { + sprintf(autosplit, "_%03d", context->ffmpeg_autosplit_count); + } } if (rd->scemode & R_EXTENSION) { @@ -1086,19 +1091,28 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) strcat(string, autosplit); } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } -int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) +{ + ffmpeg_filepath_get(NULL, string, rd, preview, suffix); +} + +int BKE_ffmpeg_start(void *context_v, struct Scene *scene, RenderData *rd, int rectx, int recty, + ReportList *reports, bool preview, const char *suffix) { int success; + FFMpegContext *context = context_v; - ffmpeg_autosplit_count = 0; - ffmpeg_preview = preview; + context->ffmpeg_autosplit_count = 0; + context->ffmpeg_preview = preview; - success = start_ffmpeg_impl(rd, rectx, recty, reports); + success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); #ifdef WITH_AUDASPACE - if (audio_stream) { - AVCodecContext *c = audio_stream->codec; + if (context->audio_stream) { + AVCodecContext *c = context->audio_stream->codec; AUD_DeviceSpecs specs; specs.channels = c->channels; @@ -1123,7 +1137,7 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, } specs.rate = rd->ffcodecdata.audio_mixrate; - audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); + context->audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); #ifdef FFMPEG_CODEC_TIME_BASE c->time_base.den = specs.rate; c->time_base.num = 1; @@ -1133,16 +1147,16 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, return success; } -static void end_ffmpeg_impl(int is_autosplit); +static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit); #ifdef WITH_AUDASPACE -static void write_audio_frames(double to_pts) +static void write_audio_frames(FFMpegContext *context, double to_pts) { int finished = 0; - while (audio_stream && !finished) { - if ((audio_time >= to_pts) || - (write_audio_frame())) + while (context->audio_stream && !finished) { + if ((context->audio_time >= to_pts) || + (write_audio_frame(context))) { finished = 1; } @@ -1150,8 +1164,10 @@ static void write_audio_frames(double to_pts) } #endif -int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports) +int BKE_ffmpeg_append(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, ReportList *reports) { + FFMpegContext *context = context_v; AVFrame *avframe; int success = 1; @@ -1160,111 +1176,112 @@ int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, i /* why is this done before writing the video frame and again at end_ffmpeg? */ // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base)); - if (video_stream) { - avframe = generate_video_frame((unsigned char *) pixels, reports); - success = (avframe && write_video_frame(rd, frame - start_frame, avframe, reports)); + if (context->video_stream) { + avframe = generate_video_frame(context, (unsigned char *) pixels, reports); + success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports)); - if (ffmpeg_autosplit) { - if (avio_tell(outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { - end_ffmpeg_impl(true); - ffmpeg_autosplit_count++; - success &= start_ffmpeg_impl(rd, rectx, recty, reports); + if (context->ffmpeg_autosplit) { + if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { + end_ffmpeg_impl(context, true); + context->ffmpeg_autosplit_count++; + success &= start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); } } } #ifdef WITH_AUDASPACE - write_audio_frames((frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); + write_audio_frames(context, (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); #endif return success; } -static void end_ffmpeg_impl(int is_autosplit) +static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) { unsigned int i; PRINT("Closing ffmpeg...\n"); #if 0 - if (audio_stream) { /* SEE UPPER */ - write_audio_frames(); + if (context->audio_stream) { /* SEE UPPER */ + write_audio_frames(context); } #endif #ifdef WITH_AUDASPACE if (is_autosplit == false) { - if (audio_mixdown_device) { - AUD_closeReadDevice(audio_mixdown_device); - audio_mixdown_device = 0; + if (context->audio_mixdown_device) { + AUD_closeReadDevice(context->audio_mixdown_device); + context->audio_mixdown_device = 0; } } #endif - if (video_stream && video_stream->codec) { + if (context->video_stream && context->video_stream->codec) { PRINT("Flushing delayed frames...\n"); - flush_ffmpeg(); + flush_ffmpeg(context); } - if (outfile) { - av_write_trailer(outfile); + if (context->outfile) { + av_write_trailer(context->outfile); } /* Close the video codec */ - if (video_stream && video_stream->codec) { - avcodec_close(video_stream->codec); - PRINT("zero video stream %p\n", video_stream); - video_stream = 0; + if (context->video_stream && context->video_stream->codec) { + avcodec_close(context->video_stream->codec); + PRINT("zero video stream %p\n", context->video_stream); + context->video_stream = 0; } /* Close the output file */ - if (outfile) { - for (i = 0; i < outfile->nb_streams; i++) { - if (&outfile->streams[i]) { - av_freep(&outfile->streams[i]); + if (context->outfile) { + for (i = 0; i < context->outfile->nb_streams; i++) { + if (&context->outfile->streams[i]) { + av_freep(&context->outfile->streams[i]); } } } /* free the temp buffer */ - if (current_frame) { - delete_picture(current_frame); - current_frame = 0; + if (context->current_frame) { + delete_picture(context->current_frame); + context->current_frame = 0; } - if (outfile && outfile->oformat) { - if (!(outfile->oformat->flags & AVFMT_NOFILE)) { - avio_close(outfile->pb); + if (context->outfile && context->outfile->oformat) { + if (!(context->outfile->oformat->flags & AVFMT_NOFILE)) { + avio_close(context->outfile->pb); } } - if (outfile) { - av_free(outfile); - outfile = 0; + if (context->outfile) { + av_free(context->outfile); + context->outfile = 0; } - if (audio_input_buffer) { - av_free(audio_input_buffer); - audio_input_buffer = 0; + if (context->audio_input_buffer) { + av_free(context->audio_input_buffer); + context->audio_input_buffer = 0; } #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (audio_output_buffer) { - av_free(audio_output_buffer); - audio_output_buffer = 0; + if (context->audio_output_buffer) { + av_free(context->audio_output_buffer); + context->audio_output_buffer = 0; } #endif - if (audio_deinterleave_buffer) { - av_free(audio_deinterleave_buffer); - audio_deinterleave_buffer = 0; + if (context->audio_deinterleave_buffer) { + av_free(context->audio_deinterleave_buffer); + context->audio_deinterleave_buffer = 0; } - if (img_convert_ctx) { - sws_freeContext(img_convert_ctx); - img_convert_ctx = 0; + if (context->img_convert_ctx) { + sws_freeContext(context->img_convert_ctx); + context->img_convert_ctx = 0; } } -void BKE_ffmpeg_end(void) +void BKE_ffmpeg_end(void *context_v) { - end_ffmpeg_impl(false); + FFMpegContext *context = context_v; + end_ffmpeg_impl(context, false); } /* properties */ @@ -1658,4 +1675,31 @@ bool BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd) return false; } -#endif +void *BKE_ffmpeg_context_create(void) +{ + FFMpegContext *context; + + /* new ffmpeg data struct */ + context = MEM_callocN(sizeof(FFMpegContext), "new ffmpeg context"); + + context->ffmpeg_codec = AV_CODEC_ID_MPEG4; + context->ffmpeg_audio_codec = AV_CODEC_ID_NONE; + context->ffmpeg_video_bitrate = 1150; + context->ffmpeg_audio_bitrate = 128; + context->ffmpeg_gop_size = 12; + context->ffmpeg_autosplit = 0; + context->ffmpeg_autosplit_count = 0; + context->ffmpeg_preview = false; + + return context; +} + +void BKE_ffmpeg_context_free(void *context_v) +{ + FFMpegContext *context = context_v; + if (context) { + MEM_freeN(context); + } +} + +#endif /* WITH_FFMPEG */ diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c index 9cac86d62bd..ba58038bbd8 100644 --- a/source/blender/blenkernel/intern/writeframeserver.c +++ b/source/blender/blenkernel/intern/writeframeserver.c @@ -66,12 +66,15 @@ #include "BKE_report.h" #include "DNA_scene_types.h" +#include "MEM_guardedalloc.h" -static int sock; -static int connsock; -static int write_ppm; -static int render_width; -static int render_height; +typedef struct FrameserverContext { + int sock; + int connsock; + int write_ppm; + int render_width; + int render_height; +} FrameserverContext; #if defined(_WIN32) @@ -110,10 +113,11 @@ static int closesocket(int fd) } #endif -int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview)) +int BKE_frameserver_start(void *context_v, struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview), const char *UNUSED(suffix)) { struct sockaddr_in addr; int arg = 1; + FrameserverContext *context = context_v; (void)scene; /* unused */ @@ -122,33 +126,33 @@ int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx return 0; } - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + if ((context->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot open socket"); return 0; } - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg)); + setsockopt(context->sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg)); addr.sin_family = AF_INET; addr.sin_port = htons(U.frameserverport); addr.sin_addr.s_addr = INADDR_ANY; - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (bind(context->sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot bind to socket"); return 0; } - if (listen(sock, SOMAXCONN) < 0) { + if (listen(context->sock, SOMAXCONN) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot establish listen backlog"); return 0; } - connsock = -1; + context->connsock = -1; - render_width = rectx; - render_height = recty; + context->render_width = rectx; + context->render_height = recty; return 1; } @@ -177,7 +181,7 @@ static char good_bye[] = "<body><pre>\n" "Render stopped. Goodbye</pre></body></html>"; -static int safe_write(char *s, int tosend) +static int safe_write(const int connsock, char *s, int tosend) { int total = tosend; do { @@ -192,12 +196,12 @@ static int safe_write(char *s, int tosend) return total; } -static int safe_puts(char *s) +static int safe_puts(const int connsock, char *s) { - return safe_write(s, strlen(s)); + return safe_write(connsock, s, strlen(s)); } -static int handle_request(RenderData *rd, char *req) +static int handle_request(FrameserverContext *context, RenderData *rd, char *req) { char *p; char *path; @@ -214,16 +218,16 @@ static int handle_request(RenderData *rd, char *req) *p = 0; - if (STREQ(path, "/index.html") || STREQ(path, "/")) { - safe_puts(index_page); + if (STREQ(path, "/index.html") || strcmp(path, "/")) { + safe_puts(context->connsock, index_page); return -1; } - write_ppm = 0; + context->write_ppm = 0; pathlen = strlen(path); if (pathlen > 12 && memcmp(path, "/images/ppm/", 12) == 0) { - write_ppm = 1; + context->write_ppm = 1; return atoi(path + 12); } if (STREQ(path, "/info.txt")) { @@ -241,24 +245,24 @@ static int handle_request(RenderData *rd, char *req) "ratescale %d\n", rd->sfra, rd->efra, - render_width, - render_height, + context->render_width, + context->render_height, rd->frs_sec, 1 ); - safe_puts(buf); + safe_puts(context->connsock, buf); return -1; } if (STREQ(path, "/close.txt")) { - safe_puts(good_bye); + safe_puts(context->connsock, good_bye); G.is_break = true; /* Abort render */ return -1; } return -1; } -int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) +int BKE_frameserver_loop(void *context_v, RenderData *rd, ReportList *UNUSED(reports)) { fd_set readfds; struct timeval tv; @@ -271,18 +275,20 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) #endif char buf[4096]; - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + FrameserverContext *context = context_v; + + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&readfds); - FD_SET(sock, &readfds); + FD_SET(context->sock, &readfds); - rval = select(sock + 1, &readfds, NULL, NULL, &tv); + rval = select(context->sock + 1, &readfds, NULL, NULL, &tv); if (rval < 0) { return -1; } @@ -293,19 +299,19 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) socklen = sizeof(addr); - if ((connsock = accept(sock, (struct sockaddr *)&addr, &socklen)) < 0) { + if ((context->connsock = accept(context->sock, (struct sockaddr *)&addr, &socklen)) < 0) { return -1; } FD_ZERO(&readfds); - FD_SET(connsock, &readfds); + FD_SET(context->connsock, &readfds); for (;;) { /* give 10 seconds for telnet testing... */ tv.tv_sec = 10; tv.tv_usec = 0; - rval = select(connsock + 1, &readfds, NULL, NULL, &tv); + rval = select(context->connsock + 1, &readfds, NULL, NULL, &tv); if (rval > 0) { break; } @@ -319,7 +325,7 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) } } - len = recv(connsock, buf, sizeof(buf) - 1, 0); + len = recv(context->connsock, buf, sizeof(buf) - 1, 0); if (len < 0) { return -1; @@ -327,13 +333,13 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) buf[len] = 0; - return handle_request(rd, buf); + return handle_request(context, rd, buf); } -static void serve_ppm(int *pixels, int rectx, int recty) +static void serve_ppm(FrameserverContext *context, int *pixels, int rectx, int recty) { unsigned char *rendered_frame; - unsigned char *row = (unsigned char *) malloc(render_width * 3); + unsigned char *row = (unsigned char *) malloc(context->render_width * 3); int y; char header[1024]; @@ -348,7 +354,7 @@ static void serve_ppm(int *pixels, int rectx, int recty) "255\n", rectx, recty); - safe_puts(header); + safe_puts(context->connsock, header); rendered_frame = (unsigned char *)pixels; @@ -364,36 +370,54 @@ static void serve_ppm(int *pixels, int rectx, int recty) target += 3; src += 4; } - safe_write((char *)row, 3 * rectx); + safe_write(context->connsock, (char *)row, 3 * rectx); } free(row); - closesocket(connsock); - connsock = -1; + closesocket(context->connsock); + context->connsock = -1; } -int BKE_frameserver_append(RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels, - int rectx, int recty, ReportList *UNUSED(reports)) +int BKE_frameserver_append(void *context_v, RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels, + int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports)) { + FrameserverContext *context = context_v; + fprintf(stderr, "Serving frame: %d\n", frame); - if (write_ppm) { - serve_ppm(pixels, rectx, recty); + if (context->write_ppm) { + serve_ppm(context, pixels, rectx, recty); } - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } return 1; } -void BKE_frameserver_end(void) +void BKE_frameserver_end(void *context_v) { - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + FrameserverContext *context = context_v; + + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } - closesocket(sock); + closesocket(context->sock); shutdown_socket_system(); } +void *BKE_frameserver_context_create(void) +{ + FrameserverContext *context = MEM_mallocN(sizeof(FrameserverContext), "Frameserver Context"); + return context; +} + +void BKE_frameserver_context_free(void *context_v) +{ + FrameserverContext *context = context_v; + if (context) { + MEM_freeN(context); + } +} + #endif /* WITH_FRAMESERVER */ |