From 5eaeb9bd0d4d21ab89f5519bba716c5aedc715fc Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 4 Mar 2022 08:44:12 +0100 Subject: GPencil: Fix wrong parameters in `gpencil_check_same_material_color` The stroke and fill parameters were flipped. --- source/blender/blenkernel/intern/gpencil_curve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index ee6b77e6463..20b8342f090 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -50,8 +50,8 @@ static int gpencil_check_same_material_color(Object *ob_gp, const float color_stroke[4], const float color_fill[4], - const bool do_fill, const bool do_stroke, + const bool do_fill, Material **r_mat) { int index = -1; -- cgit v1.2.3 From 1763ffa0be4806949a2644a4a520647c9a1eefbd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 4 Mar 2022 07:59:07 -0500 Subject: Cleanup: Unused variable warnings --- source/blender/editors/uvedit/uvedit_select.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index ca66981cd3a..578e05b2c03 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1229,6 +1229,7 @@ void ED_uvedit_selectmode_flush(Scene *scene, BMEditMesh *em) const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); + UNUSED_VARS_NDEBUG(ts); /* Vertex Mode only. */ if (ts->uv_selectmode & UV_SELECT_VERTEX) { @@ -1295,6 +1296,7 @@ void uvedit_deselect_flush(Scene *scene, BMEditMesh *em) const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); + UNUSED_VARS_NDEBUG(ts); BMFace *efa; BMLoop *l; -- cgit v1.2.3 From 45079b169dc560dc3ff1ecaa8b040a5f7b5709ab Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Fri, 4 Mar 2022 15:21:18 +0100 Subject: Fix T96160: freezing when using multiple Handle Type Selection nodes Differential Revision: https://developer.blender.org/D14245 --- .../blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc index e4e87e519f7..0d4a9ff8649 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -126,7 +126,6 @@ class HandleTypeFieldInput final : public GeometryFieldInput { bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast(&other) != nullptr; if (const HandleTypeFieldInput *other_handle_selection = dynamic_cast(&other)) { return mode_ == other_handle_selection->mode_ && type_ == other_handle_selection->type_; -- cgit v1.2.3 From 295d5c6ef5b9a464cfdd52eaffb3c0e710ad4155 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 4 Mar 2022 11:11:56 -0500 Subject: Fix T96164: Crash with curve domain attributes When converting from the new type to the old, the curve domain attributes weren't properly resized, so their data was not properly allocated. --- source/blender/blenkernel/intern/curve_eval.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index d6525a11cff..1ffbed39216 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -436,6 +436,8 @@ std::unique_ptr curves_to_curve_eval(const Curves &curves) curve_eval->add_spline(std::move(spline)); } + curve_eval->attributes.reallocate(curve_eval->splines().size()); + CurveComponentLegacy dst_component; dst_component.replace(curve_eval.get(), GeometryOwnershipType::Editable); -- cgit v1.2.3 From 887ccb853711d89a67d77fa1037cd977298780b3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 4 Mar 2022 22:28:57 -0500 Subject: Fix T96152: Crash realizing curve instances The "curve_type" was transferred to instances because it isn't a built-in curve attribute. Then it was interpolated as a point domain attribute from the instance domain in the realize instances node. The fix was just missing from 9ec12c26f16ea3da1e6de95d5. `curve_type` needs to be marked as a built-in attribute. --- .../blender/blenkernel/intern/geometry_component_curves.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/source/blender/blenkernel/intern/geometry_component_curves.cc b/source/blender/blenkernel/intern/geometry_component_curves.cc index 5723d110aa0..86cbea9a9bb 100644 --- a/source/blender/blenkernel/intern/geometry_component_curves.cc +++ b/source/blender/blenkernel/intern/geometry_component_curves.cc @@ -467,6 +467,18 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() make_array_write_attribute, tag_component_topology_changed); + static BuiltinCustomDataLayerProvider curve_type("curve_type", + ATTR_DOMAIN_CURVE, + CD_PROP_INT8, + CD_PROP_INT8, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + curve_access, + make_array_read_attribute, + make_array_write_attribute, + tag_component_topology_changed); + static BuiltinCustomDataLayerProvider resolution("resolution", ATTR_DOMAIN_CURVE, CD_PROP_INT32, @@ -504,6 +516,7 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() &handle_type_left, &nurbs_order, &nurbs_weight, + &curve_type, &resolution, &cyclic}, {&curve_custom_data, &point_custom_data}); -- cgit v1.2.3 From a5f972c0180be7a87cfcdd3a02c1363e4b7e8350 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 6 Mar 2022 21:13:52 +1100 Subject: Cleanup: use loops for key-map entries that map values to number keys Also add __all__ referencing the only two members of the modules that should be accessed externally. --- .../keyconfig/keymap_data/blender_default.py | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 83946fbf68f..95a0f62042b 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1,5 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later +__all__ = ( + "generate_keymaps", + "Params", +) + # ------------------------------------------------------------------------------ # Developer Notes # @@ -361,12 +366,12 @@ def _template_items_editmode_mesh_select_mode(params): return [ ( "mesh.select_mode", - {"type": k, "value": 'PRESS', **key_expand, **key_extend}, + {"type": NUMBERS_1[i], "value": 'PRESS', **key_expand, **key_extend}, {"properties": [*prop_extend, *prop_expand, ("type", e)]} ) for key_expand, prop_expand in (({}, ()), ({"ctrl": True}, (("use_expand", True),))) for key_extend, prop_extend in (({}, ()), ({"shift": True}, (("use_extend", True),))) - for k, e in (('ONE', 'VERT'), ('TWO', 'EDGE'), ('THREE', 'FACE')) + for i, e in enumerate(('VERT', 'EDGE', 'FACE')) ] @@ -385,9 +390,9 @@ def _template_items_uv_select_mode(params): *_template_items_editmode_mesh_select_mode(params), # Hack to prevent fall-through, when sync select isn't enabled (and the island button isn't visible). ("mesh.select_mode", {"type": 'FOUR', "value": 'PRESS'}, None), - *(("uv.select_mode", {"type": k, "value": 'PRESS'}, + *(("uv.select_mode", {"type": NUMBERS_1[i], "value": 'PRESS'}, {"properties": [("type", e)]}) - for k, e in (('ONE', 'VERTEX'), ('TWO', 'EDGE'), ('THREE', 'FACE'), ('FOUR', 'ISLAND'))) + for i, e in enumerate(('VERTEX', 'EDGE', 'FACE', 'ISLAND'))) ] @@ -3627,12 +3632,9 @@ def km_grease_pencil_stroke_edit_mode(params): # Vertex group menu op_menu("GPENCIL_MT_gpencil_vertex_group", {"type": 'G', "value": 'PRESS', "ctrl": True}), # Select mode - ("gpencil.selectmode_toggle", {"type": 'ONE', "value": 'PRESS'}, - {"properties": [("mode", 0)]}), - ("gpencil.selectmode_toggle", {"type": 'TWO', "value": 'PRESS'}, - {"properties": [("mode", 1)]}), - ("gpencil.selectmode_toggle", {"type": 'THREE', "value": 'PRESS'}, - {"properties": [("mode", 2)]}), + *(("gpencil.selectmode_toggle", {"type": NUMBERS_1[i], "value": 'PRESS'}, + {"properties": [("mode", i)]}) + for i in range(3)), # Active layer op_menu("GPENCIL_MT_layer_active", {"type": 'Y', "value": 'PRESS'}), # Keyframe menu @@ -6082,10 +6084,8 @@ def km_sculpt_expand_modal(_params): ("RECURSION_STEP_GEODESIC", {"type": 'R', "value": 'PRESS'}, None), ("RECURSION_STEP_TOPOLOGY", {"type": 'R', "value": 'PRESS', "alt": True}, None), ("MOVE_TOGGLE", {"type": 'SPACE', "value": 'ANY', "any": True}, None), - ("FALLOFF_GEODESICS", {"type": 'ONE', "value": 'PRESS', "any": True}, None), - ("FALLOFF_TOPOLOGY", {"type": 'TWO', "value": 'PRESS', "any": True}, None), - ("FALLOFF_TOPOLOGY_DIAGONALS", {"type": 'THREE', "value": 'PRESS', "any": True}, None), - ("FALLOFF_SPHERICAL", {"type": 'FOUR', "value": 'PRESS', "any": True}, None), + *((e, {"type": NUMBERS_1[i], "value": 'PRESS', "any": True}, None) for i, e in enumerate( + ("FALLOFF_GEODESICS", "FALLOFF_TOPOLOGY", "FALLOFF_TOPOLOGY_DIAGONALS", "FALLOFF_SPHERICAL"))), ("SNAP_TOGGLE", {"type": 'LEFT_CTRL', "value": 'ANY'}, None), ("LOOP_COUNT_INCREASE", {"type": 'W', "value": 'PRESS', "any": True, "repeat": True}, None), ("LOOP_COUNT_DECREASE", {"type": 'Q', "value": 'PRESS', "any": True, "repeat": True}, None), -- cgit v1.2.3 From fae45a43fab410eb1e46a9bf4d4b6133b5a7bbdd Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 7 Mar 2022 10:51:22 +1100 Subject: Cleanup: use doxy-sections for pipeline, text_drag & effects Also improve on the doc-string for RE_RenderFrame & RE_RenderAnim. --- source/blender/editors/space_text/text_draw.c | 66 +++++++++-- source/blender/render/RE_pipeline.h | 10 +- source/blender/render/intern/pipeline.c | 68 +++++++++-- source/blender/sequencer/intern/effects.c | 155 +++++++++++++++++++++----- source/blender/sequencer/intern/effects.h | 7 -- 5 files changed, 249 insertions(+), 57 deletions(-) diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 8fefd258f7a..0e29c68cf23 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -36,7 +36,9 @@ #include "WM_api.h" #include "WM_types.h" -/******************** text font drawing ******************/ +/* -------------------------------------------------------------------- */ +/** \name Text Font Drawing + * \{ */ typedef struct TextDrawContext { int font_id; @@ -141,7 +143,11 @@ static void format_draw_color(const TextDrawContext *tdc, char formatchar) } } -/************************** draw text *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Text + * \{ */ /** * Notes on word-wrap @@ -556,7 +562,11 @@ static void text_draw(const SpaceText *st, flatten_string_free(&fs); } -/************************ cache utilities *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cache Utilities + * \{ */ typedef struct DrawCache { int *line_height; @@ -766,7 +776,11 @@ void text_free_caches(SpaceText *st) } } -/************************ word-wrap utilities *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Word-Wrap Utilities + * \{ */ /* cache should be updated in caller */ static int text_get_visible_lines_no(const SpaceText *st, int lineno) @@ -845,7 +859,11 @@ int text_get_total_lines(SpaceText *st, ARegion *region) return drawcache->total_lines; } -/************************ draw scrollbar *****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Scroll-Bar + * \{ */ static void calc_text_rcts(SpaceText *st, ARegion *region, rcti *scroll, rcti *back) { @@ -1006,7 +1024,11 @@ static void draw_textscroll(const SpaceText *st, rcti *scroll, rcti *back) col); } -/*********************** draw documentation *******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Documentation + * \{ */ #if 0 static void draw_documentation(const SpaceText *st, ARegion *region) @@ -1118,7 +1140,11 @@ static void draw_documentation(const SpaceText *st, ARegion *region) } #endif -/*********************** draw suggestion list *******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Suggestion List + * \{ */ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc, ARegion *region) { @@ -1221,7 +1247,11 @@ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc } } -/*********************** draw cursor ************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Cursor + * \{ */ static void draw_text_decoration(SpaceText *st, ARegion *region) { @@ -1383,7 +1413,11 @@ static void draw_text_decoration(SpaceText *st, ARegion *region) immUnbindProgram(); } -/******************* draw matching brackets *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Matching Brackets + * \{ */ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegion *region) { @@ -1544,7 +1578,11 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi } } -/*********************** main region drawing *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Region Drawing + * \{ */ void draw_text_main(SpaceText *st, ARegion *region) { @@ -1707,7 +1745,11 @@ void draw_text_main(SpaceText *st, ARegion *region) text_font_end(&tdc); } -/************************** update ***************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Update & Coordinate Conversion + * \{ */ void text_update_character_width(SpaceText *st) { @@ -1861,3 +1903,5 @@ error: r_pixel_co[0] = r_pixel_co[1] = -1; return false; } + +/** \} */ diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index 2d43aa7b27f..f532c705534 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -322,9 +322,13 @@ bool RE_WriteRenderViewsMovie(struct ReportList *reports, bool preview); /** - * Only #RE_NewRender() needed, main Blender render calls. - * * General Blender frame render call. + * + * \note Only #RE_NewRender() needed, main Blender render calls. + * + * \param write_still: Saves frames to disk (typically disabled). Useful for batch-operations + * (rendering from Python for e.g.) when an additional save action for is inconvenient. + * This is the default behavior for #RE_RenderAnim. */ void RE_RenderFrame(struct Render *re, struct Main *bmain, @@ -334,7 +338,7 @@ void RE_RenderFrame(struct Render *re, int frame, bool write_still); /** - * Saves images to disk. + * A version of #RE_RenderFrame that saves images to disk. */ void RE_RenderAnim(struct Render *re, struct Main *bmain, diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 2a93fb2c46b..aa006713755 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -116,14 +116,20 @@ * - save file or append in movie */ -/* ********* globals ******** */ +/* -------------------------------------------------------------------- */ +/** \name Globals + * \{ */ /* here we store all renders */ static struct { ListBase renderlist; } RenderGlobal = {{NULL, NULL}}; -/* ********* callbacks ******** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks + * \{ */ static void render_callback_exec_null(Render *re, Main *bmain, eCbEvent evt) { @@ -141,7 +147,11 @@ static void render_callback_exec_id(Render *re, Main *bmain, ID *id, eCbEvent ev BKE_callback_exec_id(bmain, id, evt); } -/* ********* alloc and free ******** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Allocation & Free + * \{ */ static int do_write_image_or_movie(Render *re, Main *bmain, @@ -308,7 +318,11 @@ static bool render_scene_has_layers_to_render(Scene *scene, ViewLayer *single_la return false; } -/* *************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Render API + * \{ */ Render *RE_GetRender(const char *name) { @@ -686,7 +700,11 @@ void RE_FreePersistentData(const Scene *scene) } } -/* ********* initialize state ******** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Initialize State + * \{ */ static void re_init_resolution(Render *re, Render *source, int winx, int winy, rcti *disprect) { @@ -905,7 +923,11 @@ void RE_test_break_cb(Render *re, void *handle, int (*f)(void *handle)) re->tbh = handle; } -/* ********* GL Context ******** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name OpenGL Context + * \{ */ void RE_gl_context_create(Render *re) { @@ -944,6 +966,16 @@ void *RE_gpu_context_get(Render *re) return re->gpu_context; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render & Composite Scenes (Implementation & Public API) + * + * Main high-level functions defined here are: + * - #RE_RenderFrame + * - #RE_RenderAnim + * \{ */ + /* ************ This part uses API, for rendering Blender scenes ********** */ /* make sure disprect is not affected by the render border */ @@ -1940,6 +1972,12 @@ void RE_RenderFreestyleExternal(Render *re) } #endif +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read/Write Render Result (Images & Movies) + * \{ */ + bool RE_WriteRenderViewsImage( ReportList *reports, RenderResult *rr, Scene *scene, const bool stamp, char *name) { @@ -2600,11 +2638,6 @@ bool RE_ReadRenderResult(Scene *scene, Scene *scenode) return success; } -void RE_init_threadcount(Render *re) -{ - re->r.threads = BKE_render_num_threads(&re->r); -} - void RE_layer_load_from_file( RenderLayer *layer, ReportList *reports, const char *filename, int x, int y) { @@ -2785,6 +2818,12 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA", true); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Miscellaneous Public Render API + * \{ */ + bool RE_allow_render_generic_object(Object *ob) { /* override not showing object when duplis are used with particles */ @@ -2796,3 +2835,10 @@ bool RE_allow_render_generic_object(Object *ob) } return true; } + +void RE_init_threadcount(Render *re) +{ + re->r.threads = BKE_render_num_threads(&re->r); +} + +/** \} */ diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index aa433eeed09..faa4cd14825 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -58,6 +58,10 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type); +/* -------------------------------------------------------------------- */ +/** \name Internal Utilities + * \{ */ + static void slice_get_byte_buffers(const SeqRenderData *context, const ImBuf *ibuf1, const ImBuf *ibuf2, @@ -108,7 +112,11 @@ static void slice_get_float_buffers(const SeqRenderData *context, } } -/*********************** Glow effect *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Glow Effect + * \{ */ enum { GlowR = 0, @@ -178,7 +186,11 @@ static ImBuf *prepare_effect_imbufs(const SeqRenderData *context, return out; } -/*********************** Alpha Over *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Alpha Over Effect + * \{ */ static void init_alpha_over_or_under(Sequence *seq) { @@ -288,7 +300,11 @@ static void do_alphaover_effect(const SeqRenderData *context, } } -/*********************** Alpha Under *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Alpha Under Effect + * \{ */ static void do_alphaunder_effect_byte( float fac, int x, int y, unsigned char *rect1, unsigned char *rect2, unsigned char *out) @@ -405,7 +421,11 @@ static void do_alphaunder_effect(const SeqRenderData *context, } } -/*********************** Cross *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Cross Effect + * \{ */ static void do_cross_effect_byte( float fac, int x, int y, unsigned char *rect1, unsigned char *rect2, unsigned char *out) @@ -482,7 +502,11 @@ static void do_cross_effect(const SeqRenderData *context, } } -/*********************** Gamma Cross *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gamma Cross + * \{ */ /* copied code from initrender.c */ static unsigned short gamtab[65536]; @@ -726,7 +750,11 @@ static void do_gammacross_effect(const SeqRenderData *context, } } -/*********************** Add *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Add Effect + * \{ */ static void do_add_effect_byte( float fac, int x, int y, unsigned char *rect1, unsigned char *rect2, unsigned char *out) @@ -802,7 +830,11 @@ static void do_add_effect(const SeqRenderData *context, } } -/*********************** Sub *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Subtract Effect + * \{ */ static void do_sub_effect_byte( float fac, int x, int y, unsigned char *rect1, unsigned char *rect2, unsigned char *out) @@ -880,7 +912,11 @@ static void do_sub_effect(const SeqRenderData *context, } } -/*********************** Drop *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drop Effect + * \{ */ /* Must be > 0 or add precopy, etc to the function */ #define XOFF 8 @@ -954,7 +990,11 @@ static void do_drop_effect_float( memcpy(out, rt1, sizeof(*out) * yoff * 4 * x); } -/*********************** Mul *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Multiply Effect + * \{ */ static void do_mul_effect_byte( float fac, int x, int y, unsigned char *rect1, unsigned char *rect2, unsigned char *out) @@ -1035,7 +1075,12 @@ static void do_mul_effect(const SeqRenderData *context, } } -/*********************** Blend Mode ***************************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Blend Mode Effect + * \{ */ + typedef void (*IMB_blend_func_byte)(unsigned char *dst, const unsigned char *src1, const unsigned char *src2); @@ -1268,7 +1313,13 @@ static void do_blend_mode_effect(const SeqRenderData *context, fac, context->rectx, total_lines, rect1, rect2, seq->blend_mode, rect_out); } } -/*********************** Color Mix Effect *************************/ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Color Mix Effect + * \{ */ + static void init_colormix_effect(Sequence *seq) { ColorMixVars *data; @@ -1314,7 +1365,11 @@ static void do_colormix_effect(const SeqRenderData *context, } } -/*********************** Wipe *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Wipe Effect + * \{ */ typedef struct WipeZone { float angle; @@ -1334,7 +1389,9 @@ static void precalc_wipe_zone(WipeZone *wipezone, WipeVars *wipe, int xo, int yo wipezone->pythangle = 1.0f / sqrtf(wipezone->angle * wipezone->angle + 1.0f); } -/* This function calculates the blur band for the wipe effects */ +/** + * This function calculates the blur band for the wipe effects. + */ static float in_band(float width, float dist, int side, int dir) { float alpha; @@ -1757,7 +1814,11 @@ static ImBuf *do_wipe_effect(const SeqRenderData *context, return out; } -/*********************** Transform *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Effect + * \{ */ static void init_transform_effect(Sequence *seq) { @@ -1909,7 +1970,11 @@ static void do_transform_effect(const SeqRenderData *context, transform->interpolation); } -/*********************** Glow *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Glow Effect + * \{ */ static void RVBlurBitmap2_float(float *map, int width, int height, float blur, int quality) { @@ -2229,7 +2294,11 @@ static ImBuf *do_glow_effect(const SeqRenderData *context, return out; } -/*********************** Solid color *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Solid Color Effect + * \{ */ static void init_solid_color(Sequence *seq) { @@ -2324,9 +2393,13 @@ static ImBuf *do_solid_color(const SeqRenderData *context, return out; } -/*********************** Mulitcam *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Mulit-Camera Effect + * \{ */ -/* no effect inputs for multicam, we use give_ibuf_seq */ +/** No effect inputs for multi-camera, we use #give_ibuf_seq. */ static int num_inputs_multicam(void) { return 0; @@ -2367,9 +2440,13 @@ static ImBuf *do_multicam(const SeqRenderData *context, return out; } -/*********************** Adjustment *************************/ +/** \} */ -/* no effect inputs for adjustment, we use give_ibuf_seq */ +/* -------------------------------------------------------------------- */ +/** \name Adjustment Effect + * \{ */ + +/** No effect inputs for adjustment, we use #give_ibuf_seq. */ static int num_inputs_adjustment(void) { return 0; @@ -2438,7 +2515,11 @@ static ImBuf *do_adjustment(const SeqRenderData *context, return out; } -/*********************** Speed *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Speed Effect + * \{ */ static void init_speed_effect(Sequence *seq) { @@ -2639,7 +2720,11 @@ static ImBuf *do_speed_effect(const SeqRenderData *context, return IMB_dupImBuf(ibuf1); } -/*********************** overdrop *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Over-Drop Effect + * \{ */ static void do_overdrop_effect(const SeqRenderData *context, Sequence *UNUSED(seq), @@ -2675,7 +2760,11 @@ static void do_overdrop_effect(const SeqRenderData *context, } } -/*********************** Gaussian Blur *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gaussian Blur + * \{ */ /* NOTE: This gaussian blur implementation accumulates values in the square * kernel rather that doing X direction and then Y direction because of the @@ -3099,7 +3188,11 @@ static ImBuf *do_gaussian_blur_effect(const SeqRenderData *context, return out; } -/*********************** text *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Text Effect + * \{ */ static void init_text_effect(Sequence *seq) { @@ -3341,7 +3434,11 @@ static ImBuf *do_text_effect(const SeqRenderData *context, return out; } -/*********************** sequence effect factory *************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sequence Effect Factory + * \{ */ static void init_noop(Sequence *UNUSED(seq)) { @@ -3592,6 +3689,12 @@ static struct SeqEffectHandle get_sequence_effect_impl(int seq_type) return rval; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public Sequencer Effect API + * \{ */ + struct SeqEffectHandle SEQ_effect_handle_get(Sequence *seq) { struct SeqEffectHandle rval = {false, false, NULL}; @@ -3639,3 +3742,5 @@ int SEQ_effect_get_num_inputs(int seq_type) } return 0; } + +/** \} */ diff --git a/source/blender/sequencer/intern/effects.h b/source/blender/sequencer/intern/effects.h index 573f8df4352..c15701a2047 100644 --- a/source/blender/sequencer/intern/effects.h +++ b/source/blender/sequencer/intern/effects.h @@ -15,13 +15,6 @@ struct Scene; struct SeqRenderData; struct Sequence; -/* ********************************************************************** - * sequencer.c - * - * Sequencer editing functions - * ********************************************************************** - */ - struct SeqEffectHandle seq_effect_get_sequence_blend(struct Sequence *seq); /** * Build frame map when speed in mode #SEQ_SPEED_MULTIPLY is animated. -- cgit v1.2.3 From 8ec35c05b2fe40f23fe9be9d95b03622c844b4f2 Mon Sep 17 00:00:00 2001 From: Red Mser Date: Mon, 7 Mar 2022 12:06:33 +1100 Subject: Curve: sync active material with selection Changing active spline updates active material index. Reviewed By: campbellbarton Ref D14250 --- source/blender/editors/curve/editcurve.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index a33fbb29f85..ba9502b80bf 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -4863,6 +4863,12 @@ bool ED_curve_editnurb_select_pick( BKE_curve_nurb_active_set(cu, nu); } + /* Change active material on object. */ + if (nu->mat_nr != obedit->actcol - 1) { + obedit->actcol = nu->mat_nr + 1; + WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, NULL); + } + if (vc.view_layer->basact != basact) { ED_object_base_activate(C, basact); } -- cgit v1.2.3 From 4681987d92c7de644fd3abddb726fa2300b59486 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Mon, 7 Mar 2022 07:46:24 +0100 Subject: Fix T96156: Snap to 3D cursor can't be undone Caused by oversight in 2bcf93bbbeb. Operator returns `OPERATOR_CANCELLED` when it should return `OPERATOR_FINISHED`. Reviewed By: mano-wii, campbellbarton Differential Revision: https://developer.blender.org/D14243 --- source/blender/editors/space_view3d/view3d_snap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 4334ede0a06..29e5917c7ed 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -616,9 +616,9 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) const int pivot_point = scene->toolsettings->transform_pivot_point; if (snap_selected_to_location(C, snap_target_global, use_offset, pivot_point, true)) { - return OPERATOR_CANCELLED; + return OPERATOR_FINISHED; } - return OPERATOR_FINISHED; + return OPERATOR_CANCELLED; } void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) -- cgit v1.2.3 From 073d2390f0b8d37a5f02309ac1dcaf7618d7a4f9 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Mon, 7 Mar 2022 08:43:37 +0100 Subject: Fix T96163: Image editor doesn't refresh when undo/redo. Undo would invalidate image owned GPU textures only. Textures that are owned by the editor were not refreshed. This patch would invalidate all the GPU textures by marking the whole image dirty. This can be improved later as we could add partial updates of GPU textures. Reviewed By: mont29 Maniphest Tasks: T96163 Differential Revision: https://developer.blender.org/D14259 --- source/blender/editors/space_image/image_undo.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index e81f3b6a490..9bfcf48ecd9 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -570,7 +570,8 @@ static void uhandle_restore_list(ListBase *undo_handles, bool use_init) if (changed) { BKE_image_mark_dirty(image, ibuf); - BKE_image_free_gputextures(image); /* force OpenGL reload */ + /* TODO(jbakker): only mark areas that are actually updated to improve performance. */ + BKE_image_partial_update_mark_full_update(image); if (ibuf->rect_float) { ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ -- cgit v1.2.3 From 25fc5876d3bb37018adcfe1a1b943b90d45f5f9a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 7 Mar 2022 19:55:33 +1100 Subject: Revert "Fix Crash: Switching to wireframe mode." This reverts commit cb986446e29a51b07bdb73b999a0339df5ecdeb4. --- source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index f1ac19f4651..4b802ae3a0a 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -580,14 +580,6 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, /* Fast-path (occlusion queries). */ GPU_SELECT_ALL); - /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is - * performed before the viewport is fully initialized (region->draw_buffer = NULL). - * When this is the case we should not use depth testing. */ - GPUViewport *gpu_viewport = WM_draw_region_get_viewport(region); - if (use_depth_test && gpu_viewport == NULL) { - return -1; - } - if (GPU_select_is_cached()) { GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); GPU_select_cache_load_id(); @@ -605,7 +597,8 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, * because all future passes the will use the cached depths. */ GPUFrameBuffer *depth_read_fb = NULL; if (use_depth_test) { - GPUTexture *depth_tx = GPU_viewport_depth_texture(gpu_viewport); + GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C)); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); GPU_framebuffer_ensure_config(&depth_read_fb, { GPU_ATTACHMENT_TEXTURE(depth_tx), -- cgit v1.2.3 From 72e20785e1e2ed7b15a9765ebb6df646fd32dbff Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 7 Mar 2022 21:31:48 +1100 Subject: Fix T96205: Active shape key gets lost upon edit mode undo Regression in d961adb866cc2d7a95e4c6a7f06c49e346ec1abe, it's important that for the Mesh used for undo storage matches the shape-key instead of using the coordinates of the Basis key. Prior to bfdbc78466ac14d45f353db9aa39cb21bb962701 a different method of restoring the basis shape-key coordinates was used (restoring from the input `Mesh.mvert` array). When undo wrote the edit-mesh into the mesh this was always NULL so the basis shape keys coordinates were never used. Now a parameter has been added so undo can use the active shape for the meshes vertex coordinates. Reviewed By: sergey Maniphest Tasks: T96205 Ref D14258 --- source/blender/bmesh/intern/bmesh_mesh_convert.cc | 26 ++++++++++++++++------- source/blender/bmesh/intern/bmesh_mesh_convert.h | 5 +++++ source/blender/editors/mesh/editmesh_undo.c | 1 + 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 917e5c788e5..15197cdccc6 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -691,8 +691,16 @@ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey) * \param bm: The source BMesh. * \param key: The destination key. * \param mvert: The destination vertex array (in some situations it's coordinates are updated). + * \param active_shapekey_to_mvert: When editing a non-basis shape key, the coordinates for the + * basis are typically copied into the `mvert` array since it makes sense for the meshes + * vertex coordinates to match the "Basis" key. + * When enabled, skip this step and copy #BMVert.co directly to #MVert.co, + * See #BMeshToMeshParams.active_shapekey_to_mvert doc-string. */ -static void bm_to_mesh_shape(BMesh *bm, Key *key, MVert *mvert) +static void bm_to_mesh_shape(BMesh *bm, + Key *key, + MVert *mvert, + const bool active_shapekey_to_mvert) { KeyBlock *actkey = static_cast(BLI_findlink(&key->block, bm->shapenr - 1)); @@ -776,12 +784,14 @@ static void bm_to_mesh_shape(BMesh *bm, Key *key, MVert *mvert) * In this case it's important to overwrite these coordinates with the basis-keys coordinates. */ bool update_vertex_coords_from_refkey = false; int cd_shape_offset_refkey = -1; - if ((actkey != key->refkey) && (cd_shape_keyindex_offset != -1)) { - const int refkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, key->refkey); - if (refkey_uuid != -1) { - cd_shape_offset_refkey = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, refkey_uuid); - if (cd_shape_offset_refkey != -1) { - update_vertex_coords_from_refkey = true; + if (active_shapekey_to_mvert == false) { + if ((actkey != key->refkey) && (cd_shape_keyindex_offset != -1)) { + const int refkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, key->refkey); + if (refkey_uuid != -1) { + cd_shape_offset_refkey = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, refkey_uuid); + if (cd_shape_offset_refkey != -1) { + update_vertex_coords_from_refkey = true; + } } } } @@ -1151,7 +1161,7 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } if (me->key) { - bm_to_mesh_shape(bm, me->key, me->mvert); + bm_to_mesh_shape(bm, me->key, me->mvert, params->active_shapekey_to_mvert); } /* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.h b/source/blender/bmesh/intern/bmesh_mesh_convert.h index 07ffc8b43df..6da5412a81c 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.h +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.h @@ -69,6 +69,11 @@ struct BMeshToMeshParams { * that have become invalid from updating the shape-key, see T71865. */ uint update_shapekey_indices : 1; + /** + * Instead of copying the basis shape-key into the #MVert array, + * copy the #BMVert.co directly to #MVert.co (used for reading undo data). + */ + uint active_shapekey_to_mvert : 1; struct CustomData_MeshMasks cd_mask_extra; }; /** diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 2960070714c..3452edd51dd 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -615,6 +615,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo .calc_object_remap = false, .update_shapekey_indices = false, .cd_mask_extra = {.vmask = CD_MASK_SHAPE_KEYINDEX}, + .active_shapekey_to_mvert = true, })); um->selectmode = em->selectmode; -- cgit v1.2.3 From 7ca13eef7c3349b346b178b8f4ca64838d2f4ccf Mon Sep 17 00:00:00 2001 From: Yann Lanthony Date: Mon, 7 Mar 2022 11:37:50 +0100 Subject: Improve multi-user gpencil data performance with modifiers When a grease pencil data-block has multiple users and is subject to modifiers, layer transforms or parenting, performance (especially playback) is greatly affected. This was caused by the grease pencil eval process which does per instance full-copies of the original datablock in case those kinds of transformations need to be applied. This commit changes the behavior of the eval process to do shallow copies (layers with empty frames) of the datablock instead and duplicates only the visible strokes. When we need to have a unique eval data per instance, only copy the strokes of visible frames to this copy. Performance: On a test file with 1350 frames 33k strokes and 480k points in a single grease pencil object that was instanced 13 times: - master: 2.8 - 3.3 fps - patch: 42 - 52 fps Co-authored by: @filedescriptor This patch was contributed by The SPA Studios. Reviewed By: #grease_pencil, pepeland Differential Revision: https://developer.blender.org/D14238 --- source/blender/blenkernel/intern/gpencil.c | 21 +++-- .../blender/blenkernel/intern/gpencil_modifier.c | 102 ++++++++++++--------- 2 files changed, 72 insertions(+), 51 deletions(-) diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 16d43d40c50..92ed273cac8 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -2568,11 +2568,13 @@ void BKE_gpencil_visible_stroke_advanced_iter(ViewLayer *view_layer, layer_cb(gpl, gpf, NULL, thunk); } - LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { - if (gps->totpoints == 0) { - continue; + if (stroke_cb) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->totpoints == 0) { + continue; + } + stroke_cb(gpl, gpf, gps, thunk); } - stroke_cb(gpl, gpf, gps, thunk); } } /* Draw Active frame on top. */ @@ -2590,12 +2592,13 @@ void BKE_gpencil_visible_stroke_advanced_iter(ViewLayer *view_layer, gpl->opacity = prev_opacity; continue; } - - LISTBASE_FOREACH (bGPDstroke *, gps, &act_gpf->strokes) { - if (gps->totpoints == 0) { - continue; + if (stroke_cb) { + LISTBASE_FOREACH (bGPDstroke *, gps, &act_gpf->strokes) { + if (gps->totpoints == 0) { + continue; + } + stroke_cb(gpl, act_gpf, gps, thunk); } - stroke_cb(gpl, act_gpf, gps, thunk); } } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index bc3aa88d096..d432b18ff5f 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -618,48 +618,65 @@ static void gpencil_assign_object_eval(Object *object) } } -/* Helper: Copy active frame from original datablock to evaluated datablock for modifiers. */ -static void gpencil_copy_activeframe_to_eval( - Depsgraph *depsgraph, Scene *scene, Object *ob, bGPdata *gpd_orig, bGPdata *gpd_eval) +static bGPdata *gpencil_copy_structure_for_eval(bGPdata *gpd) { + /* Create a temporary copy gpd. */ + ID *newid = NULL; + BKE_libblock_copy_ex(NULL, &gpd->id, &newid, LIB_ID_COPY_LOCALIZE); + bGPdata *gpd_eval = (bGPdata *)newid; + BLI_listbase_clear(&gpd_eval->layers); - bGPDlayer *gpl_eval = gpd_eval->layers.first; - LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd_orig->layers) { - - if (gpl_eval != NULL) { - bGPDframe *gpf_orig = gpl_orig->actframe; - - int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_orig); - if ((gpf_orig == NULL) || (gpf_orig && gpf_orig->framenum != remap_cfra)) { - gpf_orig = BKE_gpencil_layer_frame_get(gpl_orig, remap_cfra, GP_GETFRAME_USE_PREV); - } + if (gpd->mat != NULL) { + gpd_eval->mat = MEM_dupallocN(gpd->mat); + } - if (gpf_orig != NULL) { - int gpf_index = BLI_findindex(&gpl_orig->frames, gpf_orig); - bGPDframe *gpf_eval = BLI_findlink(&gpl_eval->frames, gpf_index); + /* Duplicate structure: layers and frames without strokes. */ + LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd->layers) { + bGPDlayer *gpl_eval = BKE_gpencil_layer_duplicate(gpl_orig, true, false); + BLI_addtail(&gpd_eval->layers, gpl_eval); + gpl_eval->runtime.gpl_orig = gpl_orig; + /* Update frames orig pointers (helps for faster lookup in copy_frame_to_eval_cb). */ + BKE_gpencil_layer_original_pointers_update(gpl_orig, gpl_eval); + } - if (gpf_eval != NULL) { - /* Delete old strokes. */ - BKE_gpencil_free_strokes(gpf_eval); - /* Copy again strokes. */ - BKE_gpencil_frame_copy_strokes(gpf_orig, gpf_eval); + return gpd_eval; +} - gpf_eval->runtime.gpf_orig = (bGPDframe *)gpf_orig; - BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf_eval); - } - } +void copy_frame_to_eval_cb(bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, void *thunk) +{ + /* Early return when callback is not provided with a frame. */ + if (gpf == NULL) { + return; + } - gpl_eval = gpl_eval->next; - } + /* Free any existing eval stroke data. This happens in case we have a single user on the data + * block and the strokes have not been deleted. */ + if (!BLI_listbase_is_empty(&gpf->strokes)) { + BKE_gpencil_free_strokes(gpf); } + + /* Get original frame. */ + bGPDframe *gpf_orig = gpf->runtime.gpf_orig; + /* Copy strokes to eval frame and update internal orig pointers. */ + BKE_gpencil_frame_copy_strokes(gpf_orig, gpf); + BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf); } -static bGPdata *gpencil_copy_for_eval(bGPdata *gpd) +void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *scene, Object *ob) { - const int flags = LIB_ID_COPY_LOCALIZE; + /* Remap layers' active frame with time modifiers applied. */ + bGPdata *gpd_eval = ob->data; + LISTBASE_FOREACH (bGPDlayer *, gpl_eval, &gpd_eval->layers) { + bGPDframe *gpf_eval = gpl_eval->actframe; + int remap_cfra = gpencil_remap_time_get(depsgraph, scene, ob, gpl_eval); + if (gpf_eval == NULL || gpf_eval->framenum != remap_cfra) { + gpl_eval->actframe = BKE_gpencil_layer_frame_get(gpl_eval, remap_cfra, GP_GETFRAME_USE_PREV); + } + } - bGPdata *result = (bGPdata *)BKE_id_copy_ex(NULL, &gpd->id, NULL, flags); - return result; + /* Copy only visible frames to evaluated version. */ + BKE_gpencil_visible_stroke_advanced_iter( + NULL, ob, copy_frame_to_eval_cb, NULL, NULL, true, scene->r.cfra); } void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *ob) @@ -688,7 +705,7 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o if (ob->runtime.gpd_eval != NULL) { /* Make sure to clear the pointer in case the runtime eval data points to the same data block. * This can happen when the gpencil data block was not tagged for a depsgraph update after last - * call to this function. */ + * call to this function (e.g. a frame change). */ if (gpd_eval == ob->runtime.gpd_eval) { gpd_eval = NULL; } @@ -707,18 +724,19 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o return; } - /* If only one user, don't need a new copy, just update data of the frame. */ - if (gpd_orig->id.us == 1) { - BLI_assert(ob->data != NULL); - gpencil_copy_activeframe_to_eval(depsgraph, scene, ob, ob_orig->data, gpd_eval); - return; + /* If datablock has only one user, we can update its eval data directly. + * Otherwise, we need to have distinct copies for each instance, since applied transformations + * may differ. */ + if (gpd_orig->id.us > 1) { + /* Copy of the original datablock's structure (layers and empty frames). */ + ob->runtime.gpd_eval = gpencil_copy_structure_for_eval(gpd_orig); + /* Overwrite ob->data with gpd_eval here. */ + gpencil_assign_object_eval(ob); } - /* Copy full datablock to evaluated version. */ - ob->runtime.gpd_eval = gpencil_copy_for_eval(gpd_orig); - /* Overwrite ob->data with gpd_eval here. */ - gpencil_assign_object_eval(ob); - BKE_gpencil_update_orig_pointers(ob_orig, ob); + BLI_assert(ob->data != NULL); + /* Only copy strokes from visible frames to evaluated data.*/ + gpencil_copy_visible_frames_to_eval(depsgraph, scene, ob); } void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob) -- cgit v1.2.3 From 0e51defcf42e1cb231d36da9ecc2cc0fbe6ae505 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 7 Mar 2022 21:47:00 +1100 Subject: Fix T95591: Crash on drawing with measure tool with tweak fallback tool Using press to activate the Tweak tool doesn't work well when used a fallback tool as the drag event is often used by the current tool - making it impossible not to select when dragging (unless the fallback tool is disabled entirely). Resolve this by using CLICK events when the Tweak tool is used as a fallback. Even though this avoids the crash, check for null-pointer de-reference since changes to the key-map shouldn't cause operators to crash. Note that the ability for operators to access a gizmo before it's fully initialized is a more general problem that should be addressed, but out of scope for a bug-fix. Reviewed By: zeddb, JulienKaspar, Severin Maniphest Tasks: T95591 Ref D14231 --- .../keyconfig/keymap_data/blender_default.py | 23 +++++++++++++--------- .../editors/space_view3d/view3d_gizmo_ruler.c | 20 +++++++++++++++++++ 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index eab97b30fff..3694f275d35 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -442,13 +442,18 @@ def _template_items_change_frame(params): # Tool System Templates -def _template_items_tool_select(params, operator, cursor_operator, *, extend): +def _template_items_tool_select(params, operator, cursor_operator, fallback, *, extend): if params.select_mouse == 'LEFTMOUSE': - # Immediate select without quick delay. + # By default use 'PRESS' for immediate select without quick delay. + # Fallback key-maps 'CLICK' since 'PRESS' events passes through (allowing either click or drag). + # + # NOTE: When the active (non-fallback) tool uses a key-map that activates it's primary tool on drag, + # it's important that this key-map uses click and not press. Otherwise it becomes impossible to use + # the tool without selecting elements under the cursor. return [ - (operator, {"type": 'LEFTMOUSE', "value": 'PRESS'}, + (operator, {"type": 'LEFTMOUSE', "value": 'CLICK' if fallback else 'PRESS'}, {"properties": [("deselect_all", True)]}), - (operator, {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True}, + (operator, {"type": 'LEFTMOUSE', "value": 'CLICK' if fallback else 'PRESS', "shift": True}, {"properties": [(extend, True)]}), ] else: @@ -6283,7 +6288,7 @@ def km_image_editor_tool_uv_select(params, *, fallback): {"space_type": 'IMAGE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "uv.select", "uv.cursor_set", extend="extend")), + params, "uv.select", "uv.cursor_set", fallback, extend="extend")), *([] if (not params.use_fallback_tool_rmb) else _template_uv_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), ]}, @@ -6490,7 +6495,7 @@ def km_3d_view_tool_select(params, *, fallback): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "view3d.select", "view3d.cursor3d", extend="toggle")), + params, "view3d.select", "view3d.cursor3d", fallback, extend="toggle")), *([] if (not params.use_fallback_tool_rmb) else _template_view3d_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy, exclude_mod="ctrl")), ]}, @@ -7402,7 +7407,7 @@ def km_3d_view_tool_edit_gpencil_select(params, *, fallback): {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "gpencil.select", "view3d.cursor3d", extend="toggle")), + params, "gpencil.select", "view3d.cursor3d", fallback, extend="toggle")), *([] if (not params.use_fallback_tool_rmb) else _template_view3d_gpencil_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), ]}, @@ -7540,7 +7545,7 @@ def km_3d_view_tool_sculpt_gpencil_select(params): return ( "3D View Tool: Sculpt Gpencil, Tweak", {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, - {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", extend="toggle")}, + {"items": _template_items_tool_select(params, "gpencil.select", "view3d.cursor3d", False, extend="toggle")}, ) @@ -7580,7 +7585,7 @@ def km_sequencer_editor_tool_generic_select(params, *, fallback): {"space_type": 'SEQUENCE_EDITOR', "region_type": 'WINDOW'}, {"items": [ *([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select( - params, "sequencer.select", "sequencer.cursor_set", extend="toggle")), + params, "sequencer.select", "sequencer.cursor_set", fallback, extend="toggle")), *([] if (not params.use_fallback_tool_rmb) else _template_sequencer_preview_select( type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)), diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 1082483dcd7..adcb101bca9 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -469,6 +469,19 @@ static bool view3d_ruler_item_mousemove(const bContext *C, return false; } +/** + * When the gizmo-group has been created immediately before running an operator + * to manipulate rulers, it's possible the new gizmo-group has not yet been initialized. + * in 3.0 this happened because left-click drag would both select and add a new ruler, + * significantly increasing the likelihood of this happening. + * Workaround this crash by checking the gizmo's custom-data has not been cleared. + * The key-map has also been modified not to trigger this bug, see T95591. + */ +static bool gizmo_ruler_check_for_operator(const wmGizmoGroup *gzgroup) +{ + return gzgroup->customdata != NULL; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1308,6 +1321,10 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e wmGizmoGroup *gzgroup = WM_gizmomap_group_find(gzmap, view3d_gzgt_ruler_id); const bool use_depth = (v3d->shading.type >= OB_SOLID); + if (!gizmo_ruler_check_for_operator(gzgroup)) { + return OPERATOR_CANCELLED; + } + /* Create new line */ RulerItem *ruler_item; ruler_item = ruler_item_add(gzgroup); @@ -1383,6 +1400,9 @@ static int view3d_ruler_remove_invoke(bContext *C, wmOperator *op, const wmEvent wmGizmoMap *gzmap = region->gizmo_map; wmGizmoGroup *gzgroup = WM_gizmomap_group_find(gzmap, view3d_gzgt_ruler_id); if (gzgroup) { + if (!gizmo_ruler_check_for_operator(gzgroup)) { + return OPERATOR_CANCELLED; + } RulerInfo *ruler_info = gzgroup->customdata; if (ruler_info->item_active) { RulerItem *ruler_item = ruler_info->item_active; -- cgit v1.2.3 From a61ee1dcae9b48d9312c8e04c3b3c6e6e3fba578 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 7 Mar 2022 21:51:50 +1100 Subject: Cleanup: quiet warnings --- source/blender/blenkernel/intern/gpencil_modifier.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index d432b18ff5f..ffc1f2d8774 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -642,7 +642,10 @@ static bGPdata *gpencil_copy_structure_for_eval(bGPdata *gpd) return gpd_eval; } -void copy_frame_to_eval_cb(bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, void *thunk) +static void copy_frame_to_eval_cb(bGPDlayer *UNUSED(gpl), + bGPDframe *gpf, + bGPDstroke *UNUSED(gps), + void *UNUSED(thunk)) { /* Early return when callback is not provided with a frame. */ if (gpf == NULL) { @@ -662,7 +665,7 @@ void copy_frame_to_eval_cb(bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, void BKE_gpencil_frame_original_pointers_update(gpf_orig, gpf); } -void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *scene, Object *ob) +static void gpencil_copy_visible_frames_to_eval(Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Remap layers' active frame with time modifiers applied. */ bGPdata *gpd_eval = ob->data; -- cgit v1.2.3 From 548eabbaa14fb6bcdc927333b7d912eea0c61a59 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 7 Mar 2022 22:32:06 +1100 Subject: Revert "Revert "Fix Crash: Switching to wireframe mode."" This reverts commit 25fc5876d3bb37018adcfe1a1b943b90d45f5f9a. Committed this unintentionally, looking into an alternate fix. --- source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 4b802ae3a0a..f1ac19f4651 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -580,6 +580,14 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, /* Fast-path (occlusion queries). */ GPU_SELECT_ALL); + /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is + * performed before the viewport is fully initialized (region->draw_buffer = NULL). + * When this is the case we should not use depth testing. */ + GPUViewport *gpu_viewport = WM_draw_region_get_viewport(region); + if (use_depth_test && gpu_viewport == NULL) { + return -1; + } + if (GPU_select_is_cached()) { GPU_select_begin(buffer, ARRAY_SIZE(buffer), &rect, gpu_select_mode, 0); GPU_select_cache_load_id(); @@ -597,8 +605,7 @@ static int gizmo_find_intersected_3d_intern(wmGizmo **visible_gizmos, * because all future passes the will use the cached depths. */ GPUFrameBuffer *depth_read_fb = NULL; if (use_depth_test) { - GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C)); - GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); + GPUTexture *depth_tx = GPU_viewport_depth_texture(gpu_viewport); GPU_framebuffer_ensure_config(&depth_read_fb, { GPU_ATTACHMENT_TEXTURE(depth_tx), -- cgit v1.2.3 From 4ffe2fec169c67655aad6a866be3a949b4062108 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Mon, 7 Mar 2022 12:31:36 +0100 Subject: Python: Add new `annotation_pre` & `annotation_post` handlers Annotation tool is used as a general mark tool for many add-ons. To be able to detect when an annotation is done is very handy to integrate the annotation tool in add-ons and other studio workflows. The new callback names are: `annotation_pre` and `annotation_post` Both callbacks are exposed via the Python module `bpy.app.handlers` Example use: ``` import bpy def annotation_starts(gpd): print("Annotation starts") def annotation_done(gpd): print("Annotation done") bpy.app.handlers.annotation_pre.clear() bpy.app.handlers.annotation_pre.append(annotation_starts) bpy.app.handlers.annotation_post.clear() bpy.app.handlers.annotation_post.append(annotation_done) ``` Note: The handlers are called for any annotation tool, including eraser. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D14221 --- source/blender/blenkernel/BKE_callbacks.h | 2 ++ source/blender/editors/gpencil/annotate_paint.c | 7 +++++++ source/blender/python/intern/bpy_app_handlers.c | 2 ++ 3 files changed, 11 insertions(+) diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index 66089d29c45..2cd28c4dfa5 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -95,6 +95,8 @@ typedef enum { BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST, BKE_CB_EVT_XR_SESSION_START_PRE, + BKE_CB_EVT_ANNOTATION_PRE, + BKE_CB_EVT_ANNOTATION_POST, BKE_CB_EVT_TOT, } eCbEvent; diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 1a7b034f2f8..b33b676c078 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -22,6 +22,7 @@ #include "PIL_time.h" +#include "BKE_callbacks.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" @@ -1509,6 +1510,9 @@ static void annotation_paint_initstroke(tGPsdata *p, Scene *scene = p->scene; ToolSettings *ts = scene->toolsettings; + /* Call to the annotation pre handler to notify python the annotation starts. */ + BKE_callback_exec_id_depsgraph(p->bmain, &p->gpd->id, p->depsgraph, BKE_CB_EVT_ANNOTATION_PRE); + /* get active layer (or add a new one if non-existent) */ p->gpl = BKE_gpencil_layer_active_get(p->gpd); if (p->gpl == NULL) { @@ -1675,6 +1679,9 @@ static void annotation_paint_strokeend(tGPsdata *p) annotation_stroke_newfrombuffer(p); } + /* Call to the annotation post handler to notify python the annotation is done. */ + BKE_callback_exec_id_depsgraph(p->bmain, &p->gpd->id, p->depsgraph, BKE_CB_EVT_ANNOTATION_POST); + /* clean up buffer now */ annotation_session_validatebuffer(p); } diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index 3155f3a63bd..f2cf4249042 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -64,6 +64,8 @@ static PyStructSequence_Field app_cb_info_fields[] = { {"load_factory_preferences_post", "on loading factory preferences (after)"}, {"load_factory_startup_post", "on loading factory startup (after)"}, {"xr_session_start_pre", "on starting an xr session (before)"}, + {"annotation_pre", "on drawing an annotation (before)"}, + {"annotation_post", "on drawing an annotation (after)"}, /* sets the permanent tag */ #define APP_CB_OTHER_FIELDS 1 -- cgit v1.2.3 From 00f6e4c99051b6182d9703891278e180366d96c6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Mon, 7 Mar 2022 12:37:13 +0100 Subject: Fix T96180: driver in node tree not updating in real time Differential Revision: https://developer.blender.org/D14260 --- source/blender/depsgraph/intern/builder/deg_builder_relations.cc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index d63d1bafb3e..3b2e0f92700 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1646,6 +1646,14 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) add_relation(property_exit_key, parameters_key, "Driven Property -> Properties"); } } + + /* Assume drivers on a node tree affect the evaluated output of the node tree. In theory we could + * check if the driven value actually affects the output, i.e. if it drives a node that is linked + * to the output. */ + if (GS(id_ptr->name) == ID_NT) { + ComponentKey ntree_output_key(id_ptr, NodeType::NTREE_OUTPUT); + add_relation(driver_key, ntree_output_key, "Drivers -> NTree Output"); + } } void DepsgraphRelationBuilder::build_driver_variables(ID *id, FCurve *fcu) -- cgit v1.2.3 From e908ebc0947d04747cb0bf0e79388b77e5693042 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 7 Mar 2022 13:43:31 +0100 Subject: Fix T96207: wrong default value of Principled BSDF specular tint Contributed by MysteryPancake. Differential Revision: https://developer.blender.org/D14256 --- source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc index 4c378d9bc09..c65d598e019 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.cc @@ -59,7 +59,7 @@ static void node_declare(NodeDeclarationBuilder &b) .max(1.0f) .subtype(PROP_FACTOR); b.add_input(N_("Specular Tint")) - .default_value(0.5f) + .default_value(0.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); -- cgit v1.2.3 From ea3b2e8736164e01d7fc5630eb7c7adbc01c8bc4 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Mon, 7 Mar 2022 16:15:16 +0100 Subject: Fix T95531: Draw y axis values in Driver Editor When drawing the driver editor, only skip drawing the "scrubbing area" and not the Y-axis values or the scroll bars. The issue was introduced in rBb3431a88465db2433b46e1f6426c801125d0047d to avoid drawing the playhead in the Driver Editor but also prevented the text on the y axis from being drawn. Reviewed by: Severin, sybren Maniphest Tasks: T95531 Differential Revision: https://developer.blender.org/D14022 --- source/blender/editors/space_graph/space_graph.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 7d5e8836490..537f6db2d4d 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -311,16 +311,14 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region) /* draw entirely, view changes should be handled here */ const SpaceGraph *sipo = CTX_wm_space_graph(C); - /* Driver Editor's X axis is not time. */ - if (sipo->mode == SIPO_MODE_DRIVERS) { - return; - } - const Scene *scene = CTX_data_scene(C); View2D *v2d = ®ion->v2d; - /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME); + /* Driver Editor's X axis is not time. */ + if (sipo->mode != SIPO_MODE_DRIVERS) { + /* scrubbing region */ + ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME); + } /* scrollers */ /* FIXME: args for scrollers depend on the type of data being shown. */ -- cgit v1.2.3 From 5ae26e410ab1c23a705f7d9a4873f3bb29ba86d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 7 Mar 2022 16:48:44 +0100 Subject: Fix T94360: Assert on dragging keyframes Fix an assert by commenting out the assert. In normal situations all keyframes are sorted. However, while keys are transformed, they may change order and then this assertion no longer holds. The effect is that the drawing isn't perfect during the transform; the "constant value" bars aren't updated until the transformation is confirmed. Apart from that, the code runs fine, so it seems like a workable workaround. --- source/blender/editors/animation/keyframes_keylist.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 0b795fea278..7dc3415c904 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -807,8 +807,11 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co continue; } - /* Normal sequence */ - BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); + /* In normal situations all keyframes are sorted. However, while keys are transformed, they + * may change order and then this assertion no longer holds. The effect is that the drawing + * isn't perfect during the transform; the "constant value" bars aren't updated until the + * transformation is confirmed. */ + /* BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); */ compute_keyblock_data(&block, bezt, bezt + 1); -- cgit v1.2.3 From 5dca3ee6a23f002b59c78c54da53c9f45e2d1173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 7 Mar 2022 17:04:10 +0100 Subject: Fix T95256: Crash when creating off-screen pose asset Fix crash when creating a pose asset for which the file list entry in the asset browser is scrolled off-screen. Because of the off-screen-ness, it wasn't loaded into memory, which eventually caused an unexpected NULL pointer. The solution was to use a different function (`filelist_file_find_id`) that can reliably find the file list entry, after which the cache entry can be created. Reviewed by: Severin Differential Revision: https://developer.blender.org/D14265 --- source/blender/editors/space_file/filesel.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index f9783d1b19f..8182ed2624c 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -531,19 +531,15 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def FileSelectParams *params = ED_fileselect_get_active_params(sfile); struct FileList *files = sfile->files; - const int num_files_filtered = filelist_files_ensure(files); - for (int file_index = 0; file_index < num_files_filtered; ++file_index) { - const FileDirEntry *file = filelist_file_ex(files, file_index, false); - - if (filelist_file_get_id(file) != asset_id) { - continue; - } - - params->active_file = file_index; - filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); - break; + const int file_index = filelist_file_find_id(files, asset_id); + const FileDirEntry *file = filelist_file_ex(files, file_index, true); + if (file == NULL) { + return; } + params->active_file = file_index; + filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL); WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL); } -- cgit v1.2.3 From 6b8dde93b0ca898da16ff367d110f785e35e8981 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Mon, 7 Mar 2022 16:15:16 +0100 Subject: Fix T95531: Draw y axis values in Driver Editor When drawing the driver editor, only skip drawing the "scrubbing area" and not the Y-axis values or the scroll bars. The issue was introduced in rBb3431a88465db2433b46e1f6426c801125d0047d to avoid drawing the playhead in the Driver Editor but also prevented the text on the y axis from being drawn. Reviewed by: Severin, sybren Maniphest Tasks: T95531 Differential Revision: https://developer.blender.org/D14022 --- source/blender/editors/space_graph/space_graph.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 22427675ff3..f041f2d1d3b 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -295,16 +295,14 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region) /* draw entirely, view changes should be handled here */ const SpaceGraph *sipo = CTX_wm_space_graph(C); - /* Driver Editor's X axis is not time. */ - if (sipo->mode == SIPO_MODE_DRIVERS) { - return; - } - const Scene *scene = CTX_data_scene(C); View2D *v2d = ®ion->v2d; - /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME); + /* Driver Editor's X axis is not time. */ + if (sipo->mode != SIPO_MODE_DRIVERS) { + /* scrubbing region */ + ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME); + } /* scrollers */ /* FIXME: args for scrollers depend on the type of data being shown. */ -- cgit v1.2.3 From 2d06b97d2918f389ac266b3789aa340c223df463 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 7 Mar 2022 17:04:10 +0100 Subject: Fix T95256: Crash when creating off-screen pose asset Fix crash when creating a pose asset for which the file list entry in the asset browser is scrolled off-screen. Because of the off-screen-ness, it wasn't loaded into memory, which eventually caused an unexpected NULL pointer. The solution was to use a different function (`filelist_file_find_id`) that can reliably find the file list entry, after which the cache entry can be created. Reviewed by: Severin Differential Revision: https://developer.blender.org/D14265 --- source/blender/editors/space_file/filesel.c | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index f6629061878..933c840798b 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -515,19 +515,15 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def FileSelectParams *params = ED_fileselect_get_active_params(sfile); struct FileList *files = sfile->files; - const int num_files_filtered = filelist_files_ensure(files); - for (int file_index = 0; file_index < num_files_filtered; ++file_index) { - const FileDirEntry *file = filelist_file_ex(files, file_index, false); - - if (filelist_file_get_id(file) != asset_id) { - continue; - } - - params->active_file = file_index; - filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); - break; + const int file_index = filelist_file_find_id(files, asset_id); + const FileDirEntry *file = filelist_file_ex(files, file_index, true); + if (file == NULL) { + return; } + params->active_file = file_index; + filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL); WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL); } -- cgit v1.2.3 From 5bb2b4236dcf2b84121074660bcf8ed0de03eeeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 7 Mar 2022 17:10:13 +0100 Subject: Cleanup: move asssertion-test-only variable into `#ifndef NDEBUG` block Move `ToolSettings *ts` into an `#ifdef NDEBUG` block, as it's only used for a `BLI_assert` call. --- source/blender/editors/uvedit/uvedit_select.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 578e05b2c03..e574dc0620c 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1266,10 +1266,12 @@ void ED_uvedit_selectmode_flush(Scene *scene, BMEditMesh *em) * For face selections with sticky mode enabled, this can create invalid selection states. */ void uvedit_select_flush(Scene *scene, BMEditMesh *em) { - ToolSettings *ts = scene->toolsettings; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); +#ifndef NDEBUG + ToolSettings *ts = scene->toolsettings; BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); +#endif BMFace *efa; BMLoop *l; -- cgit v1.2.3 From f130d4f2112c99225a22d99f3b61da16f30f42ef Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 7 Mar 2022 17:25:50 +0100 Subject: Cleanup: fix various typos Contributed by luzpaz. Differential Revision: https://developer.blender.org/D14203 --- CMakeLists.txt | 2 +- build_files/cmake/platform/platform_unix.cmake | 2 +- build_files/windows/find_dependencies.cmd | 2 +- intern/cycles/device/queue.h | 2 +- source/blender/blenkernel/BKE_customdata.h | 2 +- source/blender/blenkernel/intern/DerivedMesh.cc | 2 +- source/blender/blenkernel/intern/constraint.c | 2 +- source/blender/bmesh/intern/bmesh_mesh_convert.cc | 2 +- source/blender/bmesh/operators/bmo_fill_edgeloop.c | 2 +- source/blender/freestyle/intern/view_map/ViewMap.h | 2 +- source/blender/freestyle/intern/winged_edge/WXEdge.cpp | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f8800fe303..f7d85969ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -502,7 +502,7 @@ else() set(WITH_EXPERIMENTAL_FEATURES OFF) endif() -# Unit testsing +# Unit testing option(WITH_GTESTS "Enable GTest unit testing" OFF) option(WITH_OPENGL_RENDER_TESTS "Enable OpenGL render related unit testing (Experimental)" OFF) option(WITH_OPENGL_DRAW_TESTS "Enable OpenGL UI drawing related unit testing (Experimental)" OFF) diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 77d1db97997..0a7119802c8 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -858,7 +858,7 @@ endif() # If atomic operations are possible without libatomic then linker flags are left as-is. function(CONFIGURE_ATOMIC_LIB_IF_NEEDED) # Source which is used to enforce situation when software emulation of atomics is required. - # Assume that using 64bit integer gives a definitive asnwer (as in, if 64bit atomic operations + # Assume that using 64bit integer gives a definitive answer (as in, if 64bit atomic operations # are possible using assembly/intrinsics 8, 16, and 32 bit operations will also be possible. set(_source "#include diff --git a/build_files/windows/find_dependencies.cmd b/build_files/windows/find_dependencies.cmd index fec2bd2e752..fa893c949cf 100644 --- a/build_files/windows/find_dependencies.cmd +++ b/build_files/windows/find_dependencies.cmd @@ -5,7 +5,7 @@ for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X) for %%X in (git.exe) do (set GIT=%%~$PATH:X) REM For python, default on 39 but if that does not exist also check REM the 310,311 and 312 folders to see if those are there, it checks -REM this far ahead to ensure good lib folder compatiblity in the future. +REM this far ahead to ensure good lib folder compatibility in the future. set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe if EXIST %PYTHON% ( goto detect_python_done diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h index 183ac1728a4..2bd6e7ae460 100644 --- a/intern/cycles/device/queue.h +++ b/intern/cycles/device/queue.h @@ -100,7 +100,7 @@ class DeviceQueue { * based on number of cores and/or available memory. */ virtual int num_concurrent_states(const size_t state_size) const = 0; - /* Number of states which keeps the device occupied with work without loosing performance. + /* Number of states which keeps the device occupied with work without losing performance. * The renderer will add more work (when available) when number of active paths falls below this * value. */ virtual int num_concurrent_busy_states() const = 0; diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index ea21fa9b404..940dc3c4f6c 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -716,7 +716,7 @@ void CustomData_data_transfer(const struct MeshPairRemap *me_remap, * \param write_layers_buff: An optional buffer for r_write_layers (to avoid allocating it). * \param write_layers_size: The size of pre-allocated \a write_layer_buff. * - * \warning After this funcion has ran, given custom data is no more valid from Blender POV + * \warning After this function has ran, given custom data is no more valid from Blender POV * (its `totlayer` is invalid). This function shall always be called with localized data * (as it is in write_meshes()). * diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 39074a5c75f..904a43a7c28 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -1468,7 +1468,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, /* set the DerivedMesh to only copy needed data */ CustomData_MeshMasks_update(&mask, &append_mask); - /* XXX WHAT? ovewrites mask ??? */ + /* XXX WHAT? overwrites mask ??? */ /* CD_MASK_ORCO may have been cleared above */ mask = md_datamask->mask; mask.vmask |= CD_MASK_ORIGINDEX; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 2afe4dda35c..7a97139748f 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -5515,7 +5515,7 @@ static void constraints_init_typeinfo(void) constraintsTypeInfo[12] = &CTI_ACTION; /* Action Constraint */ constraintsTypeInfo[13] = &CTI_LOCKTRACK; /* Locked-Track Constraint */ constraintsTypeInfo[14] = &CTI_DISTLIMIT; /* Limit Distance Constraint */ - constraintsTypeInfo[15] = &CTI_STRETCHTO; /* StretchTo Constaint */ + constraintsTypeInfo[15] = &CTI_STRETCHTO; /* StretchTo Constraint */ constraintsTypeInfo[16] = &CTI_MINMAX; /* Floor Constraint */ /* constraintsTypeInfo[17] = &CTI_RIGIDBODYJOINT; */ /* RigidBody Constraint - Deprecated */ constraintsTypeInfo[18] = &CTI_CLAMPTO; /* ClampTo Constraint */ diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index dcbb14e3578..fd14a3416e2 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -607,7 +607,7 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert) * * WARNING: There is an exception to the rule of ignoring coordinates in the destination: * that is when shape-key data in `bm` can't be found (which is itself an error/exception). - * In this case our own rule is violated as the alternative is loosing the shape-data entirely. + * In this case our own rule is violated as the alternative is losing the shape-data entirely. * * Flushing Coordinates Back to the #BMesh * --------------------------------------- diff --git a/source/blender/bmesh/operators/bmo_fill_edgeloop.c b/source/blender/bmesh/operators/bmo_fill_edgeloop.c index 352b9336a20..86d204ea6a7 100644 --- a/source/blender/bmesh/operators/bmo_fill_edgeloop.c +++ b/source/blender/bmesh/operators/bmo_fill_edgeloop.c @@ -3,7 +3,7 @@ /** \file * \ingroup bmesh * - * Fill discreet edge loop(s) with faces. + * Fill discrete edge loop(s) with faces. */ #include "MEM_guardedalloc.h" diff --git a/source/blender/freestyle/intern/view_map/ViewMap.h b/source/blender/freestyle/intern/view_map/ViewMap.h index 729b31965c8..fe7c2e62d9a 100644 --- a/source/blender/freestyle/intern/view_map/ViewMap.h +++ b/source/blender/freestyle/intern/view_map/ViewMap.h @@ -938,7 +938,7 @@ class ViewEdge : public Interface1D { FEdge *_FEdgeB; // last edge of the embedded fedges chain Id _Id; unsigned _ChainingTimeStamp; - // The silhouette view edge separates 2 2D spaces. The one on the left is necessarly the Shape + // The silhouette view edge separates two 2D spaces. The one on the left is necessarily the Shape // _Shape (the one to which this edge belongs to) and _aShape is the one on its right NOT HANDLED // BY THE COPY CONSTRUCTOR ViewShape *_aShape; diff --git a/source/blender/freestyle/intern/winged_edge/WXEdge.cpp b/source/blender/freestyle/intern/winged_edge/WXEdge.cpp index a56ac003703..b18d232dbbe 100644 --- a/source/blender/freestyle/intern/winged_edge/WXEdge.cpp +++ b/source/blender/freestyle/intern/winged_edge/WXEdge.cpp @@ -74,7 +74,7 @@ WXSmoothEdge *WXFaceLayer::BuildSmoothEdge() //----------------------------- // We retrieve the 2 edges for which we have opposite signs for each extremity RetrieveCuspEdgesIndices(cuspEdgesIndices); - if (cuspEdgesIndices.size() != 2) { // we necessarly have 2 cusp edges + if (cuspEdgesIndices.size() != 2) { // we necessarily have 2 cusp edges return nullptr; } -- cgit v1.2.3 From 60481e4d99920d28c840342c0b27fa083dd4ffd9 Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Mon, 7 Mar 2022 15:58:24 +0100 Subject: Fix T96195: f-curve factorized polynomial generator broken UI The polynomial parameters were not shown correctly. Differential Revision: https://developer.blender.org/D14254 --- source/blender/editors/animation/fmodifier_ui.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index c4d8484e6a8..8e0fad2d513 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -427,7 +427,7 @@ static void generator_panel_draw(const bContext *C, Panel *panel) uiLayout *first_row = uiLayoutRow(col, true); uiItemFullR(first_row, ptr, prop, 0, 0, 0, IFACE_("y = (Ax + B)"), ICON_NONE); uiItemFullR(first_row, ptr, prop, 1, 0, 0, "", ICON_NONE); - for (int i = 2; i < data->arraysize - 1; i++) { + for (int i = 2; i < data->arraysize - 1; i += 2) { /* \u2715 is the multiplication symbol. */ uiLayout *row = uiLayoutRow(col, true); uiItemFullR(row, ptr, prop, i, 0, 0, IFACE_("\u2715 (Ax + B)"), ICON_NONE); -- cgit v1.2.3 From 5b4ab896634fd118cb46740f6f90e45f96d550ac Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Mon, 7 Mar 2022 17:34:52 +0100 Subject: Shader Nodes: add Alpha output to Object Info node An alpha component can be specified for an object's color. This adds an alpha socket to the object info shader node allowing for the alpha component of the object's color to be accessed in the shader editor. Differential Revision: https://developer.blender.org/D14141 --- intern/cycles/blender/object.cpp | 4 +++- intern/cycles/kernel/geom/object.h | 10 ++++++++++ intern/cycles/kernel/osl/services.cpp | 5 +++++ intern/cycles/kernel/osl/services.h | 1 + intern/cycles/kernel/osl/shaders/node_object_info.osl | 2 ++ intern/cycles/kernel/svm/geometry.h | 3 +++ intern/cycles/kernel/svm/types.h | 1 + intern/cycles/kernel/types.h | 2 ++ intern/cycles/scene/object.cpp | 2 ++ intern/cycles/scene/object.h | 1 + intern/cycles/scene/shader_nodes.cpp | 6 ++++++ .../gpu/shaders/material/gpu_shader_material_object_info.glsl | 2 ++ source/blender/nodes/shader/nodes/node_shader_object_info.cc | 1 + 13 files changed, 39 insertions(+), 1 deletion(-) diff --git a/intern/cycles/blender/object.cpp b/intern/cycles/blender/object.cpp index 559b0d42a29..054142a9ca4 100644 --- a/intern/cycles/blender/object.cpp +++ b/intern/cycles/blender/object.cpp @@ -319,7 +319,9 @@ Object *BlenderSync::sync_object(BL::Depsgraph &b_depsgraph, (object->get_geometry() && object->get_geometry()->is_modified())) { object->name = b_ob.name().c_str(); object->set_pass_id(b_ob.pass_index()); - object->set_color(get_float3(b_ob.color())); + const BL::Array object_color = b_ob.color(); + object->set_color(get_float3(object_color)); + object->set_alpha(object_color[3]); object->set_tfm(tfm); /* dupli texture coordinates and random_id */ diff --git a/intern/cycles/kernel/geom/object.h b/intern/cycles/kernel/geom/object.h index e8f5dfcc529..86c57c84b47 100644 --- a/intern/cycles/kernel/geom/object.h +++ b/intern/cycles/kernel/geom/object.h @@ -263,6 +263,16 @@ ccl_device_inline float3 object_color(KernelGlobals kg, int object) return make_float3(kobject->color[0], kobject->color[1], kobject->color[2]); } +/* Alpha of the object */ + +ccl_device_inline float object_alpha(KernelGlobals kg, int object) +{ + if (object == OBJECT_NONE) + return 0.0f; + + return kernel_tex_fetch(__objects, object).alpha; +} + /* Pass ID number of object */ ccl_device_inline float object_pass_id(KernelGlobals kg, int object) diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 85bdb47600e..79547872c68 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -76,6 +76,7 @@ ustring OSLRenderServices::u_raster("raster"); ustring OSLRenderServices::u_ndc("NDC"); ustring OSLRenderServices::u_object_location("object:location"); ustring OSLRenderServices::u_object_color("object:color"); +ustring OSLRenderServices::u_object_alpha("object:alpha"); ustring OSLRenderServices::u_object_index("object:index"); ustring OSLRenderServices::u_geom_dupli_generated("geom:dupli_generated"); ustring OSLRenderServices::u_geom_dupli_uv("geom:dupli_uv"); @@ -873,6 +874,10 @@ bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg float3 f = object_color(kg, sd->object); return set_attribute_float3(f, type, derivatives, val); } + else if (name == u_object_alpha) { + float f = object_alpha(kg, sd->object); + return set_attribute_float(f, type, derivatives, val); + } else if (name == u_object_index) { float f = object_pass_id(kg, sd->object); return set_attribute_float(f, type, derivatives, val); diff --git a/intern/cycles/kernel/osl/services.h b/intern/cycles/kernel/osl/services.h index 0685003ca5c..653fa017140 100644 --- a/intern/cycles/kernel/osl/services.h +++ b/intern/cycles/kernel/osl/services.h @@ -259,6 +259,7 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_ndc; static ustring u_object_location; static ustring u_object_color; + static ustring u_object_alpha; static ustring u_object_index; static ustring u_geom_dupli_generated; static ustring u_geom_dupli_uv; diff --git a/intern/cycles/kernel/osl/shaders/node_object_info.osl b/intern/cycles/kernel/osl/shaders/node_object_info.osl index 37e545f9988..8ed73231213 100644 --- a/intern/cycles/kernel/osl/shaders/node_object_info.osl +++ b/intern/cycles/kernel/osl/shaders/node_object_info.osl @@ -5,12 +5,14 @@ shader node_object_info(output point Location = point(0.0, 0.0, 0.0), output color Color = color(1.0, 1.0, 1.0), + output float Alpha = 1.0, output float ObjectIndex = 0.0, output float MaterialIndex = 0.0, output float Random = 0.0) { getattribute("object:location", Location); getattribute("object:color", Color); + getattribute("object:alpha", Alpha); getattribute("object:index", ObjectIndex); getattribute("material:index", MaterialIndex); getattribute("object:random", Random); diff --git a/intern/cycles/kernel/svm/geometry.h b/intern/cycles/kernel/svm/geometry.h index c1a5fdb8ca4..4b5368dd765 100644 --- a/intern/cycles/kernel/svm/geometry.h +++ b/intern/cycles/kernel/svm/geometry.h @@ -116,6 +116,9 @@ ccl_device_noinline void svm_node_object_info(KernelGlobals kg, stack_store_float3(stack, out_offset, object_color(kg, sd->object)); return; } + case NODE_INFO_OB_ALPHA: + data = object_alpha(kg, sd->object); + break; case NODE_INFO_OB_INDEX: data = object_pass_id(kg, sd->object); break; diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 777a27b8716..bede58f7a54 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -142,6 +142,7 @@ typedef enum NodeGeometry { typedef enum NodeObjectInfo { NODE_INFO_OB_LOCATION, NODE_INFO_OB_COLOR, + NODE_INFO_OB_ALPHA, NODE_INFO_OB_INDEX, NODE_INFO_MAT_INDEX, NODE_INFO_OB_RANDOM diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 24eb783daf9..07d4a95780b 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -1307,6 +1307,7 @@ typedef struct KernelObject { float pass_id; float random_number; float color[3]; + float alpha; int particle_index; float dupli_generated[3]; @@ -1330,6 +1331,7 @@ typedef struct KernelObject { uint visibility; int primitive_type; + int pad[2]; } KernelObject; static_assert_align(KernelObject, 16); diff --git a/intern/cycles/scene/object.cpp b/intern/cycles/scene/object.cpp index 97c86804507..fda9a211e60 100644 --- a/intern/cycles/scene/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -76,6 +76,7 @@ NODE_DEFINE(Object) SOCKET_TRANSFORM(tfm, "Transform", transform_identity()); SOCKET_UINT(visibility, "Visibility", ~0); SOCKET_COLOR(color, "Color", zero_float3()); + SOCKET_FLOAT(alpha, "Alpha", 0.0f); SOCKET_UINT(random_id, "Random ID", 0); SOCKET_INT(pass_id, "Pass ID", 0); SOCKET_BOOLEAN(use_holdout, "Use Holdout", false); @@ -414,6 +415,7 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s kobject.color[0] = color.x; kobject.color[1] = color.y; kobject.color[2] = color.z; + kobject.alpha = ob->alpha; kobject.pass_id = pass_id; kobject.random_number = random_number; kobject.particle_index = particle_index; diff --git a/intern/cycles/scene/object.h b/intern/cycles/scene/object.h index e18968bc0ed..55689ccfa58 100644 --- a/intern/cycles/scene/object.h +++ b/intern/cycles/scene/object.h @@ -44,6 +44,7 @@ class Object : public Node { NODE_SOCKET_API(uint, random_id) NODE_SOCKET_API(int, pass_id) NODE_SOCKET_API(float3, color) + NODE_SOCKET_API(float, alpha) NODE_SOCKET_API(ustring, asset_name) vector attributes; NODE_SOCKET_API(uint, visibility) diff --git a/intern/cycles/scene/shader_nodes.cpp b/intern/cycles/scene/shader_nodes.cpp index ba85cbde1d0..272a0dde7d8 100644 --- a/intern/cycles/scene/shader_nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -4216,6 +4216,7 @@ NODE_DEFINE(ObjectInfoNode) SOCKET_OUT_VECTOR(location, "Location"); SOCKET_OUT_COLOR(color, "Color"); + SOCKET_OUT_FLOAT(alpha, "Alpha"); SOCKET_OUT_FLOAT(object_index, "Object Index"); SOCKET_OUT_FLOAT(material_index, "Material Index"); SOCKET_OUT_FLOAT(random, "Random"); @@ -4239,6 +4240,11 @@ void ObjectInfoNode::compile(SVMCompiler &compiler) compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_COLOR, compiler.stack_assign(out)); } + out = output("Alpha"); + if (!out->links.empty()) { + compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_ALPHA, compiler.stack_assign(out)); + } + out = output("Object Index"); if (!out->links.empty()) { compiler.add_node(NODE_OBJECT_INFO, NODE_INFO_OB_INDEX, compiler.stack_assign(out)); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl index ff77b0beea2..607cf119b36 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_object_info.glsl @@ -4,12 +4,14 @@ void node_object_info(mat4 obmat, float mat_index, out vec3 location, out vec4 color, + out float alpha, out float object_index, out float material_index, out float random) { location = obmat[3].xyz; color = obcolor; + alpha = obcolor.w; object_index = info.x; material_index = mat_index; random = info.z; diff --git a/source/blender/nodes/shader/nodes/node_shader_object_info.cc b/source/blender/nodes/shader/nodes/node_shader_object_info.cc index 6ed5a7b715d..03c1a018d37 100644 --- a/source/blender/nodes/shader/nodes/node_shader_object_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_object_info.cc @@ -9,6 +9,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_output(N_("Location")); b.add_output(N_("Color")); + b.add_output(N_("Alpha")); b.add_output(N_("Object Index")); b.add_output(N_("Material Index")); b.add_output(N_("Random")); -- cgit v1.2.3 From 15186f4259a2e0611a64a3aed8db8858e8ca081f Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Mon, 7 Mar 2022 17:36:27 +0100 Subject: Shader Nodes: added alpha mode selector to Image Texture node Enables image user nodes to display the file alpha mode, similar to the colorspace setting. Also removes image_has_alpha in favor of using BKE_image_has_alpha, because it did not check if the image actually had an alpha channel, just if the file format was capable of supporting an alpha channel. Differential Revision: https://developer.blender.org/D14153 --- source/blender/editors/space_image/image_buttons.c | 18 +----------------- source/blender/editors/space_node/drawnode.cc | 17 +++++++++++++---- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 6f3d40abce1..0af32a717a4 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -718,22 +718,6 @@ static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg)) RNA_property_update(C, &cb->ptr, cb->prop); } -static bool image_has_alpha(Image *ima, ImageUser *iuser) -{ - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - if (ibuf == NULL) { - return false; - } - - int imtype = BKE_image_ftype_to_imtype(ibuf->ftype, &ibuf->foptions); - char valid_channels = BKE_imtype_valid_channels(imtype, false); - bool has_alpha = (valid_channels & IMA_CHAN_FLAG_ALPHA) != 0; - - BKE_image_release_ibuf(ima, ibuf, NULL); - - return has_alpha; -} - void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, @@ -943,7 +927,7 @@ void uiTemplateImage(uiLayout *layout, if (compact == 0) { if (ima->source != IMA_SRC_GENERATED) { - if (image_has_alpha(ima, iuser)) { + if (BKE_image_has_alpha(ima)) { uiLayout *sub = uiLayoutColumn(col, false); uiItemR(sub, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE); diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index afb205f9f9e..fab2946ad76 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -304,9 +304,11 @@ static void node_buts_image_user(uiLayout *layout, const bool show_layer_selection, const bool show_color_management) { - if (!imaptr->data) { + Image *image = (Image *)imaptr->data; + if (!image) { return; } + ImageUser *iuser = (ImageUser *)iuserptr->data; uiLayout *col = uiLayoutColumn(layout, false); @@ -318,8 +320,6 @@ static void node_buts_image_user(uiLayout *layout, /* don't use iuser->framenr directly * because it may not be updated if auto-refresh is off */ Scene *scene = CTX_data_scene(C); - ImageUser *iuser = (ImageUser *)iuserptr->data; - /* Image *ima = imaptr->data; */ /* UNUSED */ char numstr[32]; const int framenr = BKE_image_user_frame_get(iuser, CFRA, nullptr); @@ -343,11 +343,20 @@ static void node_buts_image_user(uiLayout *layout, } if (show_color_management) { - uiLayout *split = uiLayoutSplit(layout, 0.5f, true); + uiLayout *split = uiLayoutSplit(layout, 0.33f, true); PointerRNA colorspace_settings_ptr = RNA_pointer_get(imaptr, "colorspace_settings"); uiItemL(split, IFACE_("Color Space"), ICON_NONE); uiItemR(split, &colorspace_settings_ptr, "name", DEFAULT_FLAGS, "", ICON_NONE); + if (image->source != IMA_SRC_GENERATED) { + split = uiLayoutSplit(layout, 0.33f, true); + uiItemL(split, IFACE_("Alpha"), ICON_NONE); + uiItemR(split, imaptr, "alpha_mode", DEFAULT_FLAGS, "", ICON_NONE); + + bool is_data = IMB_colormanagement_space_name_is_data(image->colorspace_settings.name); + uiLayoutSetActive(split, !is_data); + } + /* Avoid losing changes image is painted. */ if (BKE_image_is_dirty((Image *)imaptr->data)) { uiLayoutSetEnabled(split, false); -- cgit v1.2.3 From 21d633f83b3ab09342ad32c4c3d896d3a8308404 Mon Sep 17 00:00:00 2001 From: Falk David Date: Mon, 7 Mar 2022 17:47:43 +0100 Subject: GPencil: Temporary fix to avoid crashes on startup This quick fix will populate the runtime orig pointers to avoid crashes when a grease pencil object uses layer transforms, parenting or modifiers. This will have to be revisited and fixed with a better solution. --- .../blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index 6346bab1fe8..b1635ce6e09 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -41,6 +41,7 @@ #include "DNA_ID.h" #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -733,6 +734,16 @@ void update_id_after_copy(const Depsgraph *depsgraph, scene_setup_view_layers_after_remap(depsgraph, id_node, reinterpret_cast(id_cow)); break; } + /* FIXME: This is a temporary fix to update the runtime pointers properly, see T96216. Should + * be removed at some point. */ + case ID_GD: { + bGPdata *gpd_cow = (bGPdata *)id_cow; + bGPDlayer *gpl = (bGPDlayer *)(gpd_cow->layers.first); + if (gpl != NULL && gpl->runtime.gpl_orig == NULL) { + BKE_gpencil_data_update_orig_pointers((bGPdata *)id_orig, gpd_cow); + } + break; + } default: break; } -- cgit v1.2.3 From 1cb303242fa67574449315c59a786a7ad52ea957 Mon Sep 17 00:00:00 2001 From: Leon Schittek Date: Mon, 7 Mar 2022 17:47:57 +0100 Subject: UI: align labels of number fields and value sliders Previously the labels and values in number fields and value sliders used different padding for the text. This looks weird when they are placed underneath each other in a column and, as noted by a comment in the code of `widget_numslider`, they are actually meant to be aligned. This patch fixes that by using the same padding that is used for the number field for the value slider, as well. This also has the benefit, that the labels of the value sliders don't shift anymore when adjusting the corner roundness. Differential Revision: https://developer.blender.org/D14091 --- source/blender/editors/interface/interface_widgets.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index d1f3843c643..35cf952b5ce 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -3319,6 +3319,8 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) /** \name Button Draw Callbacks * \{ */ +#define NUM_BUT_PADDING_FACTOR 0.425f + static void widget_numbut_draw( uiWidgetColors *wcol, rcti *rect, const float zoom, int state, int roundboxalign, bool emboss) { @@ -3413,11 +3415,10 @@ static void widget_numbut_draw( } if (!(state & UI_STATE_TEXT_INPUT)) { - const float textofs = 0.425f * BLI_rcti_size_y(rect); + const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); - /* text space */ - rect->xmin += textofs; - rect->xmax -= textofs; + rect->xmin += text_padding; + rect->xmax -= text_padding; } } @@ -3745,7 +3746,6 @@ static void widget_numslider( /* Backdrop first. */ const float ofs = widget_radius_from_zoom(zoom, wcol); - const float toffs = ofs * 0.75f; round_box_edges(&wtb, roundboxalign, rect, ofs); wtb.draw_outline = false; @@ -3838,8 +3838,9 @@ static void widget_numslider( /* Add space at either side of the button so text aligns with number-buttons * (which have arrow icons). */ if (!(state & UI_STATE_TEXT_INPUT)) { - rect->xmax -= toffs; - rect->xmin += toffs; + const float text_padding = NUM_BUT_PADDING_FACTOR * BLI_rcti_size_y(rect); + rect->xmax -= text_padding; + rect->xmin += text_padding; } } -- cgit v1.2.3 From 0597902bde1f6d9a77bf04144ad7fd5f7be9d651 Mon Sep 17 00:00:00 2001 From: Sebastian Parborg Date: Mon, 7 Mar 2022 19:02:34 +0100 Subject: Fix memory leak when reading ffmpeg video frames. We had forgotten to unref packets after reading them. This lead to a memory leak inside of ffmpeg. --- source/blender/imbuf/intern/anim_movie.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index 7cde49f44b7..096089d4c41 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -874,6 +874,8 @@ static int ffmpeg_read_video_frame(struct anim *anim, AVPacket *packet) if (packet->stream_index == anim->videoStream) { break; } + av_packet_unref(packet); + packet->stream_index = -1; } return ret; -- cgit v1.2.3 From 2b3367cdf8694a3e48ed5cc2be5381aabceed9c4 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 7 Mar 2022 13:54:49 -0600 Subject: Fix T93573: Curve evaluated mesh selected in edit mode This fixes the second part of T93573 that 8506f3d9fe9359518e didn't properly address. Specifically, outlines of instances still had the selected color in edit mode in wireframe view. This change is the same as that commit, just in a different place. Differential Revision: https://developer.blender.org/D14229 --- source/blender/draw/engines/overlay/overlay_wireframe.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c index 1eb8fc981cf..b97e76085e9 100644 --- a/source/blender/draw/engines/overlay/overlay_wireframe.c +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -291,8 +291,12 @@ void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, const bool is_sculpt_mode = ((ob->mode & OB_MODE_SCULPT) != 0) && (ob->sculpt != NULL); const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && !DRW_state_is_image_render(); + const bool is_instance = (ob->base_flag & BASE_FROM_DUPLI); + const bool instance_parent_in_edit_mode = is_instance ? DRW_object_is_in_edit_mode( + DRW_object_get_dupli_parent(ob)) : + false; const bool use_coloring = (use_wire && !is_edit_mode && !is_sculpt_mode && - !has_edit_mesh_cage); + !has_edit_mesh_cage && !instance_parent_in_edit_mode); geom = DRW_cache_object_face_wireframe_get(ob); if (geom || use_sculpt_pbvh) { -- cgit v1.2.3 From b8f1ae5c388608e0e47dbc84f75a01453f2013ef Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 7 Mar 2022 16:26:55 -0600 Subject: Cleanup: Rename geometry set "curve" to "curves" Similar to 245722866d6977c8b, just another function I missed before. --- source/blender/blenkernel/BKE_geometry_set.hh | 4 ++-- source/blender/blenkernel/intern/displist.cc | 2 +- source/blender/blenkernel/intern/geometry_set.cc | 2 +- .../nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc | 2 +- .../nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc | 4 ++-- source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc | 4 ++-- source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 2 +- source/blender/nodes/geometry/nodes/node_geo_transform.cc | 4 ++-- 15 files changed, 19 insertions(+), 19 deletions(-) diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index f7767cc2a60..169554b4453 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -500,8 +500,8 @@ struct GeometrySet { /** * Clear the existing curves data-block and replace it with the given one. */ - void replace_curve(Curves *curves, - GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + void replace_curves(Curves *curves, + GeometryOwnershipType ownership = GeometryOwnershipType::Owned); private: /** diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index f0894ee04e2..791e0faab3b 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -865,7 +865,7 @@ static GeometrySet curve_calc_modifiers_post(Depsgraph *depsgraph, else { std::unique_ptr curve_eval = curve_eval_from_dna_curve( *cu, ob->runtime.curve_cache->deformed_nurbs); - geometry_set.replace_curve(curve_eval_to_curves(*curve_eval)); + geometry_set.replace_curves(curve_eval_to_curves(*curve_eval)); } for (; md; md = md->next) { diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index ca372ba8f38..0eece6e9ad0 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -353,7 +353,7 @@ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) component.replace(mesh, ownership); } -void GeometrySet::replace_curve(Curves *curves, GeometryOwnershipType ownership) +void GeometrySet::replace_curves(Curves *curves, GeometryOwnershipType ownership) { if (curves == nullptr) { this->remove(); diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc index 2fe06a17adf..2c801642bd7 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_reverse.cc @@ -41,7 +41,7 @@ static void node_geo_exec(GeoNodeExecParams params) } }); - geometry_set.replace_curve(curve_eval_to_curves(*curve)); + geometry_set.replace_curves(curve_eval_to_curves(*curve)); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc index 537c7c42610..56e9068882b 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_set_handles.cc @@ -101,7 +101,7 @@ static void node_geo_exec(GeoNodeExecParams params) bezier_spline.mark_cache_invalid(); } - geometry_set.replace_curve(curve_eval_to_curves(*curve)); + geometry_set.replace_curves(curve_eval_to_curves(*curve)); if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index 6702ee6c0aa..08aa7415073 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -120,7 +120,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu const std::unique_ptr curve = curves_to_curve_eval( *geometry_set.get_curves_for_read()); if (curve->splines().is_empty()) { - geometry_set.replace_curve(nullptr); + geometry_set.replace_curves(nullptr); return; } @@ -132,7 +132,7 @@ static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCu Mesh *mesh = cdt_to_mesh(results); geometry_set.replace_mesh(mesh); - geometry_set.replace_curve(nullptr); + geometry_set.replace_curves(nullptr); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 24d72ad553b..0b01efc4e9e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -606,7 +606,7 @@ static void calculate_curve_fillet(GeometrySet &geometry_set, const std::unique_ptr input_curve = curves_to_curve_eval(*component.get_for_read()); std::unique_ptr output_curve = fillet_curve(*input_curve, fillet_param); - geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); + geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index c5814a9a1dd..5a4c2ad1660 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -242,7 +242,7 @@ static void geometry_set_curve_resample(GeometrySet &geometry_set, std::unique_ptr output_curve = resample_curve( geometry_set.get_component_for_read(), mode_param); - geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); + geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 55610ec86ab..92c0cafa551 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -403,7 +403,7 @@ static void node_geo_exec(GeoNodeExecParams params) } }); new_curve->attributes = curve->attributes; - geometry_set.replace_curve(curve_eval_to_curves(*new_curve)); + geometry_set.replace_curves(curve_eval_to_curves(*new_curve)); }); params.set_output("Curve", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index bbe57b2b3fa..6456af5f295 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -342,7 +342,7 @@ static void node_geo_exec(GeoNodeExecParams params) } std::unique_ptr output_curve = subdivide_curve( *curves_to_curve_eval(*component.get_for_read()), cuts); - geometry_set.replace_curve(curve_eval_to_curves(*output_curve)); + geometry_set.replace_curves(curve_eval_to_curves(*output_curve)); }); params.set_output("Curve", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index a3dab1b50fe..df360818313 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -566,7 +566,7 @@ static void geometry_set_curve_trim(GeometrySet &geometry_set, } }); - geometry_set.replace_curve(curve_eval_to_curves(*curve)); + geometry_set.replace_curves(curve_eval_to_curves(*curve)); } static void node_geo_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 3baee8a25bb..cf6837817c2 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -477,10 +477,10 @@ static void separate_curve_selection(GeometrySet &geometry_set, std::unique_ptr r_curve = curve_separate( *curves_to_curve_eval(*src_component.get_for_read()), selection, selection_domain, invert); if (r_curve) { - geometry_set.replace_curve(curve_eval_to_curves(*r_curve)); + geometry_set.replace_curves(curve_eval_to_curves(*r_curve)); } else { - geometry_set.replace_curve(nullptr); + geometry_set.replace_curves(nullptr); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index 1ceab18c01b..eb216c08289 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -599,7 +599,7 @@ static void duplicate_splines(GeometrySet &geometry_set, dst_component, ATTR_DOMAIN_CURVE, selection, attributes, curve_offsets); } - geometry_set.replace_curve(dst_component.get_for_write()); + geometry_set.replace_curves(dst_component.get_for_write()); } static void duplicate_faces(GeometrySet &geometry_set, diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 91cde52f9eb..d2b824141d7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -35,7 +35,7 @@ static void node_geo_exec(GeoNodeExecParams params) } std::unique_ptr curve = geometry::mesh_to_curve_convert(component, selection); - geometry_set.replace_curve(curve_eval_to_curves(*curve)); + geometry_set.replace_curves(curve_eval_to_curves(*curve)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index 95cec8eab11..9159ac081e0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -129,7 +129,7 @@ static void translate_geometry_set(GeometrySet &geometry, if (Curves *curves = geometry.get_curves_for_write()) { std::unique_ptr curve = curves_to_curve_eval(*curves); curve->translate(translation); - geometry.replace_curve(curve_eval_to_curves(*curve)); + geometry.replace_curves(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { translate_mesh(*mesh, translation); @@ -152,7 +152,7 @@ void transform_geometry_set(GeometrySet &geometry, if (Curves *curves = geometry.get_curves_for_write()) { std::unique_ptr curve = curves_to_curve_eval(*curves); curve->transform(transform); - geometry.replace_curve(curve_eval_to_curves(*curve)); + geometry.replace_curves(curve_eval_to_curves(*curve)); } if (Mesh *mesh = geometry.get_mesh_for_write()) { transform_mesh(*mesh, transform); -- cgit v1.2.3 From c238728105272b0f11ff5b03a701cc180bf68bb8 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 7 Mar 2022 18:58:04 -0600 Subject: Fix: Curves cyclic access function duplicates attribute This was an oversight in 6594e802ab94ff11. First it must check if the attribute exists before adding it. --- source/blender/blenkernel/intern/curves_geometry.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 3eea579230a..2a22b2eb0f3 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -181,7 +181,12 @@ VArray CurvesGeometry::cyclic() const MutableSpan CurvesGeometry::cyclic() { - bool *data = (bool *)CustomData_add_layer_named( + bool *data = (bool *)CustomData_duplicate_referenced_layer_named( + &this->curve_data, CD_PROP_BOOL, ATTR_CYCLIC.c_str(), this->curve_size); + if (data != nullptr) { + return {data, this->curve_size}; + } + data = (bool *)CustomData_add_layer_named( &this->curve_data, CD_PROP_BOOL, CD_CALLOC, nullptr, this->curve_size, ATTR_CYCLIC.c_str()); return {data, this->curve_size}; } -- cgit v1.2.3 From 901a03725ed58166e5b2401dfadd7c1cb7e490a2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Mon, 7 Mar 2022 19:06:40 -0600 Subject: Curves: Port mesh to curve node to new data-block The main improvement is a code simplification, because attributes don't have to be transferred separately for each curve, and all attributes can be handled generically. Performance improves significantly when the output contains many curves. Basic testing with a 2 million curve output shows an approximate 10x performance improvement. --- source/blender/geometry/GEO_mesh_to_curve.hh | 6 +- .../geometry/intern/mesh_to_curve_convert.cc | 152 +++++++-------------- .../nodes/legacy/node_geo_legacy_mesh_to_curve.cc | 6 +- .../nodes/geometry/nodes/node_geo_mesh_to_curve.cc | 3 +- 4 files changed, 56 insertions(+), 111 deletions(-) diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh index a99ee9c7fee..c480e4178cf 100644 --- a/source/blender/geometry/GEO_mesh_to_curve.hh +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -2,11 +2,10 @@ #include "BLI_index_mask.hh" -#include "BKE_spline.hh" - #pragma once struct Mesh; +struct Curves; class MeshComponent; /** \file @@ -20,7 +19,6 @@ namespace blender::geometry { * intersections of more than three edges will become breaks in splines. Attributes that * are not built-in on meshes and not curves are transferred to the result curve. */ -std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, - const IndexMask selection); +Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection); } // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 121a1b5de39..0c2377fde6d 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -2,7 +2,6 @@ #include "BLI_array.hh" #include "BLI_set.hh" -#include "BLI_string_ref.hh" #include "BLI_task.hh" #include "DNA_mesh_types.h" @@ -10,85 +9,48 @@ #include "BKE_attribute_access.hh" #include "BKE_attribute_math.hh" +#include "BKE_curves.hh" #include "BKE_geometry_set.hh" -#include "BKE_spline.hh" #include "GEO_mesh_to_curve.hh" namespace blender::geometry { template -static void copy_attribute_to_points(const VArray &source_data, - Span map, - MutableSpan dest_data) +static void copy_with_map(const VArray &src, Span map, MutableSpan dst) { - for (const int point_index : map.index_range()) { - const int vert_index = map[point_index]; - dest_data[point_index] = source_data[vert_index]; - } + devirtualize_varray(src, [&](const auto &src) { + threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + const int vert_index = map[i]; + dst[i] = src[vert_index]; + } + }); + }); } -static std::unique_ptr create_curve_from_vert_indices( - const MeshComponent &mesh_component, Span> vert_indices, IndexRange cyclic_splines) +static Curves *create_curve_from_vert_indices(const MeshComponent &mesh_component, + const Span vert_indices, + const Span curve_offsets, + const IndexRange cyclic_splines) { - std::unique_ptr curve = std::make_unique(); - curve->resize(vert_indices.size()); - - MutableSpan splines = curve->splines(); + Curves *curves_id = bke::curves_new_nomain(vert_indices.size(), curve_offsets.size()); + bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id->geometry); + curves.offsets().drop_back(1).copy_from(curve_offsets); + curves.offsets().last() = vert_indices.size(); + curves.curve_types().fill(CURVE_TYPE_POLY); - for (const int i : vert_indices.index_range()) { - splines[i] = std::make_unique(); - splines[i]->resize(vert_indices[i].size()); - } - for (const int i : cyclic_splines) { - splines[i]->set_cyclic(true); - } + curves.cyclic().fill(false); + curves.cyclic().slice(cyclic_splines).fill(true); Set source_attribute_ids = mesh_component.attribute_ids(); - /* Copy builtin control point attributes. */ - if (source_attribute_ids.contains("tilt")) { - const VArray tilt_attribute = mesh_component.attribute_get_for_read( - "tilt", ATTR_DOMAIN_POINT, 0.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points(tilt_attribute, vert_indices[i], splines[i]->tilts()); - } - }); - source_attribute_ids.remove_contained("tilt"); - } - else { - for (SplinePtr &spline : splines) { - spline->tilts().fill(0.0f); - } - } - - if (source_attribute_ids.contains("radius")) { - const VArray radius_attribute = mesh_component.attribute_get_for_read( - "radius", ATTR_DOMAIN_POINT, 1.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points(radius_attribute, vert_indices[i], splines[i]->radii()); - } - }); - source_attribute_ids.remove_contained("radius"); - } - else { - for (SplinePtr &spline : splines) { - spline->radii().fill(1.0f); - } - } - - VArray mesh_positions = mesh_component.attribute_get_for_read( - "position", ATTR_DOMAIN_POINT, float3(0)); - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points(mesh_positions, vert_indices[i], splines[i]->positions()); - } - }); + CurveComponent curves_component; + curves_component.replace(curves_id, GeometryOwnershipType::Editable); for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) { - if (mesh_component.attribute_is_builtin(attribute_id)) { + if (mesh_component.attribute_is_builtin(attribute_id) && + !curves_component.attribute_is_builtin(attribute_id)) { /* Don't copy attributes that are built-in on meshes but not on curves. */ continue; } @@ -105,33 +67,24 @@ static std::unique_ptr create_curve_from_vert_indices( continue; } - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute.type()); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - /* Create attribute on the spline points. */ - splines[i]->attributes.create(attribute_id, data_type); - std::optional spline_attribute = splines[i]->attributes.get_for_write( - attribute_id); - BLI_assert(spline_attribute); - - /* Copy attribute based on the map for this spline. */ - attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points( - mesh_attribute.typed(), vert_indices[i], spline_attribute->typed()); - }); - } + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute.type(), [&](auto dummy) { + using T = decltype(dummy); + bke::OutputAttribute_Typed attribute = + curves_component.attribute_try_get_for_output_only(attribute_id, ATTR_DOMAIN_POINT); + copy_with_map(mesh_attribute.typed(), vert_indices, attribute.as_span()); + attribute.save(); }); } - curve->assert_valid_point_attributes(); - return curve; + return curves_id; } struct CurveFromEdgesOutput { /** The indices in the mesh for each control point of each result splines. */ - Vector> vert_indices; + Vector vert_indices; + /** The first index of each curve in the result. */ + Vector curve_offsets; /** A subset of splines that should be set cyclic. */ IndexRange cyclic_splines; }; @@ -139,7 +92,9 @@ struct CurveFromEdgesOutput { static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, Span> edges) { - Vector> vert_indices; + Vector vert_indices; + vert_indices.reserve(edges.size()); + Vector curve_offsets; /* Compute the number of edges connecting to each vertex. */ Array neighbor_count(verts.size(), 0); @@ -191,15 +146,16 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, continue; } - Vector spline_indices; - spline_indices.append(current_vert); + /* Start a new curve in the output. */ + curve_offsets.append(vert_indices.size()); + vert_indices.append(current_vert); /* Follow connected edges until we read a vertex with more than two connected edges. */ while (true) { int last_vert = current_vert; current_vert = next_vert; - spline_indices.append(current_vert); + vert_indices.append(current_vert); unused_edges[current_vert]--; unused_edges[last_vert]--; @@ -212,13 +168,11 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, const int next_b = neighbors[offset + 1]; next_vert = (last_vert == next_a) ? next_b : next_a; } - - vert_indices.append(std::move(spline_indices)); } } /* All splines added after this are cyclic. */ - const int cyclic_start = vert_indices.size(); + const int cyclic_start = curve_offsets.size(); /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ for (const int start_vert : verts.index_range()) { @@ -229,16 +183,15 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, int current_vert = start_vert; int next_vert = neighbors[neighbor_offsets[current_vert]]; - Vector spline_indices; - - spline_indices.append(current_vert); + curve_offsets.append(vert_indices.size()); + vert_indices.append(current_vert); /* Follow connected edges until we loop back to the start vertex. */ while (next_vert != start_vert) { const int last_vert = current_vert; current_vert = next_vert; - spline_indices.append(current_vert); + vert_indices.append(current_vert); unused_edges[current_vert]--; unused_edges[last_vert]--; @@ -247,13 +200,11 @@ static CurveFromEdgesOutput edges_to_curve_point_indices(Span verts, const int next_b = neighbors[offset + 1]; next_vert = (last_vert == next_a) ? next_b : next_a; } - - vert_indices.append(std::move(spline_indices)); } - const int final_size = vert_indices.size(); + const IndexRange cyclic_curves = curve_offsets.index_range().drop_front(cyclic_start); - return {std::move(vert_indices), IndexRange(cyclic_start, final_size - cyclic_start)}; + return {std::move(vert_indices), std::move(curve_offsets), cyclic_curves}; } /** @@ -270,8 +221,7 @@ static Vector> get_selected_edges(const Mesh &mesh, const In return selected_edges; } -std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, - const IndexMask selection) +Curves *mesh_to_curve_convert(const MeshComponent &mesh_component, const IndexMask selection) { const Mesh &mesh = *mesh_component.get_for_read(); Vector> selected_edges = get_selected_edges(*mesh_component.get_for_read(), @@ -280,7 +230,7 @@ std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_compo selected_edges); return create_curve_from_vert_indices( - mesh_component, output.vert_indices, output.cyclic_splines); + mesh_component, output.vert_indices, output.curve_offsets, output.cyclic_splines); } } // namespace blender::geometry diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc index 8991261a21a..72cb540df4a 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_mesh_to_curve.cc @@ -45,10 +45,8 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr curve = geometry::mesh_to_curve_convert( - component, IndexMask(selected_edge_indices)); - - params.set_output("Curve", GeometrySet::create_with_curves(curve_eval_to_curves(*curve))); + Curves *curves = geometry::mesh_to_curve_convert(component, IndexMask(selected_edge_indices)); + params.set_output("Curve", GeometrySet::create_with_curves(curves)); } } // namespace blender::nodes::node_geo_legacy_mesh_to_curve_cc diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index d2b824141d7..f6ee3d00dee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -34,8 +34,7 @@ static void node_geo_exec(GeoNodeExecParams params) return; } - std::unique_ptr curve = geometry::mesh_to_curve_convert(component, selection); - geometry_set.replace_curves(curve_eval_to_curves(*curve)); + geometry_set.replace_curves(geometry::mesh_to_curve_convert(component, selection)); geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); }); -- cgit v1.2.3 From 7b663976645973f15b243c101497626c590c2cde Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:48:31 +1100 Subject: Cleanup: spelling in comments, use C++ comments for disabled code --- source/blender/blenloader/BLO_read_write.h | 4 ++-- source/blender/blenloader/intern/readfile.c | 6 +++--- source/blender/blenloader/intern/versioning_legacy.c | 7 +++---- source/blender/bmesh/tools/bmesh_bevel.c | 2 +- source/blender/editors/animation/keyframes_keylist.cc | 2 +- source/blender/editors/mesh/editmesh_tools.c | 4 ++-- source/blender/editors/mesh/editmesh_undo.c | 3 ++- source/blender/editors/object/object_add.cc | 3 ++- source/blender/editors/space_outliner/outliner_tools.cc | 2 +- source/blender/editors/transform/transform_mode.c | 4 ++-- source/blender/makesrna/RNA_types.h | 2 +- source/blender/makesrna/intern/rna_access.c | 4 ++-- source/blender/modifiers/intern/MOD_edgesplit.c | 3 ++- source/blender/python/intern/bpy_app_handlers.c | 2 +- source/blender/sequencer/intern/effects.c | 2 +- 15 files changed, 26 insertions(+), 24 deletions(-) diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 8f0f9fae97f..536c3989aff 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -286,8 +286,8 @@ void BLO_expand_id(BlendExpander *expander, struct ID *id); * This function ensures that reports are printed, * in the case of library linking errors this is important! * - * bit kludge but better than doubling up on prints, - * we could alternatively have a versions of a report function which forces printing - campbell + * NOTE(@campbellbarton) a kludge but better than doubling up on prints, + * we could alternatively have a versions of a report function which forces printing. */ void BLO_reportf_wrap(struct BlendFileReadReport *reports, eReportType type, diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9539436cf69..370137908c3 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -161,7 +161,7 @@ * which keeps large arrays in memory from data-blocks we may not even use. * * \note This is disabled when using compression, - * while zlib supports seek it's unusably slow, see: T61880. + * while zlib supports seek it's unusable slow, see: T61880. */ #define USE_BHEAD_READ_ON_DEMAND @@ -4711,9 +4711,9 @@ static void read_library_linked_ids(FileData *basefd, read_library_linked_id(basefd, fd, mainvar, id, realid); } - /* realid shall never be NULL - unless some source file/lib is broken + /* `realid` shall never be NULL - unless some source file/lib is broken * (known case: some directly linked shapekey from a missing lib...). */ - /* BLI_assert(*realid != NULL); */ + // BLI_assert(*realid != NULL); /* Now that we have a real ID, replace all pointers to placeholders in * fd->libmap with pointers to the real data-blocks. We do this for all diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 2908b2b151b..5b026c1cca0 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1075,11 +1075,10 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) } } - /* ton: made this 230 instead of 229, - * to be sure (tuho files) and this is a reliable check anyway + /* NOTE(@ton): made this 230 instead of 229, + * to be sure (files from the `tuhopuu` branch) and this is a reliable check anyway * nevertheless, we might need to think over a fitness (initialize) - * check apart from the do_versions() - */ + * check apart from the do_versions(). */ if (bmain->versionfile <= 230) { bScreen *screen; diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index 10bdc2c7294..da138cf9216 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -5571,7 +5571,7 @@ static void bevel_build_cutoff(BevelParams *bp, BMesh *bm, BevVert *bv) /* Add verts from each cutoff face. */ face_bmverts[i] = mesh_vert(bv->vmesh, i, 1, 0)->v; } - /* BLI_array_append(bmfaces, repface); */ + // BLI_array_append(bmfaces, repface); bev_create_ngon(bm, face_bmverts, n_bndv, bmfaces, NULL, bmedges, bp->mat_nr, true); BLI_array_free(bmedges); diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index 7dc3415c904..3356ef4d47d 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -811,7 +811,7 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co * may change order and then this assertion no longer holds. The effect is that the drawing * isn't perfect during the transform; the "constant value" bars aren't updated until the * transformation is confirmed. */ - /* BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); */ + // BLI_assert(is_cfra_eq(col->cfra, bezt[0].vec[1][0])); compute_keyblock_data(&block, bezt, bezt + 1); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 2577218d6a9..6b3f50549ef 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4349,7 +4349,7 @@ static Base *mesh_separate_tagged( Base *base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag); /* normally would call directly after but in this case delay recalc */ - /* DAG_relations_tag_update(bmain); */ + // DAG_relations_tag_update(bmain); /* new in 2.5 */ BKE_object_material_array_assign(bmain, @@ -4423,7 +4423,7 @@ static Base *mesh_separate_arrays(Main *bmain, Base *base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, dupflag); /* normally would call directly after but in this case delay recalc */ - /* DAG_relations_tag_update(bmain); */ + // DAG_relations_tag_update(bmain); /* new in 2.5 */ BKE_object_material_array_assign(bmain, diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index 3bebcdc9bae..9c7d712a739 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -591,7 +591,8 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo um->me.key = NULL; } - /* BM_mesh_validate(em->bm); */ /* for troubleshooting */ + /* Uncomment for troubleshooting. */ + // BM_mesh_validate(em->bm); BM_mesh_bm_to_me( NULL, diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 7befad3b8d7..b8ffaf87118 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -3385,7 +3385,8 @@ Base *ED_object_add_duplicate( const int remap_flag = BKE_object_is_in_editmode(ob) ? ID_REMAP_FORCE_OBDATA_IN_EDITMODE : 0; BKE_libblock_relink_to_newid(bmain, &ob->id, remap_flag); - /* DAG_relations_tag_update(bmain); */ /* caller must do */ + /* Correct but the caller must do this. */ + // DAG_relations_tag_update(bmain); if (ob->data != nullptr) { DEG_id_tag_update_ex(bmain, (ID *)ob->data, ID_RECALC_EDITORS); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index 8fcf967bce8..fdefcc6133e 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -2618,7 +2618,7 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) nullptr); WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN, nullptr); - /* ED_undo_push(C, "Refresh Drivers"); No undo needed - shouldn't have any impact? */ + // ED_undo_push(C, "Refresh Drivers"); /* No undo needed - shouldn't have any impact? */ break; case OUTLINER_ANIMOP_CLEAR_DRV: diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index 6162dfc9bb5..2fd81486bb6 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -1212,8 +1212,8 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) } /* TODO(germano): Some of these operations change the `t->mode`. - * This can be bad for Redo. - * BLI_assert(t->mode == mode); */ + * This can be bad for Redo. */ + // BLI_assert(t->mode == mode); } void transform_mode_default_modal_orientation_set(TransInfo *t, int type) diff --git a/source/blender/makesrna/RNA_types.h b/source/blender/makesrna/RNA_types.h index 74bcc95d1dd..d4c38060e1b 100644 --- a/source/blender/makesrna/RNA_types.h +++ b/source/blender/makesrna/RNA_types.h @@ -258,7 +258,7 @@ typedef enum PropertyFlag { /** * flag contains multiple enums. - * NOTE: not to be confused with prop->enumbitflags + * NOTE: not to be confused with `prop->enumbitflags` * this exposes the flag as multiple options in python and the UI. * * \note These can't be animated so use with care. diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 7ccb8181b51..a6fa369dc73 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2437,7 +2437,7 @@ void RNA_property_int_set(PointerRNA *ptr, PropertyRNA *prop, int value) BLI_assert(RNA_property_type(prop) == PROP_INT); BLI_assert(RNA_property_array_check(prop) == false); /* useful to check on bad values but set function should clamp */ - /* BLI_assert(RNA_property_int_clamp(ptr, prop, &value) == 0); */ + // BLI_assert(RNA_property_int_clamp(ptr, prop, &value) == 0); if ((idprop = rna_idproperty_check(&prop, ptr))) { RNA_property_int_clamp(ptr, prop, &value); @@ -3812,7 +3812,7 @@ void RNA_property_collection_add(PointerRNA *ptr, PropertyRNA *prop, PointerRNA } IDP_AppendArray(idprop, item); /* IDP_AppendArray does a shallow copy (memcpy), only free memory. */ - /* IDP_FreePropertyContent(item); */ + // IDP_FreePropertyContent(item); MEM_freeN(item); rna_idproperty_touch(idprop); } diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index 9450a444042..b19caefae29 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -94,7 +94,8 @@ Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd) BM_mesh_edgesplit(bm, false, true, false); - /* BM_mesh_validate(bm); */ /* for troubleshooting */ + /* Uncomment for troubleshooting. */ + // BM_mesh_validate(bm); result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); BM_mesh_free(bm); diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index f2cf4249042..ba3e6a3d74b 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -278,7 +278,7 @@ void BPY_app_handlers_reset(const short do_all) } else { /* remove */ - /* PySequence_DelItem(ls, i); */ /* more obvious but slower */ + // PySequence_DelItem(ls, i); /* more obvious but slower */ PyList_SetSlice(ls, i, i + 1, NULL); } } diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index faa4cd14825..01211aeeec6 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -2396,7 +2396,7 @@ static ImBuf *do_solid_color(const SeqRenderData *context, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Mulit-Camera Effect +/** \name Multi-Camera Effect * \{ */ /** No effect inputs for multi-camera, we use #give_ibuf_seq. */ -- cgit v1.2.3 From 08f88bba6937f965e1d0d0c2f2e37dfbc25b29a5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:48:32 +1100 Subject: Event System: exclude cursor & NDOF motion from ISHOTKEY() macro In practice this didn't cause a bug since assigning hot-keys was also checking for "press" events (which NDOF_MOTION doesn't generate). Add ISNDOF_BUTTON macro which is now used by ISHOTKEY to avoid problems in the future. --- source/blender/windowmanager/wm_event_types.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index d5c8c5022cc..578663b6fe8 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -244,6 +244,7 @@ enum { NDOF_MOTION = 0x0190, /* 400 */ #define _NDOF_MIN NDOF_MOTION +#define _NDOF_BUTTON_MIN NDOF_BUTTON_MENU /* used internally, never sent */ NDOF_BUTTON_NONE = NDOF_MOTION, @@ -296,6 +297,7 @@ enum { NDOF_BUTTON_C = 0x01b6, /* 438 */ #define _NDOF_MAX NDOF_BUTTON_C +#define _NDOF_BUTTON_MAX NDOF_BUTTON_C /* ********** End of Input devices. ********** */ @@ -386,13 +388,16 @@ enum { /** Test whether the event is a NDOF event. */ #define ISNDOF(event_type) ((event_type) >= _NDOF_MIN && (event_type) <= _NDOF_MAX) +#define ISNDOF_BUTTON(event_type) \ + ((event_type) >= _NDOF_BUTTON_MIN && (event_type) <= _NDOF_BUTTON_MAX) #define IS_EVENT_ACTIONZONE(event_type) \ ELEM(event_type, EVT_ACTIONZONE_AREA, EVT_ACTIONZONE_REGION, EVT_ACTIONZONE_FULLSCREEN) /** Test whether event type is acceptable as hotkey (excluding modifiers). */ #define ISHOTKEY(event_type) \ - ((ISKEYBOARD(event_type) || ISMOUSE(event_type) || ISNDOF(event_type)) && \ + ((ISKEYBOARD(event_type) || ISMOUSE_BUTTON(event_type) || ISMOUSE_WHEEL(event_type) || \ + ISNDOF_BUTTON(event_type)) && \ (ISKEYMODIFIER(event_type) == false)) enum eEventType_Mask { -- cgit v1.2.3 From 08d8eee006f7e2a7ac05ef691bbaee230cbfbe5a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:48:33 +1100 Subject: Event System: refactor click detection into function Also simplify modifier & keymodifier assignment. --- .../blender/windowmanager/intern/wm_event_system.c | 183 +++++++++------------ 1 file changed, 77 insertions(+), 106 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 0e262f8c388..27a7042fd62 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4765,6 +4765,39 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d return event_new; } +/** + * Update the event-state for any kind of event that supports #KM_PRESS / #KM_RELEASE. + */ +static void wm_event_state_update_and_click_set(wmEvent *event, wmEvent *event_state) +{ + BLI_assert(ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)); + BLI_assert(ELEM(event->val, KM_PRESS, KM_RELEASE)); + + /* Only copy these flags into the `event_state`. */ + const eWM_EventFlag event_state_flag_mask = WM_EVENT_IS_REPEAT; + + wm_event_prev_values_set(event, event_state); + + /* Copy to event state. */ + event_state->val = event->val; + event_state->type = event->type; + event_state->modifier = event->modifier; + event_state->flag = (event->flag & event_state_flag_mask); + /* NOTE: It's important that `keymodifier` is handled in the keyboard event handling logic + * since the `event_state` and the `event` are not kept in sync. */ + + /* Double click test. */ + if (wm_event_is_double_click(event)) { + CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click"); + event->val = KM_DBL_CLICK; + } + else if (event->val == KM_PRESS) { + if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { + wm_event_prev_click_set(event, event_state); + } + } +} + void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata) { if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) { @@ -4924,20 +4957,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wm_tablet_data_from_ghost(&bd->tablet, &event.tablet); wm_eventemulation(&event, false); - wm_event_prev_values_set(&event, event_state); - - /* Copy to event state. */ - event_state->val = event.val; - event_state->type = event.type; - - /* Double click test. */ - if (wm_event_is_double_click(&event)) { - CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click"); - event.val = KM_DBL_CLICK; - } - if (event.val == KM_PRESS) { - wm_event_prev_click_set(&event, event_state); - } + wm_event_state_update_and_click_set(&event, event_state); /* Add to other window if event is there (not to both!). */ wmWindow *win_other = wm_event_cursor_other_windows(wm, win, &event); @@ -4966,9 +4986,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventKeyDown: case GHOST_kEventKeyUp: { GHOST_TEventKeyData *kd = customdata; - /* Only copy these flags into the `event_state`. */ - const eWM_EventFlag event_state_flag_mask = WM_EVENT_IS_REPEAT; - bool keymodifier = 0; event.type = convert_key(kd->key); event.ascii = kd->ascii; /* Might be not NULL terminated. */ @@ -4979,12 +4996,6 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.val = (type == GHOST_kEventKeyDown) ? KM_PRESS : KM_RELEASE; wm_eventemulation(&event, false); - wm_event_prev_values_set(&event, event_state); - - /* Copy to event state. */ - event_state->val = event.val; - event_state->type = event.type; - event_state->flag = (event.flag & event_state_flag_mask); /* Exclude arrow keys, escape, etc from text input. */ if (type == GHOST_kEventKeyUp) { @@ -5017,108 +5028,68 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void switch (event.type) { case EVT_LEFTSHIFTKEY: - case EVT_RIGHTSHIFTKEY: - if (event.val == KM_PRESS) { - keymodifier = true; - } - if (keymodifier) { - event.modifier |= KM_SHIFT; - event_state->modifier |= KM_SHIFT; - } - else { - event.modifier &= ~KM_SHIFT; - event_state->modifier &= ~KM_SHIFT; - } + case EVT_RIGHTSHIFTKEY: { + SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_SHIFT); break; + } case EVT_LEFTCTRLKEY: - case EVT_RIGHTCTRLKEY: - if (event.val == KM_PRESS) { - keymodifier = true; - } - if (keymodifier) { - event.modifier |= KM_CTRL; - event_state->modifier |= KM_CTRL; - } - else { - event.modifier &= ~KM_CTRL; - event_state->modifier &= ~KM_CTRL; - } + case EVT_RIGHTCTRLKEY: { + SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_CTRL); break; + } case EVT_LEFTALTKEY: - case EVT_RIGHTALTKEY: - if (event.val == KM_PRESS) { - keymodifier = true; - } - if (keymodifier) { - event.modifier |= KM_ALT; - event_state->modifier |= KM_ALT; - } - else { - event.modifier &= ~KM_ALT; - event_state->modifier &= ~KM_ALT; - } + case EVT_RIGHTALTKEY: { + SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_ALT); + break; + } + case EVT_OSKEY: { + SET_FLAG_FROM_TEST(event.modifier, (event.val == KM_PRESS), KM_OSKEY); break; - case EVT_OSKEY: + } + default: { if (event.val == KM_PRESS) { - keymodifier = true; - } - if (keymodifier) { - event.modifier |= KM_OSKEY; - event_state->modifier |= KM_OSKEY; + if (event.keymodifier == 0) { + /* Only set in `eventstate`, for next event. */ + event_state->keymodifier = event.type; + } } else { - event.modifier &= ~KM_OSKEY; - event_state->modifier &= ~KM_OSKEY; + BLI_assert(event.val == KM_RELEASE); + if (event.keymodifier == event.type) { + event.keymodifier = event_state->keymodifier = 0; + } } - break; - default: - if (event.val == KM_PRESS && event.keymodifier == 0) { - /* Only set in `eventstate`, for next event. */ - event_state->keymodifier = event.type; + + /* This case happens on holding a key pressed, + * it should not generate press events with the same key as modifier. */ + if (event.keymodifier == event.type) { + event.keymodifier = 0; } - else if (event.val == KM_RELEASE && event.keymodifier == event.type) { - event.keymodifier = event_state->keymodifier = 0; + else if (event.keymodifier == EVT_UNKNOWNKEY) { + /* This case happens with an external number-pad, and also when using 'dead keys' + * (to compose complex latin characters e.g.), it's not really clear why. + * Since it's impossible to map a key modifier to an unknown key, + * it shouldn't harm to clear it. */ + event_state->keymodifier = event.keymodifier = 0; } break; + } } - /* Double click test. */ - /* If previous event was same type, and previous was release, and now it presses... */ - if (wm_event_is_double_click(&event)) { - CLOG_INFO(WM_LOG_HANDLERS, 1, "Send double click"); - event.val = KM_DBL_CLICK; - } - - /* This case happens on holding a key pressed, - * it should not generate press events with the same key as modifier. */ - if (event.keymodifier == event.type) { - event.keymodifier = 0; - } - - /* This case happens with an external number-pad, and also when using 'dead keys' - * (to compose complex latin characters e.g.), it's not really clear why. - * Since it's impossible to map a key modifier to an unknown key, - * it shouldn't harm to clear it. */ - if (event.keymodifier == EVT_UNKNOWNKEY) { - event_state->keymodifier = event.keymodifier = 0; - } + /* It's important `event.modifier` has been initialized first. */ + wm_event_state_update_and_click_set(&event, event_state); /* If test_break set, it catches this. Do not set with modifier presses. - * XXX Keep global for now? */ - if ((event.type == EVT_ESCKEY && event.val == KM_PRESS) && - /* Check other modifiers because ms-windows uses these to bring up the task manager. */ - ((event.modifier & (KM_SHIFT | KM_CTRL | KM_ALT)) == 0)) { + * Exclude modifiers because MS-Windows uses these to bring up the task manager. + * + * NOTE: in general handling events here isn't great design as + * event handling should be managed by the event handling loop. + * Make an exception for `G.is_break` as it ensures we can always cancel operations + * such as rendering or baking no matter which operation is currently handling events. */ + if ((event.type == EVT_ESCKEY) && (event.val == KM_PRESS) && (event.modifier == 0)) { G.is_break = true; } - /* Double click test - only for press. */ - if (event.val == KM_PRESS) { - /* Don't reset timer & location when holding the key generates repeat events. */ - if ((event.flag & WM_EVENT_IS_REPEAT) == 0) { - wm_event_prev_click_set(&event, event_state); - } - } - wm_event_add(win, &event); break; -- cgit v1.2.3 From f052fb564605a788378d361e161a3447d30814a6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:48:34 +1100 Subject: Event System: add ISKEYBOARD_OR_BUTTON macro This simplifies checking for event types that support press & release. --- source/blender/windowmanager/WM_types.h | 2 +- source/blender/windowmanager/intern/wm_event_system.c | 12 +++++------- source/blender/windowmanager/wm_event_types.h | 10 ++++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index d7fe34a3b4c..b2296ced923 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -635,7 +635,7 @@ typedef struct wmTabletData { * Notes: * * - The previous values are only set for mouse button and keyboard events. - * See: #ISMOUSE_BUTTON & #ISKEYBOARD macros. + * See: #ISKEYBOARD_OR_BUTTON macro. * * - Previous x/y are exceptions: #wmEvent.prev * these are set on mouse motion, see #MOUSEMOVE & track-pad events. diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 27a7042fd62..6cf7a33e06e 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -145,7 +145,7 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) copy_v2_v2_int(win->eventstate->prev_xy, win->eventstate->xy); copy_v2_v2_int(event->prev_xy, win->eventstate->xy); } - else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { + else if (ISKEYBOARD_OR_BUTTON(event->type)) { win->eventstate->prev_val = event->prev_val = win->eventstate->val; win->eventstate->prev_type = event->prev_type = win->eventstate->type; @@ -3205,7 +3205,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) win->event_queue_check_drag = false; } } - else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { + else if (ISKEYBOARD_OR_BUTTON(event->type)) { /* All events that don't set #wmEvent.prev_type must be ignored. */ /* Test for CLICK events. */ @@ -4770,7 +4770,7 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d */ static void wm_event_state_update_and_click_set(wmEvent *event, wmEvent *event_state) { - BLI_assert(ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)); + BLI_assert(ISKEYBOARD_OR_BUTTON(event->type)); BLI_assert(ELEM(event->val, KM_PRESS, KM_RELEASE)); /* Only copy these flags into the `event_state`. */ @@ -4839,15 +4839,13 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void * while not common, avoid a false alarm. */ #ifndef NDEBUG if ((event_state->type || event_state->val) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->type) || ISKEYBOARD(event_state->type) || - (event_state->type == EVENT_NONE))) { + !(ISKEYBOARD_OR_BUTTON(event_state->type) || (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, "Non-keyboard/mouse button found in 'win->eventstate->type = %d'", event_state->type); } if ((event_state->prev_type || event_state->prev_val) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->prev_type) || ISKEYBOARD(event_state->prev_type) || - (event_state->type == EVENT_NONE))) { + !(ISKEYBOARD_OR_BUTTON(event_state->prev_type) || (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, "Non-keyboard/mouse button found in 'win->eventstate->prev_type = %d'", event_state->prev_type); diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 578663b6fe8..fd0760d1d1c 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -363,6 +363,16 @@ enum { (((event_type) >= _EVT_KEYBOARD_MIN && (event_type) <= _EVT_KEYBOARD_MAX) || \ ((event_type) >= EVT_F1KEY && (event_type) <= EVT_F24KEY)) +/** + * Test whether the event is a key on the keyboard + * or any other kind of button that supports press & release + * (use for click & click-drag detection). + * + * \note Mouse wheel events are excluded from this macro, while they do generate press events it + * doesn't make sense to have click & click-drag events for a mouse-wheel as it can't be held down. + */ +#define ISKEYBOARD_OR_BUTTON(event_type) (ISMOUSE_BUTTON(event_type) || ISKEYBOARD(event_type)) + /** Test whether the event is a modifier key. */ #define ISKEYMODIFIER(event_type) \ (((event_type) >= EVT_LEFTCTRLKEY && (event_type) <= EVT_LEFTSHIFTKEY) || \ -- cgit v1.2.3 From b58e660828ebfa2b7ba6a6f5b80b5fb8c205564f Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:48:40 +1100 Subject: Event System: click/click-drag support for NDOF buttons Support this for completeness, as it's simpler to support click-drag for all events types that support press/release instead of having to document which kinds buttons support click-drag. --- source/blender/windowmanager/intern/wm_event_system.c | 4 ++++ source/blender/windowmanager/wm_event_types.h | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 6cf7a33e06e..cc98f7a08a6 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -5142,11 +5142,15 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kRelease: event.val = KM_RELEASE; break; + default: + BLI_assert_unreachable(); } event.custom = 0; event.customdata = NULL; + wm_event_state_update_and_click_set(&event, event_state); + wm_event_add(win, &event); break; diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index fd0760d1d1c..5ced501dd49 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -371,7 +371,8 @@ enum { * \note Mouse wheel events are excluded from this macro, while they do generate press events it * doesn't make sense to have click & click-drag events for a mouse-wheel as it can't be held down. */ -#define ISKEYBOARD_OR_BUTTON(event_type) (ISMOUSE_BUTTON(event_type) || ISKEYBOARD(event_type)) +#define ISKEYBOARD_OR_BUTTON(event_type) \ + (ISMOUSE_BUTTON(event_type) || ISKEYBOARD(event_type) || ISNDOF_BUTTON(event_type)) /** Test whether the event is a modifier key. */ #define ISKEYMODIFIER(event_type) \ -- cgit v1.2.3 From 88b2bbc649ad588c9f6fcf35d9193e9c8ac86a51 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:54:47 +1100 Subject: Event System: support changing NDOF button shortcuts --- source/blender/editors/interface/interface_context_menu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 07efcdcbe38..4d30d6731a2 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -1036,7 +1036,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev /* We do have a shortcut, but only keyboard ones are editable that way... */ if (kmi) { - if (ISKEYBOARD(kmi->type)) { + if (ISKEYBOARD(kmi->type) || ISNDOF_BUTTON(kmi->type)) { #if 0 /* would rather use a block but, but gets weirdly positioned... */ uiDefBlockBut(block, menu_change_shortcut, -- cgit v1.2.3 From 59c972b40ab0bb7a072984c7f787784e0e0b1d9e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 13:58:45 +1100 Subject: Event System: remove unused NDOF buttons GHOST always converts NDOF keyboard buttons into keyboard events so there is no need to expose their values for Blender event types. --- source/blender/makesrna/intern/rna_wm.c | 4 ++++ source/blender/windowmanager/wm_event_types.h | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 8835591f303..dcba0ff574b 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -98,11 +98,13 @@ static const EnumPropertyItem event_ndof_type_items[] = { {NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "Dominant", ""}, {NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "Plus", ""}, {NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "Minus", ""}, +# if 0 /* Never used (converted to keyboard events by GHOST). */ /* keyboard emulation */ {NDOF_BUTTON_ESC, "NDOF_BUTTON_ESC", 0, "Esc"}, {NDOF_BUTTON_ALT, "NDOF_BUTTON_ALT", 0, "Alt"}, {NDOF_BUTTON_SHIFT, "NDOF_BUTTON_SHIFT", 0, "Shift"}, {NDOF_BUTTON_CTRL, "NDOF_BUTTON_CTRL", 0, "Ctrl"}, +# endif /* general-purpose buttons */ {NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "Button 1", ""}, {NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "Button 2", ""}, @@ -311,11 +313,13 @@ const EnumPropertyItem rna_enum_event_type_items[] = { {NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "NDOF Dominant", "NdofDom"}, {NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "NDOF Plus", "Ndof+"}, {NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "NDOF Minus", "Ndof-"}, +#if 0 /* Never used (converted to keyboard events by GHOST). */ /* keyboard emulation */ {NDOF_BUTTON_ESC, "NDOF_BUTTON_ESC", 0, "NDOF Esc", "NdofEsc"}, {NDOF_BUTTON_ALT, "NDOF_BUTTON_ALT", 0, "NDOF Alt", "NdofAlt"}, {NDOF_BUTTON_SHIFT, "NDOF_BUTTON_SHIFT", 0, "NDOF Shift", "NdofShift"}, {NDOF_BUTTON_CTRL, "NDOF_BUTTON_CTRL", 0, "NDOF Ctrl", "NdofCtrl"}, +#endif /* general-purpose buttons */ {NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "NDOF Button 1", "NdofB1"}, {NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "NDOF Button 2", "NdofB2"}, diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 5ced501dd49..0a0fdfa769a 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -275,11 +275,17 @@ enum { NDOF_BUTTON_DOMINANT = 0x01a3, /* 419 */ NDOF_BUTTON_PLUS = 0x01a4, /* 420 */ NDOF_BUTTON_MINUS = 0x01a5, /* 421 */ + +/* Disabled as GHOST converts these to keyboard events + * which use regular keyboard event handling logic. */ +#if 0 /* keyboard emulation */ NDOF_BUTTON_ESC = 0x01a6, /* 422 */ NDOF_BUTTON_ALT = 0x01a7, /* 423 */ NDOF_BUTTON_SHIFT = 0x01a8, /* 424 */ NDOF_BUTTON_CTRL = 0x01a9, /* 425 */ +#endif + /* general-purpose buttons */ NDOF_BUTTON_1 = 0x01aa, /* 426 */ NDOF_BUTTON_2 = 0x01ab, /* 427 */ -- cgit v1.2.3 From c0c840cc3109577476c70ae58ed918dea26b2640 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 14:15:34 +1100 Subject: Cleanup: prefer UNUSED_VARS_NDEBUG for debug only variables Otherwise it's possible to accidentally use these variables in debug builds, breaking release builds. --- source/blender/editors/uvedit/uvedit_select.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index e574dc0620c..adf78ec6e96 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1266,12 +1266,11 @@ void ED_uvedit_selectmode_flush(Scene *scene, BMEditMesh *em) * For face selections with sticky mode enabled, this can create invalid selection states. */ void uvedit_select_flush(Scene *scene, BMEditMesh *em) { + ToolSettings *ts = scene->toolsettings; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); -#ifndef NDEBUG - ToolSettings *ts = scene->toolsettings; BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); -#endif + UNUSED_VARS_NDEBUG(ts); BMFace *efa; BMLoop *l; -- cgit v1.2.3 From 06d9c2c223b345d1ac1fcb5fc9602db36572b4a7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 14:32:34 +1100 Subject: Cleanup: spelling in comments --- source/blender/blenkernel/intern/dynamicpaint.c | 2 +- source/blender/blenlib/BLI_fileops.h | 6 +++--- source/blender/blenlib/intern/path_util.c | 2 +- source/blender/blenloader/intern/readfile.c | 12 +++++++----- source/blender/bmesh/tools/bmesh_bevel.c | 2 +- source/blender/editors/space_file/filesel.c | 4 ++-- source/blender/editors/space_outliner/outliner_dragdrop.cc | 2 +- source/blender/imbuf/IMB_thumbs.h | 2 +- source/blender/io/collada/DocumentImporter.cpp | 3 +-- source/blender/io/collada/SceneExporter.cpp | 2 +- source/blender/render/intern/texture_procedural.c | 12 ++++++------ 11 files changed, 25 insertions(+), 24 deletions(-) diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index 1136ec45fd4..ce07e501897 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -6142,7 +6142,7 @@ static bool dynamicPaint_generateBakeData(DynamicPaintSurface *surface, /* generate surface space partitioning grid */ surfaceGenerateGrid(surface); - /* calculate current frame adjacency point distances and global dirs */ + /* Calculate current frame adjacency point distances and global directions. */ dynamicPaint_prepareAdjacencyData(surface, false); /* Copy current frame vertices to check against in next frame */ diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index 6f8b479dddd..04b40904187 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -149,10 +149,10 @@ eFileAttributes BLI_file_attributes(const char *path); * \{ */ /** - * Scans the contents of the directory named *dirname, and allocates and fills in an - * array of entries describing them in *filelist. + * Scans the contents of the directory named `dir`, and allocates and fills in an + * array of entries describing them in `r_filelist`. * - * \return The length of filelist array. + * \return The length of `r_filelist` array. */ unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **r_filelist); /** diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 69ba9f7fb7b..a6f09d074e7 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -629,7 +629,7 @@ bool BLI_path_parent_dir(char *path) BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */ if (!BLI_path_extension_check(tmp, parent_dir)) { - strcpy(path, tmp); /* We assume pardir is always shorter... */ + strcpy(path, tmp); /* We assume the parent directory is always shorter. */ return true; } diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 370137908c3..4ea6287399c 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -161,14 +161,14 @@ * which keeps large arrays in memory from data-blocks we may not even use. * * \note This is disabled when using compression, - * while zlib supports seek it's unusable slow, see: T61880. + * while ZLIB supports seek it's unusably slow, see: T61880. */ #define USE_BHEAD_READ_ON_DEMAND -/* use GHash for BHead name-based lookups (speeds up linking) */ +/** Use #GHash for #BHead name-based lookups (speeds up linking). */ #define USE_GHASH_BHEAD -/* Use GHash for restoring pointers by name */ +/** Use #GHash for restoring pointers by name. */ #define USE_GHASH_RESTORE_POINTER static CLG_LogRef LOG = {"blo.readfile"}; @@ -194,8 +194,10 @@ typedef struct BHeadN { #define BHEADN_FROM_BHEAD(bh) ((BHeadN *)POINTER_OFFSET(bh, -(int)offsetof(BHeadN, bhead))) -/* We could change this in the future, for now it's simplest if only data is delayed - * because ID names are used in lookup tables. */ +/** + * We could change this in the future, for now it's simplest if only data is delayed + * because ID names are used in lookup tables. + */ #define BHEAD_USE_READ_ON_DEMAND(bhead) ((bhead)->code == DATA) void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format, ...) diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index da138cf9216..a550b638e46 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -1859,7 +1859,7 @@ static void move_weld_profile_planes(BevVert *bv, BoundVert *bndv1, BoundVert *b float l1 = normalize_v3(no); /* "no" is new normal projection plane, but don't move if it is coplanar with both of the - * projection dirs. */ + * projection directions. */ float no2[3], no3[3]; cross_v3_v3v3(no2, d1, bndv1->profile.proj_dir); float l2 = normalize_v3(no2); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 933c840798b..011506368ee 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -1125,8 +1125,8 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche */ for (int i = 0; i < n; i++) { FileDirEntry *file = filelist_file(sfile->files, i); - /* Do not check whether file is a file or dir here! Causes T44243 - * (we do accept dirs at this stage). */ + /* Do not check whether file is a file or dir here! Causes: T44243 + * (we do accept directories at this stage). */ if (fnmatch(pattern, file->relpath, 0) == 0) { filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); if (!match) { diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index edd2e5f304f..735625f679e 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -1358,7 +1358,7 @@ static TreeElement *outliner_item_drag_element_find(SpaceOutliner *space_outline ARegion *region, const wmEvent *event) { - /* NOTE: using EVT_TWEAK_ events to trigger dragging is fine, + /* NOTE: using click-drag events to trigger dragging is fine, * it sends coordinates from where dragging was started */ const float my = UI_view2d_region_to_view_y(®ion->v2d, event->mval[1]); return outliner_find_item_at_y(space_outliner, &space_outliner->tree, my); diff --git a/source/blender/imbuf/IMB_thumbs.h b/source/blender/imbuf/IMB_thumbs.h index 70ff0ad83cd..623a3b2b5f4 100644 --- a/source/blender/imbuf/IMB_thumbs.h +++ b/source/blender/imbuf/IMB_thumbs.h @@ -71,7 +71,7 @@ void IMB_thumb_delete(const char *path, ThumbSize size); struct ImBuf *IMB_thumb_manage(const char *path, ThumbSize size, ThumbSource source); /** - * Create the necessary dirs to store the thumbnails. + * Create the necessary directories to store the thumbnails. */ void IMB_thumb_makedirs(void); diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp index 6515f7553ad..2ce97bc8b5d 100644 --- a/source/blender/io/collada/DocumentImporter.cpp +++ b/source/blender/io/collada/DocumentImporter.cpp @@ -396,8 +396,7 @@ Object *DocumentImporter::create_instance_node(Object *source_ob, anim_importer.read_node_transform(instance_node, obn); /* if we also have a source_node (always ;), take its * transformation matrix and apply it to the newly instantiated - * object to account for node hierarchy transforms in - * .dae */ + * object to account for node hierarchy transforms in `.dae`. */ if (source_node) { COLLADABU::Math::Matrix4 mat4 = source_node->getTransformationMatrix(); COLLADABU::Math::Matrix4 bmat4 = diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp index 6cdfce5f35a..ea95729666a 100644 --- a/source/blender/io/collada/SceneExporter.cpp +++ b/source/blender/io/collada/SceneExporter.cpp @@ -189,7 +189,7 @@ void SceneExporter::writeNode(Object *ob) "blender", con_tag, "lin_error", con->lin_error); /* not ideal: add the target object name as another parameter. - * No real mapping in the .dae + * No real mapping in the `.dae`. * Need support for multiple target objects also. */ const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {nullptr, nullptr}; diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index 4b1439529c1..73dbcfdfeab 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -102,10 +102,10 @@ static int blend(const Tex *tex, const float texvec[3], TexResult *texres) y = texvec[1]; } - if (tex->stype == TEX_LIN) { /* lin */ + if (tex->stype == TEX_LIN) { /* Linear. */ texres->tin = (1.0f + x) / 2.0f; } - else if (tex->stype == TEX_QUAD) { /* quad */ + else if (tex->stype == TEX_QUAD) { /* Quadratic. */ texres->tin = (1.0f + x) / 2.0f; if (texres->tin < 0.0f) { texres->tin = 0.0f; @@ -114,7 +114,7 @@ static int blend(const Tex *tex, const float texvec[3], TexResult *texres) texres->tin *= texres->tin; } } - else if (tex->stype == TEX_EASE) { /* ease */ + else if (tex->stype == TEX_EASE) { /* Ease. */ texres->tin = (1.0f + x) / 2.0f; if (texres->tin <= 0.0f) { texres->tin = 0.0f; @@ -127,10 +127,10 @@ static int blend(const Tex *tex, const float texvec[3], TexResult *texres) texres->tin = (3.0f * t - 2.0f * t * texres->tin); } } - else if (tex->stype == TEX_DIAG) { /* diag */ + else if (tex->stype == TEX_DIAG) { /* Diagonal. */ texres->tin = (2.0f + x + y) / 4.0f; } - else if (tex->stype == TEX_RAD) { /* radial */ + else if (tex->stype == TEX_RAD) { /* Radial. */ texres->tin = (atan2f(y, x) / (float)(2 * M_PI) + 0.5f); } else { /* sphere TEX_SPHERE */ @@ -139,7 +139,7 @@ static int blend(const Tex *tex, const float texvec[3], TexResult *texres) texres->tin = 0.0f; } if (tex->stype == TEX_HALO) { - texres->tin *= texres->tin; /* halo */ + texres->tin *= texres->tin; /* Halo. */ } } -- cgit v1.2.3 From 3c889fe2b084d09e2e4702211aadc0a2d8444c45 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 14:45:33 +1100 Subject: Cleanup: pass const scene argument to UV selection functions --- source/blender/editors/include/ED_uvedit.h | 14 ++--- source/blender/editors/uvedit/uvedit_intern.h | 4 +- source/blender/editors/uvedit/uvedit_select.c | 83 ++++++++++++++++----------- 3 files changed, 57 insertions(+), 44 deletions(-) diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index cf4d023ccdf..40a57a321d8 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -223,7 +223,7 @@ void uvedit_edge_select_set_noflush(const struct Scene *scene, * Updates selection state for UVs based on the select mode and sticky mode. Similar to * #EDBM_selectmode_set. */ -void ED_uvedit_selectmode_clean(struct Scene *scene, struct Object *obedit); +void ED_uvedit_selectmode_clean(const struct Scene *scene, struct Object *obedit); void ED_uvedit_selectmode_clean_multi(struct bContext *C); /** @@ -231,16 +231,16 @@ void ED_uvedit_selectmode_clean_multi(struct bContext *C); * * Flushes selections upwards as dictated by the UV select mode. */ -void ED_uvedit_selectmode_flush(struct Scene *scene, struct BMEditMesh *em); +void ED_uvedit_selectmode_flush(const struct Scene *scene, struct BMEditMesh *em); /** * Mode independent UV de-selection flush. */ -void uvedit_deselect_flush(struct Scene *scene, struct BMEditMesh *em); +void uvedit_deselect_flush(const struct Scene *scene, struct BMEditMesh *em); /** * Mode independent UV selection flush. */ -void uvedit_select_flush(struct Scene *scene, struct BMEditMesh *em); +void uvedit_select_flush(const struct Scene *scene, struct BMEditMesh *em); bool ED_uvedit_nearest_uv(const struct Scene *scene, struct Object *obedit, @@ -254,15 +254,15 @@ bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, float *dist_sq, float r_uv[2]); -struct BMFace **ED_uvedit_selected_faces(struct Scene *scene, +struct BMFace **ED_uvedit_selected_faces(const struct Scene *scene, struct BMesh *bm, int len_max, int *r_faces_len); -struct BMLoop **ED_uvedit_selected_edges(struct Scene *scene, +struct BMLoop **ED_uvedit_selected_edges(const struct Scene *scene, struct BMesh *bm, int len_max, int *r_edges_len); -struct BMLoop **ED_uvedit_selected_verts(struct Scene *scene, +struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene, struct BMesh *bm, int len_max, int *r_verts_len); diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index b2a411147a5..b73a02e8de6 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -155,8 +155,8 @@ void UV_OT_shortest_path_select(struct wmOperatorType *ot); /* uvedit_select.c */ -bool uvedit_select_is_any_selected(struct Scene *scene, struct Object *obedit); -bool uvedit_select_is_any_selected_multi(struct Scene *scene, +bool uvedit_select_is_any_selected(const struct Scene *scene, struct Object *obedit); +bool uvedit_select_is_any_selected_multi(const struct Scene *scene, struct Object **objects, uint objects_len); /** diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index adf78ec6e96..2728c29141a 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -55,18 +55,21 @@ #include "uvedit_intern.h" -static void uv_select_all_perform(Scene *scene, Object *obedit, int action); - -static void uv_select_all_perform_multi_ex( - Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude); -static void uv_select_all_perform_multi(Scene *scene, +static void uv_select_all_perform(const Scene *scene, Object *obedit, int action); + +static void uv_select_all_perform_multi_ex(const Scene *scene, + Object **objects, + const uint objects_len, + int action, + const Object *ob_exclude); +static void uv_select_all_perform_multi(const Scene *scene, Object **objects, const uint objects_len, int action); -static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bool select); -static void uv_select_flush_from_tag_loop(Scene *scene, Object *obedit, const bool select); -static void uv_select_flush_from_loop_edge_flag(Scene *scene, BMEditMesh *em); +static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select); +static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select); +static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMEditMesh *em); static void uv_select_tag_update_for_object(Depsgraph *depsgraph, const ToolSettings *ts, @@ -1200,7 +1203,10 @@ bool uvedit_vert_is_all_other_faces_selected(const Scene *scene, /** * Clear specified UV flag (vert/edge/pinned). */ -static void bm_uv_flag_clear(Scene *scene, BMesh *bm, const int flag, const int cd_loop_uv_offset) +static void bm_uv_flag_clear(const Scene *scene, + BMesh *bm, + const int flag, + const int cd_loop_uv_offset) { BMFace *efa; BMLoop *l; @@ -1223,9 +1229,9 @@ static void bm_uv_flag_clear(Scene *scene, BMesh *bm, const int flag, const int * * \{ */ -void ED_uvedit_selectmode_flush(Scene *scene, BMEditMesh *em) +void ED_uvedit_selectmode_flush(const Scene *scene, BMEditMesh *em) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); @@ -1262,11 +1268,11 @@ void ED_uvedit_selectmode_flush(Scene *scene, BMEditMesh *em) /** \name UV Flush selection (up/down) * \{ */ -/* Careful when using this in face select mode. - * For face selections with sticky mode enabled, this can create invalid selection states. */ -void uvedit_select_flush(Scene *scene, BMEditMesh *em) +void uvedit_select_flush(const Scene *scene, BMEditMesh *em) { - ToolSettings *ts = scene->toolsettings; + /* Careful when using this in face select mode. + * For face selections with sticky mode enabled, this can create invalid selection states. */ + const ToolSettings *ts = scene->toolsettings; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); @@ -1291,9 +1297,9 @@ void uvedit_select_flush(Scene *scene, BMEditMesh *em) } } -void uvedit_deselect_flush(Scene *scene, BMEditMesh *em) +void uvedit_deselect_flush(const Scene *scene, BMEditMesh *em) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); @@ -2166,7 +2172,7 @@ void UV_OT_select_less(wmOperatorType *ot) /** \name (De)Select All Operator * \{ */ -bool uvedit_select_is_any_selected(Scene *scene, Object *obedit) +bool uvedit_select_is_any_selected(const Scene *scene, Object *obedit) { const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -2194,7 +2200,9 @@ bool uvedit_select_is_any_selected(Scene *scene, Object *obedit) return false; } -bool uvedit_select_is_any_selected_multi(Scene *scene, Object **objects, const uint objects_len) +bool uvedit_select_is_any_selected_multi(const Scene *scene, + Object **objects, + const uint objects_len) { bool found = false; for (uint ob_index = 0; ob_index < objects_len; ob_index++) { @@ -2207,7 +2215,7 @@ bool uvedit_select_is_any_selected_multi(Scene *scene, Object **objects, const u return found; } -static void uv_select_all(Scene *scene, BMEditMesh *em, bool select_all) +static void uv_select_all(const Scene *scene, BMEditMesh *em, bool select_all) { BMFace *efa; BMLoop *l; @@ -2226,7 +2234,7 @@ static void uv_select_all(Scene *scene, BMEditMesh *em, bool select_all) } } -static void uv_select_invert(Scene *scene, BMEditMesh *em) +static void uv_select_invert(const Scene *scene, BMEditMesh *em) { const ToolSettings *ts = scene->toolsettings; BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); @@ -2265,7 +2273,7 @@ static void uv_select_invert(Scene *scene, BMEditMesh *em) } } -static void uv_select_all_perform(Scene *scene, Object *obedit, int action) +static void uv_select_all_perform(const Scene *scene, Object *obedit, int action) { const ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -2306,8 +2314,11 @@ static void uv_select_all_perform(Scene *scene, Object *obedit, int action) } } -static void uv_select_all_perform_multi_ex( - Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude) +static void uv_select_all_perform_multi_ex(const Scene *scene, + Object **objects, + const uint objects_len, + int action, + const Object *ob_exclude) { if (action == SEL_TOGGLE) { action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT : @@ -2323,7 +2334,7 @@ static void uv_select_all_perform_multi_ex( } } -static void uv_select_all_perform_multi(Scene *scene, +static void uv_select_all_perform_multi(const Scene *scene, Object **objects, const uint objects_len, int action) @@ -3136,7 +3147,7 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph, /** * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face */ -static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, +static void uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene, BMEditMesh *em, UvVertMap *vmap, const uint efa_index, @@ -3194,7 +3205,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene, * \note This function is very similar to #uv_select_flush_from_tag_loop, * be sure to update both upon changing. */ -static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bool select) +static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select) { /* Selecting UV Faces with some modes requires us to change * the selection in other faces (depending on the sticky mode). @@ -3263,7 +3274,7 @@ static void uv_select_flush_from_tag_face(Scene *scene, Object *obedit, const bo * \note This function is very similar to #uv_select_flush_from_tag_face, * be sure to update both upon changing. */ -static void uv_select_flush_from_tag_loop(Scene *scene, Object *obedit, const bool select) +static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select) { /* Selecting UV Loops with some modes requires us to change * the selection in other faces (depending on the sticky mode). @@ -3345,7 +3356,7 @@ static void uv_select_flush_from_tag_loop(Scene *scene, Object *obedit, const bo * * \note Current behavior is selecting only; deselecting can be added but the behavior isn't * required anywhere.*/ -static void uv_select_flush_from_loop_edge_flag(Scene *scene, BMEditMesh *em) +static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMEditMesh *em) { const ToolSettings *ts = scene->toolsettings; BMFace *efa; @@ -4358,7 +4369,7 @@ void UV_OT_select_overlap(wmOperatorType *ot) * So an edge that has two connected edge loops only assigns one loop in the array. * \{ */ -BMFace **ED_uvedit_selected_faces(Scene *scene, BMesh *bm, int len_max, int *r_faces_len) +BMFace **ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len) { const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); CLAMP_MAX(len_max, bm->totface); @@ -4386,7 +4397,7 @@ finally: return faces; } -BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_edges_len) +BMLoop **ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len) { const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); CLAMP_MAX(len_max, bm->totloop); @@ -4442,7 +4453,7 @@ finally: return edges; } -BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_verts_len) +BMLoop **ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len) { const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); CLAMP_MAX(len_max, bm->totloop); @@ -4508,7 +4519,9 @@ finally: * * Use only when sync select disabled. */ -static void uv_isolate_selected_islands(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset) +static void uv_isolate_selected_islands(const Scene *scene, + BMEditMesh *em, + const int cd_loop_uv_offset) { BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0); BMFace *efa; @@ -4556,9 +4569,9 @@ static void uv_isolate_selected_islands(Scene *scene, BMEditMesh *em, const int MEM_freeN(is_island_not_selected); } -void ED_uvedit_selectmode_clean(Scene *scene, Object *obedit) +void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit) { - ToolSettings *ts = scene->toolsettings; + const ToolSettings *ts = scene->toolsettings; BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0); BMEditMesh *em = BKE_editmesh_from_object(obedit); char sticky = ts->uv_sticky; -- cgit v1.2.3 From 63ac1660c0503b2990c1b02aeb4e08ca81bf4e3b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 15:13:53 +1100 Subject: Fix T92861: Transform fails for multiple pose object sharing ob-data Support transforming two pose objects at once even when they share object data as this is per-object. --- source/blender/editors/transform/transform_convert.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index 4a2169b381e..dbe67bd0d66 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1113,7 +1113,8 @@ static void init_TransDataContainers(TransInfo *t, &objects_len, { .object_mode = object_mode, - .no_dup_data = true, + /* Pose transform operates on `ob->pose` so don't skip duplicate object-data. */ + .no_dup_data = (object_mode & OB_MODE_POSE) == 0, }); free_objects = true; } -- cgit v1.2.3 From 2ddb53de5403fb02e4de053084b2d00b2372a122 Mon Sep 17 00:00:00 2001 From: Pratik Borhade Date: Tue, 8 Mar 2022 09:50:32 +0100 Subject: Fix T96228: TypeError on use of Copy from Active Track operation Issue was introduced after the python 3.10 switch Explicit conversion to int will fix the issue. Same issue is likely to happen with `MovieTrackingSettings.default_search_size` So I did the same change over there. Differential Revision: https://developer.blender.org/D14273 --- release/scripts/startup/bl_operators/clip.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/release/scripts/startup/bl_operators/clip.py b/release/scripts/startup/bl_operators/clip.py index f0e099244f4..734fb071d42 100644 --- a/release/scripts/startup/bl_operators/clip.py +++ b/release/scripts/startup/bl_operators/clip.py @@ -123,8 +123,8 @@ def CLIP_default_settings_from_track(clip, track, framenr): search[1] = search[1] * height settings.default_correlation_min = track.correlation_min - settings.default_pattern_size = max(pattern[0], pattern[1]) - settings.default_search_size = max(search[0], search[1]) + settings.default_pattern_size = int(max(pattern[0], pattern[1])) + settings.default_search_size = int(max(search[0], search[1])) settings.default_frames_limit = track.frames_limit settings.default_pattern_match = track.pattern_match settings.default_margin = track.margin -- cgit v1.2.3 From b588c09eee40db46ed10ed5f723c89aef7ea816b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 8 Mar 2022 10:23:38 +0100 Subject: Fix: add missing case in switch statement for curves --- source/blender/blenkernel/intern/paint.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index d42c8ea37d5..238cf1ad74e 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -551,6 +551,8 @@ ePaintMode BKE_paintmode_get_active_from_context(const bContext *C) return PAINT_MODE_TEXTURE_3D; case OB_MODE_EDIT: return PAINT_MODE_SCULPT_UV; + case OB_MODE_SCULPT_CURVES: + return PAINT_MODE_SCULPT_CURVES; default: return PAINT_MODE_TEXTURE_2D; } -- cgit v1.2.3 From 4b598739c5c18c83048646e27e91f1f0bf53acc6 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 8 Mar 2022 10:33:51 +0100 Subject: Curves: disable stroke spacing for curve sculpt brushes This makes the brushes more smooth, because the brush has an effect after every mouse move, instead of only every x pixels. For this to work well, the brushes have to look at the stroke segments instead of at the mouse positions separately. A more fine grained check might be added in the future. --- source/blender/editors/sculpt_paint/paint_stroke.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index ae7570d21a1..1705e36363e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1000,6 +1000,10 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) return true; } + if (mode == PAINT_MODE_SCULPT_CURVES) { + return false; + } + return paint_supports_dynamic_size(br, mode); } -- cgit v1.2.3 From 94d2470c416f70f7b0a357b1c3f3fc80d3e7360b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 8 Mar 2022 10:36:46 +0100 Subject: Curves: increase default viewport resolution for curves object This part has to be refactored soon anyway, because more types curves have to be drawn for the new Curves object. For now, 3 is a better default than 2, because that matches the actual resolution of the curve currently. --- source/blender/draw/intern/draw_cache_impl_curves.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/intern/draw_cache_impl_curves.cc b/source/blender/draw/intern/draw_cache_impl_curves.cc index df1ac12605a..aea71d965d1 100644 --- a/source/blender/draw/intern/draw_cache_impl_curves.cc +++ b/source/blender/draw/intern/draw_cache_impl_curves.cc @@ -340,7 +340,7 @@ bool hair_ensure_procedural_data(Object *object, HairBatchCache *cache = curves_batch_cache_get(curves); *r_hair_cache = &cache->hair; - const int steps = 2; /* TODO: don't hard-code? */ + const int steps = 3; /* TODO: don't hard-code? */ (*r_hair_cache)->final[subdiv].strands_res = 1 << (steps + subdiv); /* Refreshed on combing and simulation. */ -- cgit v1.2.3 From 1d902a6367d1d18670ed75e3508cd0db00021350 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Tue, 8 Mar 2022 10:38:13 +0100 Subject: Fix T96213: Crash when texture painting across multiple materials. Issue only happens in release builds on windows. That said it was an actual error in the code. This class is compiled inline in release builds. When updating multiple textures it would reuse the same memory to collect the changes. When the previous loaded tilenumber was exactly the same but from a different image the tile buffer wasn't loaded. Reviewed By: sergey Maniphest Tasks: T96213 Differential Revision: https://developer.blender.org/D14274 --- source/blender/blenkernel/BKE_image_partial_update.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_image_partial_update.hh b/source/blender/blenkernel/BKE_image_partial_update.hh index ca7c4f40593..b00ec98353c 100644 --- a/source/blender/blenkernel/BKE_image_partial_update.hh +++ b/source/blender/blenkernel/BKE_image_partial_update.hh @@ -227,7 +227,7 @@ template struct PartialUpdateChecker { ePartialUpdateCollectResult result_code; private: - TileNumber last_tile_number; + TileNumber last_tile_number = 0; public: CollectResult(PartialUpdateChecker *checker, ePartialUpdateCollectResult result_code) -- cgit v1.2.3 From 10c11bb89736d19f06131378dc539fb225b9b562 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 8 Mar 2022 10:41:52 +0100 Subject: Curves: add initial comb, grow and shrink brush The exact behavior of the brushes is still being iterated on, but it helps having a base implementation that we can work upon. All of that is still hidden behind an experimental feature flag anyway. The brushes will get a name in the ui soon. Differential Revision: https://developer.blender.org/D14241 --- .../editors/sculpt_paint/curves_sculpt_ops.cc | 343 +++++++++++++++++++-- source/blender/makesdna/DNA_brush_enums.h | 2 + source/blender/makesrna/intern/rna_brush.c | 2 + 3 files changed, 320 insertions(+), 27 deletions(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 12c03804981..4bf81b72c3c 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -11,6 +11,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" #include "BKE_paint.h" +#include "BKE_spline.hh" #include "WM_api.h" #include "WM_toolsystem.h" @@ -150,15 +151,22 @@ class DeleteOperation : public CurvesSculptStrokeOperation { } }; -class MoveOperation : public CurvesSculptStrokeOperation { +/** + * Moves individual points under the brush and does a length preservation step afterwards. + */ +class CombOperation : public CurvesSculptStrokeOperation { private: - Vector points_to_move_indices_; - IndexMask points_to_move_; float2 last_mouse_position_; public: void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override { + BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); + + if (stroke_extension.is_first) { + return; + } + Scene &scene = *CTX_data_scene(C); Object &object = *CTX_data_active_object(C); ARegion *region = CTX_wm_region(C); @@ -168,6 +176,10 @@ class MoveOperation : public CurvesSculptStrokeOperation { CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); const float brush_radius = BKE_brush_size_get(&scene, &brush); + const float brush_strength = BKE_brush_alpha_get(&scene, &brush); + + const float4x4 ob_mat = object.obmat; + const float4x4 ob_imat = ob_mat.inverted(); float4x4 projection; ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); @@ -176,39 +188,312 @@ class MoveOperation : public CurvesSculptStrokeOperation { CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); MutableSpan positions = curves.positions(); - if (stroke_extension.is_first) { - /* Find point indices to move. */ - points_to_move_ = index_mask_ops::find_indices_based_on_predicate( - curves.points_range(), 512, points_to_move_indices_, [&](const int64_t point_i) { - const float3 position = positions[point_i]; - float2 screen_position; - ED_view3d_project_float_v2_m4(region, position, screen_position, projection.values); - const float distance = len_v2v2(screen_position, stroke_extension.mouse_position); - return distance <= brush_radius; - }); - } - else { - /* Move points based on mouse movement. */ - const float2 mouse_diff = stroke_extension.mouse_position - last_mouse_position_; - threading::parallel_for(points_to_move_.index_range(), 512, [&](const IndexRange range) { - for (const int point_i : points_to_move_.slice(range)) { + const float2 mouse_prev = last_mouse_position_; + const float2 mouse_cur = stroke_extension.mouse_position; + const float2 mouse_diff = mouse_cur - mouse_prev; + const float mouse_diff_len = math::length(mouse_diff); + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange curve_points = curves.range_for_curve(curve_i); + /* Compute lengths of the segments. Those are used to make sure that the lengths don't + * change. */ + Vector segment_lengths(curve_points.size() - 1); + for (const int segment_i : IndexRange(curve_points.size() - 1)) { + const float3 &p1 = positions[curve_points[segment_i]]; + const float3 &p2 = positions[curve_points[segment_i] + 1]; + const float length = math::distance(p1, p2); + segment_lengths[segment_i] = length; + } + bool curve_changed = false; + for (const int point_i : curve_points.drop_front(1)) { const float3 old_position = positions[point_i]; + + /* Find the position of the point in screen space. */ float2 old_position_screen; ED_view3d_project_float_v2_m4( region, old_position, old_position_screen, projection.values); - const float2 new_position_screen = old_position_screen + mouse_diff; + + /* Project the point onto the line drawn by the mouse. Note, it's projected on the + * infinite line, not only on the line segment. */ + float2 old_position_screen_proj; + /* t is 0 when the point is closest to the previous mouse position and 1 when it's + * closest to the current mouse position. */ + const float t = closest_to_line_v2( + old_position_screen_proj, old_position_screen, mouse_prev, mouse_cur); + + /* Compute the distance to the mouse line segment. */ + const float2 old_position_screen_proj_segment = mouse_prev + + std::clamp(t, 0.0f, 1.0f) * mouse_diff; + const float distance_screen = math::distance(old_position_screen, + old_position_screen_proj_segment); + if (distance_screen > brush_radius) { + /* Ignore the point because it's too far away. */ + continue; + } + /* Compute a falloff that is based on how far along the point along the last stroke + * segment is. */ + const float t_overshoot = brush_radius / mouse_diff_len; + const float t_falloff = 1.0f - std::max(t, 0.0f) / (1.0f + t_overshoot); + /* A falloff that is based on how far away the point is from the stroke. */ + const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); + /* Combine the different falloffs and brush strength. */ + const float weight = brush_strength * t_falloff * radius_falloff; + + /* Offset the old point position in screen space and transform it back into 3D space. */ + const float2 new_position_screen = old_position_screen + mouse_diff * weight; float3 new_position; - ED_view3d_win_to_3d(v3d, region, old_position, new_position_screen, new_position); + ED_view3d_win_to_3d( + v3d, region, ob_mat * old_position, new_position_screen, new_position); + new_position = ob_imat * new_position; positions[point_i] = new_position; + + curve_changed = true; } - }); + if (!curve_changed) { + continue; + } + /* Ensure that the length of each segment stays the same. */ + for (const int segment_i : IndexRange(curve_points.size() - 1)) { + const float3 &p1 = positions[curve_points[segment_i]]; + float3 &p2 = positions[curve_points[segment_i] + 1]; + const float3 direction = math::normalize(p2 - p1); + const float desired_length = segment_lengths[segment_i]; + p2 = p1 + direction * desired_length; + } + } + }); - curves.tag_positions_changed(); - DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(region); + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + } +}; + +/** + * Drags the tip point of each curve and resamples the rest of the curve. + */ +class GrowOperation : public CurvesSculptStrokeOperation { + private: + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override + { + BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); + + if (stroke_extension.is_first) { + return; } - last_mouse_position_ = stroke_extension.mouse_position; + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + const float brush_strength = BKE_brush_alpha_get(&scene, &brush); + + const float4x4 ob_mat = object.obmat; + const float4x4 ob_imat = ob_mat.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan positions = curves.positions(); + + const float2 mouse_prev = last_mouse_position_; + const float2 mouse_cur = stroke_extension.mouse_position; + const float2 mouse_diff = mouse_cur - mouse_prev; + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange curve_points = curves.range_for_curve(curve_i); + const int last_point_i = curve_points.last(); + + const float3 old_position = positions[last_point_i]; + + float2 old_position_screen; + ED_view3d_project_float_v2_m4( + region, old_position, old_position_screen, projection.values); + + const float distance_screen = math::distance(old_position_screen, mouse_prev); + if (distance_screen > brush_radius) { + continue; + } + + const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); + const float weight = brush_strength * radius_falloff; + + const float2 new_position_screen = old_position_screen + mouse_diff * weight; + float3 new_position; + ED_view3d_win_to_3d(v3d, region, ob_mat * old_position, new_position_screen, new_position); + new_position = ob_imat * new_position; + + this->move_last_point_and_resample(positions, curve_points, new_position); + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + } + + void move_last_point_and_resample(MutableSpan positions, + const IndexRange curve_points, + const float3 &new_last_point_position) const + { + Vector old_lengths; + old_lengths.append(0.0f); + /* Used to (1) normalize the segment sizes over time and (2) support making zero-length + * segments */ + const float extra_length = 0.001f; + for (const int segment_i : IndexRange(curve_points.size() - 1)) { + const float3 &p1 = positions[curve_points[segment_i]]; + const float3 &p2 = positions[curve_points[segment_i] + 1]; + const float length = math::distance(p1, p2); + old_lengths.append(old_lengths.last() + length + extra_length); + } + Vector point_factors; + for (float &old_length : old_lengths) { + point_factors.append(old_length / old_lengths.last()); + } + + PolySpline new_spline; + new_spline.resize(curve_points.size()); + MutableSpan new_spline_positions = new_spline.positions(); + for (const int i : IndexRange(curve_points.size() - 1)) { + new_spline_positions[i] = positions[curve_points[i]]; + } + new_spline_positions.last() = new_last_point_position; + new_spline.mark_cache_invalid(); + + for (const int i : IndexRange(curve_points.size())) { + const float factor = point_factors[i]; + const Spline::LookupResult lookup = new_spline.lookup_evaluated_factor(factor); + const float index_factor = lookup.evaluated_index + lookup.factor; + float3 p; + new_spline.sample_with_index_factors( + new_spline_positions, {&index_factor, 1}, {&p, 1}); + positions[curve_points[i]] = p; + } + } +}; + +/** + * Resamples the curves to a shorter length. + */ +class ShrinkOperation : public CurvesSculptStrokeOperation { + private: + float2 last_mouse_position_; + + public: + void on_stroke_extended(bContext *C, const StrokeExtension &stroke_extension) override + { + BLI_SCOPED_DEFER([&]() { last_mouse_position_ = stroke_extension.mouse_position; }); + + if (stroke_extension.is_first) { + return; + } + + Scene &scene = *CTX_data_scene(C); + Object &object = *CTX_data_active_object(C); + ARegion *region = CTX_wm_region(C); + View3D *v3d = CTX_wm_view3d(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; + Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); + const float brush_radius = BKE_brush_size_get(&scene, &brush); + const float brush_strength = BKE_brush_alpha_get(&scene, &brush); + + const float4x4 ob_mat = object.obmat; + const float4x4 ob_imat = ob_mat.inverted(); + + float4x4 projection; + ED_view3d_ob_project_mat_get(rv3d, &object, projection.values); + + Curves &curves_id = *static_cast(object.data); + CurvesGeometry &curves = CurvesGeometry::wrap(curves_id.geometry); + MutableSpan positions = curves.positions(); + + const float2 mouse_prev = last_mouse_position_; + const float2 mouse_cur = stroke_extension.mouse_position; + const float2 mouse_diff = mouse_cur - mouse_prev; + + threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) { + for (const int curve_i : curves_range) { + const IndexRange curve_points = curves.range_for_curve(curve_i); + const int last_point_i = curve_points.last(); + + const float3 old_tip_position = positions[last_point_i]; + + float2 old_tip_position_screen; + ED_view3d_project_float_v2_m4( + region, old_tip_position, old_tip_position_screen, projection.values); + + const float distance_screen = math::distance(old_tip_position_screen, mouse_prev); + if (distance_screen > brush_radius) { + continue; + } + + const float radius_falloff = pow2f(1.0f - distance_screen / brush_radius); + const float weight = brush_strength * radius_falloff; + + const float2 offset_tip_position_screen = old_tip_position_screen + weight * mouse_diff; + float3 offset_tip_position; + ED_view3d_win_to_3d(v3d, + region, + ob_mat * old_tip_position, + offset_tip_position_screen, + offset_tip_position); + offset_tip_position = ob_imat * offset_tip_position; + const float shrink_length = math::distance(offset_tip_position, old_tip_position); + + this->shrink_curve(positions, curve_points, shrink_length); + } + }); + + curves.tag_positions_changed(); + DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(region); + } + + void shrink_curve(MutableSpan positions, + const IndexRange curve_points, + const float shrink_length) const + { + PolySpline spline; + spline.resize(curve_points.size()); + MutableSpan spline_positions = spline.positions(); + spline_positions.copy_from(positions.slice(curve_points)); + spline.mark_cache_invalid(); + const float old_length = spline.length(); + const float new_length = std::max(0.0f, old_length - shrink_length); + const float length_factor = new_length / old_length; + + Vector old_point_lengths; + old_point_lengths.append(0.0f); + for (const int i : spline_positions.index_range().drop_back(1)) { + const float3 &p1 = spline_positions[i]; + const float3 &p2 = spline_positions[i + 1]; + const float length = math::distance(p1, p2); + old_point_lengths.append(old_point_lengths.last() + length); + } + + for (const int i : spline_positions.index_range()) { + const float eval_length = old_point_lengths[i] * length_factor; + const Spline::LookupResult lookup = spline.lookup_evaluated_length(eval_length); + const float index_factor = lookup.evaluated_index + lookup.factor; + float3 p; + spline.sample_with_index_factors(spline_positions, {&index_factor, 1}, {&p, 1}); + positions[curve_points[i]] = p; + } } }; @@ -652,11 +937,15 @@ static std::unique_ptr start_brush_operation(bConte Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); switch (brush.curves_sculpt_tool) { case CURVES_SCULPT_TOOL_TEST1: - return std::make_unique(); + return std::make_unique(); case CURVES_SCULPT_TOOL_TEST2: return std::make_unique(); case CURVES_SCULPT_TOOL_TEST3: return std::make_unique(); + case CURVES_SCULPT_TOOL_TEST4: + return std::make_unique(); + case CURVES_SCULPT_TOOL_TEST5: + return std::make_unique(); } BLI_assert_unreachable(); return {}; diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 88be670457a..f0c4da92378 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -460,6 +460,8 @@ typedef enum eBrushCurvesSculptTool { CURVES_SCULPT_TOOL_TEST1 = 0, CURVES_SCULPT_TOOL_TEST2 = 1, CURVES_SCULPT_TOOL_TEST3 = 2, + CURVES_SCULPT_TOOL_TEST4 = 3, + CURVES_SCULPT_TOOL_TEST5 = 4, } eBrushCurvesSculptTool; /** When #BRUSH_ACCUMULATE is used */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index e282b474d8d..12c8a41009d 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -247,6 +247,8 @@ const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""}, {CURVES_SCULPT_TOOL_TEST2, "TEST2", ICON_NONE, "Test 2", ""}, {CURVES_SCULPT_TOOL_TEST3, "TEST3", ICON_NONE, "Test 3", ""}, + {CURVES_SCULPT_TOOL_TEST4, "TEST4", ICON_NONE, "Test 4", ""}, + {CURVES_SCULPT_TOOL_TEST5, "TEST5", ICON_NONE, "Test 5", ""}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From 1c1efe3ac605b187254377058c93fd34c94aaf9a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 20:42:24 +1100 Subject: Fix memory leak evaluating PyDrivers Missed decref in 686ab4c9401a90b22fb17e46c992eb513fe4f693 caused every driver evaluation to create the BPy_StructRNA depsgraph without freeing the previously allocated depsgraph. --- source/blender/python/intern/bpy_driver.c | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 61bb183068d..e2b6b2bec29 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -401,6 +401,7 @@ static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, PyErr_Print(); PyErr_Clear(); } + Py_DECREF(py_depsgraph); } float BPY_driver_exec(struct PathResolvedRNA *anim_rna, -- cgit v1.2.3 From 0df8c22c8a369e8dd2d7ad6653556e622f98471b Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 8 Mar 2022 11:01:34 +0100 Subject: LibOverrides: Tweak to ensure no overrides can be created on linked data. Was already mostly the case, but from RNA API there was no proper check. --- source/blender/blenkernel/intern/lib_override.c | 1 + source/blender/makesrna/intern/rna_ID.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 922c1beda38..6a0f3bb5611 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -2854,6 +2854,7 @@ bool BKE_lib_override_library_status_check_reference(Main *bmain, ID *local) bool BKE_lib_override_library_operations_create(Main *bmain, ID *local) { + BLI_assert(!ID_IS_LINKED(local)); BLI_assert(local->override_library != NULL); const bool is_template = (local->override_library->reference == NULL); bool created = false; diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 94ffa330064..a9725da7841 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -744,6 +744,11 @@ static void rna_ID_override_library_operations_update(ID *id, return; } + if (ID_IS_LINKED(id)) { + BKE_reportf(reports, RPT_ERROR, "ID '%s' is linked, cannot edit its overrides", id->name); + return; + } + BKE_lib_override_library_operations_create(bmain, id); WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); -- cgit v1.2.3 From f76f48be23e04dbbbe313e428636cfcbcf2be59c Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 8 Mar 2022 12:08:21 +0100 Subject: Fix (unreported) liboverride rules from linked data disapearing. Code cleaning up no-more-needed override data during diffing process would systematically remove override data from linked IDs. While this is not a critical issue in theory, it has bad consequences at the very least on user UI/UX, and potentially can cause bugs in some corner-cases scenarii. --- source/blender/blenkernel/intern/lib_override.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 6a0f3bb5611..392af2c9f5b 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -2979,6 +2979,12 @@ bool BKE_lib_override_library_main_operations_create(Main *bmain, const bool for id->override_library, IDOVERRIDE_LIBRARY_TAG_UNUSED, false); } } + else { + /* Clear 'unused' tag for un-processed IDs, otherwise e.g. linked overrides will loose their + * list of overridden properties. */ + BKE_lib_override_library_properties_tag( + id->override_library, IDOVERRIDE_LIBRARY_TAG_UNUSED, false); + } id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_AUTOREFRESH; } FOREACH_MAIN_ID_END; -- cgit v1.2.3 From 73dc8c24e44ba8b462f88c1c629a84c98979ef41 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 8 Mar 2022 22:07:59 +1100 Subject: PyAPI: optimize depsgraph use in PyDrivers Avoid re-creating & freeing the depsgraph for every driver evaluation. Now the depsgraph is kept in the name-space (matching self), only re-created when the value changes. In a contrived test-case with many drivers this gave ~15% overall speedup for animation playback. --- source/blender/python/intern/bpy_driver.c | 74 ++++++++++++------------ source/blender/python/intern/bpy_intern_string.c | 4 +- source/blender/python/intern/bpy_intern_string.h | 1 + 3 files changed, 41 insertions(+), 38 deletions(-) diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index e2b6b2bec29..68018d22753 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -162,9 +162,11 @@ static struct { /* borrowed reference to the 'self' in 'bpy_pydriver_Dict' * keep for as long as the same self is used. */ PyObject *self; + BPy_StructRNA *depsgraph; } g_pydriver_state_prev = { .evaltime = FLT_MAX, .self = NULL, + .depsgraph = NULL, }; static void bpy_pydriver_namespace_update_frame(const float evaltime) @@ -199,6 +201,38 @@ static void bpy_pydriver_namespace_clear_self(void) } } +static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph) +{ + struct PointerRNA depsgraph_ptr; + RNA_pointer_create(NULL, &RNA_Depsgraph, depsgraph, &depsgraph_ptr); + return pyrna_struct_CreatePyObject(&depsgraph_ptr); +} + +/** + * Adds a variable 'depsgraph' to the name-space. This can then be used to obtain evaluated + * data-blocks, and the current view layer and scene. See T75553. + */ +static void bpy_pydriver_namespace_update_depsgraph(struct Depsgraph *depsgraph) +{ + /* This should never happen, but it's probably better to have None in Python + * than a NULL-wrapping Depsgraph Python struct. */ + BLI_assert(depsgraph != NULL); + if (UNLIKELY(depsgraph == NULL)) { + PyDict_SetItem(bpy_pydriver_Dict, bpy_intern_str_depsgraph, Py_None); + g_pydriver_state_prev.depsgraph = NULL; + return; + } + + if ((g_pydriver_state_prev.depsgraph == NULL) || + ((depsgraph != g_pydriver_state_prev.depsgraph->ptr.data))) { + PyObject *item = bpy_pydriver_depsgraph_as_pyobject(depsgraph); + PyDict_SetItem(bpy_pydriver_Dict, bpy_intern_str_depsgraph, item); + Py_DECREF(item); + + g_pydriver_state_prev.depsgraph = (BPy_StructRNA *)item; + } +} + void BPY_driver_reset(void) { PyGILState_STATE gilstate; @@ -226,6 +260,7 @@ void BPY_driver_reset(void) /* freed when clearing driver dict */ g_pydriver_state_prev.self = NULL; + g_pydriver_state_prev.depsgraph = NULL; if (use_gil) { PyGILState_Release(gilstate); @@ -369,41 +404,6 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d } #endif /* USE_BYTECODE_WHITELIST */ - -static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph) -{ - /* This should never happen, but it's probably better to have None in Python - * than a NULL-wrapping Depsgraph py struct. */ - BLI_assert(depsgraph != NULL); - if (depsgraph == NULL) { - Py_RETURN_NONE; - } - - struct PointerRNA depsgraph_ptr; - RNA_pointer_create(NULL, &RNA_Depsgraph, depsgraph, &depsgraph_ptr); - return pyrna_struct_CreatePyObject(&depsgraph_ptr); -} - -/** - * Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated - * data-blocks, and the current view layer and scene. See T75553. - */ -static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars, - struct Depsgraph *depsgraph) -{ - PyObject *py_depsgraph = bpy_pydriver_depsgraph_as_pyobject(depsgraph); - const char *depsgraph_variable_name = "depsgraph"; - - if (PyDict_SetItemString(driver_vars, depsgraph_variable_name, py_depsgraph) == -1) { - fprintf(stderr, - "\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n", - depsgraph_variable_name); - PyErr_Print(); - PyErr_Clear(); - } - Py_DECREF(py_depsgraph); -} - float BPY_driver_exec(struct PathResolvedRNA *anim_rna, ChannelDriver *driver, ChannelDriver *driver_orig, @@ -489,6 +489,8 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, bpy_pydriver_namespace_clear_self(); } + bpy_pydriver_namespace_update_depsgraph(anim_eval_context->depsgraph); + if (driver_orig->expr_comp == NULL) { driver_orig->flag |= DRIVER_FLAG_RECOMPILE; } @@ -613,8 +615,6 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, } #endif /* USE_BYTECODE_WHITELIST */ - bpy_pydriver_namespace_add_depsgraph(driver_vars, anim_eval_context->depsgraph); - #if 0 /* slow, with this can avoid all Py_CompileString above. */ /* execute expression to get a value */ retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars); diff --git a/source/blender/python/intern/bpy_intern_string.c b/source/blender/python/intern/bpy_intern_string.c index 1cf8f3b65a0..817494cb70a 100644 --- a/source/blender/python/intern/bpy_intern_string.c +++ b/source/blender/python/intern/bpy_intern_string.c @@ -14,7 +14,7 @@ #include "BLI_utildefines.h" -static PyObject *bpy_intern_str_arr[16]; +static PyObject *bpy_intern_str_arr[17]; PyObject *bpy_intern_str___annotations__; PyObject *bpy_intern_str___doc__; @@ -31,6 +31,7 @@ PyObject *bpy_intern_str_frame; PyObject *bpy_intern_str_properties; PyObject *bpy_intern_str_register; PyObject *bpy_intern_str_self; +PyObject *bpy_intern_str_depsgraph; PyObject *bpy_intern_str_unregister; void bpy_intern_string_init(void) @@ -58,6 +59,7 @@ void bpy_intern_string_init(void) BPY_INTERN_STR(bpy_intern_str_properties, "properties"); BPY_INTERN_STR(bpy_intern_str_register, "register"); BPY_INTERN_STR(bpy_intern_str_self, "self"); + BPY_INTERN_STR(bpy_intern_str_depsgraph, "depsgraph"); BPY_INTERN_STR(bpy_intern_str_unregister, "unregister"); #undef BPY_INTERN_STR diff --git a/source/blender/python/intern/bpy_intern_string.h b/source/blender/python/intern/bpy_intern_string.h index 1d74e8c31c5..cc8bf8a6f3b 100644 --- a/source/blender/python/intern/bpy_intern_string.h +++ b/source/blender/python/intern/bpy_intern_string.h @@ -28,6 +28,7 @@ extern PyObject *bpy_intern_str_frame; extern PyObject *bpy_intern_str_properties; extern PyObject *bpy_intern_str_register; extern PyObject *bpy_intern_str_self; +extern PyObject *bpy_intern_str_depsgraph; extern PyObject *bpy_intern_str_unregister; #ifdef __cplusplus -- cgit v1.2.3 From 3cb3167278a7f9d488b66a7fc591aea8288408e9 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 8 Mar 2022 13:15:41 +0100 Subject: Simplify sound property handling Sound properties like volume, pitch and muting are handled in `BKE_sound_add_scene_sound()`. This is unnecessary, because in properties are then set to real values in `SEQ_edit_update_muting()` and `seq_update_seq_cb()`. Alternatively, it may be better to remove all other updates leave them in `BKE_sound_add_scene_sound()`. But I want to add muting per channel, whhich is easier and probably cleaner to do with function `SEQ_edit_update_muting()`. Reviewed By: sergey Differential Revision: https://developer.blender.org/D14269 --- source/blender/blenkernel/intern/sound.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index fd9735ff07f..8b72fd05057 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -727,16 +727,11 @@ void *BKE_sound_add_scene_sound( } sound_verify_evaluated_id(&sequence->sound->id); const double fps = FPS; - void *handle = AUD_Sequence_add(scene->sound_scene, - sequence->sound->playback_handle, - startframe / fps, - endframe / fps, - frameskip / fps + sequence->sound->offset_time); - AUD_SequenceEntry_setMuted(handle, (sequence->flag & SEQ_MUTE) != 0); - AUD_SequenceEntry_setAnimationData(handle, AUD_AP_VOLUME, CFRA, &sequence->volume, 0); - AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PITCH, CFRA, &sequence->pitch, 0); - AUD_SequenceEntry_setAnimationData(handle, AUD_AP_PANNING, CFRA, &sequence->pan, 0); - return handle; + return AUD_Sequence_add(scene->sound_scene, + sequence->sound->playback_handle, + startframe / fps, + endframe / fps, + frameskip / fps + sequence->sound->offset_time); } void *BKE_sound_add_scene_sound_defaults(Scene *scene, Sequence *sequence) -- cgit v1.2.3 From 64a5fd7a1dce5ea4d24bcced42d885b165cc2d41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 8 Mar 2022 13:38:21 +0100 Subject: Fix wrong edge crease validity check in the Cycles Alembic procedural --- intern/cycles/scene/alembic_read.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/intern/cycles/scene/alembic_read.cpp b/intern/cycles/scene/alembic_read.cpp index 0ad3782360d..be652e9bcd6 100644 --- a/intern/cycles/scene/alembic_read.cpp +++ b/intern/cycles/scene/alembic_read.cpp @@ -469,7 +469,7 @@ static void add_subd_edge_creases(CachedData &cached_data, const SubDSchemaData &data, chrono_t time) { - if (!(data.crease_indices.valid() && data.crease_indices.valid() && + if (!(data.crease_indices.valid() && data.crease_lengths.valid() && data.crease_sharpnesses.valid())) { return; } -- cgit v1.2.3 From 3a672fe6fbdc22123b4c6342c5ac44ecf189eacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Mon, 7 Mar 2022 16:42:21 +0100 Subject: GPU: disable compute shader for problematic drivers Some drivers for legacy platforms seem to have issues with compute shaders, as revealed by T94936. This disables compute shader for the known drivers where this issue is present. It is not clear if the issue is Windows only or not, so this disable them for all operating systems. See T94936 for a list of configurations where the issue is reproducible or not. Differential Revision: https://developer.blender.org/D14264 --- source/blender/gpu/opengl/gl_backend.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/gpu/opengl/gl_backend.cc b/source/blender/gpu/opengl/gl_backend.cc index 92d180f1140..0a169f619bd 100644 --- a/source/blender/gpu/opengl/gl_backend.cc +++ b/source/blender/gpu/opengl/gl_backend.cc @@ -283,6 +283,11 @@ static void detect_workarounds() GCaps.shader_image_load_store_support = false; GCaps.broken_amd_driver = true; } + /* Compute shaders have some issues with those versions (see T94936). */ + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_OFFICIAL) && + (strstr(version, "4.5.14831") || strstr(version, "4.5.14760"))) { + GCaps.compute_shader_support = false; + } /* We have issues with this specific renderer. (see T74024) */ if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE) && (strstr(renderer, "AMD VERDE") || strstr(renderer, "AMD KAVERI") || -- cgit v1.2.3 From d5e4cab76c625c45b73868cf27021de1fada2830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dietrich?= Date: Tue, 8 Mar 2022 13:27:54 +0100 Subject: Fix T96224: GPU subdiv crash with smooth normals and tangents Tangents are computed from UVs on the CPU side when using GPU subdivision and require that the normals are available there as well (at least for smooth shading, flat normals can be computed on the fly). This simply adds the missing normals update call for the `MeshRenderData` setup for the subdivision case. Differential Revision: https://developer.blender.org/D14278 --- source/blender/draw/intern/draw_cache_extract_mesh.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index fb791916b46..c36a8ca5e2b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -870,6 +870,7 @@ static void mesh_buffer_cache_create_requested_subdiv(MeshBatchCache *cache, } mesh_render_data_update_looptris(mr, MR_ITER_LOOPTRI, MR_DATA_LOOPTRI); + mesh_render_data_update_normals(mr, MR_DATA_TAN_LOOP_NOR); mesh_render_data_update_loose_geom(mr, mbc, MR_ITER_LEDGE | MR_ITER_LVERT, MR_DATA_LOOSE_GEOM); DRW_subdivide_loose_geom(subdiv_cache, mbc); -- cgit v1.2.3 From 178f4fa71ebec87993094184ddfb98b7e6f74a86 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Tue, 8 Mar 2022 12:29:57 +0100 Subject: Cleanup: fix compiler warning --- source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index 874e29dda86..2fb9f724130 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -176,7 +176,7 @@ static void node_geo_exec(GeoNodeExecParams params) const GeometryNodeCurvePrimitiveCircleMode mode = (GeometryNodeCurvePrimitiveCircleMode) storage.mode; - Curves *curves; + Curves *curves = nullptr; if (mode == GEO_NODE_CURVE_PRIMITIVE_CIRCLE_TYPE_POINTS) { float3 center_point; curves = create_point_circle_curve(params.extract_input("Point 1"), -- cgit v1.2.3 From bea7d2e22dae905f7b57d3aeb1436e5a959fa976 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 8 Mar 2022 14:56:30 +0100 Subject: LibOverride: Do not assert when root ID is not object/collection. While uncommon, this is still a valid case... --- source/blender/blenkernel/intern/lib_override.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 392af2c9f5b..9b7f9d4f36d 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -1063,7 +1063,7 @@ static void lib_override_library_create_post_process(Main *bmain, break; } default: - BLI_assert(0); + break; } } if (default_instantiating_collection == NULL) { -- cgit v1.2.3 From 7bd3762b7cfc13336b08ac5915e21f1842b7ba24 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Tue, 8 Mar 2022 14:57:09 +0100 Subject: UI: Minor tweaks to IDTemplate operations. --- source/blender/editors/interface/interface_templates.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 32b3bb5e926..43968e2c986 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -686,7 +686,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL); RNA_property_update(C, &template_ui->ptr, template_ui->prop); - undo_push_label = "Override Data-Block"; + undo_push_label = "Make Local"; } break; case UI_ID_ALONE: @@ -1004,7 +1004,7 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_REDALERT); } - if (id->lib) { + if (ID_IS_LINKED(id)) { if (id->tag & LIB_TAG_INDIRECT) { but = uiDefIconBut(block, UI_BTYPE_BUT, -- cgit v1.2.3 From 5791835678067b5656469647990d769249065ee1 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Tue, 8 Mar 2022 16:09:44 +0100 Subject: Geometry Nodes: fix spline length node after recent refactor Differential Revision: https://developer.blender.org/D14276 --- .../blender/nodes/geometry/nodes/node_geo_input_spline_length.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc index f952e15fbbe..c3d87055745 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -25,13 +25,16 @@ static VArray construct_spline_length_gvarray(const CurveComponent &compo const std::unique_ptr curve = curves_to_curve_eval(*component.get_for_read()); Span splines = curve->splines(); - auto length_fn = [splines](int i) { return splines[i]->length(); }; + Array spline_lenghts(splines.size()); + for (const int i : splines.index_range()) { + spline_lenghts[i] = splines[i]->length(); + } if (domain == ATTR_DOMAIN_CURVE) { - return VArray::ForFunc(splines.size(), length_fn); + return VArray::ForContainer(std::move(spline_lenghts)); } if (domain == ATTR_DOMAIN_POINT) { - VArray length = VArray::ForFunc(splines.size(), length_fn); + VArray length = VArray::ForContainer(std::move(spline_lenghts)); return component.attribute_try_adapt_domain( std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT); } -- cgit v1.2.3 From e74838d0d011d5231b9809504c410b8bd75cfa8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Ch=C3=A9hab?= Date: Tue, 8 Mar 2022 16:37:57 +0100 Subject: GPencil: New "Additive" mode for build modifier The new mode only builds the new strokes in each frame. The code is assuming somebody uses "additive" drawing, so that each frame is different only in its NEW strokes. Already existing strokes are skipped. I used a simple solution: Count the number of strokes in the previous frame and ignore this many strokes in the current frame. Differential Revision: https://developer.blender.org/D14252 --- .../gpencil_modifiers/intern/MOD_gpencilbuild.c | 51 +++++++++++++++++----- .../blender/makesdna/DNA_gpencil_modifier_types.h | 2 + .../blender/makesrna/intern/rna_gpencil_modifier.c | 5 +++ 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index b1447bcaccb..e0da483310d 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -180,23 +180,42 @@ typedef struct tStrokeBuildDetails { int totpoints; } tStrokeBuildDetails; -/* Sequential - Show strokes one after the other */ +/* Sequential and additive - Show strokes one after the other. */ static void build_sequential(BuildGpencilModifierData *mmd, bGPdata *gpd, bGPDframe *gpf, - float fac) + float fac, + bool additive) { - const size_t tot_strokes = BLI_listbase_count(&gpf->strokes); + size_t tot_strokes = BLI_listbase_count(&gpf->strokes); + size_t start_stroke; bGPDstroke *gps; size_t i; - /* 1) Compute proportion of time each stroke should occupy */ + /* 1) Determine which strokes to start with & total strokes to build. */ + + if (additive) { + if (gpf->prev) { + start_stroke = BLI_listbase_count(&gpf->prev->strokes); + } else { + start_stroke = 0; + } + if (start_stroke <= tot_strokes) { + tot_strokes = tot_strokes - start_stroke; + } else { + start_stroke = 0; + } + } else { + start_stroke = 0; + } + + /* 2) Compute proportion of time each stroke should occupy */ /* NOTE: This assumes that the total number of points won't overflow! */ tStrokeBuildDetails *table = MEM_callocN(sizeof(tStrokeBuildDetails) * tot_strokes, __func__); size_t totpoints = 0; - /* 1.1) First pass - Tally up points */ - for (gps = gpf->strokes.first, i = 0; gps; gps = gps->next, i++) { + /* 2.1) First pass - Tally up points */ + for (gps = BLI_findlink(&gpf->strokes, start_stroke), i = 0; gps; gps = gps->next, i++) { tStrokeBuildDetails *cell = &table[i]; cell->gps = gps; @@ -205,7 +224,7 @@ static void build_sequential(BuildGpencilModifierData *mmd, totpoints += cell->totpoints; } - /* 1.2) Second pass - Compute the overall indices for points */ + /* 2.2) Second pass - Compute the overall indices for points */ for (i = 0; i < tot_strokes; i++) { tStrokeBuildDetails *cell = &table[i]; @@ -218,7 +237,7 @@ static void build_sequential(BuildGpencilModifierData *mmd, cell->end_idx = cell->start_idx + cell->totpoints - 1; } - /* 2) Determine the global indices for points that should be visible */ + /* 3) Determine the global indices for points that should be visible */ size_t first_visible = 0; size_t last_visible = 0; @@ -248,7 +267,7 @@ static void build_sequential(BuildGpencilModifierData *mmd, break; } - /* 3) Go through all strokes, deciding which to keep, and/or how much of each to keep */ + /* 4) Go through all strokes, deciding which to keep, and/or how much of each to keep */ for (i = 0; i < tot_strokes; i++) { tStrokeBuildDetails *cell = &table[i]; @@ -386,10 +405,14 @@ static void build_concurrent(BuildGpencilModifierData *mmd, } /* --------------------------------------------- */ + static void generate_geometry( GpencilModifierData *md, Depsgraph *depsgraph, bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf) { BuildGpencilModifierData *mmd = (BuildGpencilModifierData *)md; + if (mmd->mode == GP_BUILD_MODE_ADDITIVE) { + mmd->transition = GP_BUILD_TRANSITION_GROW; + } const bool reverse = (mmd->transition != GP_BUILD_TRANSITION_GROW); const bool is_percentage = (mmd->flag & GP_BUILD_PERCENTAGE); @@ -494,13 +517,17 @@ static void generate_geometry( /* Time management mode */ switch (mmd->mode) { case GP_BUILD_MODE_SEQUENTIAL: - build_sequential(mmd, gpd, gpf, fac); + build_sequential(mmd, gpd, gpf, fac, false); break; case GP_BUILD_MODE_CONCURRENT: build_concurrent(mmd, gpd, gpf, fac); break; + case GP_BUILD_MODE_ADDITIVE: + build_sequential(mmd, gpd, gpf, fac, true); + break; + default: printf("Unsupported build mode (%d) for GP Build Modifier: '%s'\n", mmd->mode, @@ -544,7 +571,9 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemS(layout); - uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE); + if (mode == GP_BUILD_MODE_SEQUENTIAL || mode == GP_BUILD_MODE_CONCURRENT) { + uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE); + } row = uiLayoutRow(layout, true); uiLayoutSetActive(row, !use_percentage); uiItemR(row, ptr, "start_delay", 0, NULL, ICON_NONE); diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index e30dd4e1c8f..0539b84e093 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -401,6 +401,8 @@ typedef enum eBuildGpencil_Mode { GP_BUILD_MODE_SEQUENTIAL = 0, /* All strokes start at the same time */ GP_BUILD_MODE_CONCURRENT = 1, + /* Only the new strokes are built */ + GP_BUILD_MODE_ADDITIVE = 2, } eBuildGpencil_Mode; typedef enum eBuildGpencil_Transition { diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index a619d179a33..45f98a2a88b 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2070,6 +2070,11 @@ static void rna_def_modifier_gpencilbuild(BlenderRNA *brna) ICON_PARTICLE_TIP, "Concurrent", "Multiple strokes appear/disappear at once"}, + {GP_BUILD_MODE_ADDITIVE, + "ADDITIVE", + ICON_PARTICLE_PATH, + "Additive", + "Builds only new strokes (assuming 'additive' drawing)."}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From 62885637fb5ffbe81efe0e03a2832c5f65d0f5fe Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 8 Mar 2022 16:41:30 +0100 Subject: Cleanup: Make format --- .../blender/gpencil_modifiers/intern/MOD_gpencilbuild.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index e0da483310d..6d9d9035c84 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -181,11 +181,8 @@ typedef struct tStrokeBuildDetails { } tStrokeBuildDetails; /* Sequential and additive - Show strokes one after the other. */ -static void build_sequential(BuildGpencilModifierData *mmd, - bGPdata *gpd, - bGPDframe *gpf, - float fac, - bool additive) +static void build_sequential( + BuildGpencilModifierData *mmd, bGPdata *gpd, bGPDframe *gpf, float fac, bool additive) { size_t tot_strokes = BLI_listbase_count(&gpf->strokes); size_t start_stroke; @@ -197,15 +194,18 @@ static void build_sequential(BuildGpencilModifierData *mmd, if (additive) { if (gpf->prev) { start_stroke = BLI_listbase_count(&gpf->prev->strokes); - } else { + } + else { start_stroke = 0; } if (start_stroke <= tot_strokes) { tot_strokes = tot_strokes - start_stroke; - } else { + } + else { start_stroke = 0; } - } else { + } + else { start_stroke = 0; } -- cgit v1.2.3 From 944122d49f5c7766bfd353874bdcaf3328079cd4 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 8 Mar 2022 16:44:03 +0100 Subject: Cleanup: Use ELEM macro --- source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c index 6d9d9035c84..1a69a6a8a38 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c @@ -571,7 +571,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemS(layout); - if (mode == GP_BUILD_MODE_SEQUENTIAL || mode == GP_BUILD_MODE_CONCURRENT) { + if (ELEM(mode, GP_BUILD_MODE_SEQUENTIAL, GP_BUILD_MODE_CONCURRENT)) { uiItemR(layout, ptr, "transition", 0, NULL, ICON_NONE); } row = uiLayoutRow(layout, true); -- cgit v1.2.3 From c305b88aacb1248cdd82df486954046824da34f6 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 8 Mar 2022 16:55:19 +0100 Subject: Docs: 3.1 release description for Linux appdata --- release/freedesktop/org.blender.Blender.appdata.xml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/release/freedesktop/org.blender.Blender.appdata.xml b/release/freedesktop/org.blender.Blender.appdata.xml index 4b413f6b38d..12291860050 100644 --- a/release/freedesktop/org.blender.Blender.appdata.xml +++ b/release/freedesktop/org.blender.Blender.appdata.xml @@ -40,6 +40,27 @@ + + +

New features:

+
    +
  • GPU acceleration for the Subdivision modifier
  • +
  • Cycles Metal GPU backend, contributed by Apple
  • +
  • Point Cloud rendering
  • +
  • More Geometry Nodes, including extrude mesh
  • +
+

Enhancements:

+
    +
  • Faster and less memory usage in geometry nodes
  • +
  • Grease Pencil Dilate/Contract fill
  • +
  • Vertex Creasing support
  • +
  • Faster .obj and .fbx export
  • +
  • Image editor is able to handle large images
  • +
  • Python 3.10
  • +
  • User Interface updates
  • +
+
+

New features:

-- cgit v1.2.3 From 50bd8bb175f022df5d3d016d8ac09de5352f3bfa Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Tue, 8 Mar 2022 16:55:29 +0100 Subject: Cleanup: Remove extra . at the end of RNA string The strings must not end with point. --- source/blender/makesrna/intern/rna_gpencil_modifier.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 45f98a2a88b..33c0b29f6d0 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -2074,7 +2074,7 @@ static void rna_def_modifier_gpencilbuild(BlenderRNA *brna) "ADDITIVE", ICON_PARTICLE_PATH, "Additive", - "Builds only new strokes (assuming 'additive' drawing)."}, + "Builds only new strokes (assuming 'additive' drawing)"}, {0, NULL, 0, NULL, NULL}, }; -- cgit v1.2.3 From b217eb73f58dc2a159bd464140fb4900e0c363a9 Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 8 Mar 2022 18:26:50 +0100 Subject: Licenses: Attribution document for Blender 3.1 --- release/license/THIRD-PARTY-LICENSES.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/release/license/THIRD-PARTY-LICENSES.txt b/release/license/THIRD-PARTY-LICENSES.txt index 2bd4ac23d6d..933f03b5564 100644 --- a/release/license/THIRD-PARTY-LICENSES.txt +++ b/release/license/THIRD-PARTY-LICENSES.txt @@ -318,7 +318,7 @@ Copyright (c) 2006, Google Inc. All rights reserved. ** ISPC; version 1.16.0 -- https://github.com/ispc/ispc Copyright Intel Corporation -** NumPy; version 1.21.2 -- https://numpy.org/ +** NumPy; version 1.22.0 -- https://numpy.org/ Copyright (c) 2005-2021, NumPy Developers. ** Open Shading Language; version 1.11.14.1 -- https://github.com/imageworks/OpenShadingLanguage @@ -822,7 +822,7 @@ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ** FFTW; version 3.3.8 -- http://www.fftw.org/ Copyright (c) 2003, 2007-14 Matteo Frigo Copyright (c) 2003, 2007-14 Massachusetts Institute of Technology -** GMP; version 6.2.0 -- https://gmplib.org/ +** GMP; version 6.2.1 -- https://gmplib.org/ Copyright 1996-2020 Free Software Foundation, Inc. ** OpenAL; version 1.20.1 -- http://openal-soft.org Copyright (c) 2015, Archontis Politis @@ -1162,7 +1162,7 @@ Copyright (C) 2003-2021 x264 project ** miniLZO; version 2.08 -- http://www.oberhumer.com/opensource/lzo/ LZO and miniLZO are Copyright (C) 1996-2014 Markus Franz Xaver Oberhumer All Rights Reserved. -** The FreeType Project; version 2.10.2 -- +** The FreeType Project; version 2.11.1 -- https://sourceforge.net/projects/freetype Copyright (C) 1996-2020 by David Turner, Robert Wilhelm, and Werner Lemberg. ** X Drag and Drop; version 2000-08-08 -- @@ -1174,7 +1174,7 @@ Project initiators: Christoph Lampert Michael Militzer Peter Ross -** Zstandard; version 1.5.0 -- https://github.com/facebook/zstd +** Zstandard; version 1.6.0 -- https://github.com/facebook/zstd Copyright (c) 2016-present, Facebook, Inc. All rights reserved. GNU GENERAL PUBLIC LICENSE @@ -2956,6 +2956,8 @@ December 9, 2010 ------ +** Brotli; version 1.0.9 -- https://github.com/google/brotli +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors. ** Expat; version 2.2.10 -- https://github.com/libexpat/libexpat/ Copyright (c) 1998-2000 Thai Open Source Software Center Ltd and Clark Cooper Copyright (c) 2001-2019 Expat maintainers @@ -3627,7 +3629,7 @@ disclaims all warranties with regard to this software. ------ -** Python; version 3.9.7 -- https://www.python.org +** Python; version 3.10.2 -- https://www.python.org Copyright (c) 2001-2021 Python Software Foundation. All rights reserved. A. HISTORY OF THE SOFTWARE -- cgit v1.2.3 From 7add8163e4b628767c546dae687333225c78c8c0 Mon Sep 17 00:00:00 2001 From: Thomas Dinges Date: Tue, 8 Mar 2022 18:36:00 +0100 Subject: Blender 3.1 - version bump -> release --- source/blender/blenkernel/BKE_blender_version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 3f290e64447..f97825c6626 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -35,7 +35,7 @@ extern "C" { /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ -#define BLENDER_VERSION_CYCLE rc +#define BLENDER_VERSION_CYCLE release /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -- cgit v1.2.3 From c77597cd0e15f9d7b6f963593b545cc94950eb8d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 8 Mar 2022 12:16:20 -0600 Subject: Fix T95843: Invalid mesh runtime data after sculpt undo When the geometry of the sculpt mesh was replaced when restoring from a full undo step, the runtime data was not cleared (including any normals, triangulation data, or any other cached derived data). In the report, only the invalid normals were observed. The fix is to simply clear these caches. Later they will be reallocated and recalculated if necessary. Since the whole mesh replaced here anyway, this should be a safe fix. Differential Revision: https://developer.blender.org/D14282 --- source/blender/editors/sculpt_paint/sculpt_undo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 8819496c168..f740f38fdc6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -47,6 +47,7 @@ #include "BKE_key.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" #include "BKE_multires.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -547,6 +548,8 @@ static void sculpt_undo_geometry_restore_data(SculptUndoNodeGeometry *geometry, &geometry->pdata, &mesh->pdata, CD_MASK_MESH.pmask, CD_DUPLICATE, geometry->totpoly); BKE_mesh_update_customdata_pointers(mesh, false); + + BKE_mesh_runtime_clear_cache(mesh); } static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry) -- cgit v1.2.3 From 90c3147e05b31000ad6ca2f9db1d1b0907c7acd6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 8 Mar 2022 12:31:21 -0600 Subject: Cleanup: Correct comment Normals aren't stored in custom data anymore, nor are they stored in MVert for comparisons. --- source/blender/blenkernel/intern/mesh.cc | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 37564f9334f..824c1ab1b90 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -142,11 +142,11 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int mesh_dst->mselect = (MSelect *)MEM_dupallocN(mesh_dst->mselect); - /* Set normal layers dirty, since they aren't included in CD_MASK_MESH and are therefore not - * copied to the destination mesh. Alternatively normal layers could be copied if they aren't - * dirty, avoiding recomputation in some cases. However, a copied mesh is often changed anyway, - * so that idea is not clearly better. With proper reference counting, all custom data layers - * could be copied as the cost would be much lower. */ + /* Set normal layers dirty. They should be dirty by default on new meshes anyway, but being + * explicit about it is safer. Alternatively normal layers could be copied if they aren't dirty, + * avoiding recomputation in some cases. However, a copied mesh is often changed anyway, so that + * idea is not clearly better. With proper reference counting, all custom data layers could be + * copied as the cost would be much lower. */ BKE_mesh_normals_tag_dirty(mesh_dst); /* TODO: Do we want to add flag to prevent this? */ @@ -510,7 +510,6 @@ static int customdata_compare( return MESHCMP_VERTCOMISMATCH; } } - /* I don't care about normals, let's just do coordinates. */ } break; } -- cgit v1.2.3 From d09695b4dd0eca917004177d30df9e09d9f8732d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 8 Mar 2022 13:28:22 -0600 Subject: Cleanup: Clang tidy --- source/blender/depsgraph/intern/depsgraph_tag.cc | 2 +- .../blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc | 2 +- source/blender/editors/sculpt_paint/curves_sculpt_ops.cc | 2 +- source/blender/editors/space_outliner/outliner_draw.cc | 6 ++---- source/blender/editors/uvedit/uvedit_ops.c | 13 ++++++++----- source/blender/editors/uvedit/uvedit_select.c | 2 +- 6 files changed, 14 insertions(+), 13 deletions(-) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index b8c85430f06..9551a00cf95 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -483,7 +483,7 @@ void deg_graph_node_tag_zero(Main *bmain, if (comp_node->type == NodeType::ANIMATION) { continue; } - else if (comp_node->type == NodeType::COPY_ON_WRITE) { + if (comp_node->type == NodeType::COPY_ON_WRITE) { id_node->is_cow_explicitly_tagged = true; } diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index b1635ce6e09..19022d534b2 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -739,7 +739,7 @@ void update_id_after_copy(const Depsgraph *depsgraph, case ID_GD: { bGPdata *gpd_cow = (bGPdata *)id_cow; bGPDlayer *gpl = (bGPDlayer *)(gpd_cow->layers.first); - if (gpl != NULL && gpl->runtime.gpl_orig == NULL) { + if (gpl != nullptr && gpl->runtime.gpl_orig == nullptr) { BKE_gpencil_data_update_orig_pointers((bGPdata *)id_orig, gpd_cow); } break; diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 4bf81b72c3c..4dee7641623 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -518,7 +518,7 @@ class AddOperation : public CurvesSculptStrokeOperation { }; public: - ~AddOperation() + ~AddOperation() override { if (old_kdtree_ != nullptr) { BLI_kdtree_3d_free(old_kdtree_); diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 53de7d582b6..04d4da5e62f 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -2348,10 +2348,8 @@ static BIFIconID tree_element_get_icon_from_id(const ID *id) if (text->filepath == nullptr || (text->flags & TXT_ISMEM)) { return ICON_FILE_TEXT; } - else { - /* Helps distinguish text-based formats like the file-browser does. */ - return (BIFIconID)ED_file_extension_icon(text->filepath); - } + /* Helps distinguish text-based formats like the file-browser does. */ + return (BIFIconID)ED_file_extension_icon(text->filepath); } case ID_GR: return ICON_OUTLINER_COLLECTION; diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index bbf84ee74d7..fe6f9f0d513 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1518,11 +1518,11 @@ static int uv_hide_exec(bContext *C, wmOperator *op) BM_face_select_set(em->bm, efa, false); break; } - else if (UV_VERT_SEL_TEST(luv, !swap) && (ts->uv_selectmode == UV_SELECT_VERTEX)) { + if (UV_VERT_SEL_TEST(luv, !swap) && (ts->uv_selectmode == UV_SELECT_VERTEX)) { BM_face_select_set(em->bm, efa, false); break; } - else if (ts->uv_selectmode == UV_SELECT_ISLAND) { + if (ts->uv_selectmode == UV_SELECT_ISLAND) { BM_face_select_set(em->bm, efa, false); break; } @@ -1533,18 +1533,21 @@ static int uv_hide_exec(bContext *C, wmOperator *op) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); if (UV_EDGE_SEL_TEST(luv, !swap) && (ts->uv_selectmode == UV_SELECT_EDGE)) { - if (em->selectmode == SCE_SELECT_EDGE) + if (em->selectmode == SCE_SELECT_EDGE) { BM_edge_select_set(em->bm, l->e, false); + } else { BM_vert_select_set(em->bm, l->v, false); BM_vert_select_set(em->bm, l->next->v, false); } } else if (UV_VERT_SEL_TEST(luv, !swap) && (ts->uv_selectmode != UV_SELECT_EDGE)) { - if (em->selectmode == SCE_SELECT_EDGE) + if (em->selectmode == SCE_SELECT_EDGE) { BM_edge_select_set(em->bm, l->e, false); - else + } + else { BM_vert_select_set(em->bm, l->v, false); + } } } if (!swap) { diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 2728c29141a..3bba618f682 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -1720,7 +1720,7 @@ static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, c if (select && uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) { break; } - else if (!select && !uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) { + if (!select && !uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) { break; } } -- cgit v1.2.3 From 156e07232e79d53fa3f43d4bcfc4b0c4061331e5 Mon Sep 17 00:00:00 2001 From: Hallam Roberts Date: Tue, 8 Mar 2022 14:39:58 -0600 Subject: Geometry Nodes: Tiny optimization to UV Sphere primitive Prevents a few unneeded calls to `std::sin`, with an observed performance improvement of about 1 percent. Differential Revision: https://developer.blender.org/D14279 --- .../blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 4a09fd8d1d2..4e0e5c7c912 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -76,10 +76,10 @@ static void calculate_sphere_vertex_data(MutableSpan verts, int vert_index = 1; for (const int ring : IndexRange(1, rings - 1)) { const float theta = ring * delta_theta; + const float sin_theta = std::sin(theta); const float z = std::cos(theta); for (const int segment : IndexRange(1, segments)) { const float phi = segment * delta_phi; - const float sin_theta = std::sin(theta); const float x = sin_theta * std::cos(phi); const float y = sin_theta * std::sin(phi); copy_v3_v3(verts[vert_index].co, float3(x, y, z) * radius); -- cgit v1.2.3 From b8960267dd51f9108b3b49e9b762e6b4d35ae1dc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 08:36:36 +1100 Subject: Event System: drag events no longer default to the drag start location This avoids transform jumping which is a problem when tweaking values a small amount. A fix for T40549 was made box-select used the location when the key was pressed. While it's important for box-select or any operator where it's expected the drag-start location is used, this is only needed in some cases. Since the event stores the click location and the current location, no longer overwrite the events real location. Operators that depend on using the drag-start can use this location if they need. In some cases the region relative cursor location (Event.mval) now needs to be calculated based on the click location. - Added `WM_event_drag_start_mval` for convenient access to the region relative drag-start location (for drag events). - Added `WM_event_drag_start_xy` for window relative coordinates. - Added Python property Event.mouse_prev_click_x/y Resolves T93599. Reviewed By: Severin Ref D14213 --- source/blender/editors/interface/interface_handlers.c | 8 ++++++-- source/blender/editors/space_action/action_select.c | 8 ++++++-- source/blender/editors/space_graph/graph_select.c | 4 +++- source/blender/editors/space_node/node_edit.cc | 8 ++++++-- .../blender/editors/space_node/node_relationships.cc | 5 ++++- source/blender/editors/space_node/node_select.cc | 6 +++++- .../editors/space_outliner/outliner_dragdrop.cc | 11 ++++++++--- .../blender/editors/space_outliner/outliner_edit.cc | 7 +++++-- .../blender/editors/space_view3d/view3d_gizmo_ruler.c | 10 +++++++--- .../blender/editors/space_view3d/view3d_placement.c | 7 +++++-- source/blender/editors/transform/transform.c | 16 +++++++++++++++- source/blender/editors/transform/transform.h | 3 +++ source/blender/editors/transform/transform_generics.c | 7 ++++++- source/blender/makesrna/intern/rna_wm.c | 14 ++++++++++++++ source/blender/windowmanager/WM_api.h | 3 +++ source/blender/windowmanager/WM_types.h | 4 ++++ .../blender/windowmanager/gizmo/intern/wm_gizmo_map.c | 12 ++++++++++-- source/blender/windowmanager/intern/wm_event_query.c | 19 +++++++++++++++++++ source/blender/windowmanager/intern/wm_event_system.c | 5 ++--- source/blender/windowmanager/intern/wm_gesture.c | 15 +++++++++------ source/blender/windowmanager/intern/wm_operators.c | 9 +++++++-- 21 files changed, 147 insertions(+), 34 deletions(-) diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 8262be163b7..f5fa710067b 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -9394,7 +9394,9 @@ static int ui_list_activate_hovered_row(bContext *C, } } - const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; + int mouse_xy[2]; + WM_event_drag_start_xy(event, mouse_xy); + uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9421,7 +9423,9 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, const wmEvent *event) { /* On a tweak event, uses the coordinates from where tweaking was started. */ - const int *mouse_xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; + int mouse_xy[2]; + WM_event_drag_start_xy(event, mouse_xy); + const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 65815983767..b972ccbaf89 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -511,8 +511,12 @@ static int actkeys_box_select_invoke(bContext *C, wmOperator *op, const wmEvent } bool tweak = RNA_boolean_get(op->ptr, "tweak"); - if (tweak && actkeys_is_key_at_position(&ac, event->mval[0], event->mval[1])) { - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + if (tweak) { + int mval[2]; + WM_event_drag_start_mval(event, ac.region, mval); + if (actkeys_is_key_at_position(&ac, mval[0], mval[1])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } } return WM_gesture_box_invoke(C, op, event); diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 2ed684c6e97..39b980ac4c3 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -799,7 +799,9 @@ static int graphkeys_box_select_invoke(bContext *C, wmOperator *op, const wmEven } if (RNA_boolean_get(op->ptr, "tweak")) { - tNearestVertInfo *under_mouse = find_nearest_fcurve_vert(&ac, event->mval); + int mval[2]; + WM_event_drag_start_mval(event, ac.region, mval); + tNearestVertInfo *under_mouse = find_nearest_fcurve_vert(&ac, mval); bool mouse_is_over_element = under_mouse != NULL; if (under_mouse) { MEM_freeN(under_mouse); diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index b30be6ae0af..ab1f509af47 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -955,8 +955,10 @@ static int node_resize_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { + int mval[2]; + WM_event_drag_start_mval(event, region, mval); float mx, my; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mx, &my); + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &mx, &my); float dx = (mx - nsw->mxstart) / UI_DPI_FAC; float dy = (my - nsw->mystart) / UI_DPI_FAC; @@ -1057,7 +1059,9 @@ static int node_resize_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* convert mouse coordinates to v2d space */ float cursor[2]; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &cursor[0], &cursor[1]); const NodeResizeDirection dir = node_get_resize_direction(node, cursor[0], cursor[1]); if (dir == NODE_RESIZE_NONE) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 299e7e5658c..2eae1ce240c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -1177,8 +1177,11 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) bool detach = RNA_boolean_get(op->ptr, "detach"); + int mval[2]; + WM_event_drag_start_mval(event, ®ion, mval); + float2 cursor; - UI_view2d_region_to_view(®ion.v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); + UI_view2d_region_to_view(®ion.v2d, mval[0], mval[1], &cursor[0], &cursor[1]); RNA_float_set_array(op->ptr, "drag_start", cursor); RNA_boolean_set(op->ptr, "has_link_picked", false); diff --git a/source/blender/editors/space_node/node_select.cc b/source/blender/editors/space_node/node_select.cc index a355487057c..1d0097068f1 100644 --- a/source/blender/editors/space_node/node_select.cc +++ b/source/blender/editors/space_node/node_select.cc @@ -185,7 +185,11 @@ static bool is_event_over_node_or_socket(bContext *C, const wmEvent *event) SpaceNode *snode = CTX_wm_space_node(C); ARegion *region = CTX_wm_region(C); float2 mouse; - UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &mouse.x, &mouse.y); + + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &mouse.x, &mouse.y); return is_position_over_node_or_socket(*snode, mouse); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 735625f679e..7a2f56eaaaa 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -1360,7 +1360,10 @@ static TreeElement *outliner_item_drag_element_find(SpaceOutliner *space_outline { /* NOTE: using click-drag events to trigger dragging is fine, * it sends coordinates from where dragging was started */ - const float my = UI_view2d_region_to_view_y(®ion->v2d, event->mval[1]); + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + + const float my = UI_view2d_region_to_view_y(®ion->v2d, mval[1]); return outliner_find_item_at_y(space_outliner, &space_outliner->tree, my); } @@ -1372,6 +1375,9 @@ static int outliner_item_drag_drop_invoke(bContext *C, SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); TreeElement *te = outliner_item_drag_element_find(space_outliner, region, event); + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + if (!te) { return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } @@ -1383,8 +1389,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, } float view_mval[2]; - UI_view2d_region_to_view( - ®ion->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); if (outliner_item_is_co_within_close_toggle(te, view_mval[0])) { return (OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH); } diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 6916f5fe502..8a02c3b64f2 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -225,8 +225,11 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE const bool toggle_all = RNA_boolean_get(op->ptr, "all"); float view_mval[2]; - UI_view2d_region_to_view( - ®ion->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 437c2a9a027..224c80bdd48 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -1082,7 +1082,8 @@ static int gizmo_ruler_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) ARegion *region = ruler_info->region; - const float mval_fl[2] = {UNPACK2(event->mval)}; + float mval_fl[2]; + WM_event_drag_start_mval_fl(event, region, mval_fl); #ifdef USE_AXIS_CONSTRAINTS ruler_info->constrain_axis = CONSTRAIN_AXIS_NONE; @@ -1311,6 +1312,9 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e return OPERATOR_CANCELLED; } + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + /* Create new line */ RulerItem *ruler_item; ruler_item = ruler_item_add(gzgroup); @@ -1329,7 +1333,7 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e depsgraph, ruler_info, ruler_item, - event->mval, + mval, false #ifndef USE_SNAP_DETECT_FROM_KEYMAP_HACK , @@ -1344,7 +1348,7 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e else { negate_v3_v3(inter->drag_start_co, rv3d->ofs); copy_v3_v3(ruler_item->co[0], inter->drag_start_co); - view3d_ruler_item_project(ruler_info, ruler_item->co[0], event->mval); + view3d_ruler_item_project(ruler_info, ruler_item->co[0], mval); } copy_v3_v3(ruler_item->co[2], ruler_item->co[0]); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 98fb914cda9..1603539cf94 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -733,9 +733,11 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv * the current cursor location instead of the drag-start. */ if (event->val == KM_CLICK_DRAG) { /* Set this flag so snapping always updated. */ + int mval[2]; + WM_event_drag_start_mval(event, ipd->region, mval); int flag_orig = snap_state_new->flag; snap_state_new->flag |= V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE; - ED_view3d_cursor_snap_data_get(snap_state_new, C, event->mval[0], event->mval[1]); + ED_view3d_cursor_snap_data_get(snap_state_new, C, mval[0], mval[1]); snap_state_new->flag = flag_orig; } } @@ -1168,7 +1170,8 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve } if (do_cursor_update) { - const float mval_fl[2] = {UNPACK2(event->mval)}; + float mval_fl[2]; + WM_event_drag_start_mval_fl(event, region, mval_fl); /* Calculate the snap location on mouse-move or when toggling snap. */ ipd->is_snap_found = false; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index fd01f708ed2..9810f1cb6ad 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1706,6 +1706,12 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->launch_event = LEFTMOUSE; } + if (options & CTX_CURSOR) { + /* Cursor should always use the drag start as the combination of click-drag to place & move + * doesn't work well if the click location isn't used when transforming. */ + t->flag |= T_EVENT_DRAG_START; + } + unit_m3(t->spacemtx); initTransInfo(C, t, op, event); @@ -1819,7 +1825,15 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve use_accurate = true; } } - initMouseInput(t, &t->mouse, t->center2d, event->mval, use_accurate); + + int mval[2]; + if (t->flag & T_EVENT_DRAG_START) { + WM_event_drag_start_mval(event, t->region, mval); + } + else { + copy_v2_v2_int(mval, event->mval); + } + initMouseInput(t, &t->mouse, t->center2d, mval, use_accurate); } transform_mode_init(t, op, mode); diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 3ee5868d5be..21592032af2 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -137,6 +137,9 @@ typedef enum { /** Runs auto-merge & splits. */ T_AUTOSPLIT = 1 << 21, + /** Use drag-start position of the event, otherwise use the cursor coordinates (unmodified). */ + T_EVENT_DRAG_START = (1 << 22), + /** No cursor wrapping on region bounds */ T_NO_CURSOR_WRAP = 1 << 23, } eTFlag; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 8987325145c..776229d66a0 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -216,7 +216,12 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve int mval[2]; if (event) { - copy_v2_v2_int(mval, event->mval); + if (t->flag & T_EVENT_DRAG_START) { + WM_event_drag_start_mval(event, region, mval); + } + else { + copy_v2_v2_int(mval, event->mval); + } } else { zero_v2_int(mval); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index dcba0ff574b..bc879d8254a 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2102,6 +2102,20 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Mouse Previous Y Position", "The window relative vertical location of the mouse"); + prop = RNA_def_property(srna, "mouse_prev_press_x", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "prev_click_xy[0]"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Mouse Previous X Press Position", + "The window relative horizontal location of the last press event"); + + prop = RNA_def_property(srna, "mouse_prev_press_y", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "prev_click_xy[1]"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Mouse Previous Y Press Position", + "The window relative vertical location of the last press event"); + prop = RNA_def_property(srna, "pressure", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_default(prop, 1.0f); RNA_def_property_clear_flag(prop, PROP_EDITABLE); diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index aa380928082..bc51f7d065a 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -1453,6 +1453,9 @@ bool WM_cursor_test_motion_and_update(const int mval[2]) ATTR_NONNULL(1) ATTR_WA int WM_event_drag_threshold(const struct wmEvent *event); bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); bool WM_event_drag_test_with_delta(const struct wmEvent *event, const int delta[2]); +void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2]); +void WM_event_drag_start_mval_fl(const wmEvent *event, const ARegion *region, float r_mval[2]); +void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2]); /** * Event map that takes preferences into account. diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index b2296ced923..3d8577933cf 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -267,6 +267,10 @@ enum { KM_RELEASE = 2, KM_CLICK = 3, KM_DBL_CLICK = 4, + /** + * \note The cursor location at the point dragging starts is set to #wmEvent.prev_click_xy + * some operators such as box selection should use this location instead of #wmEvent.xy. + */ KM_CLICK_DRAG = 5, }; diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index f1ac19f4651..44b7001e054 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -815,6 +815,14 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, BLI_buffer_declare_static(wmGizmo *, visible_3d_gizmos, BLI_BUFFER_NOP, 128); bool do_step[WM_GIZMOMAP_DRAWSTEP_MAX]; + int mval[2]; + if (event->val == KM_CLICK_DRAG) { + WM_event_drag_start_mval(event, CTX_wm_region(C), mval); + } + else { + copy_v2_v2_int(mval, event->mval); + } + for (int i = 0; i < ARRAY_SIZE(do_step); i++) { do_step[i] = WM_gizmo_context_check_drawstep(C, i); } @@ -841,7 +849,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, } else if (step == WM_GIZMOMAP_DRAWSTEP_2D) { if ((gz = wm_gizmogroup_find_intersected_gizmo( - wm, gzgroup, C, event->modifier, event->mval, r_part))) { + wm, gzgroup, C, event->modifier, mval, r_part))) { break; } } @@ -853,7 +861,7 @@ wmGizmo *wm_gizmomap_highlight_find(wmGizmoMap *gzmap, /* 2D gizmos get priority. */ if (gz == NULL) { gz = gizmo_find_intersected_3d( - C, event->mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part); + C, mval, visible_3d_gizmos.data, visible_3d_gizmos.count, r_part); } } BLI_buffer_free(&visible_3d_gizmos); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index ee13e1832ed..c9f83a5d811 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -338,6 +338,25 @@ bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) return WM_event_drag_test_with_delta(event, drag_delta); } +void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2]) +{ + const int *xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; + r_mval[0] = xy[0] - region->winrct.xmin; + r_mval[1] = xy[1] - region->winrct.ymin; +} + +void WM_event_drag_start_mval_fl(const wmEvent *event, const ARegion *region, float r_mval[2]) +{ + const int *xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; + r_mval[0] = xy[0] - region->winrct.xmin; + r_mval[1] = xy[1] - region->winrct.ymin; +} + +void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2]) +{ + copy_v2_v2_int(r_xy, (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index cc98f7a08a6..ee37fc0bea3 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3169,13 +3169,13 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) win->event_queue_check_drag_handled = true; const int direction = WM_event_drag_direction(event); - const int prev_xy[2] = {UNPACK2(event->xy)}; + /* Intentionally leave `event->xy` as-is, event users are expected to use + * `event->prev_click_xy` if they need to access the drag start location. */ const short prev_val = event->val; const short prev_type = event->type; const uint8_t prev_modifier = event->modifier; const short prev_keymodifier = event->keymodifier; - copy_v2_v2_int(event->xy, event->prev_click_xy); event->val = KM_CLICK_DRAG; event->type = event->prev_click_type; event->modifier = event->prev_click_modifier; @@ -3191,7 +3191,6 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) event->modifier = prev_modifier; event->val = prev_val; event->type = prev_type; - copy_v2_v2_int(event->xy, prev_xy); win->event_queue_check_click = false; if (!((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action))) { diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index a6fbad8b171..ec01e097e98 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -49,6 +49,9 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->modal_state = GESTURE_MODAL_NOP; gesture->move = false; + int xy[2]; + WM_event_drag_start_xy(event, xy); + if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, @@ -57,14 +60,14 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new"); gesture->customdata = rect; - rect->xmin = event->xy[0] - gesture->winrct.xmin; - rect->ymin = event->xy[1] - gesture->winrct.ymin; + rect->xmin = xy[0] - gesture->winrct.xmin; + rect->ymin = xy[1] - gesture->winrct.ymin; if (type == WM_GESTURE_CIRCLE) { /* caller is responsible for initializing 'xmax' to radius. */ } else { - rect->xmax = event->xy[0] - gesture->winrct.xmin; - rect->ymax = event->xy[1] - gesture->winrct.ymin; + rect->xmax = xy[0] - gesture->winrct.xmin; + rect->ymax = xy[1] - gesture->winrct.ymin; } } else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) { @@ -72,8 +75,8 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->points_alloc = 1024; gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points"); - lasso[0] = event->xy[0] - gesture->winrct.xmin; - lasso[1] = event->xy[1] - gesture->winrct.ymin; + lasso[0] = xy[0] - gesture->winrct.xmin; + lasso[1] = xy[1] - gesture->winrct.ymin; gesture->points = 1; } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 0dce8b8f000..2f5eef652f2 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -956,8 +956,13 @@ int WM_generic_select_modal(bContext *C, wmOperator *op, const wmEvent *event) int WM_generic_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - RNA_int_set(op->ptr, "mouse_x", event->mval[0]); - RNA_int_set(op->ptr, "mouse_y", event->mval[1]); + ARegion *region = CTX_wm_region(C); + + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + + RNA_int_set(op->ptr, "mouse_x", mval[0]); + RNA_int_set(op->ptr, "mouse_y", mval[1]); op->customdata = POINTER_FROM_INT(0); -- cgit v1.2.3 From 521d4190a09b1117f4f729991a0c636be4c2b8f3 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 8 Mar 2022 15:45:17 -0600 Subject: Fix T96146: Subdivide curve node uninitialized dangling handles Handles of non-cyclic bezier curves were not initialized. Now properly copy the dangling handle positions and types from the source curve. --- source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 6456af5f295..371556c04f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -206,6 +206,10 @@ static void subdivide_bezier_spline(const BezierSpline &src, } else { dst_positions.last() = src_positions.last(); + dst_type_left.first() = src.handle_types_left().first(); + dst_type_right.last() = src.handle_types_right().last(); + dst_handles_left.first() = src_handles_left.first(); + dst_handles_right.last() = src_handles_right.last(); } } -- cgit v1.2.3 From eb326c7b402e4914e9a619b899a388be7668255b Mon Sep 17 00:00:00 2001 From: Aleksi Juvani Date: Tue, 8 Mar 2022 15:51:53 -0600 Subject: Attributes: Implement CustomData interpolation for boolean data type This commit fixes an issue, where for instance, when merging vertices with the "Merge by Distance" geometry node, the resulting vertices had their boolean attributes set unpredictably. Boolean attributes are implemented as custom data, and when welding vertices, the custom data for the resulting vertices comes from interpolating the custom data of the source vertices. This commit implements the missing interpolation function for the boolean custom data type. This interpolation function is implemented in terms of the logical or operation, that is to say, if any of the source vertices (with a weight greater than zero) have the boolean set, the boolean will also be set on the resulting vertex. This logic matches 95981c9876483256b28. In geometry nodes, attribute interpolation generally does not use the CustomData API for performance reasons, but other areas of Blender still do. Differential Revision: https://developer.blender.org/D14172 --- source/blender/blenkernel/intern/customdata.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 4492f8bbc64..b348e18a6a8 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -1450,6 +1450,21 @@ static bool layerValidate_propfloat2(void *data, const uint totitems, const bool return has_errors; } +static void layerInterp_propbool(const void **sources, + const float *weights, + const float *UNUSED(sub_weights), + int count, + void *dest) +{ + bool result = false; + for (int i = 0; i < count; i++) { + const float interp_weight = weights[i]; + const bool src = *(const bool *)sources[i]; + result |= src && (interp_weight > 0.0f); + } + *(bool *)dest = result; +} + static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { /* 0: CD_MVERT */ {sizeof(MVert), "MVert", 1, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}, @@ -1838,7 +1853,7 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { N_("Boolean"), nullptr, nullptr, - nullptr, + layerInterp_propbool, nullptr, nullptr, nullptr, -- cgit v1.2.3 From 353376c783390a158c0a3cedf9887a4df06c7cf1 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Tue, 8 Mar 2022 13:54:19 -0800 Subject: Fix T92331: Duplicated Window Should Not Be Modal Operator area_dupli_invoke should not create modal windows. See D14253 for details. Differential Revision: https://developer.blender.org/D14253 Reviewed by Brecht Van Lommel --- source/blender/editors/screen/screen_ops.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index ee3bc3cba76..9343f4a3b34 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1421,7 +1421,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) area->winy, SPACE_EMPTY, false, - true, + false, false, WIN_ALIGN_ABSOLUTE); -- cgit v1.2.3 From 626c844105afc3b2455f5a1b87ac8a7839a7e0bf Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 09:01:28 +1100 Subject: Cleanup: rename wmEvent.prev_click_* to prev_press_* At the time of naming these members only some event types generated click events so it made some sense to differentiate a click. Now all buttons support click & drag it's more logical to use the prefix "prev_press_" as any press event will set these values. Also update doc-strings. --- source/blender/editors/sculpt_paint/sculpt_cloth.c | 2 +- .../editors/sculpt_paint/sculpt_filter_color.c | 2 +- .../editors/sculpt_paint/sculpt_filter_mesh.c | 2 +- source/blender/makesrna/intern/rna_wm.c | 4 +- source/blender/windowmanager/WM_types.h | 104 +++++++++++++-------- .../windowmanager/gizmo/intern/wm_gizmo_group.c | 2 +- .../blender/windowmanager/intern/wm_event_query.c | 14 +-- .../blender/windowmanager/intern/wm_event_system.c | 38 ++++---- 8 files changed, 98 insertions(+), 70 deletions(-) diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 80ba7601cb1..a4cfb611138 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1502,7 +1502,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } - const float len = event->prev_click_xy[0] - event->xy[0]; + const float len = event->prev_press_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 93e2d721962..377f1e0ed32 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -215,7 +215,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } - const float len = event->prev_click_xy[0] - event->xy[0]; + const float len = event->prev_press_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f; float fill_color[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index ac2c83d3a9c..b73e182fcab 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -606,7 +606,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_RUNNING_MODAL; } - const float len = event->prev_click_xy[0] - event->xy[0]; + const float len = event->prev_press_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index bc879d8254a..a850f8862bc 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -2103,14 +2103,14 @@ static void rna_def_event(BlenderRNA *brna) prop, "Mouse Previous Y Position", "The window relative vertical location of the mouse"); prop = RNA_def_property(srna, "mouse_prev_press_x", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "prev_click_xy[0]"); + RNA_def_property_int_sdna(prop, NULL, "prev_press_xy[0]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Mouse Previous X Press Position", "The window relative horizontal location of the last press event"); prop = RNA_def_property(srna, "mouse_prev_press_y", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "prev_click_xy[1]"); + RNA_def_property_int_sdna(prop, NULL, "prev_press_xy[1]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Mouse Previous Y Press Position", diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 3d8577933cf..8934f714c21 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -238,6 +238,7 @@ enum { KM_SHIFT = (1 << 0), KM_CTRL = (1 << 1), KM_ALT = (1 << 2), + /** Use for Windows-Key on MS-Windows, Command-key on macOS and Super on Linux. */ KM_OSKEY = (1 << 3), /* Used for key-map item creation function arguments. */ @@ -268,7 +269,7 @@ enum { KM_CLICK = 3, KM_DBL_CLICK = 4, /** - * \note The cursor location at the point dragging starts is set to #wmEvent.prev_click_xy + * \note The cursor location at the point dragging starts is set to #wmEvent.prev_press_xy * some operators such as box selection should use this location instead of #wmEvent.xy. */ KM_CLICK_DRAG = 5, @@ -629,23 +630,39 @@ typedef struct wmTabletData { * event comes from event manager and from keymap. * * - * Previous State - * ============== + * Previous State (`prev_*`) + * ========================= * - * Events hold information about the previous event, - * this is used for detecting click and double-click events (the timer is needed for double-click). - * See #wm_event_add_ghostevent for implementation details. + * Events hold information about the previous event. * - * Notes: - * - * - The previous values are only set for mouse button and keyboard events. - * See: #ISKEYBOARD_OR_BUTTON macro. + * - Previous values are only set for events types that generate #KM_PRESS. + * See: #ISKEYBOARD_OR_BUTTON. * * - Previous x/y are exceptions: #wmEvent.prev * these are set on mouse motion, see #MOUSEMOVE & track-pad events. * * - Modal key-map handling sets `prev_val` & `prev_type` to `val` & `type`, * this allows modal keys-maps to check the original values (needed in some cases). + * + * + * Press State (`prev_press_*`) + * ============================ + * + * Events hold information about the state when the last #KM_PRESS event was added. + * This is used for generating #KM_CLICK, #KM_DBL_CLICK & #KM_CLICK_DRAG events. + * See #wm_handlers_do for the implementation. + * + * - Previous values are only set when a #KM_PRESS event is detected. + * See: #ISKEYBOARD_OR_BUTTON. + * + * - The reason to differentiate between "press" and the previous event state is + * the previous event may be set by key-release events. In the case of a single key click + * this isn't a problem however releasing other keys such as modifiers prevents click/click-drag + * events from being detected, see: T89989. + * + * - Mouse-wheel events are excluded even though they generate #KM_PRESS + * as clicking and dragging don't make sense for mouse wheel events. + * */ typedef struct wmEvent { struct wmEvent *next, *prev; @@ -667,38 +684,16 @@ typedef struct wmEvent { /** From ghost, fallback if utf8 isn't set. */ char ascii; - /** The previous value of `type`. */ - short prev_type; - /** The previous value of `val`. */ - short prev_val; - /** - * The previous value of #wmEvent.xy, - * Unlike other previous state variables, this is set on any mouse motion. - * Use `prev_click` for the value at time of pressing. - */ - int prev_xy[2]; - - /** The `type` at the point of the click action. */ - short prev_click_type; - /** The time when the key is pressed, see #PIL_check_seconds_timer. */ - double prev_click_time; - /** The location when the key is pressed (used to enforce drag thresholds). */ - int prev_click_xy[2]; - /** The `modifier` at the point of the click action. */ - uint8_t prev_click_modifier; - /** The `keymodifier` at the point of the click action. */ - short prev_click_keymodifier; - - /** - * Modifier states. - * #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY is apple or windows-key. - */ + /** Modifier states: #KM_SHIFT, #KM_CTRL, #KM_ALT & #KM_OSKEY. */ uint8_t modifier; /** The direction (for #KM_CLICK_DRAG events only). */ int8_t direction; - /** Raw-key modifier (allow using any key as a modifier). */ + /** + * Raw-key modifier (allow using any key as a modifier). + * Compatible with values in `type`. + */ short keymodifier; /** Tablet info, available for mouse move and button events. */ @@ -707,11 +702,44 @@ typedef struct wmEvent { eWM_EventFlag flag; /* Custom data. */ - /** Custom data type, stylus, 6dof, see wm_event_types.h */ + + /** Custom data type, stylus, 6-DOF, see `wm_event_types.h`. */ short custom; short customdata_free; /** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */ void *customdata; + + /* Previous State. */ + + /** The previous value of `type`. */ + short prev_type; + /** The previous value of `val`. */ + short prev_val; + /** + * The previous value of #wmEvent.xy, + * Unlike other previous state variables, this is set on any mouse motion. + * Use `prev_press_*` for the value at time of pressing. + */ + int prev_xy[2]; + + /* Previous Press State (when `val == KM_PRESS`). */ + + /** The `type` at the point of the press action. */ + short prev_press_type; + /** + * The location when the key is pressed. + * used to enforce drag threshold & calculate the `direction`. + */ + int prev_press_xy[2]; + /** The `modifier` at the point of the press action. */ + uint8_t prev_press_modifier; + /** The `keymodifier` at the point of the press action. */ + short prev_press_keymodifier; + /** + * The time when the key is pressed, see #PIL_check_seconds_timer. + * Used to detect double-click events. + */ + double prev_press_time; } wmEvent; /** diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index b3ef9b27667..beef74ce2e2 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -1137,7 +1137,7 @@ void WM_gizmo_group_refresh(const bContext *C, wmGizmoGroup *gzgroup) BLI_assert(region->gizmo_map == gzmap); /* Check if the tweak event originated from this region. */ if ((win->eventstate != NULL) && (win->event_queue_check_drag) && - BLI_rcti_isect_pt_v(®ion->winrct, win->eventstate->prev_click_xy)) { + BLI_rcti_isect_pt_v(®ion->winrct, win->eventstate->prev_press_xy)) { /* We need to run refresh again. */ gzgroup->init_flag &= ~WM_GIZMOGROUP_INIT_REFRESH; WM_gizmomap_tag_refresh_drawstep(gzmap, WM_gizmomap_drawstep_from_gizmo_group(gzgroup)); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index c9f83a5d811..9a7d31f8bb8 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -227,8 +227,8 @@ bool WM_event_is_mouse_drag_or_press(const wmEvent *event) int WM_event_drag_direction(const wmEvent *event) { const int delta[2] = { - event->xy[0] - event->prev_click_xy[0], - event->xy[1] - event->prev_click_xy[1], + event->xy[0] - event->prev_press_xy[0], + event->xy[1] - event->prev_press_xy[1], }; int theta = round_fl_to_int(4.0f * atan2f((float)delta[1], (float)delta[0]) / (float)M_PI); @@ -306,8 +306,8 @@ bool WM_cursor_test_motion_and_update(const int mval[2]) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (ISMOUSE(event->prev_click_type)) { - BLI_assert(event->prev_click_type != MOUSEMOVE); + if (ISMOUSE(event->prev_press_type)) { + BLI_assert(event->prev_press_type != MOUSEMOVE); /* Using the previous type is important is we want to check the last pressed/released button, * The `event->type` would include #MOUSEMOVE which is always the case when dragging * and does not help us know which threshold to use. */ @@ -340,21 +340,21 @@ bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2]) { - const int *xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; + const int *xy = (event->val == KM_CLICK_DRAG) ? event->prev_press_xy : event->xy; r_mval[0] = xy[0] - region->winrct.xmin; r_mval[1] = xy[1] - region->winrct.ymin; } void WM_event_drag_start_mval_fl(const wmEvent *event, const ARegion *region, float r_mval[2]) { - const int *xy = (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy; + const int *xy = (event->val == KM_CLICK_DRAG) ? event->prev_press_xy : event->xy; r_mval[0] = xy[0] - region->winrct.xmin; r_mval[1] = xy[1] - region->winrct.ymin; } void WM_event_drag_start_xy(const wmEvent *event, int r_xy[2]) { - copy_v2_v2_int(r_xy, (event->val == KM_CLICK_DRAG) ? event->prev_click_xy : event->xy); + copy_v2_v2_int(r_xy, (event->val == KM_CLICK_DRAG) ? event->prev_press_xy : event->xy); } /** \} */ diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ee37fc0bea3..a01da3b1fbd 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -154,7 +154,7 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) if (event->val == KM_PRESS) { if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { - copy_v2_v2_int(win->eventstate->prev_click_xy, event->xy); + copy_v2_v2_int(win->eventstate->prev_press_xy, event->xy); } } } @@ -3165,21 +3165,21 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) * in `action` setting #WM_HANDLER_HANDLED, but not #WM_HANDLER_BREAK. */ if ((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action)) { if (win->event_queue_check_drag) { - if (WM_event_drag_test(event, event->prev_click_xy)) { + if (WM_event_drag_test(event, event->prev_press_xy)) { win->event_queue_check_drag_handled = true; const int direction = WM_event_drag_direction(event); /* Intentionally leave `event->xy` as-is, event users are expected to use - * `event->prev_click_xy` if they need to access the drag start location. */ + * `event->prev_press_xy` if they need to access the drag start location. */ const short prev_val = event->val; const short prev_type = event->type; const uint8_t prev_modifier = event->modifier; const short prev_keymodifier = event->keymodifier; event->val = KM_CLICK_DRAG; - event->type = event->prev_click_type; - event->modifier = event->prev_click_modifier; - event->keymodifier = event->prev_click_keymodifier; + event->type = event->prev_press_type; + event->modifier = event->prev_press_modifier; + event->keymodifier = event->prev_press_keymodifier; event->direction = direction; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); @@ -3221,8 +3221,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } else if (event->val == KM_RELEASE) { if (win->event_queue_check_drag) { - if ((event->prev_click_type != event->type) && - (ISKEYMODIFIER(event->type) || (event->type == event->prev_click_keymodifier))) { + if ((event->prev_press_type != event->type) && + (ISKEYMODIFIER(event->type) || (event->type == event->prev_press_keymodifier))) { /* Support releasing modifier keys without canceling the drag event, see T89989. */ } else { @@ -3231,12 +3231,12 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } } - if (event->prev_click_type == event->type) { + if (event->prev_press_type == event->type) { if (event->val == KM_RELEASE) { if (event->prev_val == KM_PRESS) { if (win->event_queue_check_click == true) { - if (WM_event_drag_test(event, event->prev_click_xy)) { + if (WM_event_drag_test(event, event->prev_press_xy)) { win->event_queue_check_click = false; win->event_queue_check_drag = false; } @@ -3245,7 +3245,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) * accurate selecting in case the mouse drifts a little. */ int xy[2] = {UNPACK2(event->xy)}; - copy_v2_v2_int(event->xy, event->prev_click_xy); + copy_v2_v2_int(event->xy, event->prev_press_xy); event->val = KM_CLICK; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK"); @@ -4691,11 +4691,11 @@ static bool wm_event_is_double_click(const wmEvent *event) { if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) && (event->val == KM_PRESS)) { - if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_click_xy)) { + if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_press_xy)) { /* Pass. */ } else { - if ((PIL_check_seconds_timer() - event->prev_click_time) * 1000 < U.dbl_click_time) { + if ((PIL_check_seconds_timer() - event->prev_press_time) * 1000 < U.dbl_click_time) { return true; } } @@ -4715,12 +4715,12 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { - event->prev_click_time = event_state->prev_click_time = PIL_check_seconds_timer(); - event->prev_click_type = event_state->prev_click_type = event_state->type; - event->prev_click_modifier = event_state->prev_click_modifier = event_state->modifier; - event->prev_click_keymodifier = event_state->prev_click_keymodifier = event_state->keymodifier; - event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0]; - event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; + event->prev_press_time = event_state->prev_press_time = PIL_check_seconds_timer(); + event->prev_press_type = event_state->prev_press_type = event_state->type; + event->prev_press_modifier = event_state->prev_press_modifier = event_state->modifier; + event->prev_press_keymodifier = event_state->prev_press_keymodifier = event_state->keymodifier; + event->prev_press_xy[0] = event_state->prev_press_xy[0] = event_state->xy[0]; + event->prev_press_xy[1] = event_state->prev_press_xy[1] = event_state->xy[1]; } static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) -- cgit v1.2.3 From 21462565632b876ac4eadf2bd30573f2fda70c76 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 09:35:37 +1100 Subject: Cleanup: use ELEM macro --- source/blender/blenkernel/intern/fluid.c | 11 ++++---- .../blender/blenkernel/intern/gpencil_modifier.c | 8 +++--- source/blender/blenkernel/intern/idprop.c | 3 +-- source/blender/blenkernel/intern/image_gpu.cc | 2 +- source/blender/blenkernel/intern/mask.c | 2 +- source/blender/blenkernel/intern/node.cc | 4 +-- source/blender/blenkernel/intern/pointcache.c | 3 +-- source/blender/blenkernel/intern/spline_bezier.cc | 2 +- source/blender/blenkernel/intern/tracking.c | 2 +- source/blender/blenkernel/intern/writeffmpeg.c | 3 +-- source/blender/blenlib/intern/path_util.c | 2 +- .../blender/editors/interface/interface_handlers.c | 2 +- source/blender/editors/space_file/filelist.c | 3 +-- .../editors/space_sequencer/sequencer_draw.c | 2 +- source/blender/editors/space_text/text_draw.c | 2 +- .../editors/transform/transform_convert_mesh.c | 2 +- .../blender/editors/uvedit/uvedit_parametrizer.c | 2 +- source/blender/editors/uvedit/uvedit_select.c | 16 ++++++------ source/blender/gpu/intern/gpu_vertex_format.cc | 4 +-- .../blender/imbuf/intern/dds/DirectDrawSurface.cpp | 29 ++++++++++++++-------- source/blender/makesrna/intern/makesrna.c | 16 ++++++------ source/blender/makesrna/intern/rna_camera.c | 2 +- source/blender/makesrna/intern/rna_define.c | 3 +-- source/blender/makesrna/intern/rna_dynamicpaint.c | 3 +-- source/blender/makesrna/intern/rna_particle.c | 7 +++--- source/blender/makesrna/intern/rna_rna.c | 2 +- source/blender/makesrna/intern/rna_scene.c | 2 +- source/blender/makesrna/intern/rna_ui.c | 4 +-- source/blender/makesrna/intern/rna_xr.c | 6 ++--- .../blender/windowmanager/intern/wm_event_system.c | 2 +- .../windowmanager/xr/intern/wm_xr_operators.c | 2 +- source/creator/creator_args.c | 2 +- 32 files changed, 79 insertions(+), 76 deletions(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 6f2760e91a6..f85361e94ff 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1845,8 +1845,8 @@ static void sample_mesh(FluidFlowSettings *ffs, const float surface_distance = 1.732; nearest.dist_sq = surface_distance * surface_distance; /* find_nearest uses squared distance. */ - bool is_gas_flow = (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_FIRE || - ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE); + bool is_gas_flow = ELEM( + ffs->type, FLUID_FLOW_TYPE_SMOKE, FLUID_FLOW_TYPE_FIRE, FLUID_FLOW_TYPE_SMOKEFIRE); /* Emission strength for gases will be computed below. * For liquids it's not needed. Just set to non zero value @@ -2035,8 +2035,7 @@ static void emit_from_mesh_task_cb(void *__restrict userdata, /* Compute emission only for flow objects that produce fluid (i.e. skip outflow objects). * Result in bb->influence. Also computes initial velocities. Result in bb->velocity. */ - if ((data->ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY) || - (data->ffs->behavior == FLUID_FLOW_BEHAVIOR_INFLOW)) { + if (ELEM(data->ffs->behavior, FLUID_FLOW_BEHAVIOR_GEOMETRY, FLUID_FLOW_BEHAVIOR_INFLOW)) { sample_mesh(data->ffs, data->mvert, data->vert_normals, @@ -2697,8 +2696,8 @@ static bool escape_flowsobject(Object *flowobj, bool is_static = is_static_object(flowobj); bool liquid_flow = ffs->type == FLUID_FLOW_TYPE_LIQUID; - bool gas_flow = (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_FIRE || - ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE); + bool gas_flow = ELEM( + ffs->type, FLUID_FLOW_TYPE_SMOKE, FLUID_FLOW_TYPE_FIRE, FLUID_FLOW_TYPE_SMOKEFIRE); bool is_geometry = (ffs->behavior == FLUID_FLOW_BEHAVIOR_GEOMETRY); bool liquid_domain = fds->type == FLUID_DOMAIN_TYPE_LIQUID; diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index ffc1f2d8774..9c546cc118a 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -196,9 +196,11 @@ bool BKE_gpencil_has_transform_modifiers(Object *ob) LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { /* Only if enabled in edit mode. */ if (!GPENCIL_MODIFIER_EDIT(md, true) && GPENCIL_MODIFIER_ACTIVE(md, false)) { - if ((md->type == eGpencilModifierType_Armature) || (md->type == eGpencilModifierType_Hook) || - (md->type == eGpencilModifierType_Lattice) || - (md->type == eGpencilModifierType_Offset)) { + if (ELEM(md->type, + eGpencilModifierType_Armature, + eGpencilModifierType_Hook, + eGpencilModifierType_Lattice, + eGpencilModifierType_Offset)) { return true; } } diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index d00fc8814e0..7b8dfdc690c 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -903,8 +903,7 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * break; case IDP_ARRAY: { /* for now, we only support float and int and double arrays */ - if ((val->array.type == IDP_FLOAT) || (val->array.type == IDP_INT) || - (val->array.type == IDP_DOUBLE) || (val->array.type == IDP_GROUP)) { + if (ELEM(val->array.type, IDP_FLOAT, IDP_INT, IDP_DOUBLE, IDP_GROUP)) { prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); prop->subtype = val->array.type; if (val->array.len) { diff --git a/source/blender/blenkernel/intern/image_gpu.cc b/source/blender/blenkernel/intern/image_gpu.cc index c4a43d8b023..0d470c5b663 100644 --- a/source/blender/blenkernel/intern/image_gpu.cc +++ b/source/blender/blenkernel/intern/image_gpu.cc @@ -280,7 +280,7 @@ static GPUTexture **get_image_gpu_texture_ptr(Image *ima, { const bool in_range = (textarget >= 0) && (textarget < TEXTARGET_COUNT); BLI_assert(in_range); - BLI_assert(multiview_eye == 0 || multiview_eye == 1); + BLI_assert(ELEM(multiview_eye, 0, 1)); const int resolution = (texture_resolution == IMA_TEXTURE_RESOLUTION_LIMITED) ? 1 : 0; if (in_range) { diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index fca7c10ca77..da0f899001c 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -1374,7 +1374,7 @@ static void mask_calc_point_handle(MaskSplinePoint *point, else if (handle_type == HD_AUTO) { BKE_nurb_handle_calc(bezt, bezt_prev, bezt_next, 0, 0); } - else if (handle_type == HD_ALIGN || handle_type == HD_ALIGN_DOUBLESIDE) { + else if (ELEM(handle_type, HD_ALIGN, HD_ALIGN_DOUBLESIDE)) { float v1[3], v2[3]; float vec[3], h[3]; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 1c0eb0ecd44..29770ea5475 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -553,7 +553,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } else if ((ntree->type == NTREE_TEXTURE) && - (node->type == TEX_NODE_CURVE_RGB || node->type == TEX_NODE_CURVE_TIME)) { + ELEM(node->type, TEX_NODE_CURVE_RGB, TEX_NODE_CURVE_TIME)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } else if ((ntree->type == NTREE_COMPOSIT) && (node->type == CMP_NODE_MOVIEDISTORTION)) { @@ -579,7 +579,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage); } else if ((ntree->type == NTREE_COMPOSIT) && - (ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY))) { + ELEM(node->type, CMP_NODE_CRYPTOMATTE, CMP_NODE_CRYPTOMATTE_LEGACY)) { NodeCryptomatte *nc = (NodeCryptomatte *)node->storage; BLO_write_string(writer, nc->matte_id); LISTBASE_FOREACH (CryptomatteEntry *, entry, &nc->entries) { diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index 859e3499cc4..19abff19b77 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -683,8 +683,7 @@ static int ptcache_dynamicpaint_write(PTCacheFile *pf, void *dp_v) if (surface->type == MOD_DPAINT_SURFACE_T_PAINT) { in_len = sizeof(PaintPoint) * total_points; } - else if (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WEIGHT) { + else if (ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WEIGHT)) { in_len = sizeof(float) * total_points; } else if (surface->type == MOD_DPAINT_SURFACE_T_WAVE) { diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 3c2ac1dae9c..e9ae51b16f8 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -365,7 +365,7 @@ BezierSpline::InsertResult BezierSpline::calculate_segment_insertion(const int i using namespace blender::math; BLI_assert(parameter <= 1.0f && parameter >= 0.0f); - BLI_assert(next_index == 0 || next_index == index + 1); + BLI_assert(ELEM(next_index, 0, index + 1)); const float3 &point_prev = positions_[index]; const float3 &handle_prev = handle_positions_right_[index]; const float3 &handle_next = handle_positions_left_[next_index]; diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 5708d3eeaec..2a415b8f6fb 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -1473,7 +1473,7 @@ static const MovieTrackingMarker *get_usable_marker_for_interpolation( const MovieTrackingMarker *anchor_marker, const int direction) { - BLI_assert(direction == -1 || direction == 1); + BLI_assert(ELEM(direction, -1, 1)); const MovieTrackingMarker *last_marker = track->markers + track->markersnr - 1; const MovieTrackingMarker *current_marker = anchor_marker; diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index b9d013d4756..5e11cd0703a 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -622,8 +622,7 @@ static AVStream *alloc_video_stream(FFMpegContext *context, if (codec_id == AV_CODEC_ID_VP9 && rd->im_format.planes == R_IMF_PLANES_RGBA) { c->pix_fmt = AV_PIX_FMT_YUVA420P; } - else if ((codec_id == AV_CODEC_ID_H264 || codec_id == AV_CODEC_ID_VP9) && - context->ffmpeg_crf == 0) { + else if (ELEM(codec_id, AV_CODEC_ID_H264, AV_CODEC_ID_VP9) && (context->ffmpeg_crf == 0)) { /* Use 4:4:4 instead of 4:2:0 pixel format for lossless rendering. */ c->pix_fmt = AV_PIX_FMT_YUV444P; } diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index a6f09d074e7..f94d49cf1dd 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -470,7 +470,7 @@ void BLI_path_rel(char *file, const char *relfile) * can happen with old recent-files.txt files */ BLI_windows_get_default_root_dir(temp); ptemp = &temp[2]; - if (relfile[0] != '\\' && relfile[0] != '/') { + if (!ELEM(relfile[0], '\\', '/')) { ptemp++; } BLI_strncpy(ptemp, relfile, FILE_MAX - 3); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index f5fa710067b..2386b388a41 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3930,7 +3930,7 @@ static void ui_do_but_textedit( } #ifdef WITH_INPUT_IME - if (event->type == WM_IME_COMPOSITE_START || event->type == WM_IME_COMPOSITE_EVENT) { + if (ELEM(event->type, WM_IME_COMPOSITE_START, WM_IME_COMPOSITE_EVENT)) { changed = true; if (event->type == WM_IME_COMPOSITE_START && but->selend > but->selsta) { diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index daa4b53803f..d47e67b9d27 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -3462,8 +3462,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist) // files->entry->nr = totbl + 1; files->entry->poin = id; fake = id->flag & LIB_FAKEUSER; - if (idcode == ID_MA || idcode == ID_TE || idcode == ID_LA || idcode == ID_WO || - idcode == ID_IM) { + if (ELEM(idcode, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM)) { files->typeflag |= FILE_TYPE_IMAGE; } # if 0 diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 5ac4363e63d..bdad08fe20a 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -476,7 +476,7 @@ static void draw_seq_waveform_overlay(View2D *v2d, bool is_line_strip = (value_max - value_min < 0.05f); - if (was_line_strip != -1 && is_line_strip != was_line_strip) { + if (!ELEM(was_line_strip, -1, is_line_strip)) { /* If the previously added strip type isn't the same as the current one, * add transition areas so they transition smoothly between each other. */ if (is_line_strip) { diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 0e29c68cf23..d3fc3e9d352 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1108,7 +1108,7 @@ static void draw_documentation(const SpaceText *st, ARegion *region) if (*p == '\r' && *(++p) != '\n') { *(--p) = '\n'; /* Fix line endings */ } - if (*p == ' ' || *p == '\t') { + if (ELEM(*p, ' ', '\t')) { br = i; } else if (*p == '\n') { diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index b8530293d3e..d4b12142162 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1449,7 +1449,7 @@ static void VertsToTransData(TransInfo *t, td->ext = NULL; td->val = NULL; td->extra = eve; - if (t->mode == TFM_BWEIGHT || t->mode == TFM_VERT_CREASE) { + if (ELEM(t->mode, TFM_BWEIGHT, TFM_VERT_CREASE)) { td->val = bweight; td->ival = *bweight; } diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 53b8a541dba..f19b32e3ad4 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -2039,7 +2039,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) float *co1 = e->next->vert->co; float *co2 = e->next->next->vert->co; - if ((e->face != oldf1) && (e->face != oldf2)) { + if (!ELEM(e->face, oldf1, oldf2)) { float tetrav2[3], tetrav3[3]; /* tetrahedron volume = (1/3!)*|a.(b x c)| */ diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 3bba618f682..938b798f4b6 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -438,7 +438,7 @@ void uvedit_edge_select_shared_vert(const Scene *scene, const bool do_history, const int cd_loop_uv_offset) { - BLI_assert((sticky_flag == SI_STICKY_LOC) || (sticky_flag == SI_STICKY_VERTEX)); + BLI_assert(ELEM(sticky_flag, SI_STICKY_LOC, SI_STICKY_VERTEX)); /* Set edge flags. Rely on this for face visibility checks */ uvedit_edge_select_set_noflush(scene, l, select, sticky_flag, cd_loop_uv_offset); @@ -647,7 +647,7 @@ void uvedit_uv_select_shared_vert(const Scene *scene, const bool do_history, const int cd_loop_uv_offset) { - BLI_assert((sticky_flag == SI_STICKY_LOC) || (sticky_flag == SI_STICKY_VERTEX)); + BLI_assert(ELEM(sticky_flag, SI_STICKY_LOC, SI_STICKY_VERTEX)); BMEdge *e_first, *e_iter; e_first = e_iter = l->e; @@ -2251,13 +2251,13 @@ static void uv_select_invert(const Scene *scene, BMEditMesh *em) } BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if ((uv_selectmode == UV_SELECT_EDGE) || (uv_selectmode == UV_SELECT_FACE)) { + if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) { /* Use #MLOOPUV_EDGESEL to flag edges that must be selected. */ luv->flag ^= MLOOPUV_EDGESEL; luv->flag &= ~MLOOPUV_VERTSEL; } /* Use #MLOOPUV_VERTSEL to flag verts that must be selected. */ - else if ((uv_selectmode == UV_SELECT_VERTEX) || (uv_selectmode == UV_SELECT_ISLAND)) { + else if (ELEM(uv_selectmode, UV_SELECT_VERTEX, UV_SELECT_ISLAND)) { luv->flag ^= MLOOPUV_VERTSEL; luv->flag &= ~MLOOPUV_EDGESEL; } @@ -2265,10 +2265,10 @@ static void uv_select_invert(const Scene *scene, BMEditMesh *em) } /* Flush based on uv vert/edge flags and current UV select mode */ - if ((uv_selectmode == UV_SELECT_EDGE) || (uv_selectmode == UV_SELECT_FACE)) { + if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) { uv_select_flush_from_loop_edge_flag(scene, em); } - else if ((uv_selectmode == UV_SELECT_VERTEX) || (uv_selectmode == UV_SELECT_ISLAND)) { + else if (ELEM(uv_selectmode, UV_SELECT_VERTEX, UV_SELECT_ISLAND)) { uvedit_select_flush(scene, em); } } @@ -3221,7 +3221,7 @@ static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, co const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && - ((ts->uv_sticky == SI_STICKY_VERTEX) || (ts->uv_sticky == SI_STICKY_LOC))) { + ELEM(ts->uv_sticky, SI_STICKY_VERTEX, SI_STICKY_LOC)) { struct UvVertMap *vmap; uint efa_index; @@ -3366,7 +3366,7 @@ static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMEditMesh * const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && - ((ts->uv_sticky == SI_STICKY_LOC) || (ts->uv_sticky == SI_STICKY_VERTEX))) { + ELEM(ts->uv_sticky, SI_STICKY_LOC, SI_STICKY_VERTEX)) { /* Use the #MLOOPUV_EDGESEL flag to identify which verts must to be selected */ struct UvVertMap *vmap; uint efa_index; diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index eedd660d0a9..a9ac191754b 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -133,7 +133,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, case GPU_COMP_I10: /* 10_10_10 format intended for normals (xyz) or colors (rgb) * extra component packed.w can be manually set to { -2, -1, 0, 1 } */ - assert(comp_len == 3 || comp_len == 4); + assert(ELEM(comp_len, 3, 4)); /* Not strictly required, may relax later. */ assert(fetch_mode == GPU_FETCH_INT_TO_FLOAT_UNIT); @@ -143,7 +143,7 @@ uint GPU_vertformat_attr_add(GPUVertFormat *format, /* integer types can be kept as int or converted/normalized to float */ assert(fetch_mode != GPU_FETCH_FLOAT); /* only support float matrices (see Batch_update_program_bindings) */ - assert(comp_len != 8 && comp_len != 12 && comp_len != 16); + assert(!ELEM(comp_len, 8, 12, 16)); } #endif format->name_len++; /* Multi-name support. */ diff --git a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp index 9173b5a9d49..ce5dd4927be 100644 --- a/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp +++ b/source/blender/imbuf/intern/dds/DirectDrawSurface.cpp @@ -34,6 +34,9 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ +#include "BLI_utildefines.h" +#undef CLAMP + #include #include #include @@ -596,7 +599,7 @@ void DDSHeader::setDepth(uint d) void DDSHeader::setMipmapCount(uint count) { - if (count == 0 || count == 1) { + if (ELEM(count, 0, 1)) { this->flags &= ~DDSD_MIPMAPCOUNT; this->mipmapcount = 1; @@ -904,11 +907,12 @@ bool DirectDrawSurface::isValid() const bool DirectDrawSurface::isSupported() const { if (header.hasDX10Header()) { - if (header.header10.dxgiFormat == DXGI_FORMAT_BC1_UNORM || - header.header10.dxgiFormat == DXGI_FORMAT_BC2_UNORM || - header.header10.dxgiFormat == DXGI_FORMAT_BC3_UNORM || - header.header10.dxgiFormat == DXGI_FORMAT_BC4_UNORM || - header.header10.dxgiFormat == DXGI_FORMAT_BC5_UNORM) { + if (ELEM(header.header10.dxgiFormat, + DXGI_FORMAT_BC1_UNORM, + DXGI_FORMAT_BC2_UNORM, + DXGI_FORMAT_BC3_UNORM, + DXGI_FORMAT_BC4_UNORM, + DXGI_FORMAT_BC5_UNORM)) { return true; } @@ -916,10 +920,15 @@ bool DirectDrawSurface::isSupported() const } if (header.pf.flags & DDPF_FOURCC) { - if (header.pf.fourcc != FOURCC_DXT1 && header.pf.fourcc != FOURCC_DXT2 && - header.pf.fourcc != FOURCC_DXT3 && header.pf.fourcc != FOURCC_DXT4 && - header.pf.fourcc != FOURCC_DXT5 && header.pf.fourcc != FOURCC_RXGB && - header.pf.fourcc != FOURCC_ATI1 && header.pf.fourcc != FOURCC_ATI2) { + if (!ELEM(header.pf.fourcc, + FOURCC_DXT1, + FOURCC_DXT2, + FOURCC_DXT3, + FOURCC_DXT4, + FOURCC_DXT5, + FOURCC_RXGB, + FOURCC_ATI1, + FOURCC_ATI2)) { /* Unknown fourcc code. */ return false; } diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 01a6108fd8c..20261fb9eeb 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -724,10 +724,10 @@ static char *rna_def_property_get_func( } else { const PropertySubType subtype = prop->subtype; - const char *string_copy_func = (subtype == PROP_FILEPATH || subtype == PROP_DIRPATH || - subtype == PROP_FILENAME || subtype == PROP_BYTESTRING) ? - "BLI_strncpy" : - "BLI_strncpy_utf8"; + const char *string_copy_func = + ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING) ? + "BLI_strncpy" : + "BLI_strncpy_utf8"; rna_print_data_get(f, dp); @@ -1073,10 +1073,10 @@ static char *rna_def_property_set_func( } else { const PropertySubType subtype = prop->subtype; - const char *string_copy_func = (subtype == PROP_FILEPATH || subtype == PROP_DIRPATH || - subtype == PROP_FILENAME || subtype == PROP_BYTESTRING) ? - "BLI_strncpy" : - "BLI_strncpy_utf8"; + const char *string_copy_func = + ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING) ? + "BLI_strncpy" : + "BLI_strncpy_utf8"; rna_print_data_get(f, dp); diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 0378e64290b..23f6bb768d6 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -136,7 +136,7 @@ static void rna_CameraDOFSettings_aperture_blades_set(PointerRNA *ptr, const int { CameraDOFSettings *dofsettings = (CameraDOFSettings *)ptr->data; - if (value == 1 || value == 2) { + if (ELEM(value, 1, 2)) { if (dofsettings->aperture_blades == 0) { dofsettings->aperture_blades = 3; } diff --git a/source/blender/makesrna/intern/rna_define.c b/source/blender/makesrna/intern/rna_define.c index 5127b418b7f..8e2b9c1d937 100644 --- a/source/blender/makesrna/intern/rna_define.c +++ b/source/blender/makesrna/intern/rna_define.c @@ -2394,8 +2394,7 @@ void RNA_def_property_int_sdna(PropertyRNA *prop, const char *structname, const iprop->hardmax = iprop->softmax = INT8_MAX; } - if (prop->subtype == PROP_UNSIGNED || prop->subtype == PROP_PERCENTAGE || - prop->subtype == PROP_FACTOR) { + if (ELEM(prop->subtype, PROP_UNSIGNED, PROP_PERCENTAGE, PROP_FACTOR)) { iprop->hardmin = iprop->softmin = 0; } diff --git a/source/blender/makesrna/intern/rna_dynamicpaint.c b/source/blender/makesrna/intern/rna_dynamicpaint.c index d81e44791f4..ed6d4996c1e 100644 --- a/source/blender/makesrna/intern/rna_dynamicpaint.c +++ b/source/blender/makesrna/intern/rna_dynamicpaint.c @@ -237,8 +237,7 @@ static const EnumPropertyItem *rna_DynamicPaint_surface_type_itemf(bContext *UNU RNA_enum_item_add(&item, &totitem, &tmp); /* Displace */ - if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX || - surface->format == MOD_DPAINT_SURFACE_F_IMAGESEQ) { + if (ELEM(surface->format, MOD_DPAINT_SURFACE_F_VERTEX, MOD_DPAINT_SURFACE_F_IMAGESEQ)) { tmp.value = MOD_DPAINT_SURFACE_T_DISPLACE; tmp.identifier = "DISPLACE"; tmp.name = "Displace"; diff --git a/source/blender/makesrna/intern/rna_particle.c b/source/blender/makesrna/intern/rna_particle.c index f61d5456974..2770255802d 100644 --- a/source/blender/makesrna/intern/rna_particle.c +++ b/source/blender/makesrna/intern/rna_particle.c @@ -438,8 +438,7 @@ static void rna_ParticleSystem_co_hair( return; } - if (part->ren_as == PART_DRAW_OB || part->ren_as == PART_DRAW_GR || - part->ren_as == PART_DRAW_NOT) { + if (ELEM(part->ren_as, PART_DRAW_OB, PART_DRAW_GR, PART_DRAW_NOT)) { return; } @@ -836,7 +835,7 @@ static void rna_Particle_target_reset(Main *bmain, Scene *UNUSED(scene), Pointer ParticleTarget *pt = (ParticleTarget *)ptr->data; ParticleSystem *kpsys = NULL, *psys = rna_particle_system_for_target(ob, pt); - if (pt->ob == ob || pt->ob == NULL) { + if (ELEM(pt->ob, ob, NULL)) { kpsys = BLI_findlink(&ob->particlesystem, pt->psys - 1); if (kpsys) { @@ -1334,7 +1333,7 @@ static const EnumPropertyItem *rna_Particle_type_itemf(bContext *UNUSED(C), { ParticleSettings *part = (ParticleSettings *)ptr->owner_id; - if (part->type == PART_HAIR || part->type == PART_EMITTER) { + if (ELEM(part->type, PART_HAIR, PART_EMITTER)) { return part_type_items; } else { diff --git a/source/blender/makesrna/intern/rna_rna.c b/source/blender/makesrna/intern/rna_rna.c index e7c3d90573f..8f1847c00f4 100644 --- a/source/blender/makesrna/intern/rna_rna.c +++ b/source/blender/makesrna/intern/rna_rna.c @@ -1451,7 +1451,7 @@ static int rna_property_override_diff_propptr(Main *bmain, *r_override_changed = true; } - if (extended_rna_path != extended_rna_path_buffer && extended_rna_path != rna_path) { + if (!ELEM(extended_rna_path, extended_rna_path_buffer, rna_path)) { MEM_freeN(extended_rna_path); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 145746cdce4..209331065ac 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1425,7 +1425,7 @@ static const EnumPropertyItem *rna_ImageFormatSettings_exr_codec_itemf(bContext } for (i = 0; i < R_IMF_EXR_CODEC_MAX; i++) { - if ((i == R_IMF_EXR_CODEC_B44 || i == R_IMF_EXR_CODEC_B44A)) { + if (ELEM(i, R_IMF_EXR_CODEC_B44, R_IMF_EXR_CODEC_B44A)) { continue; /* B44 and B44A are not defined for 32 bit floats */ } diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index 00fb48b489a..1d0723851ad 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -549,7 +549,7 @@ static void uilist_filter_items(uiList *ui_list, parm = RNA_function_find_parameter(NULL, func, "filter_flags"); ret_len = RNA_parameter_dynamic_length_get(&list, parm); - if (ret_len != len && ret_len != 0) { + if (!ELEM(ret_len, len, 0)) { printf("%s: Error, py func returned %d items in %s, %d or none were expected.\n", __func__, RNA_parameter_dynamic_length_get(&list, parm), @@ -565,7 +565,7 @@ static void uilist_filter_items(uiList *ui_list, parm = RNA_function_find_parameter(NULL, func, "filter_neworder"); ret_len = RNA_parameter_dynamic_length_get(&list, parm); - if (ret_len != len && ret_len != 0) { + if (!ELEM(ret_len, len, 0)) { printf("%s: Error, py func returned %d items in %s, %d or none were expected.\n", __func__, RNA_parameter_dynamic_length_get(&list, parm), diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index 9fe3153eb1e..d8be9b7e80d 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -33,7 +33,7 @@ static wmXrData *rna_XrSession_wm_xr_data_get(PointerRNA *ptr) /* Callers could also get XrSessionState pointer through ptr->data, but prefer if we just * consistently pass wmXrData pointers to the WM_xr_xxx() API. */ - BLI_assert((ptr->type == &RNA_XrSessionSettings) || (ptr->type == &RNA_XrSessionState)); + BLI_assert(ELEM(ptr->type, &RNA_XrSessionSettings, &RNA_XrSessionState)); wmWindowManager *wm = (wmWindowManager *)ptr->owner_id; BLI_assert(wm && (GS(wm->id.name) == ID_WM)); @@ -739,7 +739,7 @@ static bool rna_XrSessionState_action_create(bContext *C, return false; } - const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_float_action = ELEM(ami->type, XR_FLOAT_INPUT, XR_VECTOR2F_INPUT); const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); wmOperatorType *ot = NULL; IDProperty *op_properties = NULL; @@ -791,7 +791,7 @@ static bool rna_XrSessionState_action_binding_create(bContext *C, return false; } - const bool is_float_action = (ami->type == XR_FLOAT_INPUT || ami->type == XR_VECTOR2F_INPUT); + const bool is_float_action = ELEM(ami->type, XR_FLOAT_INPUT, XR_VECTOR2F_INPUT); const bool is_button_action = (is_float_action || ami->type == XR_BOOLEAN_INPUT); const bool is_pose_action = (ami->type == XR_POSE_INPUT); float float_thresholds[2]; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index a01da3b1fbd..f6e71ba5df3 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -5202,7 +5202,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void #ifdef WITH_XR_OPENXR void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val) { - BLI_assert(val == KM_PRESS || val == KM_RELEASE); + BLI_assert(ELEM(val, KM_PRESS, KM_RELEASE)); wmEvent event = { .type = EVT_XR_ACTION, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c index 54800a5cd35..3c090423c41 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_operators.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -867,7 +867,7 @@ static void wm_xr_fly_compute_turn(eXrFlyMode mode, const float nav_inv[4][4], float r_delta[4][4]) { - BLI_assert(mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + BLI_assert(ELEM(mode, XR_FLY_TURNLEFT, XR_FLY_TURNRIGHT)); float z_axis[3], m[3][3], prev[4][4], curr[4][4]; diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 736f1c34de6..6651aa77725 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -262,7 +262,7 @@ static int *parse_int_relative_clamp_n( int i = 0; while (true) { const char *str_end = strchr(str, sep); - if ((*str == sep) || (*str == '\0')) { + if (ELEM(*str, sep, '\0')) { static const char *msg = "incorrect comma use"; *r_err_msg = msg; goto fail; -- cgit v1.2.3 From e55f4657f7b7ae8ac915eefd633d08ae56bb3574 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 13:01:05 +1100 Subject: Fix T89560: Keymap editor no longer shows keying set dropdown Include built-in keying sets when ANIM_keying_sets_enum_itemf is called without a context to allow binding keys to built-in keying sets. --- source/blender/editors/animation/keyingsets.c | 67 +++++++++++++-------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 6fcdd21bad8..dcf8835c911 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -715,55 +715,54 @@ const EnumPropertyItem *ANIM_keying_sets_enum_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { - Scene *scene = CTX_data_scene(C); KeyingSet *ks; EnumPropertyItem *item = NULL, item_tmp = {0}; int totitem = 0; int i = 0; - if (C == NULL) { - return DummyRNA_DEFAULT_items; - } + if (C != NULL) { + Scene *scene = CTX_data_scene(C); + /* active Keying Set + * - only include entry if it exists + */ + if (scene->active_keyingset) { + /* active Keying Set */ + item_tmp.identifier = "__ACTIVE__"; + item_tmp.name = "Active Keying Set"; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); - /* active Keying Set - * - only include entry if it exists - */ - if (scene->active_keyingset) { - /* active Keying Set */ - item_tmp.identifier = "__ACTIVE__"; - item_tmp.name = "Active Keying Set"; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); - - /* separator */ - RNA_enum_item_add_separator(&item, &totitem); - } + /* separator */ + RNA_enum_item_add_separator(&item, &totitem); + } - i++; + i++; - /* user-defined Keying Sets - * - these are listed in the order in which they were defined for the active scene - */ - if (scene->keyingsets.first) { - for (ks = scene->keyingsets.first; ks; ks = ks->next, i++) { - if (ANIM_keyingset_context_ok_poll(C, ks)) { - item_tmp.identifier = ks->idname; - item_tmp.name = ks->name; - item_tmp.description = ks->description; - item_tmp.value = i; - RNA_enum_item_add(&item, &totitem, &item_tmp); + /* user-defined Keying Sets + * - these are listed in the order in which they were defined for the active scene + */ + if (scene->keyingsets.first) { + for (ks = scene->keyingsets.first; ks; ks = ks->next, i++) { + if (ANIM_keyingset_context_ok_poll(C, ks)) { + item_tmp.identifier = ks->idname; + item_tmp.name = ks->name; + item_tmp.description = ks->description; + item_tmp.value = i; + RNA_enum_item_add(&item, &totitem, &item_tmp); + } } - } - /* separator */ - RNA_enum_item_add_separator(&item, &totitem); + /* separator */ + RNA_enum_item_add_separator(&item, &totitem); + } } /* builtin Keying Sets */ i = -1; for (ks = builtin_keyingsets.first; ks; ks = ks->next, i--) { - /* only show KeyingSet if context is suitable */ - if (ANIM_keyingset_context_ok_poll(C, ks)) { + /* Only show #KeyingSet if context is suitable or if there is no context which is needed + * to support key-bindings to be assigned since key bindings are not context aware. */ + if ((C == NULL) || ANIM_keyingset_context_ok_poll(C, ks)) { item_tmp.identifier = ks->idname; item_tmp.name = ks->name; item_tmp.description = ks->description; -- cgit v1.2.3 From 399ac1ff113e20688f277157ccb3c671648c9b1c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 13:19:33 +1100 Subject: Fix subtype & default for keying set operator properties Error in 1a12c9edab4ac89c8a87c20ad3a2195d0e681bc8, also correct descriptions. --- source/blender/editors/animation/keyframing.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index a4845de7d7d..25b1ccecf2e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1957,7 +1957,8 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* keyingset to use (idname) */ - prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", ""); + prop = RNA_def_string( + ot->srna, "type", "", MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } @@ -2117,7 +2118,8 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* keyingset to use (idname) */ - prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", ""); + prop = RNA_def_string( + ot->srna, "type", "", MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } -- cgit v1.2.3 From 20f53f6984997f94def980b71a4e71119dbb147d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 13:43:39 +1100 Subject: Correct assert in 399ac1ff113e20688f277157ccb3c671648c9b1c Pass NULL instead of an empty string. --- source/blender/editors/animation/keyframing.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 25b1ccecf2e..feb78ce293e 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1958,7 +1958,7 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( - ot->srna, "type", "", MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } @@ -2119,7 +2119,7 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) /* keyingset to use (idname) */ prop = RNA_def_string( - ot->srna, "type", "", MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); + ot->srna, "type", NULL, MAX_ID_NAME - 2, "Keying Set", "The Keying Set to use"); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; } -- cgit v1.2.3 From 136f0910ff662a74b02c30ae9b79275bc74c8406 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 16:10:19 +1100 Subject: Fix T96250: Invalid default for uv_relax_method Correct the default & version existing files. --- source/blender/blenloader/intern/versioning_300.c | 7 +++++++ source/blender/makesdna/DNA_scene_defaults.h | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 2a840ea585a..32dcb977110 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -2578,5 +2578,12 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *ts = scene->toolsettings; + if (ts->uv_relax_method == 0) { + ts->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN; + } + } } } diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 82f788e0623..74db1d14bbc 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -360,6 +360,10 @@ /* GP Stroke Placement */ \ .gpencil_v3d_align = GP_PROJECT_VIEWSPACE, \ .gpencil_v2d_align = GP_PROJECT_VIEWSPACE, \ + \ + /* UV painting */ \ + .uv_sculpt_settings = 0, \ + .uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN, \ } /* clang-format off */ -- cgit v1.2.3 From 9416004092c5db4b16e8c3f241f3ae789c8789bc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 16:29:22 +1100 Subject: Cleanup: use doxygen comments & sections for animation files --- source/blender/editors/animation/anim_intern.h | 61 +++++++++---- source/blender/editors/animation/keyframing.c | 34 ++++--- source/blender/editors/include/ED_anim_api.h | 119 +++++++++++++------------ 3 files changed, 129 insertions(+), 85 deletions(-) diff --git a/source/blender/editors/animation/anim_intern.h b/source/blender/editors/animation/anim_intern.h index e7277c51cd3..8d8d624c69b 100644 --- a/source/blender/editors/animation/anim_intern.h +++ b/source/blender/editors/animation/anim_intern.h @@ -14,49 +14,80 @@ extern ListBase builtin_keyingsets; /* Operator Define Prototypes ------------------- */ -/* Main Keyframe Management operators: - * These handle keyframes management from various spaces. They only make use of - * Keying Sets. - */ +/* -------------------------------------------------------------------- */ +/** \name Main Keyframe Management operators + * + * These handle keyframes management from various spaces. + * They only make use of Keying Sets. + * \{ */ + void ANIM_OT_keyframe_insert(struct wmOperatorType *ot); void ANIM_OT_keyframe_delete(struct wmOperatorType *ot); void ANIM_OT_keyframe_insert_by_name(struct wmOperatorType *ot); void ANIM_OT_keyframe_delete_by_name(struct wmOperatorType *ot); -/* Main Keyframe Management operators: - * These handle keyframes management from various spaces. They will handle the menus - * required for each space. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Keyframe Management operators + * + * These handle keyframes management from various spaces. + * They will handle the menus required for each space. + * \{ */ + void ANIM_OT_keyframe_insert_menu(struct wmOperatorType *ot); void ANIM_OT_keyframe_delete_v3d(struct wmOperatorType *ot); void ANIM_OT_keyframe_clear_v3d(struct wmOperatorType *ot); -/* Keyframe management operators for UI buttons (RMB menu). */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Keyframe management operators for UI buttons (RMB menu) + * \{ */ + void ANIM_OT_keyframe_insert_button(struct wmOperatorType *ot); void ANIM_OT_keyframe_delete_button(struct wmOperatorType *ot); void ANIM_OT_keyframe_clear_button(struct wmOperatorType *ot); -/* .......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name KeyingSet management operators for UI buttons (RMB menu) + * \{ */ -/* KeyingSet management operators for UI buttons (RMB menu) */ void ANIM_OT_keyingset_button_add(struct wmOperatorType *ot); void ANIM_OT_keyingset_button_remove(struct wmOperatorType *ot); -/* KeyingSet management operators for RNA collections/UI buttons */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name KeyingSet management operators for RNA collections/UI buttons + * \{ */ + void ANIM_OT_keying_set_add(struct wmOperatorType *ot); void ANIM_OT_keying_set_remove(struct wmOperatorType *ot); void ANIM_OT_keying_set_path_add(struct wmOperatorType *ot); void ANIM_OT_keying_set_path_remove(struct wmOperatorType *ot); -/* KeyingSet general operators */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name KeyingSet general operators + * \{ */ + void ANIM_OT_keying_set_active_set(struct wmOperatorType *ot); -/* .......... */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Driver management operators for UI buttons (RMB menu) + * \{ */ -/* Driver management operators for UI buttons (RMB menu) */ void ANIM_OT_driver_button_add(struct wmOperatorType *ot); void ANIM_OT_driver_button_remove(struct wmOperatorType *ot); void ANIM_OT_driver_button_edit(struct wmOperatorType *ot); void ANIM_OT_copy_driver_button(struct wmOperatorType *ot); void ANIM_OT_paste_driver_button(struct wmOperatorType *ot); + +/** \} */ diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index feb78ce293e..90c127b620b 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -244,7 +244,7 @@ FCurve *ED_action_fcurve_ensure(struct Main *bmain, return fcu; } -/* Helper for update_autoflags_fcurve() */ +/** Helper for #update_autoflags_fcurve(). */ static void update_autoflags_fcurve_direct(FCurve *fcu, PropertyRNA *prop) { /* set additional flags for the F-Curve (i.e. only integer values) */ @@ -304,7 +304,8 @@ void update_autoflags_fcurve(FCurve *fcu, bContext *C, ReportList *reports, Poin /* ************************************************** */ /* KEYFRAME INSERTION */ -/* Move the point where a key is about to be inserted to be inside the main cycle range. +/** + * Move the point where a key is about to be inserted to be inside the main cycle range. * Returns the type of the cycle if it is enabled and valid. */ static eFCU_Cycle_Type remap_cyclic_keyframe_location(FCurve *fcu, float *px, float *py) @@ -345,7 +346,7 @@ static eFCU_Cycle_Type remap_cyclic_keyframe_location(FCurve *fcu, float *px, fl return type; } -/* Used to make curves newly added to a cyclic Action cycle with the correct period. */ +/** Used to make curves newly added to a cyclic Action cycle with the correct period. */ static void make_new_fcurve_cyclic(const bAction *act, FCurve *fcu) { /* The curve must contain one (newly-added) keyframe. */ @@ -652,11 +653,13 @@ enum { KEYNEEDED_DELNEXT, } /*eKeyNeededStatus*/; -/* This helper function determines whether a new keyframe is needed */ -/* Cases where keyframes should not be added: - * 1. Keyframe to be added between two keyframes with similar values - * 2. Keyframe to be added on frame where two keyframes are already situated - * 3. Keyframe lies at point that intersects the linear line between two keyframes +/** + * This helper function determines whether a new keyframe is needed. + * + * Cases where keyframes should not be added: + * 1. Keyframe to be added between two keyframes with similar values. + * 2. Keyframe to be added on frame where two keyframes are already situated. + * 3. Keyframe lies at point that intersects the linear line between two keyframes. */ static short new_key_needed(FCurve *fcu, float cFrame, float nValue) { @@ -769,7 +772,7 @@ static short new_key_needed(FCurve *fcu, float cFrame, float nValue) /* ------------------ RNA Data-Access Functions ------------------ */ -/* Try to read value using RNA-properties obtained already */ +/** Try to read value using RNA-properties obtained already. */ static float *setting_get_rna_values( PointerRNA *ptr, PropertyRNA *prop, float *buffer, int buffer_size, int *r_count) { @@ -844,7 +847,8 @@ enum { VISUALKEY_SCA, }; -/* This helper function determines if visual-keyframing should be used when +/** + * This helper function determines if visual-keyframing should be used when * inserting keyframes for the given channel. As visual-keyframing only works * on Object and Pose-Channel blocks, this should only get called for those * blocktypes, when using "standard" keying but 'Visual Keying' option in Auto-Keying @@ -1012,7 +1016,8 @@ static bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop) return false; } -/* This helper function extracts the value to use for visual-keyframing +/** + * This helper function extracts the value to use for visual-keyframing * In the event that it is not possible to perform visual keying, try to fall-back * to using the default method. Assumes that all data it has been passed is valid. */ @@ -1305,7 +1310,7 @@ bool insert_keyframe_direct(ReportList *reports, return insert_keyframe_value(reports, &ptr, prop, fcu, anim_eval_context, curval, keytype, flag); } -/* Find or create the FCurve based on the given path, and insert the specified value into it. */ +/** Find or create the #FCurve based on the given path, and insert the specified value into it. */ static bool insert_keyframe_fcurve_value(Main *bmain, ReportList *reports, PointerRNA *ptr, @@ -1829,9 +1834,10 @@ enum { COMMONKEY_MODE_DELETE, } /*eCommonModifyKey_Modes*/; -/* Polling callback for use with ANIM_*_keyframe() operators +/** + * Polling callback for use with `ANIM_*_keyframe()` operators * This is based on the standard ED_operator_areaactive callback, - * except that it does special checks for a few spacetypes too... + * except that it does special checks for a few space-types too. */ static bool modify_key_op_poll(bContext *C) { diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 4b6f5e4cac6..3402d45fa96 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -51,8 +51,9 @@ struct PropertyRNA; /** \name Context * \{ */ -/* This struct defines a structure used for animation-specific - * 'context' information +/** + * This struct defines a structure used for animation-specific + * 'context' information. */ typedef struct bAnimContext { /** data to be filtered for use in animation editor */ @@ -118,8 +119,9 @@ typedef enum eAnimCont_Types { /** \name Channels * \{ */ -/* This struct defines a structure used for quick and uniform access for - * channels of animation data +/** + * This struct defines a structure used for quick and uniform access for + * channels of animation data. */ typedef struct bAnimListElem { struct bAnimListElem *next, *prev; @@ -257,7 +259,8 @@ typedef enum eAnim_KeyType { ALE_GROUP, /* Action Group summary */ } eAnim_KeyType; -/* Flags for specifying the types of updates (i.e. recalculation/refreshing) that +/** + * Flags for specifying the types of updates (i.e. recalculation/refreshing) that * needs to be performed to the data contained in a channel following editing. * For use with ANIM_animdata_update() */ @@ -379,13 +382,13 @@ typedef enum eAnimFilter_Flags { #define EXPANDED_DRVD(adt) ((adt->flag & ADT_DRIVERS_COLLAPSED) == 0) /* Actions (also used for Dopesheet) */ -/* Action Channel Group */ +/** Action Channel Group. */ #define EDITABLE_AGRP(agrp) (((agrp)->flag & AGRP_PROTECTED) == 0) #define EXPANDED_AGRP(ac, agrp) \ (((!(ac) || ((ac)->spacetype != SPACE_GRAPH)) && ((agrp)->flag & AGRP_EXPANDED)) || \ (((ac) && ((ac)->spacetype == SPACE_GRAPH)) && ((agrp)->flag & AGRP_EXPANDED_G))) #define SEL_AGRP(agrp) (((agrp)->flag & AGRP_SELECTED) || ((agrp)->flag & AGRP_ACTIVE)) -/* F-Curve Channels */ +/** F-Curve Channels. */ #define EDITABLE_FCU(fcu) ((fcu->flag & FCURVE_PROTECTED) == 0) #define SEL_FCU(fcu) (fcu->flag & FCURVE_SELECTED) @@ -394,16 +397,16 @@ typedef enum eAnimFilter_Flags { #define SEL_SHAPEKEY(kb) (kb->flag & KEYBLOCK_SEL) /* Grease Pencil only */ -/* Grease Pencil datablock settings */ +/** Grease Pencil data-block settings. */ #define EXPANDED_GPD(gpd) (gpd->flag & GP_DATA_EXPAND) -/* Grease Pencil Layer settings */ +/** Grease Pencil Layer settings. */ #define EDITABLE_GPL(gpl) ((gpl->flag & GP_LAYER_LOCKED) == 0) #define SEL_GPL(gpl) (gpl->flag & GP_LAYER_SELECT) /* Mask Only */ -/* Grease Pencil datablock settings */ +/** Grease Pencil data-block settings. */ #define EXPANDED_MASK(mask) (mask->flag & MASK_ANIMF_EXPAND) -/* Grease Pencil Layer settings */ +/** Grease Pencil Layer settings. */ #define EDITABLE_MASK(masklay) ((masklay->flag & MASK_LAYERFLAG_LOCKED) == 0) #define SEL_MASKLAY(masklay) (masklay->flag & SELECT) @@ -426,20 +429,20 @@ typedef enum eAnimFilter_Flags { /** \name Channel Defines * \{ */ -/* channel heights */ +/** Channel heights. */ #define ACHANNEL_FIRST_TOP(ac) \ (UI_view2d_scale_get_y(&(ac)->region->v2d) * -UI_TIME_SCRUB_MARGIN_Y - ACHANNEL_SKIP) #define ACHANNEL_HEIGHT(ac) (0.8f * (ac)->yscale_fac * U.widget_unit) #define ACHANNEL_SKIP (0.1f * U.widget_unit) #define ACHANNEL_STEP(ac) (ACHANNEL_HEIGHT(ac) + ACHANNEL_SKIP) -/* Additional offset to give some room at the end. */ +/** Additional offset to give some room at the end. */ #define ACHANNEL_TOT_HEIGHT(ac, item_amount) \ (-ACHANNEL_FIRST_TOP(ac) + ACHANNEL_STEP(ac) * (item_amount + 1)) -/* channel widths */ +/** Channel widths. */ #define ACHANNEL_NAMEWIDTH (10 * U.widget_unit) -/* channel toggle-buttons */ +/** Channel toggle-buttons. */ #define ACHANNEL_BUTTON_WIDTH (0.8f * U.widget_unit) /** \} */ @@ -448,7 +451,7 @@ typedef enum eAnimFilter_Flags { /** \name NLA Channel Defines * \{ */ -/* NLA channel heights */ +/** NLA channel heights */ #define NLACHANNEL_FIRST_TOP(ac) \ (UI_view2d_scale_get_y(&(ac)->region->v2d) * -UI_TIME_SCRUB_MARGIN_Y - NLACHANNEL_SKIP) #define NLACHANNEL_HEIGHT(snla) \ @@ -456,14 +459,14 @@ typedef enum eAnimFilter_Flags { (1.2f * U.widget_unit)) #define NLACHANNEL_SKIP (0.1f * U.widget_unit) #define NLACHANNEL_STEP(snla) (NLACHANNEL_HEIGHT(snla) + NLACHANNEL_SKIP) -/* Additional offset to give some room at the end. */ +/** Additional offset to give some room at the end. */ #define NLACHANNEL_TOT_HEIGHT(ac, item_amount) \ (-NLACHANNEL_FIRST_TOP(ac) + NLACHANNEL_STEP(((SpaceNla *)(ac)->sl)) * (item_amount + 1)) -/* channel widths */ +/** Channel widths */ #define NLACHANNEL_NAMEWIDTH (10 * U.widget_unit) -/* channel toggle-buttons */ +/** Channel toggle-buttons */ #define NLACHANNEL_BUTTON_WIDTH (0.8f * U.widget_unit) /** \} */ @@ -521,7 +524,7 @@ void ANIM_animdata_freelist(ListBase *anim_data); /** \name Drawing TypeInfo * \{ */ -/* role or level of animchannel in the hierarchy */ +/** Role or level of anim-channel in the hierarchy. */ typedef enum eAnimChannel_Role { /** datablock expander - a "composite" channel type */ ACHANNEL_ROLE_EXPANDER = -1, @@ -561,7 +564,7 @@ typedef enum eAnimChannel_Settings { ACHANNEL_SETTING_ALWAYS_VISIBLE = 8, } eAnimChannel_Settings; -/* Drawing, mouse handling, and flag setting behavior... */ +/** Drawing, mouse handling, and flag setting behavior. */ typedef struct bAnimChannelType { /* -- Type data -- */ /* name of the channel type, for debugging */ @@ -570,30 +573,31 @@ typedef struct bAnimChannelType { eAnimChannel_Role channel_role; /* -- Drawing -- */ - /* get RGB color that is used to draw the majority of the backdrop */ + /** Get RGB color that is used to draw the majority of the backdrop. */ void (*get_backdrop_color)(bAnimContext *ac, bAnimListElem *ale, float r_color[3]); - /* draw backdrop strip for channel */ + /** Draw backdrop strip for channel. */ void (*draw_backdrop)(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc); - /* get depth of indention (relative to the depth channel is nested at) */ + /** Get depth of indention (relative to the depth channel is nested at). */ short (*get_indent_level)(bAnimContext *ac, bAnimListElem *ale); - /* get offset in pixels for the start of the channel (in addition to the indent depth) */ + /** Get offset in pixels for the start of the channel (in addition to the indent depth). */ short (*get_offset)(bAnimContext *ac, bAnimListElem *ale); - /* get name (for channel lists) */ + /** Get name (for channel lists). */ void (*name)(bAnimListElem *ale, char *name); - /* get RNA property+pointer for editing the name */ + /** Get RNA property+pointer for editing the name. */ bool (*name_prop)(bAnimListElem *ale, struct PointerRNA *ptr, struct PropertyRNA **prop); - /* get icon (for channel lists) */ + /** Get icon (for channel lists). */ int (*icon)(bAnimListElem *ale); /* -- Settings -- */ - /* check if the given setting is valid in the current context */ + /** Check if the given setting is valid in the current context. */ bool (*has_setting)(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting); - /* get the flag used for this setting */ + /** Get the flag used for this setting. */ int (*setting_flag)(bAnimContext *ac, eAnimChannel_Settings setting, bool *neg); - /* get the pointer to int/short where data is stored, - * with type being sizeof(ptr_data) which should be fine for runtime use... - * - assume that setting has been checked to be valid for current context + /** + * Get the pointer to int/short where data is stored, + * with type being `sizeof(ptr_data)` which should be fine for runtime use. + * - assume that setting has been checked to be valid for current context. */ void *(*setting_ptr)(bAnimListElem *ale, eAnimChannel_Settings setting, short *type); } bAnimChannelType; @@ -720,11 +724,11 @@ bool ANIM_remove_empty_action_from_animdata(struct AnimData *adt); /* flags for Current Frame Drawing */ typedef enum eAnimEditDraw_CurrentFrame { - /* plain time indicator with no special indicators */ + /** Plain time indicator with no special indicators. */ /* DRAWCFRA_PLAIN = 0, */ /* UNUSED */ - /* time indication in seconds or frames */ + /** Time indication in seconds or frames. */ DRAWCFRA_UNIT_SECONDS = (1 << 0), - /* draw indicator extra wide (for timeline) */ + /** Draw indicator extra wide (for timeline). */ DRAWCFRA_WIDE = (1 << 1), } eAnimEditDraw_CurrentFrame; @@ -784,9 +788,9 @@ struct NlaTrack *ANIM_nla_context_track(const struct bContext *C); struct NlaStrip *ANIM_nla_context_strip(const struct bContext *C); struct FCurve *ANIM_graph_context_fcurve(const struct bContext *C); -/* Needed for abstraction between the graph editor and the NLA editor. */ +/** Needed for abstraction between the graph editor and the NLA editor. */ typedef bool (*PanelTypePollFn)(const struct bContext *C, struct PanelType *pt); -/* Avoid including "UI_interface.h" here. */ +/** Avoid including `UI_interface.h` here. */ typedef void (*uiListPanelIDFromDataFunc)(void *data_link, char *r_idname); /** @@ -912,20 +916,21 @@ void ED_nla_postop_refresh(bAnimContext *ac); /* anim_draw.c */ -/* flags for conversion mapping */ +/** Flags for conversion mapping. */ typedef enum eAnimUnitConv_Flags { - /* restore to original internal values */ + /** Restore to original internal values. */ ANIM_UNITCONV_RESTORE = (1 << 0), - /* ignore handles (i.e. only touch main keyframes) */ + /** Ignore handles (i.e. only touch main keyframes). */ ANIM_UNITCONV_ONLYKEYS = (1 << 1), - /* only touch selected BezTriples */ + /** Only touch selected BezTriples. */ ANIM_UNITCONV_ONLYSEL = (1 << 2), - /* only touch selected vertices */ + /** Only touch selected vertices. */ ANIM_UNITCONV_SELVERTS = (1 << 3), /* ANIM_UNITCONV_SKIPKNOTS = (1 << 4), */ /* UNUSED */ - /* Scale FCurve i a way it fits to -1..1 space */ + /** Scale FCurve i a way it fits to -1..1 space. */ ANIM_UNITCONV_NORMALIZE = (1 << 5), - /* Only when normalization is used: use scale factor from previous run, + /** + * Only when normalization is used: use scale factor from previous run, * prevents curves from jumping all over the place when tweaking them. */ ANIM_UNITCONV_NORMALIZE_FREEZE = (1 << 6), @@ -953,10 +958,11 @@ float ANIM_unit_mapping_get_factor( */ #define BEZKEYTYPE(bezt) ((bezt)->hide) -/* set/clear/toggle macro - * - channel - channel with a 'flag' member that we're setting - * - smode - 0=clear, 1=set, 2=invert - * - sflag - bitflag to set +/** + * Set/Clear/Toggle macro. + * \param channel: Channel with a 'flag' member that we're setting. + * \param smode: 0=clear, 1=set, 2=invert. + * \param sflag: bit-flag to set. */ #define ACHANNEL_SET_FLAG(channel, smode, sflag) \ { \ @@ -972,10 +978,11 @@ float ANIM_unit_mapping_get_factor( } \ ((void)0) -/* set/clear/toggle macro, where the flag is negative - * - channel - channel with a 'flag' member that we're setting - * - smode - 0=clear, 1=set, 2=invert - * - sflag - bitflag to set +/** + * Set/Clear/Toggle macro, where the flag is negative. + * \param channel: channel with a 'flag' member that we're setting. + * \param smode: 0=clear, 1=set, 2=invert. + * \param sflag: Bit-flag to set. */ #define ACHANNEL_SET_FLAG_NEG(channel, smode, sflag) \ { \ @@ -1067,13 +1074,13 @@ void ED_drivers_editor_init(struct bContext *C, struct ScrArea *area); /* ************************************************ */ typedef enum eAnimvizCalcRange { - /* Update motion paths at the current frame only. */ + /** Update motion paths at the current frame only. */ ANIMVIZ_CALC_RANGE_CURRENT_FRAME, - /* Try to limit updates to a close neighborhood of the current frame. */ + /** Try to limit updates to a close neighborhood of the current frame. */ ANIMVIZ_CALC_RANGE_CHANGED, - /* Update an entire range of the motion paths. */ + /** Update an entire range of the motion paths. */ ANIMVIZ_CALC_RANGE_FULL, } eAnimvizCalcRange; -- cgit v1.2.3 From 1f9d85f56aace91e7958566c446e079b7f1c21c6 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 9 Mar 2022 17:18:31 +1100 Subject: Fix cursor drag failing to use click-start Error in b8960267dd51f9108b3b49e9b762e6b4d35ae1dc, the flag was cleared before use. --- source/blender/editors/transform/transform.c | 6 ------ source/blender/editors/transform/transform_generics.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 9810f1cb6ad..bb80109d8c9 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1706,12 +1706,6 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->launch_event = LEFTMOUSE; } - if (options & CTX_CURSOR) { - /* Cursor should always use the drag start as the combination of click-drag to place & move - * doesn't work well if the click location isn't used when transforming. */ - t->flag |= T_EVENT_DRAG_START; - } - unit_m3(t->spacemtx); initTransInfo(C, t, op, event); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 776229d66a0..3b9e2a982dc 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -206,6 +206,12 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->obedit_type = -1; } + if (t->options & CTX_CURSOR) { + /* Cursor should always use the drag start as the combination of click-drag to place & move + * doesn't work well if the click location isn't used when transforming. */ + t->flag |= T_EVENT_DRAG_START; + } + /* Many kinds of transform only use a single handle. */ if (t->data_container == NULL) { t->data_container = MEM_callocN(sizeof(*t->data_container), __func__); -- cgit v1.2.3 From 801d7b4921dce55d1fb0d2989935408db1e1691e Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 9 Mar 2022 10:34:59 +0100 Subject: Fix T95738: wrong "Bottom" selection for cone without base Original patch by @lone_noel with changes by me. Differential Revision: https://developer.blender.org/D14102 --- .../blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index d84a2e73172..b882d4bdf09 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -510,11 +510,11 @@ static void calculate_selection_outputs(Mesh *mesh, if (config.bottom_is_point) { selection[config.last_vert] = true; } + else if (face) { + selection.slice(config.bottom_faces_start, config.bottom_faces_len).fill(true); + } else { - selection - .slice(config.bottom_faces_start, - face ? config.bottom_faces_len : config.circle_segments) - .fill(true); + selection.slice(config.last_ring_verts_start + 1, config.circle_segments).fill(true); } attribute.save(); } -- cgit v1.2.3 From 04e89c5b06739a9a507942e2a9c67fb59b975600 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Tue, 8 Mar 2022 14:43:48 +0100 Subject: Fix T96165: Incorrect render of barcelone scene with BVH2 Fix T95462: Partly transparent objects appear to glow in the dark The issue was caused by incorrect check for exceeded number of transparent bounces: the same maximum distance was used for picking up N closest intersections and counting overall intersections count. Now made it so intersection count is using ray distance which matches the way how Embree and OptiX implementation works. Benchmark result: {F12907888} There is no big time difference in the pabellon scene. The Victor scene timing doesn't seem to be very reliable as the variance in time across different benchmark runs is quite high. Differential Revision: https://developer.blender.org/D14280 --- intern/cycles/kernel/bvh/shadow_all.h | 73 +++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/intern/cycles/kernel/bvh/shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h index a5769e91a28..2f58929c1e5 100644 --- a/intern/cycles/kernel/bvh/shadow_all.h +++ b/intern/cycles/kernel/bvh/shadow_all.h @@ -29,8 +29,8 @@ ccl_device_inline IntegratorShadowState state, const uint visibility, const uint max_hits, - ccl_private uint *num_recorded_hits, - ccl_private float *throughput) + ccl_private uint *r_num_recorded_hits, + ccl_private float *r_throughput) { /* todo: * - likely and unlikely for if() statements @@ -60,15 +60,18 @@ ccl_device_inline * recorded hits is exceeded and we no longer need to find hits beyond the max * distance found. */ float t_max_world = ray->t; - /* Equal to t_max_world when traversing top level BVH, transformed into local - * space when entering instances. */ - float t_max_current = t_max_world; + + /* Current maximum distance to the intersection. + * Is calculated as a ray length, transformed to an object space when entering + * instance node. */ + float t_max_current = ray->t; + /* Conversion from world to local space for the current instance if any, 1.0 * otherwise. */ float t_world_to_instance = 1.0f; - *num_recorded_hits = 0; - *throughput = 1.0f; + *r_num_recorded_hits = 0; + *r_throughput = 1.0f; /* traversal loop */ do { @@ -237,10 +240,10 @@ ccl_device_inline /* Always use baked shadow transparency for curves. */ if (isect.type & PRIMITIVE_CURVE) { - *throughput *= intersection_curve_shadow_transparency( + *r_throughput *= intersection_curve_shadow_transparency( kg, isect.object, isect.prim, isect.u); - if (*throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + if (*r_throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { return true; } else { @@ -249,37 +252,39 @@ ccl_device_inline } if (record_intersection) { - /* Increase the number of hits, possibly beyond max_hits, we will - * simply not record those and only keep the max_hits closest. */ - uint record_index = (*num_recorded_hits)++; - + /* Test if we need to record this transparent intersection. */ const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); - if (record_index >= max_record_hits - 1) { - /* If maximum number of hits reached, find the intersection with - * the largest distance to potentially replace when another hit - * is found. */ - const int num_recorded_hits = min(max_record_hits, record_index); - float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); - int max_recorded_hit = 0; - - for (int i = 1; i < num_recorded_hits; i++) { - const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); - if (isect_t > max_recorded_t) { - max_recorded_t = isect_t; - max_recorded_hit = i; + if (*r_num_recorded_hits < max_record_hits || isect.t < t_max_world) { + /* If maximum number of hits was reached, replace the intersection with the + * highest distance. We want to find the N closest intersections. */ + const uint num_recorded_hits = min(*r_num_recorded_hits, max_record_hits); + uint isect_index = num_recorded_hits; + if (num_recorded_hits + 1 >= max_record_hits) { + float max_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); + uint max_recorded_hit = 0; + + for (uint i = 1; i < num_recorded_hits; ++i) { + const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); + if (isect_t > max_t) { + max_recorded_hit = i; + max_t = isect_t; + } + } + + if (num_recorded_hits >= max_record_hits) { + isect_index = max_recorded_hit; } - } - if (record_index >= max_record_hits) { - record_index = max_recorded_hit; + /* Limit the ray distance and stop counting hits beyond this. */ + t_max_world = max(isect.t, max_t); } - /* Limit the ray distance and stop counting hits beyond this. */ - t_max_world = max(max_recorded_t, isect.t); - t_max_current = t_max_world * t_world_to_instance; + integrator_state_write_shadow_isect(state, &isect, isect_index); } - integrator_state_write_shadow_isect(state, &isect, record_index); + /* Always increase the number of recorded hits, even beyond the maximum, + * so that we can detect this and trace another ray if needed. */ + ++(*r_num_recorded_hits); } } } @@ -318,7 +323,7 @@ ccl_device_inline #endif /* Restore world space ray length. */ - t_max_current = t_max_world; + t_max_current = ray->t; object = OBJECT_NONE; t_world_to_instance = 1.0f; -- cgit v1.2.3 From 010ea688f1b93bdd8b0fc3f68d37c541eb7b96ae Mon Sep 17 00:00:00 2001 From: Falk David Date: Wed, 9 Mar 2022 11:21:39 +0100 Subject: Fix T96233: Crash with gpencil data with vertex groups Blender crashes when a multi-user grease pencil object has vertex groups and is modified by modifiers, layer transform or parenting. The fix makes sure that we copy the vertex group names list. Reviewed By: antoniov Maniphest Tasks: T96233 Differential Revision: https://developer.blender.org/D14275 --- source/blender/blenkernel/intern/gpencil_modifier.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 9c546cc118a..33410b4521e 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -28,6 +28,7 @@ #include "DNA_screen_types.h" #include "BKE_colortools.h" +#include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_gpencil_geom.h" #include "BKE_gpencil_modifier.h" @@ -632,6 +633,8 @@ static bGPdata *gpencil_copy_structure_for_eval(bGPdata *gpd) gpd_eval->mat = MEM_dupallocN(gpd->mat); } + BKE_defgroup_copy_list(&gpd_eval->vertex_group_names, &gpd->vertex_group_names); + /* Duplicate structure: layers and frames without strokes. */ LISTBASE_FOREACH (bGPDlayer *, gpl_orig, &gpd->layers) { bGPDlayer *gpl_eval = BKE_gpencil_layer_duplicate(gpl_orig, true, false); -- cgit v1.2.3 From 4da282a1615d23ffcdfdc43998dce5091455078b Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 9 Mar 2022 11:33:17 +0100 Subject: Curves: rename initial brushes The brushes that are still called "Test" still need to be changed until they can get their correct name. Ref T96259. --- source/blender/editors/sculpt_paint/curves_sculpt_ops.cc | 14 +++++++------- source/blender/makesdna/DNA_brush_enums.h | 10 +++++----- source/blender/makesrna/intern/rna_brush.c | 6 +++--- source/blender/windowmanager/intern/wm_toolsystem.c | 2 +- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 4dee7641623..1411f30bffd 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -273,7 +273,7 @@ class CombOperation : public CurvesSculptStrokeOperation { /** * Drags the tip point of each curve and resamples the rest of the curve. */ -class GrowOperation : public CurvesSculptStrokeOperation { +class SnakeHookOperation : public CurvesSculptStrokeOperation { private: float2 last_mouse_position_; @@ -936,15 +936,15 @@ static std::unique_ptr start_brush_operation(bConte CurvesSculpt &curves_sculpt = *scene.toolsettings->curves_sculpt; Brush &brush = *BKE_paint_brush(&curves_sculpt.paint); switch (brush.curves_sculpt_tool) { - case CURVES_SCULPT_TOOL_TEST1: + case CURVES_SCULPT_TOOL_COMB: return std::make_unique(); - case CURVES_SCULPT_TOOL_TEST2: + case CURVES_SCULPT_TOOL_DELETE: return std::make_unique(); - case CURVES_SCULPT_TOOL_TEST3: + case CURVES_SCULPT_TOOL_SNAKE_HOOK: + return std::make_unique(); + case CURVES_SCULPT_TOOL_TEST1: return std::make_unique(); - case CURVES_SCULPT_TOOL_TEST4: - return std::make_unique(); - case CURVES_SCULPT_TOOL_TEST5: + case CURVES_SCULPT_TOOL_TEST2: return std::make_unique(); } BLI_assert_unreachable(); diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index f0c4da92378..f8ffe6a0a47 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -457,11 +457,11 @@ typedef enum eBrushUVSculptTool { /* Brush.curves_sculpt_tool. */ typedef enum eBrushCurvesSculptTool { - CURVES_SCULPT_TOOL_TEST1 = 0, - CURVES_SCULPT_TOOL_TEST2 = 1, - CURVES_SCULPT_TOOL_TEST3 = 2, - CURVES_SCULPT_TOOL_TEST4 = 3, - CURVES_SCULPT_TOOL_TEST5 = 4, + CURVES_SCULPT_TOOL_COMB = 0, + CURVES_SCULPT_TOOL_DELETE = 1, + CURVES_SCULPT_TOOL_SNAKE_HOOK = 2, + CURVES_SCULPT_TOOL_TEST1 = 3, + CURVES_SCULPT_TOOL_TEST2 = 4, } eBrushCurvesSculptTool; /** When #BRUSH_ACCUMULATE is used */ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 12c8a41009d..39e1609e305 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -244,11 +244,11 @@ const EnumPropertyItem rna_enum_brush_gpencil_weight_types_items[] = { }; const EnumPropertyItem rna_enum_brush_curves_sculpt_tool_items[] = { + {CURVES_SCULPT_TOOL_COMB, "COMB", ICON_NONE, "Comb", ""}, + {CURVES_SCULPT_TOOL_DELETE, "DELETE", ICON_NONE, "Delete", ""}, + {CURVES_SCULPT_TOOL_SNAKE_HOOK, "SNAKE_HOOK", ICON_NONE, "Snake Hook", ""}, {CURVES_SCULPT_TOOL_TEST1, "TEST1", ICON_NONE, "Test 1", ""}, {CURVES_SCULPT_TOOL_TEST2, "TEST2", ICON_NONE, "Test 2", ""}, - {CURVES_SCULPT_TOOL_TEST3, "TEST3", ICON_NONE, "Test 3", ""}, - {CURVES_SCULPT_TOOL_TEST4, "TEST4", ICON_NONE, "Test 4", ""}, - {CURVES_SCULPT_TOOL_TEST5, "TEST5", ICON_NONE, "Test 5", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index 51e4bc9faa8..ae1024eafbc 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -664,7 +664,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey) case CTX_MODE_VERTEX_GPENCIL: return "builtin_brush.Draw"; case CTX_MODE_SCULPT_CURVES: - return "builtin_brush.Test 1"; + return "builtin_brush.Comb"; /* end temporary hack. */ case CTX_MODE_PARTICLE: -- cgit v1.2.3 From 6a7066c2c172421424ba4aae1b10e6f3e54f08c0 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 9 Mar 2022 12:46:29 +0100 Subject: LibOverride: Add a 'is hierarchy root' macro check. --- source/blender/makesdna/DNA_ID.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 011f3618e15..e3be31d3afe 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -573,6 +573,10 @@ typedef struct PreviewImage { #define ID_IS_OVERRIDE_LIBRARY(_id) \ (ID_IS_OVERRIDE_LIBRARY_REAL(_id) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(_id)) +#define ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(_id) \ + (!ID_IS_OVERRIDE_LIBRARY_REAL(_id) || \ + ((ID *)(_id))->override_library->hierarchy_root == ((ID *)(_id))) + #define ID_IS_OVERRIDE_LIBRARY_TEMPLATE(_id) \ (((ID *)(_id))->override_library != NULL && ((ID *)(_id))->override_library->reference == NULL) -- cgit v1.2.3 From 6be665926c581a7a2dac82c50d75bb530baee863 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 9 Mar 2022 12:47:24 +0100 Subject: LibOverride/Outliner: Restrict further operations over liboverrides. Restrict a lot deletion/moving around of liboverride objects and collections in the Outliner. While some of those operations may be valid in some specific cases, in the vast majority of cases they would just end up breaking override hierarchies/relationships. Part of T95708/T95707. --- .../editors/space_outliner/outliner_collections.cc | 64 +++++++++++++++++----- .../editors/space_outliner/outliner_intern.hh | 2 +- .../editors/space_outliner/outliner_tools.cc | 11 +++- 3 files changed, 60 insertions(+), 17 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index f38f6c2855d..7c0ccd7b14c 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -9,6 +9,7 @@ #include "BLI_listbase.h" #include "BLI_utildefines.h" +#include "DNA_ID.h" #include "DNA_collection_types.h" #include "DNA_object_types.h" @@ -275,6 +276,12 @@ struct CollectionEditData { Scene *scene; SpaceOutliner *space_outliner; GSet *collections_to_edit; + + /* Whether the processed operation should be allowed on liboverride collections, or not. */ + bool is_liboverride_allowed; + /* Whether the processed operation should be allowed on hierarchy roots of liboverride + * collections, or not. */ + bool is_liboverride_hierarchy_root_allowed; }; static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata) @@ -287,27 +294,40 @@ static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *c } if (collection->flag & COLLECTION_IS_MASTER) { - /* skip - showing warning/error message might be misleading - * when deleting multiple collections, so just do nothing */ + /* Skip - showing warning/error message might be misleading + * when deleting multiple collections, so just do nothing. */ + return TRAVERSE_CONTINUE; } - else { - /* Delete, duplicate and link don't edit children, those will come along - * with the parents. */ - BLI_gset_add(data->collections_to_edit, collection); - return TRAVERSE_SKIP_CHILDS; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(collection)) { + if (ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(collection)) { + if (!data->is_liboverride_hierarchy_root_allowed) { + return TRAVERSE_SKIP_CHILDS; + } + } + else { + if (!data->is_liboverride_allowed) { + return TRAVERSE_SKIP_CHILDS; + } + } } - return TRAVERSE_CONTINUE; + /* Delete, duplicate and link don't edit children, those will come along + * with the parents. */ + BLI_gset_add(data->collections_to_edit, collection); + return TRAVERSE_SKIP_CHILDS; } void outliner_collection_delete( - bContext *C, Main *bmain, Scene *scene, ReportList *reports, bool hierarchy) + bContext *C, Main *bmain, Scene *scene, ReportList *reports, bool do_hierarchy) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = false; + data.is_liboverride_hierarchy_root_allowed = do_hierarchy; data.collections_to_edit = BLI_gset_ptr_new(__func__); @@ -356,14 +376,14 @@ void outliner_collection_delete( } if (!skip) { - BKE_collection_delete(bmain, collection, hierarchy); + BKE_collection_delete(bmain, collection, do_hierarchy); } else { - BKE_reportf( - reports, - RPT_WARNING, - "Cannot delete linked collection '%s', it is used by other linked scenes/collections", - collection->id.name + 2); + BKE_reportf(reports, + RPT_WARNING, + "Cannot delete collection '%s', it is either a linked one used by other " + "linked scenes/collections, or a library override one", + collection->id.name + 2); } } } @@ -662,6 +682,8 @@ static int collection_link_exec(bContext *C, wmOperator *op) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = false; /* No linking of non-root collections. */ + data.is_liboverride_hierarchy_root_allowed = true; if ((ID_IS_LINKED(active_collection) || ID_IS_OVERRIDE_LIBRARY(active_collection)) || ((active_collection->flag & COLLECTION_IS_MASTER) && @@ -726,6 +748,8 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = false; /* No instancing of non-root collections. */ + data.is_liboverride_hierarchy_root_allowed = true; data.collections_to_edit = BLI_gset_ptr_new(__func__); @@ -825,6 +849,8 @@ static bool collections_view_layer_poll(bContext *C, bool clear, int flag) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = true; + data.is_liboverride_hierarchy_root_allowed = true; data.collections_to_edit = BLI_gset_ptr_new(__func__); bool result = false; @@ -891,6 +917,8 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = true; + data.is_liboverride_hierarchy_root_allowed = true; bool clear = strstr(op->idname, "clear") != nullptr; int flag = strstr(op->idname, "holdout") ? LAYER_COLLECTION_HOLDOUT : strstr(op->idname, "indirect_only") ? LAYER_COLLECTION_INDIRECT_ONLY : @@ -1029,6 +1057,8 @@ static int collection_isolate_exec(bContext *C, wmOperator *op) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = true; + data.is_liboverride_hierarchy_root_allowed = true; data.collections_to_edit = BLI_gset_ptr_new(__func__); outliner_tree_traverse(space_outliner, &space_outliner->tree, @@ -1126,6 +1156,8 @@ static int collection_visibility_exec(bContext *C, wmOperator *op) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = true; + data.is_liboverride_hierarchy_root_allowed = true; data.collections_to_edit = BLI_gset_ptr_new(__func__); outliner_tree_traverse(space_outliner, @@ -1273,6 +1305,8 @@ static int collection_flag_exec(bContext *C, wmOperator *op) CollectionEditData data{}; data.scene = scene; data.space_outliner = space_outliner; + data.is_liboverride_allowed = true; + data.is_liboverride_hierarchy_root_allowed = true; data.collections_to_edit = BLI_gset_ptr_new(__func__); const bool has_layer_collection = space_outliner->outlinevis == SO_VIEW_LAYER; diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index 0516758e887..7a45531c608 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -545,7 +545,7 @@ void outliner_collection_delete(struct bContext *C, struct Main *bmain, struct Scene *scene, struct ReportList *reports, - bool hierarchy); + bool do_hierarchy); void OUTLINER_OT_collection_new(struct wmOperatorType *ot); void OUTLINER_OT_collection_duplicate_linked(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index fdefcc6133e..c58c13b2fb7 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -1705,7 +1705,16 @@ static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void return TRAVERSE_SKIP_CHILDS; } - BLI_gset_add(objects_to_delete, tselem->id); + ID *id = tselem->id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + if (!ID_IS_OVERRIDE_LIBRARY_HIERARCHY_ROOT(id)) { + /* Only allow deletion of liboverride objects if they are root overrides. */ + return TRAVERSE_SKIP_CHILDS; + } + } + + BLI_gset_add(objects_to_delete, id); return TRAVERSE_CONTINUE; } -- cgit v1.2.3 From ef3d76fbaa37110fd857f3aabff815600e43915a Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Wed, 9 Mar 2022 14:53:01 +0100 Subject: Fix wrong detection of alpha for grayscale images, after recent changes Differential Revision: https://developer.blender.org/D14286 --- source/blender/blenkernel/BKE_image.h | 2 +- source/blender/blenkernel/intern/image.c | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index b3001ecc880..a5a2d57f9d3 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -371,7 +371,7 @@ void BKE_image_merge(struct Main *bmain, struct Image *dest, struct Image *sourc bool BKE_image_scale(struct Image *image, int width, int height); /** - * Check if texture has alpha (depth=32). + * Check if texture has alpha (planes == 32 || planes == 16). */ bool BKE_image_has_alpha(struct Image *image); diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 8a212ed0d7d..e01f8cb76c8 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -5853,17 +5853,14 @@ void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, b BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); } -bool BKE_image_has_alpha(struct Image *image) +bool BKE_image_has_alpha(Image *image) { - ImBuf *ibuf; void *lock; - int planes; - - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - planes = (ibuf ? ibuf->planes : 0); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + const int planes = (ibuf ? ibuf->planes : 0); BKE_image_release_ibuf(image, ibuf, lock); - if (planes == 32) { + if (planes == 32 || planes == 16) { return true; } -- cgit v1.2.3 From beaf2baeffc9a15eba53a61460573948319fecf4 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Wed, 9 Mar 2022 15:02:04 +0100 Subject: Cleanup: fix wrong spelling of texture and indentation Contributed by luzpaz. Ref D14271, D14270 --- source/blender/blenkernel/intern/fluid.c | 2 +- source/blender/blenkernel/intern/linestyle.c | 18 +-- .../editors/animation/anim_channels_defines.c | 150 ++++++++++----------- .../blender/editors/animation/anim_channels_edit.c | 4 +- source/blender/editors/include/ED_anim_api.h | 2 +- source/blender/python/gpu/gpu_py_texture.c | 4 +- 6 files changed, 90 insertions(+), 90 deletions(-) diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index f85361e94ff..ca9d758c692 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -1937,7 +1937,7 @@ static void sample_mesh(FluidFlowSettings *ffs, interp_v2_v2v2v2(tex_co, UNPACK3(uv), weights); - /* Map texure coord between -1.0f and 1.0f. */ + /* Map texture coord between -1.0f and 1.0f. */ tex_co[0] = tex_co[0] * 2.0f - 1.0f; tex_co[1] = tex_co[1] * 2.0f - 1.0f; tex_co[2] = ffs->texture_offset; diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index e50d6263423..bbcc886f3ff 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -2035,7 +2035,7 @@ bool BKE_linestyle_use_textures(FreestyleLineStyle *linestyle, const bool use_sh void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linestyle) { - bNode *uv_along_stroke, *input_texure, *output_linestyle; + bNode *uv_along_stroke, *input_texture, *output_linestyle; bNodeSocket *fromsock, *tosock; bNodeTree *ntree; @@ -2050,9 +2050,9 @@ void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linesty uv_along_stroke->locy = 300.0f; uv_along_stroke->custom1 = 0; /* use_tips */ - input_texure = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); - input_texure->locx = 200.0f; - input_texure->locy = 300.0f; + input_texture = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + input_texture->locx = 200.0f; + input_texture->locy = 300.0f; output_linestyle = nodeAddStaticNode(C, ntree, SH_NODE_OUTPUT_LINESTYLE); output_linestyle->locx = 400.0f; @@ -2060,15 +2060,15 @@ void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linesty output_linestyle->custom1 = MA_RAMP_BLEND; output_linestyle->custom2 = 0; /* use_clamp */ - nodeSetActive(ntree, input_texure); + nodeSetActive(ntree, input_texture); fromsock = BLI_findlink(&uv_along_stroke->outputs, 0); /* UV */ - tosock = BLI_findlink(&input_texure->inputs, 0); /* UV */ - nodeAddLink(ntree, uv_along_stroke, fromsock, input_texure, tosock); + tosock = BLI_findlink(&input_texture->inputs, 0); /* UV */ + nodeAddLink(ntree, uv_along_stroke, fromsock, input_texture, tosock); - fromsock = BLI_findlink(&input_texure->outputs, 0); /* Color */ + fromsock = BLI_findlink(&input_texture->outputs, 0); /* Color */ tosock = BLI_findlink(&output_linestyle->inputs, 0); /* Color */ - nodeAddLink(ntree, input_texure, fromsock, output_linestyle, tosock); + nodeAddLink(ntree, input_texture, fromsock, output_linestyle, tosock); BKE_ntree_update_main_tree(CTX_data_main(C), ntree, NULL); } diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index edb6d188ab8..f4412ea837d 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -203,7 +203,7 @@ static void acf_generic_channel_color(bAnimContext *ac, bAnimListElem *ale, floa rgb_uchar_to_float(r_color, cp); } else { - /* FIXME: what happens when the indention is 1 greater than what it should be + /* FIXME: what happens when the indentation is 1 greater than what it should be * (due to grouping)? */ int colOfs = 10 - 10 * indent; UI_GetThemeColorShade3fv(TH_SHADE2, colOfs, r_color); @@ -252,44 +252,44 @@ static void acf_generic_channel_backdrop(bAnimContext *ac, immUnbindProgram(); } -/* Indention + Offset ------------------------------------------- */ +/* Indentation + Offset ------------------------------------------- */ -/* indention level is always the value in the name */ -static short acf_generic_indention_0(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale)) +/* indentation level is always the value in the name */ +static short acf_generic_indentation_0(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale)) { return 0; } -static short acf_generic_indention_1(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale)) +static short acf_generic_indentation_1(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale)) { return 1; } #if 0 /* XXX not used */ -static short acf_generic_indention_2(bAnimContext *ac, bAnimListElem *ale) +static short acf_generic_indentation_2(bAnimContext *ac, bAnimListElem *ale) { return 2; } #endif -/* indention which varies with the grouping status */ -static short acf_generic_indention_flexible(bAnimContext *UNUSED(ac), bAnimListElem *ale) +/* indentation which varies with the grouping status */ +static short acf_generic_indentation_flexible(bAnimContext *UNUSED(ac), bAnimListElem *ale) { short indent = 0; - /* grouped F-Curves need extra level of indention */ + /* grouped F-Curves need extra level of indentation */ if (ale->type == ANIMTYPE_FCURVE) { FCurve *fcu = (FCurve *)ale->data; - /* TODO: we need some way of specifying that the indention color should be one less. */ + /* TODO: we need some way of specifying that the indentation color should be one less. */ if (fcu->grp) { indent++; } } - /* no indention */ + /* no indentation */ return indent; } -/* basic offset for channels derived from indention */ +/* basic offset for channels derived from indentation */ static short acf_generic_basic_offset(bAnimContext *ac, bAnimListElem *ale) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); @@ -352,7 +352,7 @@ static short acf_generic_group_offset(bAnimContext *ac, bAnimListElem *ale) } } - /* offset is just the normal type - i.e. based on indention */ + /* offset is just the normal type - i.e. based on indentation */ return offset; } @@ -530,10 +530,10 @@ static bAnimChannelType ACF_SUMMARY = { "Summary", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ - acf_summary_color, /* backdrop color */ - acf_summary_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ - NULL, /* offset */ + acf_summary_color, /* backdrop color */ + acf_summary_backdrop, /* backdrop */ + acf_generic_indentation_0, /* indent level */ + NULL, /* offset */ acf_summary_name, /* name */ NULL, /* name prop */ @@ -641,7 +641,7 @@ static bAnimChannelType ACF_SCENE = { acf_generic_root_color, /* backdrop color */ acf_generic_root_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ + acf_generic_indentation_0, /* indent level */ NULL, /* offset */ acf_generic_idblock_name, /* name */ @@ -817,7 +817,7 @@ static bAnimChannelType ACF_OBJECT = { acf_generic_root_color, /* backdrop color */ acf_generic_root_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ + acf_generic_indentation_0, /* indent level */ NULL, /* offset */ acf_object_name, /* name */ @@ -992,10 +992,10 @@ static bAnimChannelType ACF_GROUP = { "Group", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ - acf_group_color, /* backdrop color */ - acf_group_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ - acf_generic_group_offset, /* offset */ + acf_group_color, /* backdrop color */ + acf_group_backdrop, /* backdrop */ + acf_generic_indentation_0, /* indent level */ + acf_generic_group_offset, /* offset */ acf_group_name, /* name */ acf_group_name_prop, /* name prop */ @@ -1118,7 +1118,7 @@ static bAnimChannelType ACF_FCURVE = { acf_generic_channel_color, /* backdrop color */ acf_generic_channel_backdrop, /* backdrop */ - acf_generic_indention_flexible, + acf_generic_indentation_flexible, /* indent level */ /* XXX rename this to f-curves only? */ acf_generic_group_offset, /* offset */ @@ -1238,7 +1238,7 @@ static bAnimChannelType ACF_NLACONTROLS = { acf_nla_controls_color, /* backdrop color */ acf_nla_controls_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ + acf_generic_indentation_0, /* indent level */ acf_generic_group_offset, /* offset */ acf_nla_controls_name, /* name */ @@ -1278,7 +1278,7 @@ static bAnimChannelType ACF_NLACURVE = { acf_generic_channel_color, /* backdrop color */ acf_generic_channel_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_group_offset, /* offset */ acf_nla_curve_name, /* name */ @@ -1368,7 +1368,7 @@ static bAnimChannelType ACF_FILLACTD = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -1453,7 +1453,7 @@ static bAnimChannelType ACF_FILLDRIVERS = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_filldrivers_name, /* name */ @@ -1532,7 +1532,7 @@ static bAnimChannelType ACF_DSMAT = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -1613,7 +1613,7 @@ static bAnimChannelType ACF_DSLIGHT = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -1637,7 +1637,7 @@ static int acf_dstex_icon(bAnimListElem *UNUSED(ale)) /* FIXME: soon to be obsolete? */ static short acf_dstex_offset(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale)) { - return 14; /* XXX: simply include this in indention instead? */ + return 14; /* XXX: simply include this in indentation instead? */ } /* Get the appropriate flag(s) for the setting when it is valid. */ @@ -1699,7 +1699,7 @@ static bAnimChannelType ACF_DSTEX = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_dstex_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -1782,7 +1782,7 @@ static bAnimChannelType ACF_DSCACHEFILE = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -1865,7 +1865,7 @@ static bAnimChannelType ACF_DSCAM = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -1954,7 +1954,7 @@ static bAnimChannelType ACF_DSCUR = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2052,7 +2052,7 @@ static bAnimChannelType ACF_DSSKEY = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2131,7 +2131,7 @@ static bAnimChannelType ACF_DSWOR = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2210,7 +2210,7 @@ static bAnimChannelType ACF_DSPART = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2291,7 +2291,7 @@ static bAnimChannelType ACF_DSMBALL = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2370,7 +2370,7 @@ static bAnimChannelType ACF_DSARM = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2462,7 +2462,7 @@ static bAnimChannelType ACF_DSNTREE = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_dsntree_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2543,7 +2543,7 @@ static bAnimChannelType ACF_DSLINESTYLE = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2622,7 +2622,7 @@ static bAnimChannelType ACF_DSMESH = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, + acf_generic_indentation_1, /* indent level */ /* XXX this only works for compositing */ acf_generic_basic_offset, /* offset */ @@ -2702,7 +2702,7 @@ static bAnimChannelType ACF_DSLAT = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, + acf_generic_indentation_1, /* indent level */ /* XXX this only works for compositing */ acf_generic_basic_offset, /* offset */ @@ -2782,7 +2782,7 @@ static bAnimChannelType ACF_DSSPK = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2863,7 +2863,7 @@ static bAnimChannelType ACF_DSHAIR = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -2944,7 +2944,7 @@ static bAnimChannelType ACF_DSPOINTCLOUD = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -3025,7 +3025,7 @@ static bAnimChannelType ACF_DSVOLUME = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -3103,7 +3103,7 @@ static bAnimChannelType ACF_DSSIMULATION = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -3184,7 +3184,7 @@ static bAnimChannelType ACF_DSGPENCIL = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -3265,7 +3265,7 @@ static bAnimChannelType ACF_DSMCLIP = { acf_generic_dataexpand_color, /* backdrop color */ acf_generic_dataexpand_backdrop, /* backdrop */ - acf_generic_indention_1, /* indent level */ + acf_generic_indentation_1, /* indent level */ acf_generic_basic_offset, /* offset */ acf_generic_idblock_name, /* name */ @@ -3380,7 +3380,7 @@ static bAnimChannelType ACF_SHAPEKEY = { acf_generic_channel_color, /* backdrop color */ acf_generic_channel_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ + acf_generic_indentation_0, /* indent level */ acf_generic_basic_offset, /* offset */ acf_shapekey_name, /* name */ @@ -3458,10 +3458,10 @@ static bAnimChannelType ACF_GPD = { "GPencil Datablock", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ - acf_gpd_color, /* backdrop color */ - acf_group_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ - acf_generic_group_offset, /* offset */ + acf_gpd_color, /* backdrop color */ + acf_group_backdrop, /* backdrop */ + acf_generic_indentation_0, /* indent level */ + acf_generic_group_offset, /* offset */ acf_generic_idblock_name, /* name */ acf_generic_idfill_name_prop, /* name prop */ @@ -3557,10 +3557,10 @@ static bAnimChannelType ACF_GPL = { "GPencil Layer", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ - acf_gpencil_channel_color, /* backdrop color */ - acf_generic_channel_backdrop, /* backdrop */ - acf_generic_indention_flexible, /* indent level */ - acf_generic_group_offset, /* offset */ + acf_gpencil_channel_color, /* backdrop color */ + acf_generic_channel_backdrop, /* backdrop */ + acf_generic_indentation_flexible, /* indent level */ + acf_generic_group_offset, /* offset */ acf_gpl_name, /* name */ acf_gpl_name_prop, /* name prop */ @@ -3639,10 +3639,10 @@ static bAnimChannelType ACF_MASKDATA = { "Mask Datablock", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ - acf_mask_color, /* backdrop color */ - acf_group_backdrop, /* backdrop */ - acf_generic_indention_0, /* indent level */ - acf_generic_group_offset, /* offset */ + acf_mask_color, /* backdrop color */ + acf_group_backdrop, /* backdrop */ + acf_generic_indentation_0, /* indent level */ + acf_generic_group_offset, /* offset */ acf_generic_idblock_name, /* name */ acf_generic_idfill_name_prop, /* name prop */ @@ -3735,10 +3735,10 @@ static bAnimChannelType ACF_MASKLAYER = { "Mask Layer", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ - acf_generic_channel_color, /* backdrop color */ - acf_generic_channel_backdrop, /* backdrop */ - acf_generic_indention_flexible, /* indent level */ - acf_generic_group_offset, /* offset */ + acf_generic_channel_color, /* backdrop color */ + acf_generic_channel_backdrop, /* backdrop */ + acf_generic_indentation_flexible, /* indent level */ + acf_generic_group_offset, /* offset */ acf_masklay_name, /* name */ acf_masklay_name_prop, /* name prop */ @@ -3875,9 +3875,9 @@ static bAnimChannelType ACF_NLATRACK = { "NLA Track", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ - acf_nlatrack_color, /* backdrop color */ - acf_generic_channel_backdrop, /* backdrop */ - acf_generic_indention_flexible, /* indent level */ + acf_nlatrack_color, /* backdrop color */ + acf_generic_channel_backdrop, /* backdrop */ + acf_generic_indentation_flexible, /* indent level */ acf_generic_group_offset, /* offset */ /* XXX? */ @@ -4056,10 +4056,10 @@ static bAnimChannelType ACF_NLAACTION = { "NLA Active Action", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ - acf_nlaaction_color, /* backdrop color (NOTE: the backdrop handles this too, - * since it needs special hacks). */ - acf_nlaaction_backdrop, /* backdrop */ - acf_generic_indention_flexible, /* indent level */ + acf_nlaaction_color, /* backdrop color (NOTE: the backdrop handles this too, + * since it needs special hacks). */ + acf_nlaaction_backdrop, /* backdrop */ + acf_generic_indentation_flexible, /* indent level */ acf_generic_group_offset, /* offset */ /* XXX? */ diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 3a57287cd60..df418b204f9 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -490,7 +490,7 @@ static void anim_flush_channel_setting_up(bAnimContext *ac, } /* Go backwards in the list, until the highest-ranking element - * (by indention has been covered). */ + * (by indentation has been covered). */ int prevLevel = matchLevel; for (bAnimListElem *ale = match->prev; ale; ale = ale->prev) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); @@ -539,7 +539,7 @@ static void anim_flush_channel_setting_down(bAnimContext *ac, bAnimListElem *const match, const int matchLevel) { - /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */ + /* go forwards in the list, until the lowest-ranking element (by indentation has been covered) */ for (bAnimListElem *ale = match->next; ale; ale = ale->next) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 3402d45fa96..4cae8bdab18 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -577,7 +577,7 @@ typedef struct bAnimChannelType { void (*get_backdrop_color)(bAnimContext *ac, bAnimListElem *ale, float r_color[3]); /** Draw backdrop strip for channel. */ void (*draw_backdrop)(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc); - /** Get depth of indention (relative to the depth channel is nested at). */ + /** Get depth of indentation (relative to the depth channel is nested at). */ short (*get_indent_level)(bAnimContext *ac, bAnimListElem *ale); /** Get offset in pixels for the start of the channel (in addition to the indent depth). */ short (*get_offset)(bAnimContext *ac, bAnimListElem *ale); diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index ab038423878..0ad55aeac78 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -545,11 +545,11 @@ static struct PyMethodDef pygpu_texture__m_methods[] = { {NULL, NULL, 0, NULL}, }; -PyDoc_STRVAR(pygpu_texure__m_doc, "This module provides utils for textures."); +PyDoc_STRVAR(pygpu_texture__m_doc, "This module provides utils for textures."); static PyModuleDef pygpu_texture_module_def = { PyModuleDef_HEAD_INIT, .m_name = "gpu.texture", - .m_doc = pygpu_texure__m_doc, + .m_doc = pygpu_texture__m_doc, .m_methods = pygpu_texture__m_methods, }; -- cgit v1.2.3 From c4d3d104017c79c06127eadf30432b90c2875f3a Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 9 Mar 2022 15:14:57 +0100 Subject: Cleanup: Deduplicate Outliner item culling logic For whatever reason the "in-view" check was using 2x the element height. From what I can see this isn't needed, so I'll remove it in a follow-up commit. --- source/blender/editors/space_outliner/outliner_draw.cc | 7 +++---- source/blender/editors/space_outliner/outliner_intern.hh | 9 ++++++++- source/blender/editors/space_outliner/outliner_utils.cc | 5 +++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index 04d4da5e62f..7b62b28abc5 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -1721,7 +1721,7 @@ static void outliner_draw_userbuts(uiBlock *block, LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); - if (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && te->ys <= region->v2d.cur.ymax) { + if (outliner_is_element_in_view(te, ®ion->v2d)) { if (tselem->type == TSE_SOME_ID) { uiBut *bt; ID *id = tselem->id; @@ -1792,8 +1792,7 @@ static bool outliner_draw_overrides_buts(uiBlock *block, LISTBASE_FOREACH (TreeElement *, te, lb) { bool item_has_warnings = false; - const bool do_draw = (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && - te->ys <= region->v2d.cur.ymax); + const bool do_draw = outliner_is_element_in_view(te, ®ion->v2d); int but_flag = UI_BUT_DRAG_LOCK; const char *tip = nullptr; @@ -1903,7 +1902,7 @@ static void outliner_draw_rnabuts( LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); - if (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && te->ys <= region->v2d.cur.ymax) { + if (outliner_is_element_in_view(te, ®ion->v2d)) { if (TreeElementRNAProperty *te_rna_prop = tree_element_cast(te)) { ptr = te_rna_prop->getPointerRNA(); prop = te_rna_prop->getPropertyRNA(); diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index 7a45531c608..b9ffea7c735 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -33,6 +33,7 @@ struct ViewLayer; struct bContext; struct bContextDataResult; struct bPoseChannel; +struct View2D; struct wmKeyConfig; struct wmOperatorType; @@ -642,9 +643,15 @@ float outliner_restrict_columns_width(const struct SpaceOutliner *space_outliner */ TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag); /** - * Find if element is visible in the outliner tree. + * Find if element is visible in the outliner tree, i.e. if all of its parents are expanded. + * Doesn't check if the item is in view-bounds, for that use #outliner_is_element_in_view(). */ bool outliner_is_element_visible(const TreeElement *te); +/** + * Check if the element is displayed within the view bounds. Doesn't check if all parents are + * open/uncollapsed. + */ +bool outliner_is_element_in_view(const TreeElement *te, const struct View2D *v2d); /** * Scroll view vertically while keeping within total bounds. */ diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index b49a2416b38..15a7a3bec15 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -386,6 +386,11 @@ bool outliner_is_element_visible(const TreeElement *te) return true; } +bool outliner_is_element_in_view(const TreeElement *te, const View2D *v2d) +{ + return ((te->ys + 2 * UI_UNIT_Y) >= v2d->cur.ymin) && (te->ys <= v2d->cur.ymax); +} + bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x) { /* Special case: count area left of Scene Collection as empty space */ -- cgit v1.2.3 From 8e3a58578a312c95f49b8d27ba4b35072772c4ef Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 9 Mar 2022 15:22:35 +0100 Subject: Outliner: Correct check for element culling I don't see a reason to use 2x the element height for the "in-view" checks. That seems incorrect (although shouldn't cause issues). So remove that, I don't expect behavior changes. --- source/blender/editors/space_outliner/outliner_utils.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 15a7a3bec15..556f87617f6 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -388,7 +388,7 @@ bool outliner_is_element_visible(const TreeElement *te) bool outliner_is_element_in_view(const TreeElement *te, const View2D *v2d) { - return ((te->ys + 2 * UI_UNIT_Y) >= v2d->cur.ymin) && (te->ys <= v2d->cur.ymax); + return ((te->ys + UI_UNIT_Y) >= v2d->cur.ymin) && (te->ys <= v2d->cur.ymax); } bool outliner_item_is_co_over_name_icons(const TreeElement *te, float view_co_x) -- cgit v1.2.3 From 39c50d8f31bf63b4ccf22eae2d33b69e4cc0dc05 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Wed, 9 Mar 2022 15:28:16 +0100 Subject: Outliner: Ensure compiler error when passing wrong tree-element to macro Passing a `TreeElement *` instead of its `TreeStoreElement *` to `TSELEM_OPEN()` would seem to work but cause a bug. Add a type check that will cause a compiler error if it fails. --- source/blender/editors/space_outliner/outliner_intern.hh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_intern.hh b/source/blender/editors/space_outliner/outliner_intern.hh index b9ffea7c735..a9bdcc56787 100644 --- a/source/blender/editors/space_outliner/outliner_intern.hh +++ b/source/blender/editors/space_outliner/outliner_intern.hh @@ -219,8 +219,9 @@ typedef enum { /* is the current element open? if so we also show children */ #define TSELEM_OPEN(telm, sv) \ - (((telm)->flag & TSE_CLOSED) == 0 || \ - (SEARCHING_OUTLINER(sv) && ((telm)->flag & TSE_CHILDSEARCH))) + (CHECK_TYPE_INLINE(telm, TreeStoreElem *), \ + (((telm)->flag & TSE_CLOSED) == 0 || \ + (SEARCHING_OUTLINER(sv) && ((telm)->flag & TSE_CHILDSEARCH)))) /** * Container to avoid passing around these variables to many functions. -- cgit v1.2.3 From 6c6f591f909a02967daff87ab348601953f61670 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 9 Mar 2022 09:56:14 -0600 Subject: Curves: Port realize instances node to the new data-block This commit replaces the temporary conversion to `CurveEval` with use of the new curves data-block. The end result is that the process looks more like the other components-- somewhere in between meshes and point clouds in terms of complexity. The final result is that the logic between meshes and curves is very similar. There are a few different strategies to reduce duplication here, so I'll investigate that separately. There is some special behavior for the radius and handle position attributes. I used the attribute API to store spans of these attributes temporarily. Using access methods on `CurvesGeometry` would be reasonable to, storing spans separately feels a bit more predictable for now though. There should be significant performance improvements in some cases, I haven't tested that specifically though. Differential Revision: https://developer.blender.org/D14247 --- .../blender/geometry/intern/realize_instances.cc | 392 ++++++++++++++------- 1 file changed, 257 insertions(+), 135 deletions(-) diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 4f7024eea82..c2337c37d1f 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -121,15 +121,37 @@ struct RealizeMeshTask { struct RealizeCurveInfo { const Curves *curves; /** - * Matches the order in #AllCurvesInfo.attributes. For point attributes, the `std::optional` - * will be empty. + * Matches the order in #AllCurvesInfo.attributes. */ - Array> spline_attributes; + Array> attributes; + + /** ID attribute on the curves. If there are no ids, this #Span is empty. */ + Span stored_ids; + + /** + * Handle position attributes must be transformed along with positions. Accessing them in + * advance isn't necessary theoretically, but is done to simplify other code and to avoid + * some overhead. + */ + Span handle_left; + Span handle_right; + + /** + * The radius attribute must be filled with a default of 1.0 if it + * doesn't exist on some (but not all) of the input curves data-blocks. + */ + Span radius; +}; + +/** Start indices in the final output curves data-block. */ +struct CurvesElementStartIndices { + int point = 0; + int curve = 0; }; struct RealizeCurveTask { - /* Start index in the final curve. */ - int start_spline_index = 0; + CurvesElementStartIndices start_indices; + const RealizeCurveInfo *curve_info; /* Transformation applied to the position of control points and handles. */ float4x4 transform; @@ -168,6 +190,8 @@ struct AllCurvesInfo { /** Preprocessed data about every original curve. This is ordered by #order. */ Array realize_info; bool create_id_attribute = false; + bool create_handle_postion_attributes = false; + bool create_radius_attribute = false; }; /** Collects all tasks that need to be executed to realize all instances. */ @@ -185,7 +209,7 @@ struct GatherTasks { struct GatherOffsets { int pointcloud_offset = 0; MeshElementStartIndices mesh_offsets; - int spline_offset = 0; + CurvesElementStartIndices curves_offsets; }; struct GatherTasksInfo { @@ -230,6 +254,17 @@ struct InstanceContext { } }; +static void copy_transformed_positions(const Span src, + const float4x4 &transform, + MutableSpan dst) +{ + threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + dst[i] = transform * src[i]; + } + }); +} + /* -------------------------------------------------------------------- */ /** \name Gather Realize Tasks * \{ */ @@ -448,12 +483,13 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, if (curves != nullptr && curves->geometry.curve_size > 0) { const int curve_index = gather_info.curves.order.index_of(curves); const RealizeCurveInfo &curve_info = gather_info.curves.realize_info[curve_index]; - gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.spline_offset, + gather_info.r_tasks.curve_tasks.append({gather_info.r_offsets.curves_offsets, &curve_info, base_transform, base_instance_context.curves, base_instance_context.id}); - gather_info.r_offsets.spline_offset += curves->geometry.curve_size; + gather_info.r_offsets.curves_offsets.point += curves->geometry.point_size; + gather_info.r_offsets.curves_offsets.curve += curves->geometry.curve_size; } break; } @@ -571,12 +607,8 @@ static void execute_realize_pointcloud_task(const RealizeInstancesOptions &optio pointcloud.totpoint}; MutableSpan dst_ids = all_dst_ids.slice(task.start_index, pointcloud.totpoint); - /* Copy transformed positions. */ - threading::parallel_for(IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - for (const int i : range) { - dst_positions[i] = task.transform * src_positions[i]; - } - }); + copy_transformed_positions(src_positions, task.transform, dst_positions); + /* Create point ids. */ if (!all_dst_ids.is_empty()) { if (options.keep_original_ids) { @@ -1007,7 +1039,7 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, /** \} */ /* -------------------------------------------------------------------- */ -/** \name Curve +/** \name Curves * \{ */ static OrderedAttributes gather_generic_curve_attributes_to_propagate( @@ -1023,9 +1055,6 @@ static OrderedAttributes gather_generic_curve_attributes_to_propagate( in_geometry_set.gather_attributes_for_propagation( src_component_types, GEO_COMPONENT_TYPE_CURVE, true, attributes_to_propagate); attributes_to_propagate.remove("position"); - attributes_to_propagate.remove("cyclic"); - attributes_to_propagate.remove("resolution"); - attributes_to_propagate.remove("tilt"); attributes_to_propagate.remove("radius"); attributes_to_propagate.remove("handle_right"); attributes_to_propagate.remove("handle_left"); @@ -1071,19 +1100,43 @@ static AllCurvesInfo preprocess_curves(const GeometrySet &geometry_set, /* Access attributes. */ CurveComponent component; component.replace(const_cast(curves), GeometryOwnershipType::ReadOnly); - curve_info.spline_attributes.reinitialize(info.attributes.size()); + curve_info.attributes.reinitialize(info.attributes.size()); for (const int attribute_index : info.attributes.index_range()) { const AttributeDomain domain = info.attributes.kinds[attribute_index].domain; - if (domain != ATTR_DOMAIN_CURVE) { - continue; - } const AttributeIDRef &attribute_id = info.attributes.ids[attribute_index]; const CustomDataType data_type = info.attributes.kinds[attribute_index].data_type; if (component.attribute_exists(attribute_id)) { GVArray attribute = component.attribute_get_for_read(attribute_id, domain, data_type); - curve_info.spline_attributes[attribute_index].emplace(std::move(attribute)); + curve_info.attributes[attribute_index].emplace(std::move(attribute)); + } + } + if (info.create_id_attribute) { + ReadAttributeLookup ids_lookup = component.attribute_try_get_for_read("id"); + if (ids_lookup) { + curve_info.stored_ids = ids_lookup.varray.get_internal_span().typed(); } } + + /* Retrieve the radius attribute, if it exists. */ + if (component.attribute_exists("radius")) { + curve_info.radius = component + .attribute_get_for_read("radius", ATTR_DOMAIN_POINT, 0.0f) + .get_internal_span(); + info.create_radius_attribute = true; + } + + /* Retrieve handle position attributes, if they exist. */ + if (component.attribute_exists("handle_right")) { + curve_info.handle_left = component + .attribute_get_for_read( + "handle_left", ATTR_DOMAIN_POINT, float3(0)) + .get_internal_span(); + curve_info.handle_right = component + .attribute_get_for_read( + "handle_right", ATTR_DOMAIN_POINT, float3(0)) + .get_internal_span(); + info.create_handle_postion_attributes = true; + } } return info; } @@ -1092,108 +1145,129 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, const AllCurvesInfo &all_curves_info, const RealizeCurveTask &task, const OrderedAttributes &ordered_attributes, - MutableSpan dst_splines, - MutableSpan dst_spline_attributes) + bke::CurvesGeometry &dst_curves, + MutableSpan dst_attribute_spans, + MutableSpan all_dst_ids, + MutableSpan all_handle_left, + MutableSpan all_handle_right, + MutableSpan all_radii) { - const RealizeCurveInfo &curve_info = *task.curve_info; - const std::unique_ptr curve = curves_to_curve_eval(*curve_info.curves); - - const Span src_splines = curve->splines(); - - /* Initialize point attributes. */ - threading::parallel_for(src_splines.index_range(), 100, [&](const IndexRange src_spline_range) { - for (const int src_spline_index : src_spline_range) { - const int dst_spline_index = src_spline_index + task.start_spline_index; - const Spline &src_spline = *src_splines[src_spline_index]; - SplinePtr dst_spline = src_spline.copy_without_attributes(); - dst_spline->transform(task.transform); - const int spline_size = dst_spline->size(); - - const CustomDataAttributes &src_point_attributes = src_spline.attributes; - CustomDataAttributes &dst_point_attributes = dst_spline->attributes; - - /* Create point ids. */ - if (all_curves_info.create_id_attribute) { - dst_point_attributes.create("id", CD_PROP_INT32); - MutableSpan dst_point_ids = dst_point_attributes.get_for_write("id")->typed(); - std::optional src_point_ids_opt = src_point_attributes.get_for_read("id"); - if (options.keep_original_ids) { - if (src_point_ids_opt.has_value()) { - const Span src_point_ids = src_point_ids_opt->typed(); - dst_point_ids.copy_from(src_point_ids); - } - else { - dst_point_ids.fill(0); - } - } - else { - if (src_point_ids_opt.has_value()) { - const Span src_point_ids = src_point_ids_opt->typed(); - for (const int i : IndexRange(dst_spline->size())) { - dst_point_ids[i] = noise::hash(task.id, src_point_ids[i]); - } - } - else { - for (const int i : IndexRange(dst_spline->size())) { - /* Mix spline index into the id, because otherwise points on different splines will - * get the same id. */ - dst_point_ids[i] = noise::hash(task.id, src_spline_index, i); - } - } - } - } + const RealizeCurveInfo &curves_info = *task.curve_info; + const Curves &curves_id = *curves_info.curves; + const bke::CurvesGeometry &curves = bke::CurvesGeometry::wrap(curves_id.geometry); - /* Copy generic point attributes. */ - for (const int attribute_index : ordered_attributes.index_range()) { - const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - if (domain != ATTR_DOMAIN_POINT) { - continue; - } - const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; - const CPPType &cpp_type = *custom_data_type_to_cpp_type(data_type); - const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index]; - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - const std::optional src_span_opt = src_point_attributes.get_for_read(attribute_id); - void *dst_buffer = MEM_malloc_arrayN(spline_size, cpp_type.size(), "Curve Attribute"); - if (src_span_opt.has_value()) { - const GSpan src_span = *src_span_opt; - cpp_type.copy_construct_n(src_span.data(), dst_buffer, spline_size); - } - else { - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); - } - cpp_type.fill_construct_n(attribute_fallback, dst_buffer, spline_size); - } - dst_point_attributes.create_by_move(attribute_id, data_type, dst_buffer); - } + const IndexRange dst_point_range{task.start_indices.point, curves.points_size()}; + const IndexRange dst_curve_range{task.start_indices.curve, curves.curves_size()}; + + copy_transformed_positions( + curves.positions(), task.transform, dst_curves.positions().slice(dst_point_range)); - dst_splines[dst_spline_index] = std::move(dst_spline); + /* Copy and transform handle positions if necessary. */ + if (all_curves_info.create_handle_postion_attributes) { + if (curves_info.handle_left.is_empty()) { + all_handle_left.fill(float3(0)); } - }); - /* Initialize spline attributes. */ - for (const int attribute_index : ordered_attributes.index_range()) { - const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - if (domain != ATTR_DOMAIN_CURVE) { - continue; + else { + copy_transformed_positions( + curves_info.handle_left, task.transform, all_handle_left.slice(dst_point_range)); } - const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; - const CPPType &cpp_type = *custom_data_type_to_cpp_type(data_type); + if (curves_info.handle_right.is_empty()) { + all_handle_right.fill(float3(0)); + } + else { + copy_transformed_positions( + curves_info.handle_right, task.transform, all_handle_right.slice(dst_point_range)); + } + } - GMutableSpan dst_span = dst_spline_attributes[attribute_index].slice(task.start_spline_index, - src_splines.size()); - if (curve_info.spline_attributes[attribute_index].has_value()) { - const GSpan src_span = *curve_info.spline_attributes[attribute_index]; - cpp_type.copy_construct_n(src_span.data(), dst_span.data(), src_splines.size()); + /* Copy radius attribute with 1.0 default if it doesn't exist. */ + if (all_curves_info.create_radius_attribute) { + if (curves_info.radius.is_empty()) { + all_radii.slice(dst_point_range).fill(1.0f); } else { - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); + all_radii.slice(dst_point_range).copy_from(curves_info.radius); + } + } + + /* Copy curve offsets. */ + const Span src_offsets = curves.offsets(); + const MutableSpan dst_offsets = dst_curves.offsets().slice(dst_curve_range); + threading::parallel_for(curves.curves_range(), 2048, [&](const IndexRange range) { + for (const int i : range) { + dst_offsets[i] = task.start_indices.point + src_offsets[i]; + } + }); + + /* Create id attribute. */ + if (!all_dst_ids.is_empty()) { + MutableSpan dst_ids = all_dst_ids.slice(task.start_indices.point, curves.points_size()); + if (options.keep_original_ids) { + if (curves_info.stored_ids.is_empty()) { + dst_ids.fill(0); } - cpp_type.fill_construct_n(attribute_fallback, dst_span.data(), src_splines.size()); + else { + dst_ids.copy_from(curves_info.stored_ids); + } + } + else { + threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange vert_range) { + if (curves_info.stored_ids.is_empty()) { + for (const int i : vert_range) { + dst_ids[i] = noise::hash(task.id, i); + } + } + else { + for (const int i : vert_range) { + const int original_id = curves_info.stored_ids[i]; + dst_ids[i] = noise::hash(task.id, original_id); + } + } + }); } } + + /* Copy generic attributes. */ + threading::parallel_for( + dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) { + for (const int attribute_index : attribute_range) { + const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; + IndexRange element_slice; + switch (domain) { + case ATTR_DOMAIN_POINT: + element_slice = IndexRange(task.start_indices.point, curves.points_size()); + break; + case ATTR_DOMAIN_CURVE: + element_slice = IndexRange(task.start_indices.curve, curves.curves_size()); + break; + default: + BLI_assert_unreachable(); + break; + } + GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(element_slice); + const CPPType &cpp_type = dst_span.type(); + const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; + if (curves_info.attributes[attribute_index].has_value()) { + const GSpan src_span = *curves_info.attributes[attribute_index]; + threading::parallel_for( + IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { + cpp_type.copy_assign_n(src_span.slice(sub_range).data(), + dst_span.slice(sub_range).data(), + sub_range.size()); + }); + } + else { + if (attribute_fallback == nullptr) { + attribute_fallback = cpp_type.default_value(); + } + threading::parallel_for( + IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { + cpp_type.fill_assign_n( + attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size()); + }); + } + } + }); } static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, @@ -1208,42 +1282,90 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, const RealizeCurveTask &last_task = tasks.last(); const Curves &last_curves = *last_task.curve_info->curves; - const int tot_splines = last_task.start_spline_index + last_curves.geometry.curve_size; + const int points_size = last_task.start_indices.point + last_curves.geometry.point_size; + const int curves_size = last_task.start_indices.curve + last_curves.geometry.curve_size; - Array dst_splines(tot_splines); + /* Allocate new curves data-block. */ + Curves *dst_curves_id = bke::curves_new_nomain(points_size, curves_size); + bke::CurvesGeometry &dst_curves = bke::CurvesGeometry::wrap(dst_curves_id->geometry); + dst_curves.offsets().last() = points_size; + CurveComponent &dst_component = r_realized_geometry.get_component_for_write(); + dst_component.replace(dst_curves_id); - std::unique_ptr dst_curve = std::make_unique(); - dst_curve->attributes.reallocate(tot_splines); - CustomDataAttributes &spline_attributes = dst_curve->attributes; + /* Prepare id attribute. */ + OutputAttribute_Typed point_ids; + MutableSpan point_ids_span; + if (all_curves_info.create_id_attribute) { + point_ids = dst_component.attribute_try_get_for_output_only("id", ATTR_DOMAIN_POINT); + point_ids_span = point_ids.as_span(); + } - /* Prepare spline attributes. */ - Vector dst_spline_attributes; + /* Prepare generic output attributes. */ + Vector dst_attributes; + Vector dst_attribute_spans; for (const int attribute_index : ordered_attributes.index_range()) { const AttributeIDRef &attribute_id = ordered_attributes.ids[attribute_index]; - const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - if (domain == ATTR_DOMAIN_CURVE) { - spline_attributes.create(attribute_id, data_type); - dst_spline_attributes.append(*spline_attributes.get_for_write(attribute_id)); - } - else { - dst_spline_attributes.append({CPPType::get()}); - } + const CustomDataType data_type = ordered_attributes.kinds[attribute_index].data_type; + OutputAttribute dst_attribute = dst_component.attribute_try_get_for_output_only( + attribute_id, domain, data_type); + dst_attribute_spans.append(dst_attribute.as_span()); + dst_attributes.append(std::move(dst_attribute)); + } + + /* Prepare handle position attributes if necessary. */ + OutputAttribute_Typed handle_left; + OutputAttribute_Typed handle_right; + MutableSpan handle_left_span; + MutableSpan handle_right_span; + if (all_curves_info.create_handle_postion_attributes) { + handle_left = dst_component.attribute_try_get_for_output_only("handle_left", + ATTR_DOMAIN_POINT); + handle_right = dst_component.attribute_try_get_for_output_only("handle_right", + ATTR_DOMAIN_POINT); + handle_left_span = handle_left.as_span(); + handle_right_span = handle_right.as_span(); + } + + /* Prepare radius attribute if necessary. */ + OutputAttribute_Typed radius; + MutableSpan radius_span; + if (all_curves_info.create_radius_attribute) { + radius = dst_component.attribute_try_get_for_output_only("radius", ATTR_DOMAIN_POINT); + radius_span = radius.as_span(); } /* Actually execute all tasks. */ threading::parallel_for(tasks.index_range(), 100, [&](const IndexRange task_range) { for (const int task_index : task_range) { const RealizeCurveTask &task = tasks[task_index]; - execute_realize_curve_task( - options, all_curves_info, task, ordered_attributes, dst_splines, dst_spline_attributes); + execute_realize_curve_task(options, + all_curves_info, + task, + ordered_attributes, + dst_curves, + dst_attribute_spans, + point_ids_span, + handle_left_span, + handle_right_span, + radius_span); } }); - dst_curve->add_splines(dst_splines); - - CurveComponent &dst_component = r_realized_geometry.get_component_for_write(); - dst_component.replace(curve_eval_to_curves(*dst_curve)); + /* Save modified attributes. */ + for (OutputAttribute &dst_attribute : dst_attributes) { + dst_attribute.save(); + } + if (point_ids) { + point_ids.save(); + } + if (radius) { + radius.save(); + } + if (all_curves_info.create_handle_postion_attributes) { + handle_left.save(); + handle_right.save(); + } } /** \} */ -- cgit v1.2.3 From 115ff08fdb251cf0a656ce729c49ac38d96cd5f9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 9 Mar 2022 10:23:17 -0600 Subject: Fix: Use "construct" instead of "assign" for uninitialized memory The realize instances code used "assign", but the attribute buffers on the result aren't necessarily initialized. This doesn't make a difference for trivial types like `int`, but it would with more complex types. --- source/blender/geometry/intern/realize_instances.cc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index c2337c37d1f..9acabae10ab 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -646,7 +646,7 @@ static void execute_realize_pointcloud_task(const RealizeInstancesOptions &optio const GSpan src_span = *pointcloud_info.attributes[attribute_index]; threading::parallel_for( IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - cpp_type.copy_assign_n( + cpp_type.copy_construct_n( src_span.slice(range).data(), dst_span.slice(range).data(), range.size()); }); } @@ -657,7 +657,7 @@ static void execute_realize_pointcloud_task(const RealizeInstancesOptions &optio /* As the fallback value for the attribute. */ threading::parallel_for( IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - cpp_type.fill_assign_n( + cpp_type.fill_construct_n( attribute_fallback, dst_span.slice(range).data(), range.size()); }); } @@ -943,9 +943,9 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, const GSpan src_span = *mesh_info.attributes[attribute_index]; threading::parallel_for( IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.copy_assign_n(src_span.slice(sub_range).data(), - dst_span.slice(sub_range).data(), - sub_range.size()); + cpp_type.copy_construct_n(src_span.slice(sub_range).data(), + dst_span.slice(sub_range).data(), + sub_range.size()); }); } else { @@ -954,7 +954,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, } threading::parallel_for( IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.fill_assign_n( + cpp_type.fill_construct_n( attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size()); }); } @@ -1251,9 +1251,9 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, const GSpan src_span = *curves_info.attributes[attribute_index]; threading::parallel_for( IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.copy_assign_n(src_span.slice(sub_range).data(), - dst_span.slice(sub_range).data(), - sub_range.size()); + cpp_type.copy_construct_n(src_span.slice(sub_range).data(), + dst_span.slice(sub_range).data(), + sub_range.size()); }); } else { @@ -1262,7 +1262,7 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, } threading::parallel_for( IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.fill_assign_n( + cpp_type.fill_construct_n( attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size()); }); } -- cgit v1.2.3 From 24c543d4af599ee6f6ae52ab469aa99807753147 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 9 Mar 2022 10:27:08 -0600 Subject: Fix: Add missing break --- source/blender/geometry/intern/realize_instances.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 9acabae10ab..94c75e8dbb1 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -935,6 +935,7 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, break; default: BLI_assert_unreachable(); + break; } GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(element_slice); const CPPType &cpp_type = dst_span.type(); -- cgit v1.2.3 From f3799437347d0e71098feb3dea220bca314662bd Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 9 Mar 2022 22:59:39 -0600 Subject: Cleanup: Simplify/deduplicate curves built-in attribute access Separate two templated functions that deal with custom data so that each built-in attribute accessor doesn't need the same boilerplace and logic. --- .../blender/blenkernel/intern/curves_geometry.cc | 83 +++++++++++++++------- 1 file changed, 57 insertions(+), 26 deletions(-) diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 2a22b2eb0f3..0ae5654546b 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -129,24 +129,67 @@ IndexRange CurvesGeometry::range_for_curve(const int index) const return {offset, offset_next - offset}; } -VArray CurvesGeometry::curve_types() const +static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) +{ + return domain == ATTR_DOMAIN_POINT ? curves.points_size() : curves.curves_size(); +} + +static CustomData &domain_custom_data(CurvesGeometry &curves, const AttributeDomain domain) +{ + return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data; +} + +static const CustomData &domain_custom_data(const CurvesGeometry &curves, + const AttributeDomain domain) +{ + return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data; +} + +template +static VArray get_varray_attribute(const CurvesGeometry &curves, + const AttributeDomain domain, + const StringRefNull name, + const T default_value) +{ + const int size = domain_size(curves, domain); + const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get()); + const CustomData &custom_data = domain_custom_data(curves, domain); + + const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str()); + if (data != nullptr) { + return VArray::ForSpan(Span(data, size)); + } + return VArray::ForSingle(default_value, size); +} + +template +static MutableSpan get_mutable_attribute(CurvesGeometry &curves, + const AttributeDomain domain, + const StringRefNull name) { - if (const int8_t *data = (const int8_t *)CustomData_get_layer_named( - &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str())) { - return VArray::ForSpan({data, this->curve_size}); + const int size = domain_size(curves, domain); + const CustomDataType type = cpp_type_to_custom_data_type(CPPType::get()); + CustomData &custom_data = domain_custom_data(curves, domain); + + T *data = (T *)CustomData_duplicate_referenced_layer_named( + &custom_data, type, name.c_str(), size); + if (data != nullptr) { + return {data, size}; } - return VArray::ForSingle(CURVE_TYPE_CATMULL_ROM, this->curve_size); + data = (T *)CustomData_add_layer_named( + &custom_data, type, CD_CALLOC, nullptr, size, name.c_str()); + return {data, size}; +} + +VArray CurvesGeometry::curve_types() const +{ + return get_varray_attribute( + *this, ATTR_DOMAIN_CURVE, ATTR_CURVE_TYPE, CURVE_TYPE_CATMULL_ROM); } MutableSpan CurvesGeometry::curve_types() { - int8_t *data = (int8_t *)CustomData_add_layer_named(&this->curve_data, - CD_PROP_INT8, - CD_CALLOC, - nullptr, - this->curve_size, - ATTR_CURVE_TYPE.c_str()); - return {data, this->curve_size}; + return get_mutable_attribute(*this, ATTR_DOMAIN_CURVE, ATTR_CURVE_TYPE); } MutableSpan CurvesGeometry::positions() @@ -171,24 +214,12 @@ Span CurvesGeometry::offsets() const VArray CurvesGeometry::cyclic() const { - const bool *data = (const bool *)CustomData_get_layer_named( - &this->curve_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); - if (data != nullptr) { - return VArray::ForSpan(Span(data, this->curve_size)); - } - return VArray::ForSingle(false, this->curve_size); + return get_varray_attribute(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC, false); } MutableSpan CurvesGeometry::cyclic() { - bool *data = (bool *)CustomData_duplicate_referenced_layer_named( - &this->curve_data, CD_PROP_BOOL, ATTR_CYCLIC.c_str(), this->curve_size); - if (data != nullptr) { - return {data, this->curve_size}; - } - data = (bool *)CustomData_add_layer_named( - &this->curve_data, CD_PROP_BOOL, CD_CALLOC, nullptr, this->curve_size, ATTR_CYCLIC.c_str()); - return {data, this->curve_size}; + return get_mutable_attribute(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC); } void CurvesGeometry::resize(const int point_size, const int curve_size) -- cgit v1.2.3 From 0ef8a6179d2a773b2570352bd0cb7eb18b666da2 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Wed, 9 Mar 2022 20:55:17 -0800 Subject: Cleanup: Move image.c to c++ Passing on all platforms: https://builder.blender.org/admin/#/builders/18/builds/329 Differential Revision: https://developer.blender.org/D13962 --- source/blender/blenkernel/BKE_image.h | 2 +- source/blender/blenkernel/CMakeLists.txt | 2 +- source/blender/blenkernel/intern/image.c | 6340 ---------------------------- source/blender/blenkernel/intern/image.cc | 6394 +++++++++++++++++++++++++++++ 4 files changed, 6396 insertions(+), 6342 deletions(-) delete mode 100644 source/blender/blenkernel/intern/image.c create mode 100644 source/blender/blenkernel/intern/image.cc diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index a5a2d57f9d3..ea0202e3b5f 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -486,7 +486,7 @@ bool BKE_image_is_dirty(struct Image *image); void BKE_image_mark_dirty(struct Image *image, struct ImBuf *ibuf); bool BKE_image_buffer_format_writable(struct ImBuf *ibuf); -bool BKE_image_is_dirty_writable(struct Image *image, bool *is_format_writable); +bool BKE_image_is_dirty_writable(struct Image *image, bool *r_is_writable); /** * Guess offset for the first frame in the sequence. diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index a12a956cbf5..3828d442f58 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -150,7 +150,7 @@ set(SRC intern/idprop_serialize.cc intern/idprop_utils.c intern/idtype.c - intern/image.c + intern/image.cc intern/image_partial_update.cc intern/image_gen.c intern/image_gpu.cc diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c deleted file mode 100644 index e01f8cb76c8..00000000000 --- a/source/blender/blenkernel/intern/image.c +++ /dev/null @@ -1,6340 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ - -/** \file - * \ingroup bke - */ - -#include -#include -#include -#include -#include -#ifndef WIN32 -# include -#else -# include -#endif - -#include - -#include "CLG_log.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" -#include "IMB_metadata.h" -#include "IMB_moviecache.h" - -#ifdef WITH_OPENEXR -# include "intern/openexr/openexr_multi.h" -#endif - -/* Allow using deprecated functionality for .blend file I/O. */ -#define DNA_DEPRECATED_ALLOW - -#include "DNA_brush_types.h" -#include "DNA_camera_types.h" -#include "DNA_defaults.h" -#include "DNA_light_types.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_packedFile_types.h" -#include "DNA_scene_types.h" -#include "DNA_sequence_types.h" -#include "DNA_simulation_types.h" -#include "DNA_world_types.h" - -#include "BLI_blenlib.h" -#include "BLI_math_vector.h" -#include "BLI_mempool.h" -#include "BLI_system.h" -#include "BLI_task.h" -#include "BLI_threads.h" -#include "BLI_timecode.h" /* For stamp time-code format. */ -#include "BLI_utildefines.h" - -#include "BLT_translation.h" - -#include "BKE_bpath.h" -#include "BKE_colortools.h" -#include "BKE_global.h" -#include "BKE_icons.h" -#include "BKE_idtype.h" -#include "BKE_image.h" -#include "BKE_lib_id.h" -#include "BKE_main.h" -#include "BKE_node.h" -#include "BKE_node_tree_update.h" -#include "BKE_packedFile.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_workspace.h" - -#include "BLF_api.h" - -#include "PIL_time.h" - -#include "RE_pipeline.h" - -#include "SEQ_utils.h" /* SEQ_get_topmost_sequence() */ - -#include "GPU_material.h" -#include "GPU_texture.h" - -#include "BLI_sys_types.h" /* for intptr_t support */ - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -#include "BLO_read_write.h" - -/* for image user iteration */ -#include "DNA_node_types.h" -#include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_view3d_types.h" - -static CLG_LogRef LOG = {"bke.image"}; - -static void image_init(Image *ima, short source, short type); -static void image_free_packedfiles(Image *ima); -static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src); - -/* Reset runtime image fields when data-block is being initialized. */ -static void image_runtime_reset(struct Image *image) -{ - memset(&image->runtime, 0, sizeof(image->runtime)); - image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); - BLI_mutex_init(image->runtime.cache_mutex); -} - -/* Reset runtime image fields when data-block is being copied. */ -static void image_runtime_reset_on_copy(struct Image *image) -{ - image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); - BLI_mutex_init(image->runtime.cache_mutex); - - image->runtime.partial_update_register = NULL; - image->runtime.partial_update_user = NULL; -} - -static void image_runtime_free_data(struct Image *image) -{ - BLI_mutex_end(image->runtime.cache_mutex); - MEM_freeN(image->runtime.cache_mutex); - image->runtime.cache_mutex = NULL; - - if (image->runtime.partial_update_user != NULL) { - BKE_image_partial_update_free(image->runtime.partial_update_user); - image->runtime.partial_update_user = NULL; - } - BKE_image_partial_update_register_free(image); -} - -static void image_init_data(ID *id) -{ - Image *image = (Image *)id; - - if (image != NULL) { - image_init(image, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); - } -} - -static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) -{ - Image *image_dst = (Image *)id_dst; - const Image *image_src = (const Image *)id_src; - - BKE_color_managed_colorspace_settings_copy(&image_dst->colorspace_settings, - &image_src->colorspace_settings); - - copy_image_packedfiles(&image_dst->packedfiles, &image_src->packedfiles); - - image_dst->stereo3d_format = MEM_dupallocN(image_src->stereo3d_format); - BLI_duplicatelist(&image_dst->views, &image_src->views); - - /* Cleanup stuff that cannot be copied. */ - image_dst->cache = NULL; - image_dst->rr = NULL; - - BLI_duplicatelist(&image_dst->renderslots, &image_src->renderslots); - LISTBASE_FOREACH (RenderSlot *, slot, &image_dst->renderslots) { - slot->render = NULL; - } - - BLI_listbase_clear(&image_dst->anims); - - BLI_duplicatelist(&image_dst->tiles, &image_src->tiles); - - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - image_dst->gputexture[i][eye][resolution] = NULL; - } - } - } - - if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { - BKE_previewimg_id_copy(&image_dst->id, &image_src->id); - } - else { - image_dst->preview = NULL; - } - - image_runtime_reset_on_copy(image_dst); -} - -static void image_free_data(ID *id) -{ - Image *image = (Image *)id; - - /* Also frees animdata. */ - BKE_image_free_buffers(image); - - image_free_packedfiles(image); - - LISTBASE_FOREACH (RenderSlot *, slot, &image->renderslots) { - if (slot->render) { - RE_FreeRenderResult(slot->render); - slot->render = NULL; - } - } - BLI_freelistN(&image->renderslots); - - BKE_image_free_views(image); - MEM_SAFE_FREE(image->stereo3d_format); - - BKE_icon_id_delete(&image->id); - BKE_previewimg_free(&image->preview); - - BLI_freelistN(&image->tiles); - - image_runtime_free_data(image); -} - -static void image_foreach_cache(ID *id, - IDTypeForeachCacheFunctionCallback function_callback, - void *user_data) -{ - Image *image = (Image *)id; - IDCacheKey key = { - .id_session_uuid = id->session_uuid, - .offset_in_ID = offsetof(Image, cache), - .cache_v = image->cache, - }; - function_callback(id, &key, (void **)&image->cache, 0, user_data); - - for (int eye = 0; eye < 2; eye++) { - for (int a = 0; a < TEXTARGET_COUNT; a++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - GPUTexture *texture = image->gputexture[a][eye][resolution]; - if (texture == NULL) { - continue; - } - key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]); - key.cache_v = texture; - function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); - } - } - } - - key.offset_in_ID = offsetof(Image, rr); - key.cache_v = image->rr; - function_callback(id, &key, (void **)&image->rr, 0, user_data); - - LISTBASE_FOREACH (RenderSlot *, slot, &image->renderslots) { - key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(slot->name); - key.cache_v = slot->render; - function_callback(id, &key, (void **)&slot->render, 0, user_data); - } -} - -static void image_foreach_path(ID *id, BPathForeachPathData *bpath_data) -{ - Image *ima = (Image *)id; - const eBPathForeachFlag flag = bpath_data->flag; - - if (BKE_image_has_packedfile(ima) && (flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) { - return; - } - /* Skip empty file paths, these are typically from generated images and - * don't make sense to add directories to until the image has been saved - * once to give it a meaningful value. */ - /* TODO re-assess whether this behavior is desired in the new generic code context. */ - if (!ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED) || - ima->filepath[0] == '\0') { - return; - } - - /* If this is a tiled image, and we're asked to resolve the tokens in the virtual - * filepath, use the first tile to generate a concrete path for use during processing. */ - bool result = false; - if (ima->source == IMA_SRC_TILED && (flag & BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN) != 0) { - char temp_path[FILE_MAX], orig_file[FILE_MAXFILE]; - BLI_strncpy(temp_path, ima->filepath, sizeof(temp_path)); - BLI_split_file_part(temp_path, orig_file, sizeof(orig_file)); - - eUDIM_TILE_FORMAT tile_format; - char *udim_pattern = BKE_image_get_tile_strformat(temp_path, &tile_format); - BKE_image_set_filepath_from_tile_number( - temp_path, udim_pattern, tile_format, ((ImageTile *)ima->tiles.first)->tile_number); - MEM_SAFE_FREE(udim_pattern); - - result = BKE_bpath_foreach_path_fixed_process(bpath_data, temp_path); - if (result) { - /* Put the filepath back together using the new directory and the original file name. */ - char new_dir[FILE_MAXDIR]; - BLI_split_dir_part(temp_path, new_dir, sizeof(new_dir)); - BLI_join_dirfile(ima->filepath, sizeof(ima->filepath), new_dir, orig_file); - } - } - else { - result = BKE_bpath_foreach_path_fixed_process(bpath_data, ima->filepath); - } - - if (result) { - if (flag & BKE_BPATH_FOREACH_PATH_RELOAD_EDITED) { - if (!BKE_image_has_packedfile(ima) && - /* Image may have been painted onto (and not saved, T44543). */ - !BKE_image_is_dirty(ima)) { - BKE_image_signal(bpath_data->bmain, ima, NULL, IMA_SIGNAL_RELOAD); - } - } - } -} - -static void image_blend_write(BlendWriter *writer, ID *id, const void *id_address) -{ - Image *ima = (Image *)id; - const bool is_undo = BLO_write_is_undo(writer); - - /* Clear all data that isn't read to reduce false detection of changed image during memfile undo. - */ - ima->lastused = 0; - ima->cache = NULL; - ima->gpuflag = 0; - BLI_listbase_clear(&ima->anims); - ima->runtime.partial_update_register = NULL; - ima->runtime.partial_update_user = NULL; - for (int i = 0; i < 3; i++) { - for (int j = 0; j < 2; j++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - ima->gputexture[i][j][resolution] = NULL; - } - } - } - - ImagePackedFile *imapf; - - BLI_assert(ima->packedfile == NULL); - if (!is_undo) { - /* Do not store packed files in case this is a library override ID. */ - if (ID_IS_OVERRIDE_LIBRARY(ima)) { - BLI_listbase_clear(&ima->packedfiles); - } - else { - /* Some trickery to keep forward compatibility of packed images. */ - if (ima->packedfiles.first != NULL) { - imapf = ima->packedfiles.first; - ima->packedfile = imapf->packedfile; - } - } - } - - /* write LibData */ - BLO_write_id_struct(writer, Image, id_address, &ima->id); - BKE_id_blend_write(writer, &ima->id); - - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { - BLO_write_struct(writer, ImagePackedFile, imapf); - BKE_packedfile_blend_write(writer, imapf->packedfile); - } - - BKE_previewimg_blend_write(writer, ima->preview); - - LISTBASE_FOREACH (ImageView *, iv, &ima->views) { - BLO_write_struct(writer, ImageView, iv); - } - BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format); - - BLO_write_struct_list(writer, ImageTile, &ima->tiles); - - ima->packedfile = NULL; - - BLO_write_struct_list(writer, RenderSlot, &ima->renderslots); -} - -static void image_blend_read_data(BlendDataReader *reader, ID *id) -{ - Image *ima = (Image *)id; - BLO_read_list(reader, &ima->tiles); - - BLO_read_list(reader, &(ima->renderslots)); - if (!BLO_read_data_is_undo(reader)) { - /* We reset this last render slot index only when actually reading a file, not for undo. */ - ima->last_render_slot = ima->render_slot; - } - - BLO_read_list(reader, &(ima->views)); - BLO_read_list(reader, &(ima->packedfiles)); - - if (ima->packedfiles.first) { - LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { - BKE_packedfile_blend_read(reader, &imapf->packedfile); - } - ima->packedfile = NULL; - } - else { - BKE_packedfile_blend_read(reader, &ima->packedfile); - } - - BLI_listbase_clear(&ima->anims); - BLO_read_data_address(reader, &ima->preview); - BKE_previewimg_blend_read(reader, ima->preview); - BLO_read_data_address(reader, &ima->stereo3d_format); - - ima->lastused = 0; - ima->gpuflag = 0; - - image_runtime_reset(ima); -} - -static void image_blend_read_lib(BlendLibReader *UNUSED(reader), ID *id) -{ - Image *ima = (Image *)id; - /* Images have some kind of 'main' cache, when NULL we should also clear all others. */ - /* Needs to be done *after* cache pointers are restored (call to - * `foreach_cache`/`blo_cache_storage_entry_restore_in_new`), easier for now to do it in - * lib_link... */ - if (ima->cache == NULL) { - BKE_image_free_buffers(ima); - } -} - -IDTypeInfo IDType_ID_IM = { - .id_code = ID_IM, - .id_filter = FILTER_ID_IM, - .main_listbase_index = INDEX_ID_IM, - .struct_size = sizeof(Image), - .name = "Image", - .name_plural = "images", - .translation_context = BLT_I18NCONTEXT_ID_IMAGE, - .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, - .asset_type_info = NULL, - - .init_data = image_init_data, - .copy_data = image_copy_data, - .free_data = image_free_data, - .make_local = NULL, - .foreach_id = NULL, - .foreach_cache = image_foreach_cache, - .foreach_path = image_foreach_path, - .owner_get = NULL, - - .blend_write = image_blend_write, - .blend_read_data = image_blend_read_data, - .blend_read_lib = image_blend_read_lib, - .blend_read_expand = NULL, - - .blend_read_undo_preserve = NULL, - - .lib_override_apply_post = NULL, -}; - -/* prototypes */ -static int image_num_files(struct Image *ima); -static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock); -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 - -/* quick lookup: supports 1 million entries, thousand passes */ -#define IMA_MAKE_INDEX(entry, index) (((entry) << 10) + (index)) -#define IMA_INDEX_ENTRY(index) ((index) >> 10) -#if 0 -# define IMA_INDEX_PASS(index) (index & ~1023) -#endif - -/* ******** IMAGE CACHE ************* */ - -typedef struct ImageCacheKey { - int index; -} ImageCacheKey; - -static unsigned int imagecache_hashhash(const void *key_v) -{ - const ImageCacheKey *key = key_v; - return key->index; -} - -static bool imagecache_hashcmp(const void *a_v, const void *b_v) -{ - const ImageCacheKey *a = a_v; - const ImageCacheKey *b = b_v; - - return (a->index != b->index); -} - -static void imagecache_keydata(void *userkey, int *framenr, int *proxy, int *render_flags) -{ - ImageCacheKey *key = userkey; - - *framenr = IMA_INDEX_ENTRY(key->index); - *proxy = IMB_PROXY_NONE; - *render_flags = 0; -} - -static void imagecache_put(Image *image, int index, ImBuf *ibuf) -{ - ImageCacheKey key; - - if (image->cache == NULL) { - // char cache_name[64]; - // SNPRINTF(cache_name, "Image Datablock %s", image->id.name); - - image->cache = IMB_moviecache_create( - "Image Datablock Cache", sizeof(ImageCacheKey), imagecache_hashhash, imagecache_hashcmp); - IMB_moviecache_set_getdata_callback(image->cache, imagecache_keydata); - } - - key.index = index; - - IMB_moviecache_put(image->cache, &key, ibuf); -} - -static void imagecache_remove(Image *image, int index) -{ - if (image->cache == NULL) { - return; - } - - ImageCacheKey key; - key.index = index; - IMB_moviecache_remove(image->cache, &key); -} - -static struct ImBuf *imagecache_get(Image *image, int index, bool *r_is_cached_empty) -{ - if (image->cache) { - ImageCacheKey key; - key.index = index; - return IMB_moviecache_get(image->cache, &key, r_is_cached_empty); - } - - return NULL; -} - -/* ***************** ALLOC & FREE, DATA MANAGING *************** */ - -static void image_free_cached_frames(Image *image) -{ - if (image->cache) { - IMB_moviecache_free(image->cache); - image->cache = NULL; - } -} - -static void image_free_packedfiles(Image *ima) -{ - while (ima->packedfiles.last) { - ImagePackedFile *imapf = ima->packedfiles.last; - if (imapf->packedfile) { - BKE_packedfile_free(imapf->packedfile); - } - BLI_remlink(&ima->packedfiles, imapf); - MEM_freeN(imapf); - } -} - -void BKE_image_free_packedfiles(Image *ima) -{ - image_free_packedfiles(ima); -} - -void BKE_image_free_views(Image *image) -{ - BLI_freelistN(&image->views); -} - -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); - } -} - -void BKE_image_free_buffers_ex(Image *ima, bool do_lock) -{ - if (do_lock) { - BLI_mutex_lock(ima->runtime.cache_mutex); - } - image_free_cached_frames(ima); - - image_free_anims(ima); - - if (ima->rr) { - RE_FreeRenderResult(ima->rr); - ima->rr = NULL; - } - - BKE_image_free_gputextures(ima); - - if (do_lock) { - BLI_mutex_unlock(ima->runtime.cache_mutex); - } -} - -void BKE_image_free_buffers(Image *ima) -{ - BKE_image_free_buffers_ex(ima, false); -} - -void BKE_image_free_data(Image *ima) -{ - image_free_data(&ima->id); -} - -/* only image block itself */ -static void image_init(Image *ima, short source, short type) -{ - BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(ima, id)); - - MEMCPY_STRUCT_AFTER(ima, DNA_struct_default_get(Image), id); - - ima->source = source; - ima->type = type; - - if (source == IMA_SRC_VIEWER) { - ima->flag |= IMA_VIEW_AS_RENDER; - } - - ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles"); - tile->tile_number = 1001; - BLI_addtail(&ima->tiles, tile); - - if (type == IMA_TYPE_R_RESULT) { - for (int i = 0; i < 8; i++) { - BKE_image_add_renderslot(ima, NULL); - } - } - - image_runtime_reset(ima); - - BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); - ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); -} - -static Image *image_alloc(Main *bmain, const char *name, short source, short type) -{ - Image *ima; - - ima = BKE_libblock_alloc(bmain, ID_IM, name, 0); - if (ima) { - image_init(ima, source, type); - } - - return ima; -} - -/* Get the ibuf from an image cache by its index and entry. - * Local use here only. - * - * Returns referenced image buffer if it exists, callee is to - * call IMB_freeImBuf to de-reference the image buffer after - * it's done handling it. - */ -static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, - int index, - int entry, - bool *r_is_cached_empty) -{ - if (index != IMA_NO_INDEX) { - index = IMA_MAKE_INDEX(entry, index); - } - - return imagecache_get(ima, index, r_is_cached_empty); -} - -static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int entry) -{ - if (index != IMA_NO_INDEX) { - index = IMA_MAKE_INDEX(entry, index); - } - - imagecache_put(ima, index, ibuf); -} - -static void image_remove_ibuf(Image *ima, int index, int entry) -{ - if (index != IMA_NO_INDEX) { - index = IMA_MAKE_INDEX(entry, index); - } - imagecache_remove(ima, index); -} - -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)"); - STRNCPY(imapf_dst->filepath, imapf_src->filepath); - - if (imapf_src->packedfile) { - imapf_dst->packedfile = BKE_packedfile_duplicate(imapf_src->packedfile); - } - - BLI_addtail(lb_dst, imapf_dst); - } -} - -void BKE_image_merge(Main *bmain, Image *dest, Image *source) -{ - /* sanity check */ - if (dest && source && dest != source) { - BLI_mutex_lock(source->runtime.cache_mutex); - BLI_mutex_lock(dest->runtime.cache_mutex); - - if (source->cache != NULL) { - struct MovieCacheIter *iter; - iter = IMB_moviecacheIter_new(source->cache); - while (!IMB_moviecacheIter_done(iter)) { - ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - ImageCacheKey *key = IMB_moviecacheIter_getUserKey(iter); - imagecache_put(dest, key->index, ibuf); - IMB_moviecacheIter_step(iter); - } - IMB_moviecacheIter_free(iter); - } - - BLI_mutex_unlock(dest->runtime.cache_mutex); - BLI_mutex_unlock(source->runtime.cache_mutex); - - BKE_id_free(bmain, source); - } -} - -bool BKE_image_scale(Image *image, int width, int height) -{ - /* NOTE: We could be clever and scale all imbuf's - * but since some are mipmaps its not so simple. */ - - ImBuf *ibuf; - void *lock; - - ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - - if (ibuf) { - IMB_scaleImBuf(ibuf, width, height); - BKE_image_mark_dirty(image, ibuf); - } - - BKE_image_release_ibuf(image, ibuf, lock); - - return (ibuf != NULL); -} - -bool BKE_image_has_opengl_texture(Image *ima) -{ - for (int eye = 0; eye < 2; eye++) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != NULL) { - return true; - } - } - } - } - return false; -} - -static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser) -{ - BLI_assert(ima != NULL && ima->tiles.first); - ImageTile *tile = ima->tiles.first; - return (iuser && iuser->tile) ? iuser->tile : tile->tile_number; -} - -ImageTile *BKE_image_get_tile(Image *ima, int tile_number) -{ - if (ima == NULL) { - return NULL; - } - - /* Tiles 0 and 1001 are a special case and refer to the first tile, typically - * coming from non-UDIM-aware code. */ - if (ELEM(tile_number, 0, 1001)) { - return ima->tiles.first; - } - - /* Must have a tiled image and a valid tile number at this point. */ - if (ima->source != IMA_SRC_TILED || tile_number < 1001 || tile_number > IMA_UDIM_MAX) { - return NULL; - } - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->tile_number == tile_number) { - return tile; - } - } - - return NULL; -} - -ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser) -{ - return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser)); -} - -int BKE_image_get_tile_from_pos(struct Image *ima, - const float uv[2], - float r_uv[2], - float r_ofs[2]) -{ - float local_ofs[2]; - if (r_ofs == NULL) { - r_ofs = local_ofs; - } - - copy_v2_v2(r_uv, uv); - zero_v2(r_ofs); - - if ((ima->source != IMA_SRC_TILED) || uv[0] < 0.0f || uv[1] < 0.0f || uv[0] >= 10.0f) { - return 0; - } - - int ix = (int)uv[0]; - int iy = (int)uv[1]; - int tile_number = 1001 + 10 * iy + ix; - - if (BKE_image_get_tile(ima, tile_number) == NULL) { - return 0; - } - r_ofs[0] = ix; - r_ofs[1] = iy; - sub_v2_v2(r_uv, r_ofs); - - return tile_number; -} - -int BKE_image_find_nearest_tile(const Image *image, const float co[2]) -{ - const float co_floor[2] = {floorf(co[0]), floorf(co[1])}; - /* Distance to the closest UDIM tile. */ - float dist_best_sq = FLT_MAX; - int tile_number_best = -1; - - LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { - const int tile_index = tile->tile_number - 1001; - /* Coordinates of the current tile. */ - const float tile_index_co[2] = {tile_index % 10, tile_index / 10}; - - if (equals_v2v2(co_floor, tile_index_co)) { - return tile->tile_number; - } - - /* Distance between co[2] and UDIM tile. */ - const float dist_sq = len_squared_v2v2(tile_index_co, co); - - if (dist_sq < dist_best_sq) { - dist_best_sq = dist_sq; - tile_number_best = tile->tile_number; - } - } - - return tile_number_best; -} - -static void image_init_color_management(Image *ima) -{ - ImBuf *ibuf; - char name[FILE_MAX]; - - BKE_image_user_file_path(NULL, ima, name); - - /* will set input color space to image format default's */ - ibuf = IMB_loadiffname(name, IB_test | IB_alphamode_detect, ima->colorspace_settings.name); - - if (ibuf) { - if (ibuf->flags & IB_alphamode_premul) { - ima->alpha_mode = IMA_ALPHA_PREMUL; - } - else if (ibuf->flags & IB_alphamode_channel_packed) { - ima->alpha_mode = IMA_ALPHA_CHANNEL_PACKED; - } - else if (ibuf->flags & IB_alphamode_ignore) { - ima->alpha_mode = IMA_ALPHA_IGNORE; - } - else { - ima->alpha_mode = IMA_ALPHA_STRAIGHT; - } - - IMB_freeImBuf(ibuf); - } -} - -char BKE_image_alpha_mode_from_extension_ex(const char *filepath) -{ - if (BLI_path_extension_check_n(filepath, ".exr", ".cin", ".dpx", ".hdr", NULL)) { - return IMA_ALPHA_PREMUL; - } - - return IMA_ALPHA_STRAIGHT; -} - -void BKE_image_alpha_mode_from_extension(Image *image) -{ - image->alpha_mode = BKE_image_alpha_mode_from_extension_ex(image->filepath); -} - -Image *BKE_image_load(Main *bmain, const char *filepath) -{ - Image *ima; - int file; - char str[FILE_MAX]; - - STRNCPY(str, filepath); - BLI_path_abs(str, BKE_main_blendfile_path(bmain)); - - /* exists? */ - file = BLI_open(str, O_BINARY | O_RDONLY, 0); - if (file == -1) { - if (!BKE_image_tile_filepath_exists(str)) { - return NULL; - } - } - else { - close(file); - } - - ima = image_alloc(bmain, BLI_path_basename(filepath), IMA_SRC_FILE, IMA_TYPE_IMAGE); - STRNCPY(ima->filepath, filepath); - - if (BLI_path_extension_check_array(filepath, imb_ext_movie)) { - ima->source = IMA_SRC_MOVIE; - } - - image_init_color_management(ima); - - return ima; -} - -Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exists) -{ - Image *ima; - char str[FILE_MAX], strtest[FILE_MAX]; - - STRNCPY(str, filepath); - BLI_path_abs(str, bmain->filepath); - - /* first search an identical filepath */ - for (ima = bmain->images.first; ima; ima = ima->id.next) { - if (!ELEM(ima->source, IMA_SRC_VIEWER, IMA_SRC_GENERATED)) { - STRNCPY(strtest, ima->filepath); - BLI_path_abs(strtest, ID_BLEND_PATH(bmain, &ima->id)); - - if (BLI_path_cmp(strtest, str) == 0) { - if ((BKE_image_has_anim(ima) == false) || (ima->id.us == 0)) { - id_us_plus(&ima->id); /* officially should not, it doesn't link here! */ - if (r_exists) { - *r_exists = true; - } - return ima; - } - } - } - } - - if (r_exists) { - *r_exists = false; - } - return BKE_image_load(bmain, filepath); -} - -Image *BKE_image_load_exists(Main *bmain, const char *filepath) -{ - return BKE_image_load_exists_ex(bmain, filepath, NULL); -} - -typedef struct ImageFillData { - short gen_type; - uint width; - uint height; - unsigned char *rect; - float *rect_float; - float fill_color[4]; -} ImageFillData; - -static void image_buf_fill_isolated(void *usersata_v) -{ - ImageFillData *usersata = usersata_v; - - const short gen_type = usersata->gen_type; - const uint width = usersata->width; - const uint height = usersata->height; - - unsigned char *rect = usersata->rect; - float *rect_float = usersata->rect_float; - - switch (gen_type) { - case IMA_GENTYPE_GRID: - BKE_image_buf_fill_checker(rect, rect_float, width, height); - break; - case IMA_GENTYPE_GRID_COLOR: - BKE_image_buf_fill_checker_color(rect, rect_float, width, height); - break; - default: - BKE_image_buf_fill_color(rect, rect_float, width, height, usersata->fill_color); - break; - } -} - -static ImBuf *add_ibuf_size(unsigned int width, - unsigned int height, - const char *name, - int depth, - int floatbuf, - short gen_type, - const float color[4], - ColorManagedColorspaceSettings *colorspace_settings) -{ - ImBuf *ibuf; - unsigned char *rect = NULL; - float *rect_float = NULL; - float fill_color[4]; - - if (floatbuf) { - ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat); - - if (colorspace_settings->name[0] == '\0') { - const char *colorspace = IMB_colormanagement_role_colorspace_name_get( - COLOR_ROLE_DEFAULT_FLOAT); - - STRNCPY(colorspace_settings->name, colorspace); - } - - if (ibuf != NULL) { - rect_float = ibuf->rect_float; - IMB_colormanagement_check_is_data(ibuf, colorspace_settings->name); - } - - if (IMB_colormanagement_space_name_is_data(colorspace_settings->name)) { - copy_v4_v4(fill_color, color); - } - else { - /* The input color here should ideally be linear already, but for now - * we just convert and postpone breaking the API for later. */ - srgb_to_linearrgb_v4(fill_color, color); - } - } - else { - ibuf = IMB_allocImBuf(width, height, depth, IB_rect); - - if (colorspace_settings->name[0] == '\0') { - const char *colorspace = IMB_colormanagement_role_colorspace_name_get( - COLOR_ROLE_DEFAULT_BYTE); - - STRNCPY(colorspace_settings->name, colorspace); - } - - if (ibuf != NULL) { - rect = (unsigned char *)ibuf->rect; - IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace_settings->name); - } - - copy_v4_v4(fill_color, color); - } - - if (!ibuf) { - return NULL; - } - - STRNCPY(ibuf->name, name); - - ImageFillData data; - - data.gen_type = gen_type; - data.width = width; - data.height = height; - data.rect = rect; - data.rect_float = rect_float; - copy_v4_v4(data.fill_color, fill_color); - - BLI_task_isolate(image_buf_fill_isolated, &data); - - return ibuf; -} - -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, - const bool is_data, - const bool tiled) -{ - /* on save, type is changed to FILE in editsima.c */ - Image *ima; - if (tiled) { - ima = image_alloc(bmain, name, IMA_SRC_TILED, IMA_TYPE_IMAGE); - } - else { - ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); - } - if (ima == NULL) { - return NULL; - } - - int view_id; - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - - // STRNCPY(ima->filepath, name); /* don't do this, this writes in ain invalid filepath! */ - ima->gen_x = width; - ima->gen_y = height; - ima->gen_type = gen_type; - ima->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0); - ima->gen_depth = depth; - copy_v4_v4(ima->gen_color, color); - - if (is_data) { - STRNCPY(ima->colorspace_settings.name, - IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA)); - } - - for (view_id = 0; view_id < 2; view_id++) { - ImBuf *ibuf; - ibuf = add_ibuf_size( - width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings); - int index = tiled ? 0 : IMA_NO_INDEX; - int entry = tiled ? 1001 : 0; - image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry); - - /* 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], ""); - } - - return ima; -} - -Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) -{ - /* on save, type is changed to FILE in editsima.c */ - Image *ima; - - if (name == NULL) { - name = BLI_path_basename(ibuf->name); - } - - ima = image_alloc(bmain, name, IMA_SRC_FILE, IMA_TYPE_IMAGE); - - if (ima) { - STRNCPY(ima->filepath, ibuf->name); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - } - - return ima; -} - -/* Pack image buffer to memory as PNG or EXR. */ -static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath) -{ - ibuf->ftype = (ibuf->rect_float) ? IMB_FTYPE_OPENEXR : IMB_FTYPE_PNG; - - IMB_saveiff(ibuf, filepath, IB_rect | IB_mem); - - if (ibuf->encodedbuffer == NULL) { - CLOG_STR_ERROR(&LOG, "memory save for pack error"); - IMB_freeImBuf(ibuf); - image_free_packedfiles(ima); - return false; - } - - ImagePackedFile *imapf; - PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); - - pf->data = ibuf->encodedbuffer; - pf->size = ibuf->encodedsize; - - imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); - STRNCPY(imapf->filepath, filepath); - imapf->packedfile = pf; - BLI_addtail(&ima->packedfiles, imapf); - - ibuf->encodedbuffer = NULL; - ibuf->encodedsize = 0; - ibuf->userflags &= ~IB_BITMAPDIRTY; - - return true; -} - -bool BKE_image_memorypack(Image *ima) -{ - bool ok = true; - - image_free_packedfiles(ima); - - if (BKE_image_is_multiview(ima)) { - /* Store each view as a separate packed files with R_IMF_VIEWS_INDIVIDUAL. */ - ImageView *iv; - int i; - - for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, NULL); - - if (!ibuf) { - ok = false; - break; - } - - /* 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], ""); - } - - ok = ok && image_memorypack_imbuf(ima, ibuf, iv->filepath); - IMB_freeImBuf(ibuf); - } - - ima->views_format = R_IMF_VIEWS_INDIVIDUAL; - } - else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); - - if (ibuf) { - ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); - IMB_freeImBuf(ibuf); - } - else { - ok = false; - } - } - - if (ok && ima->source == IMA_SRC_GENERATED) { - ima->source = IMA_SRC_FILE; - ima->type = IMA_TYPE_IMAGE; - } - - return ok; -} - -void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) -{ - const int totfiles = image_num_files(ima); - - if (totfiles == 1) { - ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); - BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new(reports, ima->filepath, basepath); - if (imapf->packedfile) { - STRNCPY(imapf->filepath, ima->filepath); - } - else { - BLI_freelinkN(&ima->packedfiles, imapf); - } - } - 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 = BKE_packedfile_new(reports, iv->filepath, basepath); - if (imapf->packedfile) { - STRNCPY(imapf->filepath, iv->filepath); - } - else { - BLI_freelinkN(&ima->packedfiles, imapf); - } - } - } -} - -void BKE_image_packfiles_from_mem(ReportList *reports, - Image *ima, - char *data, - const size_t data_len) -{ - const int totfiles = image_num_files(ima); - - if (totfiles != 1) { - BKE_report(reports, RPT_ERROR, "Cannot pack multiview images from raw data currently..."); - } - else { - ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), __func__); - BLI_addtail(&ima->packedfiles, imapf); - imapf->packedfile = BKE_packedfile_new_from_memory(data, data_len); - STRNCPY(imapf->filepath, ima->filepath); - } -} - -void BKE_image_tag_time(Image *ima) -{ - ima->lastused = PIL_check_seconds_timer_i(); -} - -static uintptr_t image_mem_size(Image *image) -{ - uintptr_t size = 0; - - /* viewers have memory depending on other rules, has no valid rect pointer */ - if (image->source == IMA_SRC_VIEWER) { - return 0; - } - - BLI_mutex_lock(image->runtime.cache_mutex); - - if (image->cache != NULL) { - struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); - - while (!IMB_moviecacheIter_done(iter)) { - ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - IMB_moviecacheIter_step(iter); - if (ibuf == NULL) { - continue; - } - ImBuf *ibufm; - int level; - - if (ibuf->rect) { - size += MEM_allocN_len(ibuf->rect); - } - if (ibuf->rect_float) { - size += MEM_allocN_len(ibuf->rect_float); - } - - for (level = 0; level < IMB_MIPMAP_LEVELS; level++) { - ibufm = ibuf->mipmap[level]; - if (ibufm) { - if (ibufm->rect) { - size += MEM_allocN_len(ibufm->rect); - } - if (ibufm->rect_float) { - size += MEM_allocN_len(ibufm->rect_float); - } - } - } - } - IMB_moviecacheIter_free(iter); - } - - BLI_mutex_unlock(image->runtime.cache_mutex); - - return size; -} - -void BKE_image_print_memlist(Main *bmain) -{ - Image *ima; - uintptr_t size, totsize = 0; - - for (ima = bmain->images.first; ima; ima = ima->id.next) { - totsize += image_mem_size(ima); - } - - printf("\ntotal image memory len: %.3f MB\n", (double)totsize / (double)(1024 * 1024)); - - for (ima = bmain->images.first; ima; ima = ima->id.next) { - size = image_mem_size(ima); - - if (size) { - printf("%s len: %.3f MB\n", ima->id.name + 2, (double)size / (double)(1024 * 1024)); - } - } -} - -static bool imagecache_check_dirty(ImBuf *ibuf, void *UNUSED(userkey), void *UNUSED(userdata)) -{ - if (ibuf == NULL) { - return false; - } - return (ibuf->userflags & IB_BITMAPDIRTY) == 0; -} - -void BKE_image_free_all_textures(Main *bmain) -{ -#undef CHECK_FREED_SIZE - - Tex *tex; - Image *ima; -#ifdef CHECK_FREED_SIZE - uintptr_t tot_freed_size = 0; -#endif - - for (ima = bmain->images.first; ima; ima = ima->id.next) { - ima->id.tag &= ~LIB_TAG_DOIT; - } - - for (tex = bmain->textures.first; tex; tex = tex->id.next) { - if (tex->ima) { - tex->ima->id.tag |= LIB_TAG_DOIT; - } - } - - for (ima = bmain->images.first; ima; ima = ima->id.next) { - if (ima->cache && (ima->id.tag & LIB_TAG_DOIT)) { -#ifdef CHECK_FREED_SIZE - uintptr_t old_size = image_mem_size(ima); -#endif - - IMB_moviecache_cleanup(ima->cache, imagecache_check_dirty, NULL); - -#ifdef CHECK_FREED_SIZE - tot_freed_size += old_size - image_mem_size(ima); -#endif - } - } -#ifdef CHECK_FREED_SIZE - printf("%s: freed total %lu MB\n", __func__, tot_freed_size / (1024 * 1024)); -#endif -} - -static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void *userdata) -{ - if (ibuf == NULL) { - return true; - } - int except_frame = *(int *)userdata; - return (ibuf->userflags & IB_BITMAPDIRTY) == 0 && (ibuf->index != IMA_NO_INDEX) && - (except_frame != IMA_INDEX_ENTRY(ibuf->index)); -} - -void BKE_image_free_anim_ibufs(Image *ima, int except_frame) -{ - BLI_mutex_lock(ima->runtime.cache_mutex); - if (ima->cache != NULL) { - IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame); - } - BLI_mutex_unlock(ima->runtime.cache_mutex); -} - -void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) -{ - Image *ima; - - for (ima = bmain->images.first; ima; ima = ima->id.next) { - if (BKE_image_is_animated(ima)) { - BKE_image_free_anim_ibufs(ima, cfra); - } - } -} - -/* *********** READ AND WRITE ************** */ - -int BKE_image_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options) -{ - memset(r_options, 0, sizeof(*r_options)); - - if (imtype == R_IMF_IMTYPE_TARGA) { - return IMB_FTYPE_TGA; - } - if (imtype == R_IMF_IMTYPE_RAWTGA) { - r_options->flag = RAWTGA; - return IMB_FTYPE_TGA; - } - if (imtype == R_IMF_IMTYPE_IRIS) { - return IMB_FTYPE_IMAGIC; - } -#ifdef WITH_HDR - if (imtype == R_IMF_IMTYPE_RADHDR) { - return IMB_FTYPE_RADHDR; - } -#endif - if (imtype == R_IMF_IMTYPE_PNG) { - r_options->quality = 15; - return IMB_FTYPE_PNG; - } -#ifdef WITH_DDS - if (imtype == R_IMF_IMTYPE_DDS) { - return IMB_FTYPE_DDS; - } -#endif - if (imtype == R_IMF_IMTYPE_BMP) { - return IMB_FTYPE_BMP; - } -#ifdef WITH_TIFF - if (imtype == R_IMF_IMTYPE_TIFF) { - return IMB_FTYPE_TIF; - } -#endif - if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - return IMB_FTYPE_OPENEXR; - } -#ifdef WITH_CINEON - if (imtype == R_IMF_IMTYPE_CINEON) { - return IMB_FTYPE_CINEON; - } - if (imtype == R_IMF_IMTYPE_DPX) { - return IMB_FTYPE_DPX; - } -#endif -#ifdef WITH_OPENJPEG - if (imtype == R_IMF_IMTYPE_JP2) { - r_options->flag |= JP2_JP2; - r_options->quality = 90; - return IMB_FTYPE_JP2; - } -#endif - - r_options->quality = 90; - return IMB_FTYPE_JPG; -} - -char BKE_image_ftype_to_imtype(const int ftype, const ImbFormatOptions *options) -{ - if (ftype == IMB_FTYPE_NONE) { - return R_IMF_IMTYPE_TARGA; - } - if (ftype == IMB_FTYPE_IMAGIC) { - return R_IMF_IMTYPE_IRIS; - } -#ifdef WITH_HDR - if (ftype == IMB_FTYPE_RADHDR) { - return R_IMF_IMTYPE_RADHDR; - } -#endif - if (ftype == IMB_FTYPE_PNG) { - return R_IMF_IMTYPE_PNG; - } -#ifdef WITH_DDS - if (ftype == IMB_FTYPE_DDS) { - return R_IMF_IMTYPE_DDS; - } -#endif - if (ftype == IMB_FTYPE_BMP) { - return R_IMF_IMTYPE_BMP; - } -#ifdef WITH_TIFF - if (ftype == IMB_FTYPE_TIF) { - return R_IMF_IMTYPE_TIFF; - } -#endif - if (ftype == IMB_FTYPE_OPENEXR) { - return R_IMF_IMTYPE_OPENEXR; - } -#ifdef WITH_CINEON - if (ftype == IMB_FTYPE_CINEON) { - return R_IMF_IMTYPE_CINEON; - } - if (ftype == IMB_FTYPE_DPX) { - return R_IMF_IMTYPE_DPX; - } -#endif - if (ftype == IMB_FTYPE_TGA) { - if (options && (options->flag & RAWTGA)) { - return R_IMF_IMTYPE_RAWTGA; - } - - return R_IMF_IMTYPE_TARGA; - } -#ifdef WITH_OPENJPEG - if (ftype == IMB_FTYPE_JP2) { - return R_IMF_IMTYPE_JP2; - } -#endif - - return R_IMF_IMTYPE_JPEG90; -} - -bool BKE_imtype_is_movie(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_AVIRAW: - case R_IMF_IMTYPE_AVIJPEG: - case R_IMF_IMTYPE_FFMPEG: - case R_IMF_IMTYPE_H264: - case R_IMF_IMTYPE_THEORA: - case R_IMF_IMTYPE_XVID: - return true; - } - return false; -} - -bool BKE_imtype_supports_zbuf(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_IRIZ: - case R_IMF_IMTYPE_OPENEXR: /* but not R_IMF_IMTYPE_MULTILAYER */ - return true; - } - return false; -} - -bool BKE_imtype_supports_compress(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_PNG: - return true; - } - return false; -} - -bool BKE_imtype_supports_quality(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_JPEG90: - case R_IMF_IMTYPE_JP2: - case R_IMF_IMTYPE_AVIJPEG: - return true; - } - return false; -} - -bool BKE_imtype_requires_linear_float(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_CINEON: - case R_IMF_IMTYPE_DPX: - case R_IMF_IMTYPE_RADHDR: - case R_IMF_IMTYPE_OPENEXR: - case R_IMF_IMTYPE_MULTILAYER: - return true; - } - return false; -} - -char BKE_imtype_valid_channels(const char imtype, bool write_file) -{ - char chan_flag = IMA_CHAN_FLAG_RGB; /* Assume all support RGB. */ - - /* Alpha. */ - switch (imtype) { - case R_IMF_IMTYPE_BMP: - if (write_file) { - break; - } - ATTR_FALLTHROUGH; - case R_IMF_IMTYPE_TARGA: - case R_IMF_IMTYPE_RAWTGA: - case R_IMF_IMTYPE_IRIS: - case R_IMF_IMTYPE_PNG: - case R_IMF_IMTYPE_TIFF: - case R_IMF_IMTYPE_OPENEXR: - case R_IMF_IMTYPE_MULTILAYER: - case R_IMF_IMTYPE_DDS: - case R_IMF_IMTYPE_JP2: - case R_IMF_IMTYPE_DPX: - chan_flag |= IMA_CHAN_FLAG_ALPHA; - break; - } - - /* BW. */ - switch (imtype) { - case R_IMF_IMTYPE_BMP: - case R_IMF_IMTYPE_PNG: - case R_IMF_IMTYPE_JPEG90: - case R_IMF_IMTYPE_TARGA: - case R_IMF_IMTYPE_RAWTGA: - case R_IMF_IMTYPE_TIFF: - case R_IMF_IMTYPE_IRIS: - chan_flag |= IMA_CHAN_FLAG_BW; - break; - } - - return chan_flag; -} - -char BKE_imtype_valid_depths(const char imtype) -{ - switch (imtype) { - case R_IMF_IMTYPE_RADHDR: - return R_IMF_CHAN_DEPTH_32; - case R_IMF_IMTYPE_TIFF: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; - case R_IMF_IMTYPE_OPENEXR: - return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; - case R_IMF_IMTYPE_MULTILAYER: - return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; - /* eeh, cineon does some strange 10bits per channel */ - case R_IMF_IMTYPE_DPX: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_10 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; - case R_IMF_IMTYPE_CINEON: - return R_IMF_CHAN_DEPTH_10; - case R_IMF_IMTYPE_JP2: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; - case R_IMF_IMTYPE_PNG: - return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; - /* most formats are 8bit only */ - default: - return R_IMF_CHAN_DEPTH_8; - } -} - -char BKE_imtype_from_arg(const char *imtype_arg) -{ - if (STREQ(imtype_arg, "TGA")) { - return R_IMF_IMTYPE_TARGA; - } - if (STREQ(imtype_arg, "IRIS")) { - return R_IMF_IMTYPE_IRIS; - } -#ifdef WITH_DDS - if (STREQ(imtype_arg, "DDS")) { - return R_IMF_IMTYPE_DDS; - } -#endif - if (STREQ(imtype_arg, "JPEG")) { - return R_IMF_IMTYPE_JPEG90; - } - if (STREQ(imtype_arg, "IRIZ")) { - return R_IMF_IMTYPE_IRIZ; - } - if (STREQ(imtype_arg, "RAWTGA")) { - return R_IMF_IMTYPE_RAWTGA; - } - if (STREQ(imtype_arg, "AVIRAW")) { - return R_IMF_IMTYPE_AVIRAW; - } - if (STREQ(imtype_arg, "AVIJPEG")) { - return R_IMF_IMTYPE_AVIJPEG; - } - if (STREQ(imtype_arg, "PNG")) { - return R_IMF_IMTYPE_PNG; - } - if (STREQ(imtype_arg, "BMP")) { - return R_IMF_IMTYPE_BMP; - } -#ifdef WITH_HDR - if (STREQ(imtype_arg, "HDR")) { - return R_IMF_IMTYPE_RADHDR; - } -#endif -#ifdef WITH_TIFF - if (STREQ(imtype_arg, "TIFF")) { - return R_IMF_IMTYPE_TIFF; - } -#endif -#ifdef WITH_OPENEXR - if (STREQ(imtype_arg, "OPEN_EXR")) { - return R_IMF_IMTYPE_OPENEXR; - } - if (STREQ(imtype_arg, "OPEN_EXR_MULTILAYER")) { - return R_IMF_IMTYPE_MULTILAYER; - } - if (STREQ(imtype_arg, "EXR")) { - return R_IMF_IMTYPE_OPENEXR; - } - if (STREQ(imtype_arg, "MULTILAYER")) { - return R_IMF_IMTYPE_MULTILAYER; - } -#endif - if (STREQ(imtype_arg, "FFMPEG")) { - return R_IMF_IMTYPE_FFMPEG; - } -#ifdef WITH_CINEON - if (STREQ(imtype_arg, "CINEON")) { - return R_IMF_IMTYPE_CINEON; - } - if (STREQ(imtype_arg, "DPX")) { - return R_IMF_IMTYPE_DPX; - } -#endif -#ifdef WITH_OPENJPEG - if (STREQ(imtype_arg, "JP2")) { - return R_IMF_IMTYPE_JP2; - } -#endif - - return R_IMF_IMTYPE_INVALID; -} - -static bool do_add_image_extension(char *string, - const char imtype, - const ImageFormatData *im_format) -{ - const char *extension = NULL; - const char *extension_test; - (void)im_format; /* may be unused, depends on build options */ - - if (imtype == R_IMF_IMTYPE_IRIS) { - if (!BLI_path_extension_check(string, extension_test = ".rgb")) { - extension = extension_test; - } - } - else if (imtype == R_IMF_IMTYPE_IRIZ) { - if (!BLI_path_extension_check(string, extension_test = ".rgb")) { - extension = extension_test; - } - } -#ifdef WITH_HDR - else if (imtype == R_IMF_IMTYPE_RADHDR) { - if (!BLI_path_extension_check(string, extension_test = ".hdr")) { - extension = extension_test; - } - } -#endif - else if (ELEM(imtype, - R_IMF_IMTYPE_PNG, - R_IMF_IMTYPE_FFMPEG, - R_IMF_IMTYPE_H264, - R_IMF_IMTYPE_THEORA, - R_IMF_IMTYPE_XVID)) { - if (!BLI_path_extension_check(string, extension_test = ".png")) { - extension = extension_test; - } - } -#ifdef WITH_DDS - else if (imtype == R_IMF_IMTYPE_DDS) { - if (!BLI_path_extension_check(string, extension_test = ".dds")) { - extension = extension_test; - } - } -#endif - else if (ELEM(imtype, R_IMF_IMTYPE_TARGA, R_IMF_IMTYPE_RAWTGA)) { - if (!BLI_path_extension_check(string, extension_test = ".tga")) { - extension = extension_test; - } - } - else if (imtype == R_IMF_IMTYPE_BMP) { - if (!BLI_path_extension_check(string, extension_test = ".bmp")) { - extension = extension_test; - } - } -#ifdef WITH_TIFF - else if (imtype == R_IMF_IMTYPE_TIFF) { - if (!BLI_path_extension_check_n(string, extension_test = ".tif", ".tiff", NULL)) { - extension = extension_test; - } - } -#endif -#ifdef WITH_OPENIMAGEIO - else if (imtype == R_IMF_IMTYPE_PSD) { - if (!BLI_path_extension_check(string, extension_test = ".psd")) { - extension = extension_test; - } - } -#endif -#ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - if (!BLI_path_extension_check(string, extension_test = ".exr")) { - extension = extension_test; - } - } -#endif -#ifdef WITH_CINEON - else if (imtype == R_IMF_IMTYPE_CINEON) { - if (!BLI_path_extension_check(string, extension_test = ".cin")) { - extension = extension_test; - } - } - else if (imtype == R_IMF_IMTYPE_DPX) { - if (!BLI_path_extension_check(string, extension_test = ".dpx")) { - extension = extension_test; - } - } -#endif -#ifdef WITH_OPENJPEG - else if (imtype == R_IMF_IMTYPE_JP2) { - if (im_format) { - if (im_format->jp2_codec == R_IMF_JP2_CODEC_JP2) { - if (!BLI_path_extension_check(string, extension_test = ".jp2")) { - extension = extension_test; - } - } - else if (im_format->jp2_codec == R_IMF_JP2_CODEC_J2K) { - if (!BLI_path_extension_check(string, extension_test = ".j2c")) { - extension = extension_test; - } - } - else { - BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); - } - } - else { - if (!BLI_path_extension_check(string, extension_test = ".jp2")) { - extension = extension_test; - } - } - } -#endif - else { // R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG, R_IMF_IMTYPE_JPEG90 etc - if (!(BLI_path_extension_check_n(string, extension_test = ".jpg", ".jpeg", NULL))) { - extension = extension_test; - } - } - - if (extension) { - /* prefer this in many cases to avoid .png.tga, but in certain cases it breaks */ - /* remove any other known image extension */ - if (BLI_path_extension_check_array(string, imb_ext_image)) { - return BLI_path_extension_replace(string, FILE_MAX, extension); - } - - return BLI_path_extension_ensure(string, FILE_MAX, extension); - } - - return false; -} - -int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) -{ - return do_add_image_extension(string, im_format->imtype, im_format); -} - -int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) -{ - return do_add_image_extension(string, imtype, NULL); -} - -void BKE_imformat_defaults(ImageFormatData *im_format) -{ - memset(im_format, 0, sizeof(*im_format)); - im_format->planes = R_IMF_PLANES_RGBA; - im_format->imtype = R_IMF_IMTYPE_PNG; - im_format->depth = R_IMF_CHAN_DEPTH_8; - im_format->quality = 90; - im_format->compress = 15; - - BKE_color_managed_display_settings_init(&im_format->display_settings); - BKE_color_managed_view_settings_init_default(&im_format->view_settings, - &im_format->display_settings); -} - -void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *imbuf) -{ - int ftype = imbuf->ftype; - int custom_flags = imbuf->foptions.flag; - char quality = imbuf->foptions.quality; - - BKE_imformat_defaults(im_format); - - /* file type */ - - if (ftype == IMB_FTYPE_IMAGIC) { - im_format->imtype = R_IMF_IMTYPE_IRIS; - } -#ifdef WITH_HDR - else if (ftype == IMB_FTYPE_RADHDR) { - im_format->imtype = R_IMF_IMTYPE_RADHDR; - } -#endif - else if (ftype == IMB_FTYPE_PNG) { - im_format->imtype = R_IMF_IMTYPE_PNG; - - if (custom_flags & PNG_16BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - - im_format->compress = quality; - } - -#ifdef WITH_DDS - else if (ftype == IMB_FTYPE_DDS) { - im_format->imtype = R_IMF_IMTYPE_DDS; - } -#endif - else if (ftype == IMB_FTYPE_BMP) { - im_format->imtype = R_IMF_IMTYPE_BMP; - } -#ifdef WITH_TIFF - else if (ftype == IMB_FTYPE_TIF) { - im_format->imtype = R_IMF_IMTYPE_TIFF; - if (custom_flags & TIF_16BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - if (custom_flags & TIF_COMPRESS_NONE) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_NONE; - } - if (custom_flags & TIF_COMPRESS_DEFLATE) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_DEFLATE; - } - if (custom_flags & TIF_COMPRESS_LZW) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_LZW; - } - if (custom_flags & TIF_COMPRESS_PACKBITS) { - im_format->tiff_codec = R_IMF_TIFF_CODEC_PACKBITS; - } - } -#endif - -#ifdef WITH_OPENEXR - else if (ftype == IMB_FTYPE_OPENEXR) { - im_format->imtype = R_IMF_IMTYPE_OPENEXR; - if (custom_flags & OPENEXR_HALF) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - if (custom_flags & OPENEXR_COMPRESS) { - im_format->exr_codec = R_IMF_EXR_CODEC_ZIP; /* Can't determine compression */ - } - if (imbuf->zbuf_float) { - im_format->flag |= R_IMF_FLAG_ZBUF; - } - } -#endif - -#ifdef WITH_CINEON - else if (ftype == IMB_FTYPE_CINEON) { - im_format->imtype = R_IMF_IMTYPE_CINEON; - } - else if (ftype == IMB_FTYPE_DPX) { - im_format->imtype = R_IMF_IMTYPE_DPX; - } -#endif - else if (ftype == IMB_FTYPE_TGA) { - if (custom_flags & RAWTGA) { - im_format->imtype = R_IMF_IMTYPE_RAWTGA; - } - else { - im_format->imtype = R_IMF_IMTYPE_TARGA; - } - } -#ifdef WITH_OPENJPEG - else if (ftype == IMB_FTYPE_JP2) { - im_format->imtype = R_IMF_IMTYPE_JP2; - im_format->quality = quality; - - if (custom_flags & JP2_16BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_16; - } - else if (custom_flags & JP2_12BIT) { - im_format->depth = R_IMF_CHAN_DEPTH_12; - } - - if (custom_flags & JP2_YCC) { - im_format->jp2_flag |= R_IMF_JP2_FLAG_YCC; - } - - if (custom_flags & JP2_CINE) { - im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_PRESET; - if (custom_flags & JP2_CINE_48FPS) { - im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_48; - } - } - - if (custom_flags & JP2_JP2) { - im_format->jp2_codec = R_IMF_JP2_CODEC_JP2; - } - else if (custom_flags & JP2_J2K) { - im_format->jp2_codec = R_IMF_JP2_CODEC_J2K; - } - else { - BLI_assert_msg(0, "Unsupported jp2 codec was specified in file type"); - } - } -#endif - - else { - im_format->imtype = R_IMF_IMTYPE_JPEG90; - im_format->quality = quality; - } - - /* planes */ - im_format->planes = imbuf->planes; -} - -#define STAMP_NAME_SIZE ((MAX_ID_NAME - 2) + 16) -/* could allow access externally - 512 is for long names, - * STAMP_NAME_SIZE is for id names, allowing them some room for description */ -typedef struct StampDataCustomField { - struct StampDataCustomField *next, *prev; - /* TODO(sergey): Think of better size here, maybe dynamically allocated even. */ - char key[512]; - char *value; - /* TODO(sergey): Support non-string values. */ -} StampDataCustomField; - -typedef struct StampData { - char file[512]; - char note[512]; - char date[512]; - char marker[512]; - char time[512]; - char frame[512]; - char frame_range[512]; - char camera[STAMP_NAME_SIZE]; - char cameralens[STAMP_NAME_SIZE]; - char scene[STAMP_NAME_SIZE]; - char strip[STAMP_NAME_SIZE]; - char rendertime[STAMP_NAME_SIZE]; - char memory[STAMP_NAME_SIZE]; - char hostname[512]; - - /* Custom fields are used to put extra meta information header from render - * engine to the result image. - * - * NOTE: This fields are not stamped onto the image. At least for now. - */ - ListBase custom_fields; -} StampData; -#undef STAMP_NAME_SIZE - -/** - * \param do_prefix: Include a label like "File ", "Date ", etc. in the stamp data strings. - * \param use_dynamic: Also include data that can change on a per-frame basis. - */ -static void stampdata( - const Scene *scene, Object *camera, StampData *stamp_data, int do_prefix, bool use_dynamic) -{ - char text[256]; - struct tm *tl; - time_t t; - - if (scene->r.stamp & R_STAMP_FILENAME) { - const char *blendfile_path = BKE_main_blendfile_path_from_global(); - SNPRINTF(stamp_data->file, - do_prefix ? "File %s" : "%s", - (blendfile_path[0] != '\0') ? blendfile_path : ""); - } - else { - stamp_data->file[0] = '\0'; - } - - if (scene->r.stamp & R_STAMP_NOTE) { - /* Never do prefix for Note */ - SNPRINTF(stamp_data->note, "%s", scene->r.stamp_udata); - } - else { - stamp_data->note[0] = '\0'; - } - - if (scene->r.stamp & R_STAMP_DATE) { - t = time(NULL); - tl = localtime(&t); - SNPRINTF(text, - "%04d/%02d/%02d %02d:%02d:%02d", - tl->tm_year + 1900, - tl->tm_mon + 1, - tl->tm_mday, - tl->tm_hour, - tl->tm_min, - tl->tm_sec); - SNPRINTF(stamp_data->date, do_prefix ? "Date %s" : "%s", text); - } - else { - stamp_data->date[0] = '\0'; - } - - if (use_dynamic && scene->r.stamp & R_STAMP_MARKER) { - const char *name = BKE_scene_find_last_marker_name(scene, CFRA); - - if (name) { - STRNCPY(text, name); - } - else { - STRNCPY(text, ""); - } - - SNPRINTF(stamp_data->marker, do_prefix ? "Marker %s" : "%s", text); - } - else { - stamp_data->marker[0] = '\0'; - } - - if (use_dynamic && scene->r.stamp & R_STAMP_TIME) { - const short timecode_style = USER_TIMECODE_SMPTE_FULL; - BLI_timecode_string_from_time( - text, sizeof(text), 0, FRA2TIME(scene->r.cfra), FPS, timecode_style); - SNPRINTF(stamp_data->time, do_prefix ? "Timecode %s" : "%s", text); - } - else { - stamp_data->time[0] = '\0'; - } - - if (use_dynamic && scene->r.stamp & R_STAMP_FRAME) { - char fmtstr[32]; - int digits = 1; - - if (scene->r.efra > 9) { - digits = integer_digits_i(scene->r.efra); - } - - SNPRINTF(fmtstr, do_prefix ? "Frame %%0%di" : "%%0%di", digits); - SNPRINTF(stamp_data->frame, fmtstr, scene->r.cfra); - } - else { - stamp_data->frame[0] = '\0'; - } - - if (scene->r.stamp & R_STAMP_FRAME_RANGE) { - SNPRINTF(stamp_data->frame_range, - do_prefix ? "Frame Range %d:%d" : "%d:%d", - scene->r.sfra, - scene->r.efra); - } - else { - stamp_data->frame_range[0] = '\0'; - } - - if (use_dynamic && scene->r.stamp & R_STAMP_CAMERA) { - SNPRINTF(stamp_data->camera, - do_prefix ? "Camera %s" : "%s", - camera ? camera->id.name + 2 : ""); - } - else { - stamp_data->camera[0] = '\0'; - } - - if (use_dynamic && scene->r.stamp & R_STAMP_CAMERALENS) { - if (camera && camera->type == OB_CAMERA) { - SNPRINTF(text, "%.2f", ((Camera *)camera->data)->lens); - } - else { - STRNCPY(text, ""); - } - - SNPRINTF(stamp_data->cameralens, do_prefix ? "Lens %s" : "%s", text); - } - else { - stamp_data->cameralens[0] = '\0'; - } - - if (scene->r.stamp & R_STAMP_SCENE) { - SNPRINTF(stamp_data->scene, do_prefix ? "Scene %s" : "%s", scene->id.name + 2); - } - else { - stamp_data->scene[0] = '\0'; - } - - if (use_dynamic && scene->r.stamp & R_STAMP_SEQSTRIP) { - const Sequence *seq = SEQ_get_topmost_sequence(scene, scene->r.cfra); - - if (seq) { - STRNCPY(text, seq->name + 2); - } - else { - STRNCPY(text, ""); - } - - SNPRINTF(stamp_data->strip, do_prefix ? "Strip %s" : "%s", text); - } - else { - stamp_data->strip[0] = '\0'; - } - - { - Render *re = RE_GetSceneRender(scene); - RenderStats *stats = re ? RE_GetStats(re) : NULL; - - if (use_dynamic && stats && (scene->r.stamp & R_STAMP_RENDERTIME)) { - BLI_timecode_string_from_time_simple(text, sizeof(text), stats->lastframetime); - - SNPRINTF(stamp_data->rendertime, do_prefix ? "RenderTime %s" : "%s", text); - } - else { - stamp_data->rendertime[0] = '\0'; - } - - if (use_dynamic && stats && (scene->r.stamp & R_STAMP_MEMORY)) { - SNPRINTF(stamp_data->memory, do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak); - } - else { - stamp_data->memory[0] = '\0'; - } - } - if (scene->r.stamp & R_STAMP_FRAME_RANGE) { - SNPRINTF(stamp_data->frame_range, - do_prefix ? "Frame Range %d:%d" : "%d:%d", - scene->r.sfra, - scene->r.efra); - } - else { - stamp_data->frame_range[0] = '\0'; - } - - if (scene->r.stamp & R_STAMP_HOSTNAME) { - char hostname[500]; /* sizeof(stamp_data->hostname) minus some bytes for a label. */ - BLI_hostname_get(hostname, sizeof(hostname)); - SNPRINTF(stamp_data->hostname, do_prefix ? "Hostname %s" : "%s", hostname); - } - else { - stamp_data->hostname[0] = '\0'; - } -} - -static void stampdata_from_template(StampData *stamp_data, - const Scene *scene, - const StampData *stamp_data_template, - bool do_prefix) -{ - if (scene->r.stamp & R_STAMP_FILENAME) { - SNPRINTF(stamp_data->file, do_prefix ? "File %s" : "%s", stamp_data_template->file); - } - else { - stamp_data->file[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_NOTE) { - SNPRINTF(stamp_data->note, "%s", stamp_data_template->note); - } - else { - stamp_data->note[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_DATE) { - SNPRINTF(stamp_data->date, do_prefix ? "Date %s" : "%s", stamp_data_template->date); - } - else { - stamp_data->date[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_MARKER) { - SNPRINTF(stamp_data->marker, do_prefix ? "Marker %s" : "%s", stamp_data_template->marker); - } - else { - stamp_data->marker[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_TIME) { - SNPRINTF(stamp_data->time, do_prefix ? "Timecode %s" : "%s", stamp_data_template->time); - } - else { - stamp_data->time[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_FRAME) { - SNPRINTF(stamp_data->frame, do_prefix ? "Frame %s" : "%s", stamp_data_template->frame); - } - else { - stamp_data->frame[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_CAMERA) { - SNPRINTF(stamp_data->camera, do_prefix ? "Camera %s" : "%s", stamp_data_template->camera); - } - else { - stamp_data->camera[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_CAMERALENS) { - SNPRINTF( - stamp_data->cameralens, do_prefix ? "Lens %s" : "%s", stamp_data_template->cameralens); - } - else { - stamp_data->cameralens[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_SCENE) { - SNPRINTF(stamp_data->scene, do_prefix ? "Scene %s" : "%s", stamp_data_template->scene); - } - else { - stamp_data->scene[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_SEQSTRIP) { - SNPRINTF(stamp_data->strip, do_prefix ? "Strip %s" : "%s", stamp_data_template->strip); - } - else { - stamp_data->strip[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_RENDERTIME) { - SNPRINTF(stamp_data->rendertime, - do_prefix ? "RenderTime %s" : "%s", - stamp_data_template->rendertime); - } - else { - stamp_data->rendertime[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_MEMORY) { - SNPRINTF(stamp_data->memory, do_prefix ? "Peak Memory %s" : "%s", stamp_data_template->memory); - } - else { - stamp_data->memory[0] = '\0'; - } - if (scene->r.stamp & R_STAMP_HOSTNAME) { - SNPRINTF( - stamp_data->hostname, do_prefix ? "Hostname %s" : "%s", stamp_data_template->hostname); - } - else { - stamp_data->hostname[0] = '\0'; - } -} - -void BKE_image_stamp_buf(Scene *scene, - Object *camera, - const StampData *stamp_data_template, - unsigned char *rect, - float *rectf, - int width, - int height, - int channels) -{ - struct StampData stamp_data; - float w, h, pad; - int x, y, y_ofs; - float h_fixed; - const int mono = blf_mono_font_render; /* XXX */ - struct ColorManagedDisplay *display; - const char *display_device; - - /* vars for calculating wordwrap */ - struct { - struct ResultBLF info; - rctf rect; - } wrap; - - /* this could be an argument if we want to operate on non linear float imbuf's - * for now though this is only used for renders which use scene settings */ - -#define TEXT_SIZE_CHECK(str, w, h) \ - ((str[0]) && ((void)(h = h_fixed), (w = BLF_width(mono, str, sizeof(str))))) - - /* must enable BLF_WORD_WRAP before using */ -#define TEXT_SIZE_CHECK_WORD_WRAP(str, w, h) \ - ((str[0]) && (BLF_boundbox_ex(mono, str, sizeof(str), &wrap.rect, &wrap.info), \ - (void)(h = h_fixed * wrap.info.lines), \ - (w = BLI_rctf_size_x(&wrap.rect)))) - -#define BUFF_MARGIN_X 2 -#define BUFF_MARGIN_Y 1 - - if (!rect && !rectf) { - return; - } - - display_device = scene->display_settings.display_device; - display = IMB_colormanagement_display_get_named(display_device); - - bool do_prefix = (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0; - if (stamp_data_template == NULL) { - stampdata(scene, camera, &stamp_data, do_prefix, true); - } - else { - stampdata_from_template(&stamp_data, scene, stamp_data_template, do_prefix); - } - - /* TODO: do_versions. */ - if (scene->r.stamp_font_id < 8) { - scene->r.stamp_font_id = 12; - } - - /* set before return */ - BLF_size(mono, scene->r.stamp_font_id, 72); - BLF_wordwrap(mono, width - (BUFF_MARGIN_X * 2)); - - BLF_buffer(mono, rectf, rect, width, height, channels, display); - BLF_buffer_col(mono, scene->r.fg_stamp); - pad = BLF_width_max(mono); - - /* use 'h_fixed' rather than 'h', aligns better */ - h_fixed = BLF_height_max(mono); - y_ofs = -BLF_descender(mono); - - x = 0; - y = height; - - if (TEXT_SIZE_CHECK(stamp_data.file, w, h)) { - /* Top left corner */ - y -= h; - - /* also a little of space to the background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - /* and draw the text. */ - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.file, sizeof(stamp_data.file)); - - /* the extra pixel for background. */ - y -= BUFF_MARGIN_Y * 2; - } - - /* Top left corner, below File */ - if (TEXT_SIZE_CHECK(stamp_data.date, w, h)) { - y -= h; - - /* and space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - 0, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.date, sizeof(stamp_data.date)); - - /* the extra pixel for background. */ - y -= BUFF_MARGIN_Y * 2; - } - - /* Top left corner, below File, Date */ - if (TEXT_SIZE_CHECK(stamp_data.rendertime, w, h)) { - y -= h; - - /* and space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - 0, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.rendertime, sizeof(stamp_data.rendertime)); - - /* the extra pixel for background. */ - y -= BUFF_MARGIN_Y * 2; - } - - /* Top left corner, below File, Date, Rendertime */ - if (TEXT_SIZE_CHECK(stamp_data.memory, w, h)) { - y -= h; - - /* and space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - 0, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.memory, sizeof(stamp_data.memory)); - - /* the extra pixel for background. */ - y -= BUFF_MARGIN_Y * 2; - } - - /* Top left corner, below File, Date, Rendertime, Memory */ - if (TEXT_SIZE_CHECK(stamp_data.hostname, w, h)) { - y -= h; - - /* and space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - 0, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.hostname, sizeof(stamp_data.hostname)); - - /* the extra pixel for background. */ - y -= BUFF_MARGIN_Y * 2; - } - - /* Top left corner, below File, Date, Memory, Rendertime, Hostname */ - BLF_enable(mono, BLF_WORD_WRAP); - if (TEXT_SIZE_CHECK_WORD_WRAP(stamp_data.note, w, h)) { - y -= h; - - /* and space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - 0, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - BLF_position(mono, x, y + y_ofs + (h - h_fixed), 0.0); - BLF_draw_buffer(mono, stamp_data.note, sizeof(stamp_data.note)); - } - BLF_disable(mono, BLF_WORD_WRAP); - - x = 0; - y = 0; - - /* Bottom left corner, leaving space for timing */ - if (TEXT_SIZE_CHECK(stamp_data.marker, w, h)) { - - /* extra space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - /* and pad the text. */ - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.marker, sizeof(stamp_data.marker)); - - /* space width. */ - x += w + pad; - } - - /* Left bottom corner */ - if (TEXT_SIZE_CHECK(stamp_data.time, w, h)) { - - /* extra space for background */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y, - x + w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - /* and pad the text. */ - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.time, sizeof(stamp_data.time)); - - /* space width. */ - x += w + pad; - } - - if (TEXT_SIZE_CHECK(stamp_data.frame, w, h)) { - - /* extra space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - x + w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - /* and pad the text. */ - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.frame, sizeof(stamp_data.frame)); - - /* space width. */ - x += w + pad; - } - - if (TEXT_SIZE_CHECK(stamp_data.camera, w, h)) { - - /* extra space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - x + w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.camera, sizeof(stamp_data.camera)); - - /* space width. */ - x += w + pad; - } - - if (TEXT_SIZE_CHECK(stamp_data.cameralens, w, h)) { - - /* extra space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - x + w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.cameralens, sizeof(stamp_data.cameralens)); - } - - if (TEXT_SIZE_CHECK(stamp_data.scene, w, h)) { - - /* Bottom right corner, with an extra space because blenfont is too strict! */ - x = width - w - 2; - - /* extra space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - x + w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - /* and pad the text. */ - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.scene, sizeof(stamp_data.scene)); - } - - if (TEXT_SIZE_CHECK(stamp_data.strip, w, h)) { - - /* Top right corner, with an extra space because blenfont is too strict! */ - x = width - w - pad; - y = height - h; - - /* extra space for background. */ - buf_rectfill_area(rect, - rectf, - width, - height, - scene->r.bg_stamp, - display, - x - BUFF_MARGIN_X, - y - BUFF_MARGIN_Y, - x + w + BUFF_MARGIN_X, - y + h + BUFF_MARGIN_Y); - - BLF_position(mono, x, y + y_ofs, 0.0); - BLF_draw_buffer(mono, stamp_data.strip, sizeof(stamp_data.strip)); - } - - /* cleanup the buffer. */ - BLF_buffer(mono, NULL, NULL, 0, 0, 0, NULL); - BLF_wordwrap(mono, 0); - -#undef TEXT_SIZE_CHECK -#undef TEXT_SIZE_CHECK_WORD_WRAP -#undef BUFF_MARGIN_X -#undef BUFF_MARGIN_Y -} - -void BKE_render_result_stamp_info(Scene *scene, - Object *camera, - struct RenderResult *rr, - bool allocate_only) -{ - struct StampData *stamp_data; - - if (!(scene && (scene->r.stamp & R_STAMP_ALL)) && !allocate_only) { - return; - } - - if (!rr->stamp_data) { - stamp_data = MEM_callocN(sizeof(StampData), "RenderResult.stamp_data"); - } - else { - stamp_data = rr->stamp_data; - } - - if (!allocate_only) { - stampdata(scene, camera, stamp_data, 0, true); - } - - if (!rr->stamp_data) { - rr->stamp_data = stamp_data; - } -} - -struct StampData *BKE_stamp_info_from_scene_static(const Scene *scene) -{ - struct StampData *stamp_data; - - if (!(scene && (scene->r.stamp & R_STAMP_ALL))) { - return NULL; - } - - /* Memory is allocated here (instead of by the caller) so that the caller - * doesn't have to know the size of the StampData struct. */ - stamp_data = MEM_callocN(sizeof(StampData), __func__); - stampdata(scene, NULL, stamp_data, 0, false); - - return stamp_data; -} - -static const char *stamp_metadata_fields[] = { - "File", - "Note", - "Date", - "Marker", - "Time", - "Frame", - "FrameRange", - "Camera", - "Lens", - "Scene", - "Strip", - "RenderTime", - "Memory", - "Hostname", - NULL, -}; - -bool BKE_stamp_is_known_field(const char *field_name) -{ - int i = 0; - while (stamp_metadata_fields[i] != NULL) { - if (STREQ(field_name, stamp_metadata_fields[i])) { - return true; - } - i++; - } - return false; -} - -void BKE_stamp_info_callback(void *data, - struct StampData *stamp_data, - StampCallback callback, - bool noskip) -{ - if ((callback == NULL) || (stamp_data == NULL)) { - return; - } - -#define CALL(member, value_str) \ - if (noskip || stamp_data->member[0]) { \ - callback(data, value_str, stamp_data->member, sizeof(stamp_data->member)); \ - } \ - ((void)0) - - /* TODO(sergey): Use stamp_metadata_fields somehow, or make it more generic - * meta information to avoid duplication. */ - CALL(file, "File"); - CALL(note, "Note"); - CALL(date, "Date"); - CALL(marker, "Marker"); - CALL(time, "Time"); - CALL(frame, "Frame"); - CALL(frame_range, "FrameRange"); - CALL(camera, "Camera"); - CALL(cameralens, "Lens"); - CALL(scene, "Scene"); - CALL(strip, "Strip"); - CALL(rendertime, "RenderTime"); - CALL(memory, "Memory"); - CALL(hostname, "Hostname"); - - LISTBASE_FOREACH (StampDataCustomField *, custom_field, &stamp_data->custom_fields) { - if (noskip || custom_field->value[0]) { - callback(data, custom_field->key, custom_field->value, strlen(custom_field->value) + 1); - } - } - -#undef CALL -} - -void BKE_render_result_stamp_data(RenderResult *rr, const char *key, const char *value) -{ - StampData *stamp_data; - if (rr->stamp_data == NULL) { - rr->stamp_data = MEM_callocN(sizeof(StampData), "RenderResult.stamp_data"); - } - stamp_data = rr->stamp_data; - StampDataCustomField *field = MEM_mallocN(sizeof(StampDataCustomField), - "StampData Custom Field"); - STRNCPY(field->key, key); - field->value = BLI_strdup(value); - BLI_addtail(&stamp_data->custom_fields, field); -} - -StampData *BKE_stamp_data_copy(const StampData *stamp_data) -{ - if (stamp_data == NULL) { - return NULL; - } - - StampData *stamp_datan = MEM_dupallocN(stamp_data); - BLI_duplicatelist(&stamp_datan->custom_fields, &stamp_data->custom_fields); - - LISTBASE_FOREACH (StampDataCustomField *, custom_fieldn, &stamp_datan->custom_fields) { - custom_fieldn->value = MEM_dupallocN(custom_fieldn->value); - } - - return stamp_datan; -} - -void BKE_stamp_data_free(StampData *stamp_data) -{ - if (stamp_data == NULL) { - return; - } - LISTBASE_FOREACH (StampDataCustomField *, custom_field, &stamp_data->custom_fields) { - MEM_freeN(custom_field->value); - } - BLI_freelistN(&stamp_data->custom_fields); - MEM_freeN(stamp_data); -} - -/* wrap for callback only */ -static void metadata_set_field(void *data, const char *propname, char *propvalue, int UNUSED(len)) -{ - /* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */ - struct ImBuf *imbuf = data; - IMB_metadata_set_field(imbuf->metadata, propname, propvalue); -} - -static void metadata_get_field(void *data, const char *propname, char *propvalue, int len) -{ - /* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */ - struct ImBuf *imbuf = data; - IMB_metadata_get_field(imbuf->metadata, propname, propvalue, len); -} - -void BKE_imbuf_stamp_info(RenderResult *rr, struct ImBuf *ibuf) -{ - struct StampData *stamp_data = rr->stamp_data; - IMB_metadata_ensure(&ibuf->metadata); - BKE_stamp_info_callback(ibuf, stamp_data, metadata_set_field, false); -} - -static void metadata_copy_custom_fields(const char *field, const char *value, void *rr_v) -{ - if (BKE_stamp_is_known_field(field)) { - return; - } - RenderResult *rr = (RenderResult *)rr_v; - BKE_render_result_stamp_data(rr, field, value); -} - -void BKE_stamp_info_from_imbuf(RenderResult *rr, struct ImBuf *ibuf) -{ - if (rr->stamp_data == NULL) { - rr->stamp_data = MEM_callocN(sizeof(StampData), "RenderResult.stamp_data"); - } - struct StampData *stamp_data = rr->stamp_data; - IMB_metadata_ensure(&ibuf->metadata); - BKE_stamp_info_callback(ibuf, stamp_data, metadata_get_field, true); - /* Copy render engine specific settings. */ - IMB_metadata_foreach(ibuf, metadata_copy_custom_fields, rr); -} - -bool BKE_imbuf_alpha_test(ImBuf *ibuf) -{ - int tot; - if (ibuf->rect_float) { - const float *buf = ibuf->rect_float; - for (tot = ibuf->x * ibuf->y; tot--; buf += 4) { - if (buf[3] < 1.0f) { - return true; - } - } - } - else if (ibuf->rect) { - unsigned char *buf = (unsigned char *)ibuf->rect; - for (tot = ibuf->x * ibuf->y; tot--; buf += 4) { - if (buf[3] != 255) { - return true; - } - } - } - - return false; -} - -void BKE_imbuf_write_prepare(ImBuf *ibuf, const ImageFormatData *imf) -{ - char imtype = imf->imtype; - char compress = imf->compress; - char quality = imf->quality; - - /* initialize all from image format */ - ibuf->foptions.flag = 0; - - if (imtype == R_IMF_IMTYPE_IRIS) { - ibuf->ftype = IMB_FTYPE_IMAGIC; - } -#ifdef WITH_HDR - else if (imtype == R_IMF_IMTYPE_RADHDR) { - ibuf->ftype = IMB_FTYPE_RADHDR; - } -#endif - else if (ELEM(imtype, - R_IMF_IMTYPE_PNG, - R_IMF_IMTYPE_FFMPEG, - R_IMF_IMTYPE_H264, - R_IMF_IMTYPE_THEORA, - R_IMF_IMTYPE_XVID)) { - ibuf->ftype = IMB_FTYPE_PNG; - - if (imtype == R_IMF_IMTYPE_PNG) { - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= PNG_16BIT; - } - - ibuf->foptions.quality = compress; - } - } -#ifdef WITH_DDS - else if (imtype == R_IMF_IMTYPE_DDS) { - ibuf->ftype = IMB_FTYPE_DDS; - } -#endif - else if (imtype == R_IMF_IMTYPE_BMP) { - ibuf->ftype = IMB_FTYPE_BMP; - } -#ifdef WITH_TIFF - else if (imtype == R_IMF_IMTYPE_TIFF) { - ibuf->ftype = IMB_FTYPE_TIF; - - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= TIF_16BIT; - } - if (imf->tiff_codec == R_IMF_TIFF_CODEC_NONE) { - ibuf->foptions.flag |= TIF_COMPRESS_NONE; - } - else if (imf->tiff_codec == R_IMF_TIFF_CODEC_DEFLATE) { - ibuf->foptions.flag |= TIF_COMPRESS_DEFLATE; - } - else if (imf->tiff_codec == R_IMF_TIFF_CODEC_LZW) { - ibuf->foptions.flag |= TIF_COMPRESS_LZW; - } - else if (imf->tiff_codec == R_IMF_TIFF_CODEC_PACKBITS) { - ibuf->foptions.flag |= TIF_COMPRESS_PACKBITS; - } - } -#endif -#ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { - ibuf->ftype = IMB_FTYPE_OPENEXR; - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= OPENEXR_HALF; - } - ibuf->foptions.flag |= (imf->exr_codec & OPENEXR_COMPRESS); - - if (!(imf->flag & R_IMF_FLAG_ZBUF)) { - /* Signal for exr saving. */ - IMB_freezbuffloatImBuf(ibuf); - } - } -#endif -#ifdef WITH_CINEON - else if (imtype == R_IMF_IMTYPE_CINEON) { - ibuf->ftype = IMB_FTYPE_CINEON; - if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { - ibuf->foptions.flag |= CINEON_LOG; - } - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= CINEON_16BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_12) { - ibuf->foptions.flag |= CINEON_12BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_10) { - ibuf->foptions.flag |= CINEON_10BIT; - } - } - else if (imtype == R_IMF_IMTYPE_DPX) { - ibuf->ftype = IMB_FTYPE_DPX; - if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { - ibuf->foptions.flag |= CINEON_LOG; - } - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= CINEON_16BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_12) { - ibuf->foptions.flag |= CINEON_12BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_10) { - ibuf->foptions.flag |= CINEON_10BIT; - } - } -#endif - else if (imtype == R_IMF_IMTYPE_TARGA) { - ibuf->ftype = IMB_FTYPE_TGA; - } - else if (imtype == R_IMF_IMTYPE_RAWTGA) { - ibuf->ftype = IMB_FTYPE_TGA; - ibuf->foptions.flag = RAWTGA; - } -#ifdef WITH_OPENJPEG - else if (imtype == R_IMF_IMTYPE_JP2) { - if (quality < 10) { - quality = 90; - } - ibuf->ftype = IMB_FTYPE_JP2; - ibuf->foptions.quality = quality; - - if (imf->depth == R_IMF_CHAN_DEPTH_16) { - ibuf->foptions.flag |= JP2_16BIT; - } - else if (imf->depth == R_IMF_CHAN_DEPTH_12) { - ibuf->foptions.flag |= JP2_12BIT; - } - - if (imf->jp2_flag & R_IMF_JP2_FLAG_YCC) { - ibuf->foptions.flag |= JP2_YCC; - } - - if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_PRESET) { - ibuf->foptions.flag |= JP2_CINE; - if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_48) { - ibuf->foptions.flag |= JP2_CINE_48FPS; - } - } - - if (imf->jp2_codec == R_IMF_JP2_CODEC_JP2) { - ibuf->foptions.flag |= JP2_JP2; - } - else if (imf->jp2_codec == R_IMF_JP2_CODEC_J2K) { - ibuf->foptions.flag |= JP2_J2K; - } - else { - BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); - } - } -#endif - else { - /* R_IMF_IMTYPE_JPEG90, etc. default we save jpegs */ - if (quality < 10) { - quality = 90; - } - ibuf->ftype = IMB_FTYPE_JPG; - ibuf->foptions.quality = quality; - } -} - -int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf) -{ - BKE_imbuf_write_prepare(ibuf, imf); - - BLI_make_existing_file(name); - - const bool ok = IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat); - if (ok == 0) { - perror(name); - } - - return ok; -} - -int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, const bool save_copy) -{ - ImBuf ibuf_back = *ibuf; - int ok; - - /* All data is RGBA anyway, this just controls how to save for some formats. */ - ibuf->planes = imf->planes; - - ok = BKE_imbuf_write(ibuf, name, imf); - - if (save_copy) { - /* note that we are not restoring _all_ settings */ - ibuf->planes = ibuf_back.planes; - ibuf->ftype = ibuf_back.ftype; - ibuf->foptions = ibuf_back.foptions; - } - - return ok; -} - -int BKE_imbuf_write_stamp(Scene *scene, - struct RenderResult *rr, - ImBuf *ibuf, - const char *name, - const struct ImageFormatData *imf) -{ - if (scene && scene->r.stamp & R_STAMP_ALL) { - BKE_imbuf_stamp_info(rr, ibuf); - } - - return BKE_imbuf_write(ibuf, name, imf); -} - -static void do_makepicstring(char *string, - const char *base, - const char *relbase, - int frame, - const char imtype, - const ImageFormatData *im_format, - const bool use_ext, - const bool use_frames, - const char *suffix) -{ - if (string == NULL) { - return; - } - BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ - BLI_path_abs(string, relbase); - - if (use_frames) { - BLI_path_frame(string, frame, 4); - } - - if (suffix) { - BLI_path_suffix(string, FILE_MAX, suffix, ""); - } - - if (use_ext) { - 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 char *suffix) -{ - 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 *suffix) -{ - do_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames, suffix); -} - -struct anim *openanim_noload(const char *name, - int flags, - int streamindex, - char colorspace[IMA_MAX_SPACE]) -{ - struct anim *anim; - - anim = IMB_open_anim(name, flags, streamindex, colorspace); - return anim; -} - -struct anim *openanim(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) -{ - struct anim *anim; - struct ImBuf *ibuf; - - anim = IMB_open_anim(name, flags, streamindex, colorspace); - if (anim == NULL) { - return NULL; - } - - ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); - if (ibuf == NULL) { - if (BLI_exists(name)) { - printf("not an anim: %s\n", name); - } - else { - printf("anim file doesn't exist: %s\n", name); - } - IMB_free_anim(anim); - return NULL; - } - IMB_freeImBuf(ibuf); - - return anim; -} - -/* ************************* New Image API *************** */ - -/* Notes about Image storage - * - packedfile - * -> written in .blend - * - filename - * -> written in .blend - * - movie - * -> comes from packedfile or filename - * - renderresult - * -> comes from packedfile or filename - * - listbase - * -> ibufs from exrhandle - * - flipbook array - * -> ibufs come from movie, temporary renderresult or sequence - * - ibuf - * -> comes from packedfile or filename or generated - */ - -Image *BKE_image_ensure_viewer(Main *bmain, int type, const char *name) -{ - Image *ima; - - for (ima = bmain->images.first; ima; ima = ima->id.next) { - if (ima->source == IMA_SRC_VIEWER) { - if (ima->type == type) { - break; - } - } - } - - if (ima == NULL) { - ima = image_alloc(bmain, name, IMA_SRC_VIEWER, type); - } - - /* Happens on reload, imagewindow cannot be image user when hidden. */ - if (ima->id.us == 0) { - id_us_ensure_real(&ima->id); - } - - return ima; -} - -static void image_viewer_create_views(const RenderData *rd, Image *ima) -{ - if ((rd->scemode & R_MULTIVIEW) == 0) { - image_add_view(ima, "", ""); - } - else { - 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, ""); - } - } -} - -void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser *iuser) -{ - bool do_reset; - const bool is_multiview = (rd->scemode & R_MULTIVIEW) != 0; - - BLI_thread_lock(LOCK_DRAW_IMAGE); - - if (!BKE_scene_multiview_is_stereo3d(rd)) { - 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)); - - /* multiview also needs to be sure all the views are synced */ - if (is_multiview && !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) { - BLI_mutex_lock(ima->runtime.cache_mutex); - - image_free_cached_frames(ima); - BKE_image_free_views(ima); - - /* add new views */ - image_viewer_create_views(rd, ima); - - BLI_mutex_unlock(ima->runtime.cache_mutex); - } - - BLI_thread_unlock(LOCK_DRAW_IMAGE); -} - -static void image_walk_ntree_all_users( - bNodeTree *ntree, - ID *id, - void *customdata, - void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) -{ - switch (ntree->type) { - case NTREE_SHADER: - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id) { - if (node->type == SH_NODE_TEX_IMAGE) { - NodeTexImage *tex = node->storage; - Image *ima = (Image *)node->id; - callback(ima, id, &tex->iuser, customdata); - } - if (node->type == SH_NODE_TEX_ENVIRONMENT) { - NodeTexImage *tex = node->storage; - Image *ima = (Image *)node->id; - callback(ima, id, &tex->iuser, customdata); - } - } - } - break; - case NTREE_TEXTURE: - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id && node->type == TEX_NODE_IMAGE) { - Image *ima = (Image *)node->id; - ImageUser *iuser = node->storage; - callback(ima, id, iuser, customdata); - } - } - break; - case NTREE_COMPOSIT: - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->id && node->type == CMP_NODE_IMAGE) { - Image *ima = (Image *)node->id; - ImageUser *iuser = node->storage; - callback(ima, id, iuser, customdata); - } - } - break; - } -} - -static void image_walk_gpu_materials( - ID *id, - ListBase *gpu_materials, - void *customdata, - void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) -{ - LISTBASE_FOREACH (LinkData *, link, gpu_materials) { - GPUMaterial *gpu_material = (GPUMaterial *)link->data; - ListBase textures = GPU_material_textures(gpu_material); - LISTBASE_FOREACH (GPUMaterialTexture *, gpu_material_texture, &textures) { - if (gpu_material_texture->iuser_available) { - callback(gpu_material_texture->ima, id, &gpu_material_texture->iuser, customdata); - } - } - } -} - -static void image_walk_id_all_users( - ID *id, - bool skip_nested_nodes, - void *customdata, - void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) -{ - switch (GS(id->name)) { - case ID_OB: { - Object *ob = (Object *)id; - if (ob->empty_drawtype == OB_EMPTY_IMAGE && ob->data) { - callback(ob->data, &ob->id, ob->iuser, customdata); - } - break; - } - case ID_MA: { - Material *ma = (Material *)id; - if (ma->nodetree && ma->use_nodes && !skip_nested_nodes) { - image_walk_ntree_all_users(ma->nodetree, &ma->id, customdata, callback); - } - image_walk_gpu_materials(id, &ma->gpumaterial, customdata, callback); - break; - } - case ID_LA: { - Light *light = (Light *)id; - if (light->nodetree && light->use_nodes && !skip_nested_nodes) { - image_walk_ntree_all_users(light->nodetree, &light->id, customdata, callback); - } - break; - } - case ID_WO: { - World *world = (World *)id; - if (world->nodetree && world->use_nodes && !skip_nested_nodes) { - image_walk_ntree_all_users(world->nodetree, &world->id, customdata, callback); - } - image_walk_gpu_materials(id, &world->gpumaterial, customdata, callback); - break; - } - case ID_TE: { - Tex *tex = (Tex *)id; - if (tex->type == TEX_IMAGE && tex->ima) { - callback(tex->ima, &tex->id, &tex->iuser, customdata); - } - if (tex->nodetree && tex->use_nodes && !skip_nested_nodes) { - image_walk_ntree_all_users(tex->nodetree, &tex->id, customdata, callback); - } - break; - } - case ID_NT: { - bNodeTree *ntree = (bNodeTree *)id; - image_walk_ntree_all_users(ntree, &ntree->id, customdata, callback); - break; - } - case ID_CA: { - Camera *cam = (Camera *)id; - LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { - callback(bgpic->ima, NULL, &bgpic->iuser, customdata); - } - break; - } - case ID_WM: { - wmWindowManager *wm = (wmWindowManager *)id; - LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); - - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype == SPACE_IMAGE) { - SpaceImage *sima = area->spacedata.first; - callback(sima->image, NULL, &sima->iuser, customdata); - } - } - } - break; - } - case ID_SCE: { - Scene *scene = (Scene *)id; - if (scene->nodetree && scene->use_nodes && !skip_nested_nodes) { - image_walk_ntree_all_users(scene->nodetree, &scene->id, customdata, callback); - } - break; - } - case ID_SIM: { - Simulation *simulation = (Simulation *)id; - image_walk_ntree_all_users(simulation->nodetree, &simulation->id, customdata, callback); - break; - } - default: - break; - } -} - -void BKE_image_walk_all_users( - const Main *mainp, - void *customdata, - void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) -{ - for (Scene *scene = mainp->scenes.first; scene; scene = scene->id.next) { - image_walk_id_all_users(&scene->id, false, customdata, callback); - } - - for (Object *ob = mainp->objects.first; ob; ob = ob->id.next) { - image_walk_id_all_users(&ob->id, false, customdata, callback); - } - - for (bNodeTree *ntree = mainp->nodetrees.first; ntree; ntree = ntree->id.next) { - image_walk_id_all_users(&ntree->id, false, customdata, callback); - } - - for (Material *ma = mainp->materials.first; ma; ma = ma->id.next) { - image_walk_id_all_users(&ma->id, false, customdata, callback); - } - - for (Light *light = mainp->materials.first; light; light = light->id.next) { - image_walk_id_all_users(&light->id, false, customdata, callback); - } - - for (World *world = mainp->materials.first; world; world = world->id.next) { - image_walk_id_all_users(&world->id, false, customdata, callback); - } - - for (Tex *tex = mainp->textures.first; tex; tex = tex->id.next) { - image_walk_id_all_users(&tex->id, false, customdata, callback); - } - - for (Camera *cam = mainp->cameras.first; cam; cam = cam->id.next) { - image_walk_id_all_users(&cam->id, false, customdata, callback); - } - - for (wmWindowManager *wm = mainp->wm.first; wm; wm = wm->id.next) { /* only 1 wm */ - image_walk_id_all_users(&wm->id, false, customdata, callback); - } -} - -static void image_tag_frame_recalc(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata) -{ - Image *changed_image = customdata; - - if (ima == changed_image && BKE_image_is_animated(ima)) { - iuser->flag |= IMA_NEED_FRAME_RECALC; - - if (iuser_id) { - /* Must copy image user changes to CoW data-block. */ - DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE); - } - } -} - -static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata) -{ - Image *changed_image = customdata; - - if (ima == changed_image) { - if (iuser->scene) { - image_update_views_format(ima, iuser); - } - if (iuser_id) { - /* Must copy image user changes to CoW data-block. */ - DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE); - } - BKE_image_partial_update_mark_full_update(ima); - } -} - -void BKE_imageuser_default(ImageUser *iuser) -{ - memset(iuser, 0, sizeof(ImageUser)); - iuser->frames = 100; - iuser->sfra = 1; -} - -void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) -{ - RenderResult *rr = ima->rr; - - iuser->multi_index = 0; - iuser->layer = iuser->pass = iuser->view = 0; - - if (rr) { - BKE_image_multilayer_index(rr, iuser); - } -} - -static void image_free_tile(Image *ima, ImageTile *tile) -{ - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other - * two. */ - if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) { - continue; - } - - for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - if (ima->gputexture[i][eye][resolution] != NULL) { - GPU_texture_free(ima->gputexture[i][eye][resolution]); - ima->gputexture[i][eye][resolution] = NULL; - } - } - } - } - BKE_image_partial_update_mark_full_update(ima); - - if (BKE_image_is_multiview(ima)) { - const int totviews = BLI_listbase_count(&ima->views); - for (int i = 0; i < totviews; i++) { - image_remove_ibuf(ima, i, tile->tile_number); - } - } - else { - image_remove_ibuf(ima, 0, tile->tile_number); - } -} - -void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) -{ - if (ima == NULL) { - return; - } - - BLI_mutex_lock(ima->runtime.cache_mutex); - - switch (signal) { - case IMA_SIGNAL_FREE: - BKE_image_free_buffers(ima); - - if (iuser) { - if (iuser->scene) { - image_update_views_format(ima, iuser); - } - } - break; - case IMA_SIGNAL_SRC_CHANGE: - if (ima->type == IMA_TYPE_UV_TEST) { - if (ima->source != IMA_SRC_GENERATED) { - ima->type = IMA_TYPE_IMAGE; - } - } - - if (ima->source == IMA_SRC_GENERATED) { - if (ima->gen_x == 0 || ima->gen_y == 0) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); - if (ibuf) { - ima->gen_x = ibuf->x; - ima->gen_y = ibuf->y; - IMB_freeImBuf(ibuf); - } - } - - /* Changing source type to generated will likely change file format - * used by generated image buffer. Saving different file format to - * the old name might confuse other applications. - * - * Here we ensure original image path wouldn't be used when saving - * generated image. - */ - ima->filepath[0] = '\0'; - } - - if (ima->source != IMA_SRC_TILED) { - /* Free all but the first tile. */ - ImageTile *base_tile = BKE_image_get_tile(ima, 0); - BLI_assert(base_tile == ima->tiles.first); - for (ImageTile *tile = base_tile->next, *tile_next; tile; tile = tile_next) { - tile_next = tile->next; - image_free_tile(ima, tile); - MEM_freeN(tile); - } - base_tile->next = NULL; - ima->tiles.last = base_tile; - } - - /* image buffers for non-sequence multilayer will share buffers with RenderResult, - * however sequence multilayer will own buffers. Such logic makes switching from - * single multilayer file to sequence completely unstable - * since changes in nodes seems this workaround isn't needed anymore, all sockets - * are nicely detecting anyway, but freeing buffers always here makes multilayer - * sequences behave stable - */ - BKE_image_free_buffers(ima); - - if (iuser) { - image_tag_frame_recalc(ima, NULL, iuser, ima); - } - BKE_image_walk_all_users(bmain, ima, image_tag_frame_recalc); - - break; - - case IMA_SIGNAL_RELOAD: - /* try to repack file */ - if (BKE_image_has_packedfile(ima)) { - const int totfiles = image_num_files(ima); - - if (totfiles != BLI_listbase_count_at_most(&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(bmain, &ima->id)); - } - else { - ImagePackedFile *imapf; - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { - PackedFile *pf; - pf = BKE_packedfile_new(NULL, imapf->filepath, ID_BLEND_PATH(bmain, &ima->id)); - if (pf) { - BKE_packedfile_free(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 (ima->source == IMA_SRC_TILED) { - ListBase new_tiles = {NULL, NULL}; - int new_start, new_range; - - char filepath[FILE_MAX]; - BLI_strncpy(filepath, ima->filepath, sizeof(filepath)); - BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - bool result = BKE_image_get_tile_info(filepath, &new_tiles, &new_start, &new_range); - if (result) { - /* Because the prior and new list of tiles are both sparse sequences, we need to be sure - * to account for how the two sets might or might not overlap. To be complete, we start - * the refresh process by clearing all existing tiles, stopping when there's only 1 tile - * left. */ - while (BKE_image_remove_tile(ima, ima->tiles.last)) { - ; - } - - int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number; - bool needs_final_cleanup = true; - - /* Add in all the new tiles. */ - LISTBASE_FOREACH (LinkData *, new_tile, &new_tiles) { - int new_tile_number = POINTER_AS_INT(new_tile->data); - BKE_image_add_tile(ima, new_tile_number, NULL); - if (new_tile_number == remaining_tile_number) { - needs_final_cleanup = false; - } - } - - /* Final cleanup if the prior remaining tile was never encountered in the new list. */ - if (needs_final_cleanup) { - BKE_image_remove_tile(ima, BKE_image_get_tile(ima, remaining_tile_number)); - } - } - BLI_freelistN(&new_tiles); - } - - if (iuser) { - image_tag_reload(ima, NULL, iuser, ima); - } - BKE_image_walk_all_users(bmain, ima, image_tag_reload); - break; - case IMA_SIGNAL_USER_NEW_IMAGE: - if (iuser) { - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { - if (ima->type == IMA_TYPE_MULTILAYER) { - BKE_image_init_imageuser(ima, iuser); - } - } - } - break; - case IMA_SIGNAL_COLORMANAGE: - BKE_image_free_buffers(ima); - break; - } - - BLI_mutex_unlock(ima->runtime.cache_mutex); - - BKE_ntree_update_tag_id_changed(bmain, &ima->id); - BKE_ntree_update_main(bmain, NULL); -} - -/* return renderpass for a given pass index and active view */ -/* fallback to available if there are missing passes for active view */ -static RenderPass *image_render_pass_get(RenderLayer *rl, - const int pass, - const int view, - int *r_passindex) -{ - RenderPass *rpass_ret = NULL; - RenderPass *rpass; - - int rp_index = 0; - const char *rp_name = ""; - - for (rpass = rl->passes.first; rpass; rpass = rpass->next, rp_index++) { - if (rp_index == pass) { - rpass_ret = rpass; - if (view == 0) { - /* no multiview or left eye */ - break; - } - - rp_name = rpass->name; - } - /* multiview */ - else if (rp_name[0] && STREQ(rpass->name, rp_name) && (rpass->view_id == view)) { - rpass_ret = rpass; - break; - } - } - - /* fallback to the first pass in the layer */ - if (rpass_ret == NULL) { - rp_index = 0; - rpass_ret = rl->passes.first; - } - - if (r_passindex) { - *r_passindex = (rpass == rpass_ret ? rp_index : pass); - } - - return rpass_ret; -} - -void BKE_image_get_tile_label(Image *ima, ImageTile *tile, char *label, int len_label) -{ - label[0] = '\0'; - if (ima == NULL || tile == NULL) { - return; - } - - if (tile->label[0]) { - BLI_strncpy(label, tile->label, len_label); - } - else { - BLI_snprintf(label, len_label, "%d", tile->tile_number); - } -} - -bool BKE_image_get_tile_info(char *filepath, - ListBase *udim_tiles, - int *udim_start, - int *udim_range) -{ - char filename[FILE_MAXFILE], dirname[FILE_MAXDIR]; - BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); - - BKE_image_ensure_tile_token(filename); - - eUDIM_TILE_FORMAT tile_format; - char *udim_pattern = BKE_image_get_tile_strformat(filename, &tile_format); - - bool is_udim = true; - int min_udim = IMA_UDIM_MAX + 1; - int max_udim = 0; - int id; - - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(dirname, &dir); - for (int i = 0; i < totfile; i++) { - if (!(dir[i].type & S_IFREG)) { - continue; - } - - if (!BKE_image_get_tile_number_from_filepath(dir[i].relname, udim_pattern, tile_format, &id)) { - continue; - } - - if (id < 1001 || id > IMA_UDIM_MAX) { - is_udim = false; - break; - } - - BLI_addtail(udim_tiles, BLI_genericNodeN(POINTER_FROM_INT(id))); - min_udim = min_ii(min_udim, id); - max_udim = max_ii(max_udim, id); - } - BLI_filelist_free(dir, totfile); - MEM_SAFE_FREE(udim_pattern); - - if (is_udim && min_udim <= IMA_UDIM_MAX) { - BLI_join_dirfile(filepath, FILE_MAX, dirname, filename); - - *udim_start = min_udim; - *udim_range = max_udim - min_udim + 1; - return true; - } - return false; -} - -ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label) -{ - if (ima->source != IMA_SRC_TILED) { - return NULL; - } - - if (tile_number < 1001 || tile_number > IMA_UDIM_MAX) { - return NULL; - } - - /* Search the first tile that has a higher number. - * We then insert before that to keep the list sorted. */ - ImageTile *next_tile; - for (next_tile = ima->tiles.first; next_tile; next_tile = next_tile->next) { - if (next_tile->tile_number == tile_number) { - /* Tile already exists. */ - return NULL; - } - if (next_tile->tile_number > tile_number) { - break; - } - } - - ImageTile *tile = MEM_callocN(sizeof(ImageTile), "image new tile"); - tile->tile_number = tile_number; - - if (next_tile) { - BLI_insertlinkbefore(&ima->tiles, next_tile, tile); - } - else { - BLI_addtail(&ima->tiles, tile); - } - - if (label) { - BLI_strncpy(tile->label, label, sizeof(tile->label)); - } - - for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = NULL; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = NULL; - } - } - } - BKE_image_partial_update_mark_full_update(ima); - - return tile; -} - -bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) -{ - if (ima == NULL || tile == NULL || ima->source != IMA_SRC_TILED) { - return false; - } - - if (BLI_listbase_is_single(&ima->tiles)) { - /* Can't remove the last remaining tile. */ - return false; - } - - image_free_tile(ima, tile); - BLI_remlink(&ima->tiles, tile); - MEM_freeN(tile); - - return true; -} - -void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number) -{ - if (ima == NULL || tile == NULL || ima->source != IMA_SRC_TILED) { - return; - } - - if (new_tile_number < 1001 || new_tile_number > IMA_UDIM_MAX) { - return; - } - - const int old_tile_number = tile->tile_number; - tile->tile_number = new_tile_number; - - if (BKE_image_is_multiview(ima)) { - const int totviews = BLI_listbase_count(&ima->views); - for (int i = 0; i < totviews; i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number, NULL); - image_remove_ibuf(ima, i, old_tile_number); - image_assign_ibuf(ima, ibuf, i, new_tile_number); - IMB_freeImBuf(ibuf); - } - } - else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number, NULL); - image_remove_ibuf(ima, 0, old_tile_number); - image_assign_ibuf(ima, ibuf, 0, new_tile_number); - IMB_freeImBuf(ibuf); - } - - for (int eye = 0; eye < 2; eye++) { - for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { - - /* Reallocate GPU tile array. */ - if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); - ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = NULL; - } - if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != NULL) { - GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); - ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = NULL; - } - } - } - BKE_image_partial_update_mark_full_update(ima); -} - -static int tile_sort_cb(const void *a, const void *b) -{ - const ImageTile *tile_a = a; - const ImageTile *tile_b = b; - return (tile_a->tile_number > tile_b->tile_number) ? 1 : 0; -} - -void BKE_image_sort_tiles(struct Image *ima) -{ - if (ima == NULL || ima->source != IMA_SRC_TILED) { - return; - } - - BLI_listbase_sort(&ima->tiles, tile_sort_cb); -} - -bool BKE_image_fill_tile(struct Image *ima, - ImageTile *tile, - int width, - int height, - const float color[4], - int gen_type, - int planes, - bool is_float) -{ - if (ima == NULL || tile == NULL || ima->source != IMA_SRC_TILED) { - return false; - } - - image_free_tile(ima, tile); - - ImBuf *tile_ibuf = add_ibuf_size( - width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings); - - if (tile_ibuf != NULL) { - image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); - BKE_image_release_ibuf(ima, tile_ibuf, NULL); - return true; - } - return false; -} - -void BKE_image_ensure_tile_token(char *filename) -{ - BLI_assert_msg(BLI_path_slash_find(filename) == NULL, - "Only the file-name component should be used!"); - - /* Is there a '<' character in the filename? Assume tokens already present. */ - if (strstr(filename, "<") != NULL) { - return; - } - - /* Is there a sequence of digits in the filename? */ - ushort digits; - char head[FILE_MAX], tail[FILE_MAX]; - BLI_path_sequence_decode(filename, head, tail, &digits); - if (digits == 4) { - sprintf(filename, "%s%s", head, tail); - return; - } - - /* Is there a sequence like u##_v#### in the filename? */ - uint cur = 0; - uint name_end = strlen(filename); - uint u_digits = 0; - uint v_digits = 0; - uint u_start = (uint)-1; - bool u_found = false; - bool v_found = false; - bool sep_found = false; - while (cur < name_end) { - if (filename[cur] == 'u') { - u_found = true; - u_digits = 0; - u_start = cur; - } - else if (filename[cur] == 'v') { - v_found = true; - v_digits = 0; - } - else if (u_found && !v_found) { - if (isdigit(filename[cur]) && u_digits < 2) { - u_digits++; - } - else if (filename[cur] == '_') { - sep_found = true; - } - else { - u_found = false; - } - } - else if (u_found && u_digits > 0 && v_found) { - if (isdigit(filename[cur])) { - if (v_digits < 4) { - v_digits++; - } - else { - u_found = false; - v_found = false; - } - } - else if (v_digits > 0) { - break; - } - } - - cur++; - } - - if (u_found && sep_found && v_found && (u_digits + v_digits > 1)) { - const char *token = ""; - const size_t token_length = strlen(token); - memmove(filename + u_start + token_length, filename + cur, name_end - cur); - memcpy(filename + u_start, token, token_length); - filename[u_start + token_length + (name_end - cur)] = '\0'; - } -} - -bool BKE_image_tile_filepath_exists(const char *filepath) -{ - BLI_assert(!BLI_path_is_rel(filepath)); - - char dirname[FILE_MAXDIR]; - BLI_split_dir_part(filepath, dirname, sizeof(dirname)); - - eUDIM_TILE_FORMAT tile_format; - char *udim_pattern = BKE_image_get_tile_strformat(filepath, &tile_format); - - bool found = false; - struct direntry *dir; - uint totfile = BLI_filelist_dir_contents(dirname, &dir); - for (int i = 0; i < totfile; i++) { - if (!(dir[i].type & S_IFREG)) { - continue; - } - - int id; - if (!BKE_image_get_tile_number_from_filepath(dir[i].path, udim_pattern, tile_format, &id)) { - continue; - } - - if (id < 1001 || id > IMA_UDIM_MAX) { - continue; - } - - found = true; - break; - } - BLI_filelist_free(dir, totfile); - MEM_SAFE_FREE(udim_pattern); - - return found; -} - -char *BKE_image_get_tile_strformat(const char *filepath, eUDIM_TILE_FORMAT *r_tile_format) -{ - if (filepath == NULL || r_tile_format == NULL) { - return NULL; - } - - if (strstr(filepath, "") != NULL) { - *r_tile_format = UDIM_TILE_FORMAT_UDIM; - return BLI_str_replaceN(filepath, "", "%d"); - } - if (strstr(filepath, "") != NULL) { - *r_tile_format = UDIM_TILE_FORMAT_UVTILE; - return BLI_str_replaceN(filepath, "", "u%d_v%d"); - } - - *r_tile_format = UDIM_TILE_FORMAT_NONE; - return NULL; -} - -bool BKE_image_get_tile_number_from_filepath(const char *filepath, - const char *pattern, - eUDIM_TILE_FORMAT tile_format, - int *r_tile_number) -{ - if (filepath == NULL || pattern == NULL || r_tile_number == NULL) { - return false; - } - - int u, v; - bool result = false; - - if (tile_format == UDIM_TILE_FORMAT_UDIM) { - if (sscanf(filepath, pattern, &u) == 1) { - *r_tile_number = u; - result = true; - } - } - else if (tile_format == UDIM_TILE_FORMAT_UVTILE) { - if (sscanf(filepath, pattern, &u, &v) == 2) { - *r_tile_number = 1001 + (u - 1) + ((v - 1) * 10); - result = true; - } - } - - return result; -} - -void BKE_image_set_filepath_from_tile_number(char *filepath, - const char *pattern, - eUDIM_TILE_FORMAT tile_format, - int tile_number) -{ - if (filepath == NULL || pattern == NULL) { - return; - } - - if (tile_format == UDIM_TILE_FORMAT_UDIM) { - sprintf(filepath, pattern, tile_number); - } - else if (tile_format == UDIM_TILE_FORMAT_UVTILE) { - int u = ((tile_number - 1001) % 10); - int v = ((tile_number - 1001) / 10); - sprintf(filepath, pattern, u + 1, v + 1); - } -} - -/* if layer or pass changes, we need an index for the imbufs list */ -/* note it is called for rendered results, but it doesn't use the index! */ -RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) -{ - RenderLayer *rl; - RenderPass *rpass = NULL; - - if (rr == NULL) { - return NULL; - } - - if (iuser) { - short index = 0, rv_index, rl_index = 0; - bool is_stereo = (iuser->flag & IMA_SHOW_STEREO) && RE_RenderResult_is_stereo(rr); - - rv_index = is_stereo ? iuser->multiview_eye : iuser->view; - if (RE_HasCombinedLayer(rr)) { - rl_index += 1; - } - - for (rl = rr->layers.first; rl; rl = rl->next, rl_index++) { - if (iuser->layer == rl_index) { - int rp_index; - rpass = image_render_pass_get(rl, iuser->pass, rv_index, &rp_index); - iuser->multi_index = index + rp_index; - break; - } - - index += BLI_listbase_count(&rl->passes); - } - } - - return rpass; -} - -void BKE_image_multiview_index(Image *ima, ImageUser *iuser) -{ - if (iuser) { - bool is_stereo = BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO); - if (is_stereo) { - iuser->multi_index = iuser->multiview_eye; - } - else { - if ((iuser->view < 0) || - (iuser->view >= BLI_listbase_count_at_most(&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 doesn't use the index! */ -bool BKE_image_is_multilayer(Image *ima) -{ - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { - 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; -} - -bool BKE_image_is_multiview(Image *ima) -{ - ImageView *view = ima->views.first; - return (view && (view->next || view->name[0])); -} - -bool BKE_image_is_stereo(Image *ima) -{ - return BKE_image_is_multiview(ima) && - (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) && - BLI_findstring(&ima->views, STEREO_RIGHT_NAME, offsetof(ImageView, name))); -} - -static void image_init_multilayer_multiview(Image *ima, RenderResult *rr) -{ - /* update image views from render views, but only if they actually changed, - * to avoid invalid memory access during render. ideally these should always - * be acquired with a mutex along with the render result, but there are still - * some places with just an image pointer that need to access views */ - if (rr && BLI_listbase_count(&ima->views) == BLI_listbase_count(&rr->views)) { - ImageView *iv = ima->views.first; - RenderView *rv = rr->views.first; - bool modified = false; - for (; rv; rv = rv->next, iv = iv->next) { - modified |= !STREQ(rv->name, iv->name); - } - if (!modified) { - return; - } - } - - BKE_image_free_views(ima); - - if (rr) { - LISTBASE_FOREACH (RenderView *, rv, &rr->views) { - ImageView *iv = MEM_callocN(sizeof(ImageView), "Viewer Image View"); - STRNCPY(iv->name, rv->name); - BLI_addtail(&ima->views, iv); - } - } -} - -RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima) -{ - RenderResult *rr = NULL; - if (ima->rr) { - rr = ima->rr; - } - else if (ima->type == IMA_TYPE_R_RESULT) { - if (ima->render_slot == ima->last_render_slot) { - rr = RE_AcquireResultRead(RE_GetSceneRender(scene)); - } - else { - rr = BKE_image_get_renderslot(ima, ima->render_slot)->render; - BKE_image_partial_update_mark_full_update(ima); - } - - /* set proper views */ - image_init_multilayer_multiview(ima, rr); - } - - return rr; -} - -void BKE_image_release_renderresult(Scene *scene, Image *ima) -{ - if (ima->rr) { - /* pass */ - } - else if (ima->type == IMA_TYPE_R_RESULT) { - if (ima->render_slot == ima->last_render_slot) { - RE_ReleaseResult(RE_GetSceneRender(scene)); - } - } -} - -bool BKE_image_is_openexr(struct Image *ima) -{ -#ifdef WITH_OPENEXR - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { - return BLI_path_extension_check(ima->filepath, ".exr"); - } -#else - UNUSED_VARS(ima); -#endif - return false; -} - -void BKE_image_backup_render(Scene *scene, Image *ima, bool free_current_slot) -{ - /* called right before rendering, ima->renderslots contains render - * result pointers for everything but the current render */ - Render *re = RE_GetSceneRender(scene); - - /* Ensure we always have a valid render slot. */ - if (!ima->renderslots.first) { - BKE_image_add_renderslot(ima, NULL); - ima->render_slot = 0; - ima->last_render_slot = 0; - } - else if (ima->render_slot >= BLI_listbase_count(&ima->renderslots)) { - ima->render_slot = 0; - ima->last_render_slot = 0; - } - - RenderSlot *last_slot = BKE_image_get_renderslot(ima, ima->last_render_slot); - RenderSlot *cur_slot = BKE_image_get_renderslot(ima, ima->render_slot); - - if (last_slot && ima->render_slot != ima->last_render_slot) { - last_slot->render = NULL; - RE_SwapResult(re, &last_slot->render); - - if (cur_slot->render) { - if (free_current_slot) { - BKE_image_clear_renderslot(ima, NULL, ima->render_slot); - } - else { - RE_SwapResult(re, &cur_slot->render); - } - } - } - - ima->last_render_slot = ima->render_slot; -} - -/**************************** 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"); - STRNCPY(iv->name, viewname); - STRNCPY(iv->filepath, 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); - } -} - -/* 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; - bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL); - - /* only load rr once for multiview */ - if (!ima->rr) { - ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y); - } - - IMB_exr_close(ibuf->userdata); - - ibuf->userdata = NULL; - if (ima->rr != NULL) { - ima->rr->framenr = framenr; - BKE_stamp_info_from_imbuf(ima->rr, ibuf); - } - - /* set proper views */ - image_init_multilayer_multiview(ima, ima->rr); -} -#endif /* WITH_OPENEXR */ - -/* common stuff to do with images after loading */ -static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) -{ - /* Preview is NULL when it has never been used as an icon before. - * Never handle previews/icons outside of main thread. */ - if (G.background == 0 && ima->preview == NULL && BLI_thread_is_main()) { - BKE_icon_changed(BKE_icon_id_ensure(&ima->id)); - } - - /* timer */ - BKE_image_tag_time(ima); - - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - /* Images should never get loaded if the corresponding tile does not exist, - * but we should at least not crash if it happens due to a bug elsewhere. */ - BLI_assert(tile != NULL); - UNUSED_VARS_NDEBUG(tile); -} - -static int imbuf_alpha_flags_for_image(Image *ima) -{ - switch (ima->alpha_mode) { - case IMA_ALPHA_STRAIGHT: - return 0; - case IMA_ALPHA_PREMUL: - return IB_alphamode_premul; - case IMA_ALPHA_CHANNEL_PACKED: - return IB_alphamode_channel_packed; - case IMA_ALPHA_IGNORE: - return IB_alphamode_ignore; - } - - return 0; -} - -/* the number of files will vary according to the stereo format */ -static int image_num_files(Image *ima) -{ - const bool is_multiview = BKE_image_is_multiview(ima); - - if (!is_multiview) { - return 1; - } - if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { - return 1; - } - /* R_IMF_VIEWS_INDIVIDUAL */ - - return BLI_listbase_count(&ima->views); -} - -static ImBuf *load_sequence_single( - Image *ima, ImageUser *iuser, int frame, const int view_id, bool *r_cache_ibuf) -{ - struct ImBuf *ibuf; - char name[FILE_MAX]; - int flag; - ImageUser iuser_t = {0}; - - *r_cache_ibuf = true; - - ima->lastframe = frame; - - if (iuser) { - iuser_t = *iuser; - } - else { - /* BKE_image_user_file_path() uses this value for file name for sequences. */ - iuser_t.framenr = frame; - /* TODO(sergey): Do we need to initialize something else here? */ - } - - iuser_t.view = view_id; - BKE_image_user_file_path(&iuser_t, ima, name); - - flag = IB_rect | IB_multilayer | IB_metadata; - flag |= imbuf_alpha_flags_for_image(ima); - - /* read ibuf */ - ibuf = IMB_loadiffname(name, flag, ima->colorspace_settings.name); - -#if 0 - if (ibuf) { - printf(AT " loaded %s\n", name); - } - else { - printf(AT " missed %s\n", name); - } -#endif - - if (ibuf) { -#ifdef WITH_OPENEXR - if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) { - /* Handle multilayer and multiview cases, don't assign ibuf here. - * will be set layer in BKE_image_acquire_ibuf from ima->rr. */ - if (IMB_exr_has_multilayer(ibuf->userdata)) { - image_create_multilayer(ima, ibuf, frame); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; - /* NULL ibuf in the cache means the image failed to load. However for multilayer we load - * pixels into RenderResult instead and intentionally leave ibuf NULL. */ - *r_cache_ibuf = false; - } - } - else { - image_init_after_load(ima, iuser, ibuf); - } -#else - image_init_after_load(ima, iuser, ibuf); -#endif - } - - return ibuf; -} - -static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, int frame) -{ - struct ImBuf *ibuf = NULL; - const bool is_multiview = BKE_image_is_multiview(ima); - const int totfiles = image_num_files(ima); - - if (!is_multiview) { - bool put_in_cache; - ibuf = load_sequence_single(ima, iuser, frame, 0, &put_in_cache); - if (put_in_cache) { - image_assign_ibuf(ima, ibuf, 0, entry); - } - } - else { - const int totviews = BLI_listbase_count(&ima->views); - struct ImBuf **ibuf_arr; - - ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); - bool *cache_ibuf_arr = MEM_mallocN(sizeof(bool) * totviews, "Image View Put In Cache"); - - for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, cache_ibuf_arr + i); - } - - if (BKE_image_is_stereo(ima) && 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)]; - - for (int i = 0; i < totviews; i++) { - if (cache_ibuf_arr[i]) { - image_assign_ibuf(ima, ibuf_arr[i], i, entry); - } - } - - /* "remove" the others (decrease their refcount) */ - for (int i = 0; i < totviews; i++) { - if (ibuf_arr[i] != ibuf) { - IMB_freeImBuf(ibuf_arr[i]); - } - } - - /* cleanup */ - MEM_freeN(ibuf_arr); - MEM_freeN(cache_ibuf_arr); - } - - return ibuf; -} - -static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int entry, int frame) -{ - struct ImBuf *ibuf = NULL; - - /* either we load from RenderResult, or we have to load a new one */ - - /* check for new RenderResult */ - if (ima->rr == NULL || frame != ima->rr->framenr) { - if (ima->rr) { - /* Cached image buffers shares pointers with render result, - * need to ensure there's no image buffers are hanging around - * with dead links after freeing the render result. - */ - image_free_cached_frames(ima); - RE_FreeRenderResult(ima->rr); - ima->rr = NULL; - } - - ibuf = image_load_sequence_file(ima, iuser, entry, frame); - - if (ibuf) { /* actually an error */ - ima->type = IMA_TYPE_IMAGE; - printf("error, multi is normal image\n"); - } - } - if (ima->rr) { - RenderPass *rpass = BKE_image_multilayer_index(ima->rr, iuser); - - if (rpass) { - // printf("load from pass %s\n", rpass->name); - /* since we free render results, we copy the rect */ - ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); - ibuf->rect_float = MEM_dupallocN(rpass->rect); - ibuf->flags |= IB_rectfloat; - ibuf->mall = IB_rectfloat; - ibuf->channels = rpass->channels; - - BKE_imbuf_stamp_info(ima->rr, ibuf); - - image_init_after_load(ima, iuser, ibuf); - image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry); - } - // else printf("pass not found\n"); - } - - return ibuf; -} - -static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const int view_id) -{ - struct ImBuf *ibuf = NULL; - ImageAnim *ia; - - ia = BLI_findlink(&ima->anims, view_id); - - if (ia->anim == NULL) { - char str[FILE_MAX]; - int flags = IB_rect; - ImageUser iuser_t; - - if (ima->flag & IMA_DEINTERLACE) { - flags |= IB_animdeinterlace; - } - - 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. */ - ia->anim = openanim(str, flags, 0, ima->colorspace_settings.name); - - /* let's initialize this user */ - if (ia->anim && iuser && iuser->frames == 0) { - iuser->frames = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); - } - } - - 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(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); - - if (ibuf) { - image_init_after_load(ima, iuser, ibuf); - } - } - - return ibuf; -} - -static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) -{ - struct ImBuf *ibuf = NULL; - const bool is_multiview = BKE_image_is_multiview(ima); - const int totfiles = image_num_files(ima); - - if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) { - image_free_anims(ima); - - for (int 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 int totviews = BLI_listbase_count(&ima->views); - - ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views (movie) Imbufs"); - - for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_movie_single(ima, iuser, frame, i); - } - - if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D) { - IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); - } - - for (int i = 0; i < totviews; i++) { - image_assign_ibuf(ima, ibuf_arr[i], i, frame); - } - - /* return the original requested ImBuf */ - ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; - - /* "remove" the others (decrease their refcount) */ - for (int i = 0; i < totviews; i++) { - if (ibuf_arr[i] != ibuf) { - IMB_freeImBuf(ibuf_arr[i]); - } - } - - /* cleanup */ - MEM_freeN(ibuf_arr); - } - - return ibuf; -} - -static ImBuf *load_image_single(Image *ima, - ImageUser *iuser, - int cfra, - const int view_id, - const bool has_packed, - bool *r_cache_ibuf) -{ - char filepath[FILE_MAX]; - struct ImBuf *ibuf = NULL; - int flag; - - *r_cache_ibuf = true; - - /* is there a PackedFile with this image ? */ - if (has_packed) { - ImagePackedFile *imapf; - - flag = IB_rect | IB_multilayer; - flag |= imbuf_alpha_flags_for_image(ima); - - imapf = BLI_findlink(&ima->packedfiles, view_id); - if (imapf->packedfile) { - ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, - imapf->packedfile->size, - flag, - ima->colorspace_settings.name, - ""); - } - } - else { - ImageUser iuser_t; - - flag = IB_rect | IB_multilayer | IB_metadata; - flag |= imbuf_alpha_flags_for_image(ima); - - /* get the correct filepath */ - BKE_image_user_frame_calc(ima, iuser, cfra); - - 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(filepath, flag, ima->colorspace_settings.name); - } - - if (ibuf) { -#ifdef WITH_OPENEXR - if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) { - /* Handle multilayer and multiview cases, don't assign ibuf here. - * will be set layer in BKE_image_acquire_ibuf from ima->rr. */ - if (IMB_exr_has_multilayer(ibuf->userdata)) { - image_create_multilayer(ima, ibuf, cfra); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; - /* NULL ibuf in the cache means the image failed to load. However for multilayer we load - * pixels into RenderResult instead and intentionally leave ibuf NULL. */ - *r_cache_ibuf = false; - } - } - else -#endif - { - image_init_after_load(ima, iuser, ibuf); - - /* Make packed file for auto-pack. */ - if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) { - ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Pack-file"); - BLI_addtail(&ima->packedfiles, imapf); - - STRNCPY(imapf->filepath, filepath); - imapf->packedfile = BKE_packedfile_new( - NULL, filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - } - } - } - - 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; - const bool is_multiview = BKE_image_is_multiview(ima); - const int 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_at_most(&ima->packedfiles, totfiles + 1)) { - image_free_packedfiles(ima); - has_packed = false; - } - } - - if (!is_multiview) { - bool put_in_cache; - ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &put_in_cache); - if (put_in_cache) { - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - } - } - else { - struct ImBuf **ibuf_arr; - const int totviews = BLI_listbase_count(&ima->views); - BLI_assert(totviews > 0); - - ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); - bool *cache_ibuf_arr = MEM_mallocN(sizeof(bool) * totviews, "Image Views Put In Cache"); - - for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, cache_ibuf_arr + i); - } - - /* multi-views/multi-layers OpenEXR files directly populate ima, and return NULL ibuf... */ - if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D && ibuf_arr[0] && - totfiles == 1 && totviews >= 2) { - IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); - } - - /* return the original requested ImBuf */ - int i = (iuser && iuser->multi_index < totviews) ? iuser->multi_index : 0; - ibuf = ibuf_arr[i]; - - for (i = 0; i < totviews; i++) { - if (cache_ibuf_arr[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); - MEM_freeN(cache_ibuf_arr); - } - - return ibuf; -} - -static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) -{ - ImBuf *ibuf = NULL; - - if (ima->rr == NULL) { - ibuf = image_load_image_file(ima, iuser, 0); - if (ibuf) { /* actually an error */ - ima->type = IMA_TYPE_IMAGE; - return ibuf; - } - } - if (ima->rr) { - RenderPass *rpass = BKE_image_multilayer_index(ima->rr, iuser); - - if (rpass) { - ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); - - image_init_after_load(ima, iuser, ibuf); - - ibuf->rect_float = rpass->rect; - ibuf->flags |= IB_rectfloat; - ibuf->channels = rpass->channels; - - BKE_imbuf_stamp_info(ima->rr, ibuf); - - image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); - } - } - - return ibuf; -} - -/* showing RGBA result itself (from compo/sequence) or - * like exr, using layers etc */ -/* always returns a single ibuf, also during render progress */ -static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_lock) -{ - Render *re; - RenderResult rres; - RenderView *rv; - float *rectf, *rectz; - unsigned int *rect; - float dither; - int channels, layer, pass; - ImBuf *ibuf; - int from_render = (ima->render_slot == ima->last_render_slot); - int actview; - - if (!(iuser && iuser->scene)) { - return NULL; - } - - /* if we the caller is not going to release the lock, don't give the image */ - if (!r_lock) { - return NULL; - } - - re = RE_GetSceneRender(iuser->scene); - - channels = 4; - layer = iuser->layer; - pass = iuser->pass; - actview = iuser->view; - - if (BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO)) { - actview = iuser->multiview_eye; - } - - RenderSlot *slot; - if (from_render) { - RE_AcquireResultImage(re, &rres, actview); - } - else if ((slot = BKE_image_get_renderslot(ima, ima->render_slot))->render) { - rres = *(slot->render); - rres.have_combined = ((RenderView *)rres.views.first)->rectf != NULL; - } - else { - memset(&rres, 0, sizeof(RenderResult)); - } - - if (!(rres.rectx > 0 && rres.recty > 0)) { - if (from_render) { - RE_ReleaseResultImage(re); - } - return NULL; - } - - /* release is done in BKE_image_release_ibuf using r_lock */ - if (from_render) { - BLI_thread_lock(LOCK_VIEWER); - *r_lock = re; - rv = NULL; - } - else { - rv = BLI_findlink(&rres.views, actview); - if (rv == NULL) { - rv = rres.views.first; - } - } - - /* this gives active layer, composite or sequence result */ - if (rv == NULL) { - rect = (unsigned int *)rres.rect32; - rectf = rres.rectf; - rectz = rres.rectz; - } - else { - rect = (unsigned int *)rv->rect32; - rectf = rv->rectf; - rectz = rv->rectz; - } - - dither = iuser->scene->r.dither_intensity; - - /* combined layer gets added as first layer */ - if (rres.have_combined && layer == 0) { - /* pass */ - } - else if (rect && layer == 0) { - /* rect32 is set when there's a Sequence pass, this pass seems - * to have layer=0 (this is from image_buttons.c) - * in this case we ignore float buffer, because it could have - * hung from previous pass which was float - */ - rectf = NULL; - } - else if (rres.layers.first) { - RenderLayer *rl = BLI_findlink(&rres.layers, layer - (rres.have_combined ? 1 : 0)); - if (rl) { - RenderPass *rpass = image_render_pass_get(rl, pass, actview, NULL); - if (rpass) { - rectf = rpass->rect; - if (pass != 0) { - channels = rpass->channels; - dither = 0.0f; /* don't dither passes */ - } - } - - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - if (STREQ(rpass->name, RE_PASSNAME_Z) && rpass->view_id == actview) { - rectz = rpass->rect; - } - } - } - } - - ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); - - /* make ibuf if needed, and initialize it */ - if (ibuf == NULL) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, 0); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - } - - /* Set color space settings for a byte buffer. - * - * This is mainly to make it so color management treats byte buffer - * from render result with Save Buffers enabled as final display buffer - * and doesn't apply any color management on it. - * - * For other cases we need to be sure it stays to default byte buffer space. - */ - if (ibuf->rect != rect) { - const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); - IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace); - } - - /* invalidate color managed buffers if render result changed */ - BLI_thread_lock(LOCK_COLORMANAGE); - if (ibuf->x != rres.rectx || ibuf->y != rres.recty || ibuf->rect_float != rectf) { - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - } - - ibuf->x = rres.rectx; - ibuf->y = rres.recty; - - if (rect) { - imb_freerectImBuf(ibuf); - ibuf->rect = rect; - } - else { - /* byte buffer of render result has been freed, make sure image buffers - * does not reference to this buffer anymore - * need check for whether byte buffer was allocated and owned by image itself - * or if it's reusing buffer from render result - */ - if ((ibuf->mall & IB_rect) == 0) { - ibuf->rect = NULL; - } - } - - if (rectf) { - ibuf->rect_float = rectf; - ibuf->flags |= IB_rectfloat; - ibuf->channels = channels; - } - else { - ibuf->rect_float = NULL; - ibuf->flags &= ~IB_rectfloat; - } - - if (rectz) { - ibuf->zbuf_float = rectz; - ibuf->flags |= IB_zbuffloat; - } - else { - ibuf->zbuf_float = NULL; - ibuf->flags &= ~IB_zbuffloat; - } - - /* TODO(sergey): Make this faster by either simply referencing the stamp - * or by changing both ImBug and RenderResult to use same data type to - * store metadata. */ - if (ibuf->metadata != NULL) { - IMB_metadata_free(ibuf->metadata); - ibuf->metadata = NULL; - } - BKE_imbuf_stamp_info(&rres, ibuf); - - BLI_thread_unlock(LOCK_COLORMANAGE); - - ibuf->dither = dither; - - return ibuf; -} - -static int 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); - int index = BKE_image_has_multiple_ibufs(ima) ? 0 : IMA_NO_INDEX; - - if (is_multilayer) { - return iuser ? iuser->multi_index : index; - } - if (is_backdrop) { - if (BKE_image_is_stereo(ima)) { - /* backdrop hackaround (since there is no iuser */ - return ima->eye; - } - } - else if (BKE_image_is_multiview(ima)) { - return iuser ? iuser->multi_index : index; - } - - return index; -} - -static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry, int *r_index) -{ - int frame = 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; - } - else if (ima->source == IMA_SRC_SEQUENCE) { - if (ima->type == IMA_TYPE_IMAGE) { - frame = iuser ? iuser->framenr : ima->lastframe; - } - else if (ima->type == IMA_TYPE_MULTILAYER) { - frame = iuser ? iuser->framenr : ima->lastframe; - } - } - else if (ima->source == IMA_SRC_TILED) { - frame = image_get_tile_number_from_iuser(ima, iuser); - } - - *r_entry = frame; - *r_index = index; -} - -/* Get the ibuf from an image cache for a given image user. - * - * Returns referenced image buffer if it exists, callee is to - * call IMB_freeImBuf to de-reference the image buffer after - * it's done handling it. - */ -static ImBuf *image_get_cached_ibuf( - Image *ima, ImageUser *iuser, int *r_entry, int *r_index, bool *r_is_cached_empty) -{ - ImBuf *ibuf = NULL; - int entry = 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) { - entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); - ima->lastframe = entry; - } - else if (ima->source == IMA_SRC_SEQUENCE) { - if (ima->type == IMA_TYPE_IMAGE) { - entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); - ima->lastframe = entry; - } - else if (ima->type == IMA_TYPE_MULTILAYER) { - entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); - } - } - else if (ima->source == IMA_SRC_FILE) { - if (ima->type == IMA_TYPE_IMAGE) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); - } - else if (ima->type == IMA_TYPE_MULTILAYER) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); - } - } - else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); - } - else if (ima->source == IMA_SRC_VIEWER) { - /* always verify entirely, not that this shouldn't happen - * as part of texture sampling in rendering anyway, so not - * a big bottleneck */ - } - else if (ima->source == IMA_SRC_TILED) { - if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { - entry = image_get_tile_number_from_iuser(ima, iuser); - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); - } - } - - if (r_entry) { - *r_entry = entry; - } - - if (r_index) { - *r_index = index; - } - - return ibuf; -} - -BLI_INLINE bool image_quick_test(Image *ima, const ImageUser *iuser) -{ - if (ima == NULL) { - return false; - } - - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - if (tile == NULL) { - return false; - } - - return true; -} - -/** - * Checks optional #ImageUser and verifies/creates #ImBuf. - * - * \warning Not thread-safe, so callee should worry about thread locks. - */ -static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) -{ - ImBuf *ibuf = NULL; - int entry = 0, index = 0; - - if (r_lock) { - *r_lock = NULL; - } - - /* quick reject tests */ - if (!image_quick_test(ima, iuser)) { - return NULL; - } - - bool is_cached_empty = false; - ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index, &is_cached_empty); - if (is_cached_empty) { - return NULL; - } - - if (ibuf == NULL) { - /* we are sure we have to load the ibuf, using source and type */ - if (ima->source == IMA_SRC_MOVIE) { - /* source is from single file, use flipbook to store ibuf */ - ibuf = image_load_movie_file(ima, iuser, entry); - } - else if (ima->source == IMA_SRC_SEQUENCE) { - if (ima->type == IMA_TYPE_IMAGE) { - /* regular files, ibufs in flipbook, allows saving */ - ibuf = image_load_sequence_file(ima, iuser, entry, entry); - } - /* no else; on load the ima type can change */ - if (ima->type == IMA_TYPE_MULTILAYER) { - /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ - ibuf = image_load_sequence_multilayer(ima, iuser, entry, entry); - } - } - else if (ima->source == IMA_SRC_TILED) { - if (ima->type == IMA_TYPE_IMAGE) { - /* regular files, ibufs in flipbook, allows saving */ - ibuf = image_load_sequence_file(ima, iuser, entry, 0); - } - /* no else; on load the ima type can change */ - if (ima->type == IMA_TYPE_MULTILAYER) { - /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ - ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); - } - } - else if (ima->source == IMA_SRC_FILE) { - - if (ima->type == IMA_TYPE_IMAGE) { - ibuf = image_load_image_file(ima, iuser, entry); /* cfra only for '#', this global is OK */ - } - /* no else; on load the ima type can change */ - if (ima->type == IMA_TYPE_MULTILAYER) { - /* keeps render result, stores ibufs in listbase, allows saving */ - ibuf = image_get_ibuf_multilayer(ima, iuser); - } - } - else if (ima->source == IMA_SRC_GENERATED) { - /* generated is: ibuf is allocated dynamically */ - /* UV testgrid or black or solid etc */ - if (ima->gen_x == 0) { - ima->gen_x = 1024; - } - if (ima->gen_y == 0) { - ima->gen_y = 1024; - } - if (ima->gen_depth == 0) { - ima->gen_depth = 24; - } - ibuf = add_ibuf_size(ima->gen_x, - ima->gen_y, - ima->filepath, - ima->gen_depth, - (ima->gen_flag & IMA_GEN_FLOAT) != 0, - ima->gen_type, - ima->gen_color, - &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, index, 0); - } - else if (ima->source == IMA_SRC_VIEWER) { - if (ima->type == IMA_TYPE_R_RESULT) { - /* always verify entirely, and potentially - * returns pointer to release later */ - ibuf = image_get_render_result(ima, iuser, r_lock); - } - else if (ima->type == IMA_TYPE_COMPOSITE) { - /* requires lock/unlock, otherwise don't return image */ - if (r_lock) { - /* unlock in BKE_image_release_ibuf */ - BLI_thread_lock(LOCK_VIEWER); - *r_lock = ima; - - /* XXX anim play for viewer nodes not yet supported */ - entry = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, NULL); - - if (!ibuf) { - /* Composite Viewer, all handled in compositor */ - /* fake ibuf, will be filled in compositor */ - ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat); - image_assign_ibuf(ima, ibuf, index, entry); - } - } - } - } - - /* We only want movies and sequences to be memory limited. */ - if (ibuf != NULL && !ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { - ibuf->userflags |= IB_PERSISTENT; - } - } - - BKE_image_tag_time(ima); - - return ibuf; -} - -ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) -{ - /* NOTE: same as #image_acquire_ibuf, but can be used to retrieve images being rendered in - * a thread safe way, always call both acquire and release. */ - - if (ima == NULL) { - return NULL; - } - - ImBuf *ibuf; - - BLI_mutex_lock(ima->runtime.cache_mutex); - - ibuf = image_acquire_ibuf(ima, iuser, r_lock); - - BLI_mutex_unlock(ima->runtime.cache_mutex); - - return ibuf; -} - -void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock) -{ - if (lock != NULL) { - /* for getting image during threaded render / compositing, need to release */ - if (lock == ima) { - BLI_thread_unlock(LOCK_VIEWER); /* viewer image */ - } - else { - RE_ReleaseResultImage(lock); /* render result */ - BLI_thread_unlock(LOCK_VIEWER); /* view image imbuf */ - } - } - - if (ibuf) { - BLI_mutex_lock(ima->runtime.cache_mutex); - IMB_freeImBuf(ibuf); - BLI_mutex_unlock(ima->runtime.cache_mutex); - } -} - -bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) -{ - ImBuf *ibuf; - - /* quick reject tests */ - if (!image_quick_test(ima, iuser)) { - return false; - } - - BLI_mutex_lock(ima->runtime.cache_mutex); - - ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL, NULL); - - if (!ibuf) { - ibuf = image_acquire_ibuf(ima, iuser, NULL); - } - - BLI_mutex_unlock(ima->runtime.cache_mutex); - - IMB_freeImBuf(ibuf); - - return ibuf != NULL; -} - -/* ******** Pool for image buffers ******** */ - -typedef struct ImagePoolItem { - struct ImagePoolItem *next, *prev; - Image *image; - ImBuf *ibuf; - int index; - int entry; -} ImagePoolItem; - -typedef struct ImagePool { - ListBase image_buffers; - BLI_mempool *memory_pool; - ThreadMutex mutex; -} ImagePool; - -ImagePool *BKE_image_pool_new(void) -{ - ImagePool *pool = MEM_callocN(sizeof(ImagePool), "Image Pool"); - pool->memory_pool = BLI_mempool_create(sizeof(ImagePoolItem), 0, 128, BLI_MEMPOOL_NOP); - - BLI_mutex_init(&pool->mutex); - - return pool; -} - -void BKE_image_pool_free(ImagePool *pool) -{ - /* Use single lock to dereference all the image buffers. */ - BLI_mutex_lock(&pool->mutex); - for (ImagePoolItem *item = pool->image_buffers.first; item != NULL; item = item->next) { - if (item->ibuf != NULL) { - BLI_mutex_lock(item->image->runtime.cache_mutex); - IMB_freeImBuf(item->ibuf); - BLI_mutex_unlock(item->image->runtime.cache_mutex); - } - } - BLI_mutex_unlock(&pool->mutex); - - BLI_mempool_destroy(pool->memory_pool); - - BLI_mutex_end(&pool->mutex); - - MEM_freeN(pool); -} - -BLI_INLINE ImBuf *image_pool_find_item( - ImagePool *pool, Image *image, int entry, int index, bool *found) -{ - ImagePoolItem *item; - - *found = false; - - for (item = pool->image_buffers.first; item; item = item->next) { - if (item->image == image && item->entry == entry && item->index == index) { - *found = true; - return item->ibuf; - } - } - - return NULL; -} - -ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool) -{ - ImBuf *ibuf; - int index, entry; - bool found; - - if (!image_quick_test(ima, iuser)) { - return NULL; - } - - if (pool == NULL) { - /* Pool could be NULL, in this case use general acquire function. */ - return BKE_image_acquire_ibuf(ima, iuser, NULL); - } - - image_get_entry_and_index(ima, iuser, &entry, &index); - - /* Use double-checked locking, to avoid locking when the requested image buffer is already in the - * pool. */ - - ibuf = image_pool_find_item(pool, ima, entry, index, &found); - if (found) { - return ibuf; - } - - /* Lock the pool, to allow thread-safe modification of the content of the pool. */ - BLI_mutex_lock(&pool->mutex); - - ibuf = image_pool_find_item(pool, ima, entry, index, &found); - - /* Will also create item even in cases image buffer failed to load, - * prevents trying to load the same buggy file multiple times. */ - if (!found) { - ImagePoolItem *item; - - /* Thread-safe acquisition of an image buffer from the image. - * The acquisition does not use image pools, so there is no risk of recursive or out-of-order - * mutex locking. */ - ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); - - item = BLI_mempool_alloc(pool->memory_pool); - item->image = ima; - item->entry = entry; - item->index = index; - item->ibuf = ibuf; - - BLI_addtail(&pool->image_buffers, item); - } - - BLI_mutex_unlock(&pool->mutex); - - return ibuf; -} - -void BKE_image_pool_release_ibuf(Image *ima, ImBuf *ibuf, ImagePool *pool) -{ - /* if pool wasn't actually used, use general release stuff, - * for pools image buffers will be dereferenced on pool free - */ - if (pool == NULL) { - BKE_image_release_ibuf(ima, ibuf, NULL); - } -} - -int BKE_image_user_frame_get(const ImageUser *iuser, int cfra, bool *r_is_in_range) -{ - const int len = iuser->frames; - - if (r_is_in_range) { - *r_is_in_range = false; - } - - if (len == 0) { - return 0; - } - - int framenr; - cfra = cfra - iuser->sfra + 1; - - /* cyclic */ - if (iuser->cycl) { - cfra = ((cfra) % len); - if (cfra < 0) { - cfra += len; - } - if (cfra == 0) { - cfra = len; - } - - if (r_is_in_range) { - *r_is_in_range = true; - } - } - - if (cfra < 0) { - cfra = 0; - } - else if (cfra > len) { - cfra = len; - } - else { - if (r_is_in_range) { - *r_is_in_range = true; - } - } - - /* transform to images space */ - framenr = cfra; - if (framenr > iuser->frames) { - framenr = iuser->frames; - } - - if (iuser->cycl) { - framenr = ((framenr) % len); - while (framenr < 0) { - framenr += len; - } - if (framenr == 0) { - framenr = len; - } - } - - /* important to apply after else we can't loop on frames 100 - 110 for eg. */ - framenr += iuser->offset; - - return framenr; -} - -void BKE_image_user_frame_calc(Image *ima, ImageUser *iuser, int cfra) -{ - if (iuser) { - if (ima && BKE_image_is_animated(ima)) { - /* Compute current frame for animated image. */ - bool is_in_range; - const int framenr = BKE_image_user_frame_get(iuser, cfra, &is_in_range); - - if (is_in_range) { - iuser->flag |= IMA_USER_FRAME_IN_RANGE; - } - else { - iuser->flag &= ~IMA_USER_FRAME_IN_RANGE; - } - - iuser->framenr = framenr; - } - else { - /* Set fixed frame number for still image. */ - iuser->framenr = 0; - iuser->flag |= IMA_USER_FRAME_IN_RANGE; - } - - if (ima && ima->gpuframenr != iuser->framenr) { - /* NOTE: a single texture and refresh doesn't really work when - * multiple image users may use different frames, this is to - * be improved with perhaps a GPU texture cache. */ - BKE_image_partial_update_mark_full_update(ima); - ima->gpuframenr = iuser->framenr; - } - - iuser->flag &= ~IMA_NEED_FRAME_RECALC; - } -} - -/* goes over all ImageUsers, and sets frame numbers if auto-refresh is set */ -static void image_editors_update_frame(Image *ima, - ID *UNUSED(iuser_id), - ImageUser *iuser, - void *customdata) -{ - int cfra = *(int *)customdata; - - if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC)) { - BKE_image_user_frame_calc(ima, iuser, cfra); - } -} - -void BKE_image_editors_update_frame(const Main *bmain, int cfra) -{ - /* This only updates images used by the user interface. For others the - * dependency graph will call BKE_image_user_id_eval_animation. */ - wmWindowManager *wm = bmain->wm.first; - image_walk_id_all_users(&wm->id, false, &cfra, image_editors_update_frame); -} - -static void image_user_id_has_animation(Image *ima, - ID *UNUSED(iuser_id), - ImageUser *UNUSED(iuser), - void *customdata) -{ - if (ima && BKE_image_is_animated(ima)) { - *(bool *)customdata = true; - } -} - -bool BKE_image_user_id_has_animation(ID *id) -{ - /* For the dependency graph, this does not consider nested node - * trees as these are handled as their own data-block. */ - bool has_animation = false; - bool skip_nested_nodes = true; - image_walk_id_all_users(id, skip_nested_nodes, &has_animation, image_user_id_has_animation); - return has_animation; -} - -static void image_user_id_eval_animation(Image *ima, - ID *UNUSED(iduser_id), - ImageUser *iuser, - void *customdata) -{ - if (ima && BKE_image_is_animated(ima)) { - Depsgraph *depsgraph = (Depsgraph *)customdata; - - if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC) || - (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER)) { - float cfra = DEG_get_ctime(depsgraph); - - BKE_image_user_frame_calc(ima, iuser, cfra); - } - } -} - -void BKE_image_user_id_eval_animation(Depsgraph *depsgraph, ID *id) -{ - /* This is called from the dependency graph to update the image - * users in data-blocks. It computes the current frame number - * and tags the image to be refreshed. - * This does not consider nested node trees as these are handled - * as their own data-block. */ - bool skip_nested_nodes = true; - image_walk_id_all_users(id, skip_nested_nodes, depsgraph, image_user_id_eval_animation); -} - -void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) -{ - BKE_image_user_file_path_ex(iuser, ima, filepath, true); -} - -void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, bool resolve_udim) -{ - if (BKE_image_is_multiview(ima)) { - ImageView *iv = BLI_findlink(&ima->views, iuser->view); - if (iv->filepath[0]) { - BLI_strncpy(filepath, iv->filepath, FILE_MAX); - } - else { - BLI_strncpy(filepath, ima->filepath, FILE_MAX); - } - } - else { - BLI_strncpy(filepath, ima->filepath, FILE_MAX); - } - - if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { - char head[FILE_MAX], tail[FILE_MAX]; - unsigned short numlen; - - int index; - if (ima->source == IMA_SRC_SEQUENCE) { - index = iuser ? iuser->framenr : ima->lastframe; - BLI_path_sequence_decode(filepath, head, tail, &numlen); - BLI_path_sequence_encode(filepath, head, tail, numlen, index); - } - else if (resolve_udim) { - index = image_get_tile_number_from_iuser(ima, iuser); - - eUDIM_TILE_FORMAT tile_format; - char *udim_pattern = BKE_image_get_tile_strformat(filepath, &tile_format); - BKE_image_set_filepath_from_tile_number(filepath, udim_pattern, tile_format, index); - MEM_SAFE_FREE(udim_pattern); - } - } - - BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); -} - -bool BKE_image_has_alpha(Image *image) -{ - void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); - const int planes = (ibuf ? ibuf->planes : 0); - BKE_image_release_ibuf(image, ibuf, lock); - - if (planes == 32 || planes == 16) { - return true; - } - - return false; -} - -void BKE_image_get_size(Image *image, ImageUser *iuser, int *r_width, int *r_height) -{ - ImBuf *ibuf = NULL; - void *lock; - - if (image != NULL) { - ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); - } - - if (ibuf && ibuf->x > 0 && ibuf->y > 0) { - *r_width = ibuf->x; - *r_height = ibuf->y; - } - else if (image != NULL && image->type == IMA_TYPE_R_RESULT && iuser != NULL && - iuser->scene != NULL) { - Scene *scene = iuser->scene; - *r_width = (scene->r.xsch * scene->r.size) / 100; - *r_height = (scene->r.ysch * scene->r.size) / 100; - if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { - *r_width *= BLI_rctf_size_x(&scene->r.border); - *r_height *= BLI_rctf_size_y(&scene->r.border); - } - } - else { - *r_width = IMG_SIZE_FALLBACK; - *r_height = IMG_SIZE_FALLBACK; - } - - if (image != NULL) { - BKE_image_release_ibuf(image, ibuf, lock); - } -} - -void BKE_image_get_size_fl(Image *image, ImageUser *iuser, float r_size[2]) -{ - int width, height; - BKE_image_get_size(image, iuser, &width, &height); - - r_size[0] = (float)width; - r_size[1] = (float)height; -} - -void BKE_image_get_aspect(Image *image, float *r_aspx, float *r_aspy) -{ - *r_aspx = 1.0; - - /* x is always 1 */ - if (image) { - *r_aspy = image->aspy / image->aspx; - } - else { - *r_aspy = 1.0f; - } -} - -unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame, int tile) -{ - ImageUser iuser; - BKE_imageuser_default(&iuser); - void *lock; - ImBuf *ibuf; - unsigned char *pixels = NULL; - - iuser.framenr = frame; - iuser.tile = tile; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf) { - pixels = (unsigned char *)ibuf->rect; - - if (pixels) { - pixels = MEM_dupallocN(pixels); - } - - BKE_image_release_ibuf(image, ibuf, lock); - } - - if (!pixels) { - return NULL; - } - - return pixels; -} - -float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int tile) -{ - ImageUser iuser; - BKE_imageuser_default(&iuser); - void *lock; - ImBuf *ibuf; - float *pixels = NULL; - - iuser.framenr = frame; - iuser.tile = tile; - - ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - - if (ibuf) { - pixels = ibuf->rect_float; - - if (pixels) { - pixels = MEM_dupallocN(pixels); - } - - BKE_image_release_ibuf(image, ibuf, lock); - } - - if (!pixels) { - return NULL; - } - - return pixels; -} - -int BKE_image_sequence_guess_offset(Image *image) -{ - return BLI_path_sequence_decode(image->filepath, NULL, NULL, NULL); -} - -bool BKE_image_has_anim(Image *ima) -{ - return (BLI_listbase_is_empty(&ima->anims) == false); -} - -bool BKE_image_has_packedfile(const Image *ima) -{ - return (BLI_listbase_is_empty(&ima->packedfiles) == false); -} - -bool BKE_image_has_filepath(Image *ima) -{ - /* This could be improved to detect cases like //../../, currently path - * remapping empty file paths empty. */ - return ima->filepath[0] != '\0'; -} - -bool BKE_image_is_animated(Image *image) -{ - return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE); -} - -bool BKE_image_has_multiple_ibufs(Image *image) -{ - return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED); -} - -bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) -{ - bool is_dirty = false; - bool is_writable = false; - - BLI_mutex_lock(image->runtime.cache_mutex); - if (image->cache != NULL) { - struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); - - while (!IMB_moviecacheIter_done(iter)) { - ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { - is_writable = BKE_image_buffer_format_writable(ibuf); - is_dirty = true; - break; - } - IMB_moviecacheIter_step(iter); - } - IMB_moviecacheIter_free(iter); - } - BLI_mutex_unlock(image->runtime.cache_mutex); - - if (r_is_writable) { - *r_is_writable = is_writable; - } - - return is_dirty; -} - -bool BKE_image_is_dirty(Image *image) -{ - return BKE_image_is_dirty_writable(image, NULL); -} - -void BKE_image_mark_dirty(Image *UNUSED(image), ImBuf *ibuf) -{ - ibuf->userflags |= IB_BITMAPDIRTY; -} - -bool BKE_image_buffer_format_writable(ImBuf *ibuf) -{ - ImageFormatData im_format; - ImbFormatOptions options_dummy; - BKE_imbuf_to_image_format(&im_format, ibuf); - return (BKE_image_imtype_to_ftype(im_format.imtype, &options_dummy) == ibuf->ftype); -} - -void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions *options) -{ - BLI_mutex_lock(image->runtime.cache_mutex); - if (image->cache != NULL) { - struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); - - while (!IMB_moviecacheIter_done(iter)) { - ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf != NULL) { - ibuf->ftype = ftype; - ibuf->foptions = *options; - } - IMB_moviecacheIter_step(iter); - } - IMB_moviecacheIter_free(iter); - } - BLI_mutex_unlock(image->runtime.cache_mutex); -} - -bool BKE_image_has_loaded_ibuf(Image *image) -{ - bool has_loaded_ibuf = false; - - BLI_mutex_lock(image->runtime.cache_mutex); - if (image->cache != NULL) { - struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); - - while (!IMB_moviecacheIter_done(iter)) { - ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf != NULL) { - has_loaded_ibuf = true; - break; - } - IMB_moviecacheIter_step(iter); - } - IMB_moviecacheIter_free(iter); - } - BLI_mutex_unlock(image->runtime.cache_mutex); - - return has_loaded_ibuf; -} - -ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) -{ - ImBuf *ibuf = NULL; - - BLI_mutex_lock(image->runtime.cache_mutex); - if (image->cache != NULL) { - struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); - - while (!IMB_moviecacheIter_done(iter)) { - ImBuf *current_ibuf = IMB_moviecacheIter_getImBuf(iter); - if (current_ibuf != NULL && STREQ(current_ibuf->name, name)) { - ibuf = current_ibuf; - IMB_refImBuf(ibuf); - break; - } - IMB_moviecacheIter_step(iter); - } - IMB_moviecacheIter_free(iter); - } - BLI_mutex_unlock(image->runtime.cache_mutex); - - return ibuf; -} - -ImBuf *BKE_image_get_first_ibuf(Image *image) -{ - ImBuf *ibuf = NULL; - - BLI_mutex_lock(image->runtime.cache_mutex); - if (image->cache != NULL) { - struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); - - while (!IMB_moviecacheIter_done(iter)) { - ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf != NULL) { - IMB_refImBuf(ibuf); - } - break; - } - IMB_moviecacheIter_free(iter); - } - BLI_mutex_unlock(image->runtime.cache_mutex); - - 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) { - /* nothing to do */ - } - else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { - const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - - for (int i = 0; i < 2; i++) { - image_add_view(ima, names[i], ima->filepath); - } - return; - } - else { - /* R_IMF_VIEWS_INDIVIDUAL */ - char prefix[FILE_MAX] = {'\0'}; - char *name = ima->filepath; - const char *ext = NULL; - - BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); - - if (prefix[0] == '\0') { - BKE_image_free_views(ima); - return; - } - - /* 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]; - SNPRINTF(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]; - - STRNCPY(str, iv->filepath); - BLI_path_abs(str, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); - - /* 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 (!BKE_image_is_multiview(ima)) { - BKE_image_free_views(ima); - } - } -} - -/**************************** Render Slots ***************************/ - -RenderSlot *BKE_image_add_renderslot(Image *ima, const char *name) -{ - RenderSlot *slot = MEM_callocN(sizeof(RenderSlot), "Image new Render Slot"); - if (name && name[0]) { - BLI_strncpy(slot->name, name, sizeof(slot->name)); - } - else { - int n = BLI_listbase_count(&ima->renderslots) + 1; - BLI_snprintf(slot->name, sizeof(slot->name), "Slot %d", n); - } - BLI_addtail(&ima->renderslots, slot); - return slot; -} - -bool BKE_image_remove_renderslot(Image *ima, ImageUser *iuser, int index) -{ - if (index == ima->last_render_slot) { - /* Don't remove render slot while rendering to it. */ - if (G.is_rendering) { - return false; - } - } - - int num_slots = BLI_listbase_count(&ima->renderslots); - if (index >= num_slots || num_slots == 1) { - return false; - } - - RenderSlot *remove_slot = BLI_findlink(&ima->renderslots, index); - RenderSlot *current_slot = BLI_findlink(&ima->renderslots, ima->render_slot); - RenderSlot *current_last_slot = BLI_findlink(&ima->renderslots, ima->last_render_slot); - - RenderSlot *next_slot; - if (current_slot == remove_slot) { - next_slot = BLI_findlink(&ima->renderslots, (index == num_slots - 1) ? index - 1 : index + 1); - } - else { - next_slot = current_slot; - } - - /* If the slot to be removed is the slot with the last render, - * make another slot the last render slot. */ - if (remove_slot == current_last_slot) { - /* Choose the currently selected slot unless that one is being removed, - * in that case take the next one. */ - RenderSlot *next_last_slot; - if (current_slot == remove_slot) { - next_last_slot = next_slot; - } - else { - next_last_slot = current_slot; - } - - if (!iuser) { - return false; - } - Render *re = RE_GetSceneRender(iuser->scene); - if (!re) { - return false; - } - RE_SwapResult(re, ¤t_last_slot->render); - RE_SwapResult(re, &next_last_slot->render); - current_last_slot = next_last_slot; - } - - current_slot = next_slot; - - BLI_remlink(&ima->renderslots, remove_slot); - - ima->render_slot = BLI_findindex(&ima->renderslots, current_slot); - ima->last_render_slot = BLI_findindex(&ima->renderslots, current_last_slot); - - if (remove_slot->render) { - RE_FreeRenderResult(remove_slot->render); - } - MEM_freeN(remove_slot); - - return true; -} - -bool BKE_image_clear_renderslot(Image *ima, ImageUser *iuser, int index) -{ - if (index == ima->last_render_slot) { - if (!iuser) { - return false; - } - if (G.is_rendering) { - return false; - } - Render *re = RE_GetSceneRender(iuser->scene); - if (!re) { - return false; - } - RE_ClearResult(re); - return true; - } - - RenderSlot *slot = BLI_findlink(&ima->renderslots, index); - if (!slot) { - return false; - } - if (slot->render) { - RE_FreeRenderResult(slot->render); - slot->render = NULL; - } - return true; -} - -RenderSlot *BKE_image_get_renderslot(Image *ima, int index) -{ - /* Can be NULL for images without render slots. */ - return BLI_findlink(&ima->renderslots, index); -} diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc new file mode 100644 index 00000000000..967f0f61e07 --- /dev/null +++ b/source/blender/blenkernel/intern/image.cc @@ -0,0 +1,6394 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ + +/** \file + * \ingroup bke + */ + +#include +#include +#include +#include +#include +#ifndef WIN32 +# include +#else +# include +#endif + +#include + +#include "BLI_array.hh" + +#include "CLG_log.h" + +#include "MEM_guardedalloc.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" +#include "IMB_metadata.h" +#include "IMB_moviecache.h" + +#ifdef WITH_OPENEXR +# include "intern/openexr/openexr_multi.h" +#endif + +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + +#include "DNA_brush_types.h" +#include "DNA_camera_types.h" +#include "DNA_defaults.h" +#include "DNA_light_types.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_packedFile_types.h" +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" +#include "DNA_simulation_types.h" +#include "DNA_world_types.h" + +#include "BLI_blenlib.h" +#include "BLI_math_vector.h" +#include "BLI_mempool.h" +#include "BLI_system.h" +#include "BLI_task.h" +#include "BLI_threads.h" +#include "BLI_timecode.h" /* For stamp time-code format. */ +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_bpath.h" +#include "BKE_colortools.h" +#include "BKE_global.h" +#include "BKE_icons.h" +#include "BKE_idtype.h" +#include "BKE_image.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_node_tree_update.h" +#include "BKE_packedFile.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" + +#include "BLF_api.h" + +#include "PIL_time.h" + +#include "RE_pipeline.h" + +#include "SEQ_utils.h" /* SEQ_get_topmost_sequence() */ + +#include "GPU_material.h" +#include "GPU_texture.h" + +#include "BLI_sys_types.h" /* for intptr_t support */ + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "BLO_read_write.h" + +/* for image user iteration */ +#include "DNA_node_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +using blender::Array; + +static CLG_LogRef LOG = {"bke.image"}; + +static void image_init(Image *ima, short source, short type); +static void image_free_packedfiles(Image *ima); +static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src); + +/* Reset runtime image fields when data-block is being initialized. */ +static void image_runtime_reset(struct Image *image) +{ + memset(&image->runtime, 0, sizeof(image->runtime)); + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(static_cast(image->runtime.cache_mutex)); +} + +/* Reset runtime image fields when data-block is being copied. */ +static void image_runtime_reset_on_copy(struct Image *image) +{ + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(static_cast(image->runtime.cache_mutex)); + + image->runtime.partial_update_register = nullptr; + image->runtime.partial_update_user = nullptr; +} + +static void image_runtime_free_data(struct Image *image) +{ + BLI_mutex_end(static_cast(image->runtime.cache_mutex)); + MEM_freeN(image->runtime.cache_mutex); + image->runtime.cache_mutex = nullptr; + + if (image->runtime.partial_update_user != nullptr) { + BKE_image_partial_update_free(image->runtime.partial_update_user); + image->runtime.partial_update_user = nullptr; + } + BKE_image_partial_update_register_free(image); +} + +static void image_init_data(ID *id) +{ + Image *image = (Image *)id; + + if (image != nullptr) { + image_init(image, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); + } +} + +static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int flag) +{ + Image *image_dst = (Image *)id_dst; + const Image *image_src = (const Image *)id_src; + + BKE_color_managed_colorspace_settings_copy(&image_dst->colorspace_settings, + &image_src->colorspace_settings); + + copy_image_packedfiles(&image_dst->packedfiles, &image_src->packedfiles); + + image_dst->stereo3d_format = static_cast( + MEM_dupallocN(image_src->stereo3d_format)); + BLI_duplicatelist(&image_dst->views, &image_src->views); + + /* Cleanup stuff that cannot be copied. */ + image_dst->cache = nullptr; + image_dst->rr = nullptr; + + BLI_duplicatelist(&image_dst->renderslots, &image_src->renderslots); + LISTBASE_FOREACH (RenderSlot *, slot, &image_dst->renderslots) { + slot->render = nullptr; + } + + BLI_listbase_clear(&image_dst->anims); + + BLI_duplicatelist(&image_dst->tiles, &image_src->tiles); + + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + image_dst->gputexture[i][eye][resolution] = nullptr; + } + } + } + + if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { + BKE_previewimg_id_copy(&image_dst->id, &image_src->id); + } + else { + image_dst->preview = nullptr; + } + + image_runtime_reset_on_copy(image_dst); +} + +static void image_free_data(ID *id) +{ + Image *image = (Image *)id; + + /* Also frees animdata. */ + BKE_image_free_buffers(image); + + image_free_packedfiles(image); + + LISTBASE_FOREACH (RenderSlot *, slot, &image->renderslots) { + if (slot->render) { + RE_FreeRenderResult(slot->render); + slot->render = nullptr; + } + } + BLI_freelistN(&image->renderslots); + + BKE_image_free_views(image); + MEM_SAFE_FREE(image->stereo3d_format); + + BKE_icon_id_delete(&image->id); + BKE_previewimg_free(&image->preview); + + BLI_freelistN(&image->tiles); + + image_runtime_free_data(image); +} + +static void image_foreach_cache(ID *id, + IDTypeForeachCacheFunctionCallback function_callback, + void *user_data) +{ + Image *image = (Image *)id; + IDCacheKey key; + key.id_session_uuid = id->session_uuid; + key.offset_in_ID = offsetof(Image, cache); + key.cache_v = image->cache; + function_callback(id, &key, (void **)&image->cache, 0, user_data); + + auto gputexture_offset = [image](int target, int eye, int resolution) { + constexpr size_t base_offset = offsetof(Image, gputexture); + const auto first = &image->gputexture[0][0][0]; + const size_t array_offset = sizeof(*first) * + (&image->gputexture[target][eye][resolution] - first); + return base_offset + array_offset; + }; + + for (int eye = 0; eye < 2; eye++) { + for (int a = 0; a < TEXTARGET_COUNT; a++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *texture = image->gputexture[a][eye][resolution]; + if (texture == nullptr) { + continue; + } + key.offset_in_ID = gputexture_offset(a, eye, resolution); + key.cache_v = texture; + function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); + } + } + } + + key.offset_in_ID = offsetof(Image, rr); + key.cache_v = image->rr; + function_callback(id, &key, (void **)&image->rr, 0, user_data); + + LISTBASE_FOREACH (RenderSlot *, slot, &image->renderslots) { + key.offset_in_ID = (size_t)BLI_ghashutil_strhash_p(slot->name); + key.cache_v = slot->render; + function_callback(id, &key, (void **)&slot->render, 0, user_data); + } +} + +static void image_foreach_path(ID *id, BPathForeachPathData *bpath_data) +{ + Image *ima = (Image *)id; + const eBPathForeachFlag flag = bpath_data->flag; + + if (BKE_image_has_packedfile(ima) && (flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0) { + return; + } + /* Skip empty file paths, these are typically from generated images and + * don't make sense to add directories to until the image has been saved + * once to give it a meaningful value. */ + /* TODO re-assess whether this behavior is desired in the new generic code context. */ + if (!ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED) || + ima->filepath[0] == '\0') { + return; + } + + /* If this is a tiled image, and we're asked to resolve the tokens in the virtual + * filepath, use the first tile to generate a concrete path for use during processing. */ + bool result = false; + if (ima->source == IMA_SRC_TILED && (flag & BKE_BPATH_FOREACH_PATH_RESOLVE_TOKEN) != 0) { + char temp_path[FILE_MAX], orig_file[FILE_MAXFILE]; + BLI_strncpy(temp_path, ima->filepath, sizeof(temp_path)); + BLI_split_file_part(temp_path, orig_file, sizeof(orig_file)); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(temp_path, &tile_format); + BKE_image_set_filepath_from_tile_number( + temp_path, udim_pattern, tile_format, ((ImageTile *)ima->tiles.first)->tile_number); + MEM_SAFE_FREE(udim_pattern); + + result = BKE_bpath_foreach_path_fixed_process(bpath_data, temp_path); + if (result) { + /* Put the filepath back together using the new directory and the original file name. */ + char new_dir[FILE_MAXDIR]; + BLI_split_dir_part(temp_path, new_dir, sizeof(new_dir)); + BLI_join_dirfile(ima->filepath, sizeof(ima->filepath), new_dir, orig_file); + } + } + else { + result = BKE_bpath_foreach_path_fixed_process(bpath_data, ima->filepath); + } + + if (result) { + if (flag & BKE_BPATH_FOREACH_PATH_RELOAD_EDITED) { + if (!BKE_image_has_packedfile(ima) && + /* Image may have been painted onto (and not saved, T44543). */ + !BKE_image_is_dirty(ima)) { + BKE_image_signal(bpath_data->bmain, ima, nullptr, IMA_SIGNAL_RELOAD); + } + } + } +} + +static void image_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Image *ima = (Image *)id; + const bool is_undo = BLO_write_is_undo(writer); + + /* Clear all data that isn't read to reduce false detection of changed image during memfile undo. + */ + ima->lastused = 0; + ima->cache = nullptr; + ima->gpuflag = 0; + BLI_listbase_clear(&ima->anims); + ima->runtime.partial_update_register = nullptr; + ima->runtime.partial_update_user = nullptr; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + ima->gputexture[i][j][resolution] = nullptr; + } + } + } + + ImagePackedFile *imapf; + + BLI_assert(ima->packedfile == nullptr); + if (!is_undo) { + /* Do not store packed files in case this is a library override ID. */ + if (ID_IS_OVERRIDE_LIBRARY(ima)) { + BLI_listbase_clear(&ima->packedfiles); + } + else { + /* Some trickery to keep forward compatibility of packed images. */ + if (ima->packedfiles.first != nullptr) { + imapf = static_cast(ima->packedfiles.first); + ima->packedfile = imapf->packedfile; + } + } + } + + /* write LibData */ + BLO_write_id_struct(writer, Image, id_address, &ima->id); + BKE_id_blend_write(writer, &ima->id); + + for (imapf = static_cast(ima->packedfiles.first); imapf; + imapf = imapf->next) { + BLO_write_struct(writer, ImagePackedFile, imapf); + BKE_packedfile_blend_write(writer, imapf->packedfile); + } + + BKE_previewimg_blend_write(writer, ima->preview); + + LISTBASE_FOREACH (ImageView *, iv, &ima->views) { + BLO_write_struct(writer, ImageView, iv); + } + BLO_write_struct(writer, Stereo3dFormat, ima->stereo3d_format); + + BLO_write_struct_list(writer, ImageTile, &ima->tiles); + + ima->packedfile = nullptr; + + BLO_write_struct_list(writer, RenderSlot, &ima->renderslots); +} + +static void image_blend_read_data(BlendDataReader *reader, ID *id) +{ + Image *ima = (Image *)id; + BLO_read_list(reader, &ima->tiles); + + BLO_read_list(reader, &(ima->renderslots)); + if (!BLO_read_data_is_undo(reader)) { + /* We reset this last render slot index only when actually reading a file, not for undo. */ + ima->last_render_slot = ima->render_slot; + } + + BLO_read_list(reader, &(ima->views)); + BLO_read_list(reader, &(ima->packedfiles)); + + if (ima->packedfiles.first) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { + BKE_packedfile_blend_read(reader, &imapf->packedfile); + } + ima->packedfile = nullptr; + } + else { + BKE_packedfile_blend_read(reader, &ima->packedfile); + } + + BLI_listbase_clear(&ima->anims); + BLO_read_data_address(reader, &ima->preview); + BKE_previewimg_blend_read(reader, ima->preview); + BLO_read_data_address(reader, &ima->stereo3d_format); + + ima->lastused = 0; + ima->gpuflag = 0; + + image_runtime_reset(ima); +} + +static void image_blend_read_lib(BlendLibReader *UNUSED(reader), ID *id) +{ + Image *ima = (Image *)id; + /* Images have some kind of 'main' cache, when null we should also clear all others. */ + /* Needs to be done *after* cache pointers are restored (call to + * `foreach_cache`/`blo_cache_storage_entry_restore_in_new`), easier for now to do it in + * lib_link... */ + if (ima->cache == nullptr) { + BKE_image_free_buffers(ima); + } +} + +constexpr IDTypeInfo get_type_info() +{ + IDTypeInfo info{}; + info.id_code = ID_IM; + info.id_filter = FILTER_ID_IM; + info.main_listbase_index = INDEX_ID_IM; + info.struct_size = sizeof(Image); + info.name = "Image"; + info.name_plural = "images"; + info.translation_context = BLT_I18NCONTEXT_ID_IMAGE; + info.flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE; + info.asset_type_info = nullptr; + + info.init_data = image_init_data; + info.copy_data = image_copy_data; + info.free_data = image_free_data; + info.make_local = nullptr; + info.foreach_id = nullptr; + info.foreach_cache = image_foreach_cache; + info.foreach_path = image_foreach_path; + info.owner_get = nullptr; + + info.blend_write = image_blend_write; + info.blend_read_data = image_blend_read_data; + info.blend_read_lib = image_blend_read_lib; + info.blend_read_expand = nullptr; + + info.blend_read_undo_preserve = nullptr; + + info.lib_override_apply_post = nullptr; + return info; +} +IDTypeInfo IDType_ID_IM = get_type_info(); + +/* prototypes */ +static int image_num_files(struct Image *ima); +static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock); +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 + +/* quick lookup: supports 1 million entries, thousand passes */ +#define IMA_MAKE_INDEX(entry, index) (((entry) << 10) + (index)) +#define IMA_INDEX_ENTRY(index) ((index) >> 10) +#if 0 +# define IMA_INDEX_PASS(index) (index & ~1023) +#endif + +/* ******** IMAGE CACHE ************* */ + +typedef struct ImageCacheKey { + int index; +} ImageCacheKey; + +static unsigned int imagecache_hashhash(const void *key_v) +{ + const ImageCacheKey *key = static_cast(key_v); + return key->index; +} + +static bool imagecache_hashcmp(const void *a_v, const void *b_v) +{ + const ImageCacheKey *a = static_cast(a_v); + const ImageCacheKey *b = static_cast(b_v); + + return (a->index != b->index); +} + +static void imagecache_keydata(void *userkey, int *framenr, int *proxy, int *render_flags) +{ + ImageCacheKey *key = static_cast(userkey); + + *framenr = IMA_INDEX_ENTRY(key->index); + *proxy = IMB_PROXY_NONE; + *render_flags = 0; +} + +static void imagecache_put(Image *image, int index, ImBuf *ibuf) +{ + ImageCacheKey key; + + if (image->cache == nullptr) { + // char cache_name[64]; + // SNPRINTF(cache_name, "Image Datablock %s", image->id.name); + + image->cache = IMB_moviecache_create( + "Image Datablock Cache", sizeof(ImageCacheKey), imagecache_hashhash, imagecache_hashcmp); + IMB_moviecache_set_getdata_callback(image->cache, imagecache_keydata); + } + + key.index = index; + + IMB_moviecache_put(image->cache, &key, ibuf); +} + +static void imagecache_remove(Image *image, int index) +{ + if (image->cache == nullptr) { + return; + } + + ImageCacheKey key; + key.index = index; + IMB_moviecache_remove(image->cache, &key); +} + +static struct ImBuf *imagecache_get(Image *image, int index, bool *r_is_cached_empty) +{ + if (image->cache) { + ImageCacheKey key; + key.index = index; + return IMB_moviecache_get(image->cache, &key, r_is_cached_empty); + } + + return nullptr; +} + +/* ***************** ALLOC & FREE, DATA MANAGING *************** */ + +static void image_free_cached_frames(Image *image) +{ + if (image->cache) { + IMB_moviecache_free(image->cache); + image->cache = nullptr; + } +} + +static void image_free_packedfiles(Image *ima) +{ + while (ima->packedfiles.last) { + ImagePackedFile *imapf = static_cast(ima->packedfiles.last); + if (imapf->packedfile) { + BKE_packedfile_free(imapf->packedfile); + } + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); + } +} + +void BKE_image_free_packedfiles(Image *ima) +{ + image_free_packedfiles(ima); +} + +void BKE_image_free_views(Image *image) +{ + BLI_freelistN(&image->views); +} + +static void image_free_anims(Image *ima) +{ + while (ima->anims.last) { + ImageAnim *ia = static_cast(ima->anims.last); + if (ia->anim) { + IMB_free_anim(ia->anim); + ia->anim = nullptr; + } + BLI_remlink(&ima->anims, ia); + MEM_freeN(ia); + } +} + +void BKE_image_free_buffers_ex(Image *ima, bool do_lock) +{ + if (do_lock) { + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + } + image_free_cached_frames(ima); + + image_free_anims(ima); + + if (ima->rr) { + RE_FreeRenderResult(ima->rr); + ima->rr = nullptr; + } + + BKE_image_free_gputextures(ima); + + if (do_lock) { + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); + } +} + +void BKE_image_free_buffers(Image *ima) +{ + BKE_image_free_buffers_ex(ima, false); +} + +void BKE_image_free_data(Image *ima) +{ + image_free_data(&ima->id); +} + +/* only image block itself */ +static void image_init(Image *ima, short source, short type) +{ + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(ima, id)); + + MEMCPY_STRUCT_AFTER(ima, DNA_struct_default_get(Image), id); + + ima->source = source; + ima->type = type; + + if (source == IMA_SRC_VIEWER) { + ima->flag |= IMA_VIEW_AS_RENDER; + } + + ImageTile *tile = MEM_cnew("Image Tiles"); + tile->tile_number = 1001; + BLI_addtail(&ima->tiles, tile); + + if (type == IMA_TYPE_R_RESULT) { + for (int i = 0; i < 8; i++) { + BKE_image_add_renderslot(ima, nullptr); + } + } + + image_runtime_reset(ima); + + BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); + ima->stereo3d_format = MEM_cnew("Image Stereo Format"); +} + +static Image *image_alloc(Main *bmain, const char *name, short source, short type) +{ + Image *ima; + + ima = static_cast(BKE_libblock_alloc(bmain, ID_IM, name, 0)); + if (ima) { + image_init(ima, source, type); + } + + return ima; +} + +/* Get the ibuf from an image cache by its index and entry. + * Local use here only. + * + * Returns referenced image buffer if it exists, callee is to + * call IMB_freeImBuf to de-reference the image buffer after + * it's done handling it. + */ +static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, + int index, + int entry, + bool *r_is_cached_empty) +{ + if (index != IMA_NO_INDEX) { + index = IMA_MAKE_INDEX(entry, index); + } + + return imagecache_get(ima, index, r_is_cached_empty); +} + +static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int entry) +{ + if (index != IMA_NO_INDEX) { + index = IMA_MAKE_INDEX(entry, index); + } + + imagecache_put(ima, index, ibuf); +} + +static void image_remove_ibuf(Image *ima, int index, int entry) +{ + if (index != IMA_NO_INDEX) { + index = IMA_MAKE_INDEX(entry, index); + } + imagecache_remove(ima, index); +} + +static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) +{ + const ImagePackedFile *imapf_src; + + BLI_listbase_clear(lb_dst); + for (imapf_src = static_cast(lb_src->first); imapf_src; + imapf_src = imapf_src->next) { + ImagePackedFile *imapf_dst = static_cast( + MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)")); + STRNCPY(imapf_dst->filepath, imapf_src->filepath); + + if (imapf_src->packedfile) { + imapf_dst->packedfile = BKE_packedfile_duplicate(imapf_src->packedfile); + } + + BLI_addtail(lb_dst, imapf_dst); + } +} + +void BKE_image_merge(Main *bmain, Image *dest, Image *source) +{ + /* sanity check */ + if (dest && source && dest != source) { + BLI_mutex_lock(static_cast(source->runtime.cache_mutex)); + BLI_mutex_lock(static_cast(dest->runtime.cache_mutex)); + + if (source->cache != nullptr) { + struct MovieCacheIter *iter; + iter = IMB_moviecacheIter_new(source->cache); + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + ImageCacheKey *key = static_cast(IMB_moviecacheIter_getUserKey(iter)); + imagecache_put(dest, key->index, ibuf); + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + + BLI_mutex_unlock(static_cast(dest->runtime.cache_mutex)); + BLI_mutex_unlock(static_cast(source->runtime.cache_mutex)); + + BKE_id_free(bmain, source); + } +} + +bool BKE_image_scale(Image *image, int width, int height) +{ + /* NOTE: We could be clever and scale all imbuf's + * but since some are mipmaps its not so simple. */ + + ImBuf *ibuf; + void *lock; + + ibuf = BKE_image_acquire_ibuf(image, nullptr, &lock); + + if (ibuf) { + IMB_scaleImBuf(ibuf, width, height); + BKE_image_mark_dirty(image, ibuf); + } + + BKE_image_release_ibuf(image, ibuf, lock); + + return (ibuf != nullptr); +} + +bool BKE_image_has_opengl_texture(Image *ima) +{ + for (int eye = 0; eye < 2; eye++) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != nullptr) { + return true; + } + } + } + } + return false; +} + +static int image_get_tile_number_from_iuser(Image *ima, const ImageUser *iuser) +{ + BLI_assert(ima != nullptr && ima->tiles.first); + ImageTile *tile = static_cast(ima->tiles.first); + return (iuser && iuser->tile) ? iuser->tile : tile->tile_number; +} + +ImageTile *BKE_image_get_tile(Image *ima, int tile_number) +{ + if (ima == nullptr) { + return nullptr; + } + + /* Tiles 0 and 1001 are a special case and refer to the first tile, typically + * coming from non-UDIM-aware code. */ + if (ELEM(tile_number, 0, 1001)) { + return static_cast(ima->tiles.first); + } + + /* Must have a tiled image and a valid tile number at this point. */ + if (ima->source != IMA_SRC_TILED || tile_number < 1001 || tile_number > IMA_UDIM_MAX) { + return nullptr; + } + + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + if (tile->tile_number == tile_number) { + return tile; + } + } + + return nullptr; +} + +ImageTile *BKE_image_get_tile_from_iuser(Image *ima, const ImageUser *iuser) +{ + return BKE_image_get_tile(ima, image_get_tile_number_from_iuser(ima, iuser)); +} + +int BKE_image_get_tile_from_pos(struct Image *ima, + const float uv[2], + float r_uv[2], + float r_ofs[2]) +{ + float local_ofs[2]; + if (r_ofs == nullptr) { + r_ofs = local_ofs; + } + + copy_v2_v2(r_uv, uv); + zero_v2(r_ofs); + + if ((ima->source != IMA_SRC_TILED) || uv[0] < 0.0f || uv[1] < 0.0f || uv[0] >= 10.0f) { + return 0; + } + + int ix = (int)uv[0]; + int iy = (int)uv[1]; + int tile_number = 1001 + 10 * iy + ix; + + if (BKE_image_get_tile(ima, tile_number) == nullptr) { + return 0; + } + r_ofs[0] = ix; + r_ofs[1] = iy; + sub_v2_v2(r_uv, r_ofs); + + return tile_number; +} + +int BKE_image_find_nearest_tile(const Image *image, const float co[2]) +{ + const float co_floor[2] = {floorf(co[0]), floorf(co[1])}; + /* Distance to the closest UDIM tile. */ + float dist_best_sq = FLT_MAX; + int tile_number_best = -1; + + LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { + const int tile_index = tile->tile_number - 1001; + /* Coordinates of the current tile. */ + const float tile_index_co[2] = {static_cast(tile_index % 10), + static_cast(tile_index / 10)}; + + if (equals_v2v2(co_floor, tile_index_co)) { + return tile->tile_number; + } + + /* Distance between co[2] and UDIM tile. */ + const float dist_sq = len_squared_v2v2(tile_index_co, co); + + if (dist_sq < dist_best_sq) { + dist_best_sq = dist_sq; + tile_number_best = tile->tile_number; + } + } + + return tile_number_best; +} + +static void image_init_color_management(Image *ima) +{ + ImBuf *ibuf; + char name[FILE_MAX]; + + BKE_image_user_file_path(nullptr, ima, name); + + /* will set input color space to image format default's */ + ibuf = IMB_loadiffname(name, IB_test | IB_alphamode_detect, ima->colorspace_settings.name); + + if (ibuf) { + if (ibuf->flags & IB_alphamode_premul) { + ima->alpha_mode = IMA_ALPHA_PREMUL; + } + else if (ibuf->flags & IB_alphamode_channel_packed) { + ima->alpha_mode = IMA_ALPHA_CHANNEL_PACKED; + } + else if (ibuf->flags & IB_alphamode_ignore) { + ima->alpha_mode = IMA_ALPHA_IGNORE; + } + else { + ima->alpha_mode = IMA_ALPHA_STRAIGHT; + } + + IMB_freeImBuf(ibuf); + } +} + +char BKE_image_alpha_mode_from_extension_ex(const char *filepath) +{ + if (BLI_path_extension_check_n(filepath, ".exr", ".cin", ".dpx", ".hdr", nullptr)) { + return IMA_ALPHA_PREMUL; + } + + return IMA_ALPHA_STRAIGHT; +} + +void BKE_image_alpha_mode_from_extension(Image *image) +{ + image->alpha_mode = BKE_image_alpha_mode_from_extension_ex(image->filepath); +} + +Image *BKE_image_load(Main *bmain, const char *filepath) +{ + Image *ima; + int file; + char str[FILE_MAX]; + + STRNCPY(str, filepath); + BLI_path_abs(str, BKE_main_blendfile_path(bmain)); + + /* exists? */ + file = BLI_open(str, O_BINARY | O_RDONLY, 0); + if (file == -1) { + if (!BKE_image_tile_filepath_exists(str)) { + return nullptr; + } + } + else { + close(file); + } + + ima = image_alloc(bmain, BLI_path_basename(filepath), IMA_SRC_FILE, IMA_TYPE_IMAGE); + STRNCPY(ima->filepath, filepath); + + if (BLI_path_extension_check_array(filepath, imb_ext_movie)) { + ima->source = IMA_SRC_MOVIE; + } + + image_init_color_management(ima); + + return ima; +} + +Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exists) +{ + Image *ima; + char str[FILE_MAX], strtest[FILE_MAX]; + + STRNCPY(str, filepath); + BLI_path_abs(str, bmain->filepath); + + /* first search an identical filepath */ + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + if (!ELEM(ima->source, IMA_SRC_VIEWER, IMA_SRC_GENERATED)) { + STRNCPY(strtest, ima->filepath); + BLI_path_abs(strtest, ID_BLEND_PATH(bmain, &ima->id)); + + if (BLI_path_cmp(strtest, str) == 0) { + if ((BKE_image_has_anim(ima) == false) || (ima->id.us == 0)) { + id_us_plus(&ima->id); /* officially should not, it doesn't link here! */ + if (r_exists) { + *r_exists = true; + } + return ima; + } + } + } + } + + if (r_exists) { + *r_exists = false; + } + return BKE_image_load(bmain, filepath); +} + +Image *BKE_image_load_exists(Main *bmain, const char *filepath) +{ + return BKE_image_load_exists_ex(bmain, filepath, nullptr); +} + +struct ImageFillData { + short gen_type; + uint width; + uint height; + unsigned char *rect; + float *rect_float; + float fill_color[4]; +}; + +static void image_buf_fill_isolated(void *usersata_v) +{ + ImageFillData *usersata = static_cast(usersata_v); + + const short gen_type = usersata->gen_type; + const uint width = usersata->width; + const uint height = usersata->height; + + unsigned char *rect = usersata->rect; + float *rect_float = usersata->rect_float; + + switch (gen_type) { + case IMA_GENTYPE_GRID: + BKE_image_buf_fill_checker(rect, rect_float, width, height); + break; + case IMA_GENTYPE_GRID_COLOR: + BKE_image_buf_fill_checker_color(rect, rect_float, width, height); + break; + default: + BKE_image_buf_fill_color(rect, rect_float, width, height, usersata->fill_color); + break; + } +} + +static ImBuf *add_ibuf_size(unsigned int width, + unsigned int height, + const char *name, + int depth, + int floatbuf, + short gen_type, + const float color[4], + ColorManagedColorspaceSettings *colorspace_settings) +{ + ImBuf *ibuf; + unsigned char *rect = nullptr; + float *rect_float = nullptr; + float fill_color[4]; + + if (floatbuf) { + ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat); + + if (colorspace_settings->name[0] == '\0') { + const char *colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_DEFAULT_FLOAT); + + STRNCPY(colorspace_settings->name, colorspace); + } + + if (ibuf != nullptr) { + rect_float = ibuf->rect_float; + IMB_colormanagement_check_is_data(ibuf, colorspace_settings->name); + } + + if (IMB_colormanagement_space_name_is_data(colorspace_settings->name)) { + copy_v4_v4(fill_color, color); + } + else { + /* The input color here should ideally be linear already, but for now + * we just convert and postpone breaking the API for later. */ + srgb_to_linearrgb_v4(fill_color, color); + } + } + else { + ibuf = IMB_allocImBuf(width, height, depth, IB_rect); + + if (colorspace_settings->name[0] == '\0') { + const char *colorspace = IMB_colormanagement_role_colorspace_name_get( + COLOR_ROLE_DEFAULT_BYTE); + + STRNCPY(colorspace_settings->name, colorspace); + } + + if (ibuf != nullptr) { + rect = (unsigned char *)ibuf->rect; + IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace_settings->name); + } + + copy_v4_v4(fill_color, color); + } + + if (!ibuf) { + return nullptr; + } + + STRNCPY(ibuf->name, name); + + ImageFillData data; + + data.gen_type = gen_type; + data.width = width; + data.height = height; + data.rect = rect; + data.rect_float = rect_float; + copy_v4_v4(data.fill_color, fill_color); + + BLI_task_isolate(image_buf_fill_isolated, &data); + + return ibuf; +} + +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, + const bool is_data, + const bool tiled) +{ + /* on save, type is changed to FILE in editsima.c */ + Image *ima; + if (tiled) { + ima = image_alloc(bmain, name, IMA_SRC_TILED, IMA_TYPE_IMAGE); + } + else { + ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); + } + if (ima == nullptr) { + return nullptr; + } + + int view_id; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + // STRNCPY(ima->filepath, name); /* don't do this, this writes in ain invalid filepath! */ + ima->gen_x = width; + ima->gen_y = height; + ima->gen_type = gen_type; + ima->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0); + ima->gen_depth = depth; + copy_v4_v4(ima->gen_color, color); + + if (is_data) { + STRNCPY(ima->colorspace_settings.name, + IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DATA)); + } + + for (view_id = 0; view_id < 2; view_id++) { + ImBuf *ibuf; + ibuf = add_ibuf_size( + width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings); + int index = tiled ? 0 : IMA_NO_INDEX; + int entry = tiled ? 1001 : 0; + image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry); + + /* 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], ""); + } + + return ima; +} + +Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) +{ + /* on save, type is changed to FILE in editsima.c */ + Image *ima; + + if (name == nullptr) { + name = BLI_path_basename(ibuf->name); + } + + ima = image_alloc(bmain, name, IMA_SRC_FILE, IMA_TYPE_IMAGE); + + if (ima) { + STRNCPY(ima->filepath, ibuf->name); + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + } + + return ima; +} + +/* Pack image buffer to memory as PNG or EXR. */ +static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath) +{ + ibuf->ftype = (ibuf->rect_float) ? IMB_FTYPE_OPENEXR : IMB_FTYPE_PNG; + + IMB_saveiff(ibuf, filepath, IB_rect | IB_mem); + + if (ibuf->encodedbuffer == nullptr) { + CLOG_STR_ERROR(&LOG, "memory save for pack error"); + IMB_freeImBuf(ibuf); + image_free_packedfiles(ima); + return false; + } + + ImagePackedFile *imapf; + PackedFile *pf = MEM_cnew("PackedFile"); + + pf->data = ibuf->encodedbuffer; + pf->size = ibuf->encodedsize; + + imapf = static_cast(MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile")); + STRNCPY(imapf->filepath, filepath); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + + ibuf->encodedbuffer = nullptr; + ibuf->encodedsize = 0; + ibuf->userflags &= ~IB_BITMAPDIRTY; + + return true; +} + +bool BKE_image_memorypack(Image *ima) +{ + bool ok = true; + + image_free_packedfiles(ima); + + if (BKE_image_is_multiview(ima)) { + /* Store each view as a separate packed files with R_IMF_VIEWS_INDIVIDUAL. */ + ImageView *iv; + int i; + + for (i = 0, iv = static_cast(ima->views.first); iv; + iv = static_cast(iv->next), i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, nullptr); + + if (!ibuf) { + ok = false; + break; + } + + /* 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], ""); + } + + ok = ok && image_memorypack_imbuf(ima, ibuf, iv->filepath); + IMB_freeImBuf(ibuf); + } + + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; + } + else { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); + + if (ibuf) { + ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); + IMB_freeImBuf(ibuf); + } + else { + ok = false; + } + } + + if (ok && ima->source == IMA_SRC_GENERATED) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + + return ok; +} + +void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) +{ + const int totfiles = image_num_files(ima); + + if (totfiles == 1) { + ImagePackedFile *imapf = static_cast( + MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); + BLI_addtail(&ima->packedfiles, imapf); + imapf->packedfile = BKE_packedfile_new(reports, ima->filepath, basepath); + if (imapf->packedfile) { + STRNCPY(imapf->filepath, ima->filepath); + } + else { + BLI_freelinkN(&ima->packedfiles, imapf); + } + } + else { + for (ImageView *iv = static_cast(ima->views.first); iv; iv = iv->next) { + ImagePackedFile *imapf = static_cast( + MEM_mallocN(sizeof(ImagePackedFile), "Image packed file")); + BLI_addtail(&ima->packedfiles, imapf); + + imapf->packedfile = BKE_packedfile_new(reports, iv->filepath, basepath); + if (imapf->packedfile) { + STRNCPY(imapf->filepath, iv->filepath); + } + else { + BLI_freelinkN(&ima->packedfiles, imapf); + } + } + } +} + +void BKE_image_packfiles_from_mem(ReportList *reports, + Image *ima, + char *data, + const size_t data_len) +{ + const int totfiles = image_num_files(ima); + + if (totfiles != 1) { + BKE_report(reports, RPT_ERROR, "Cannot pack multiview images from raw data currently..."); + } + else { + ImagePackedFile *imapf = static_cast( + MEM_mallocN(sizeof(ImagePackedFile), __func__)); + BLI_addtail(&ima->packedfiles, imapf); + imapf->packedfile = BKE_packedfile_new_from_memory(data, data_len); + STRNCPY(imapf->filepath, ima->filepath); + } +} + +void BKE_image_tag_time(Image *ima) +{ + ima->lastused = PIL_check_seconds_timer_i(); +} + +static uintptr_t image_mem_size(Image *image) +{ + uintptr_t size = 0; + + /* viewers have memory depending on other rules, has no valid rect pointer */ + if (image->source == IMA_SRC_VIEWER) { + return 0; + } + + BLI_mutex_lock(static_cast(image->runtime.cache_mutex)); + + if (image->cache != nullptr) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + IMB_moviecacheIter_step(iter); + if (ibuf == nullptr) { + continue; + } + ImBuf *ibufm; + int level; + + if (ibuf->rect) { + size += MEM_allocN_len(ibuf->rect); + } + if (ibuf->rect_float) { + size += MEM_allocN_len(ibuf->rect_float); + } + + for (level = 0; level < IMB_MIPMAP_LEVELS; level++) { + ibufm = ibuf->mipmap[level]; + if (ibufm) { + if (ibufm->rect) { + size += MEM_allocN_len(ibufm->rect); + } + if (ibufm->rect_float) { + size += MEM_allocN_len(ibufm->rect_float); + } + } + } + } + IMB_moviecacheIter_free(iter); + } + + BLI_mutex_unlock(static_cast(image->runtime.cache_mutex)); + + return size; +} + +void BKE_image_print_memlist(Main *bmain) +{ + Image *ima; + uintptr_t size, totsize = 0; + + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + totsize += image_mem_size(ima); + } + + printf("\ntotal image memory len: %.3f MB\n", (double)totsize / (double)(1024 * 1024)); + + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + size = image_mem_size(ima); + + if (size) { + printf("%s len: %.3f MB\n", ima->id.name + 2, (double)size / (double)(1024 * 1024)); + } + } +} + +static bool imagecache_check_dirty(ImBuf *ibuf, void *UNUSED(userkey), void *UNUSED(userdata)) +{ + if (ibuf == nullptr) { + return false; + } + return (ibuf->userflags & IB_BITMAPDIRTY) == 0; +} + +void BKE_image_free_all_textures(Main *bmain) +{ +#undef CHECK_FREED_SIZE + + Tex *tex; + Image *ima; +#ifdef CHECK_FREED_SIZE + uintptr_t tot_freed_size = 0; +#endif + + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + ima->id.tag &= ~LIB_TAG_DOIT; + } + + for (tex = static_cast(bmain->textures.first); tex; + tex = static_cast(tex->id.next)) { + if (tex->ima) { + tex->ima->id.tag |= LIB_TAG_DOIT; + } + } + + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + if (ima->cache && (ima->id.tag & LIB_TAG_DOIT)) { +#ifdef CHECK_FREED_SIZE + uintptr_t old_size = image_mem_size(ima); +#endif + + IMB_moviecache_cleanup(ima->cache, imagecache_check_dirty, nullptr); + +#ifdef CHECK_FREED_SIZE + tot_freed_size += old_size - image_mem_size(ima); +#endif + } + } +#ifdef CHECK_FREED_SIZE + printf("%s: freed total %lu MB\n", __func__, tot_freed_size / (1024 * 1024)); +#endif +} + +static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void *userdata) +{ + if (ibuf == nullptr) { + return true; + } + int except_frame = *(int *)userdata; + return (ibuf->userflags & IB_BITMAPDIRTY) == 0 && (ibuf->index != IMA_NO_INDEX) && + (except_frame != IMA_INDEX_ENTRY(ibuf->index)); +} + +void BKE_image_free_anim_ibufs(Image *ima, int except_frame) +{ + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + if (ima->cache != nullptr) { + IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame); + } + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); +} + +void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) +{ + Image *ima; + + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + if (BKE_image_is_animated(ima)) { + BKE_image_free_anim_ibufs(ima, cfra); + } + } +} + +/* *********** READ AND WRITE ************** */ + +int BKE_image_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options) +{ + memset(r_options, 0, sizeof(*r_options)); + + if (imtype == R_IMF_IMTYPE_TARGA) { + return IMB_FTYPE_TGA; + } + if (imtype == R_IMF_IMTYPE_RAWTGA) { + r_options->flag = RAWTGA; + return IMB_FTYPE_TGA; + } + if (imtype == R_IMF_IMTYPE_IRIS) { + return IMB_FTYPE_IMAGIC; + } +#ifdef WITH_HDR + if (imtype == R_IMF_IMTYPE_RADHDR) { + return IMB_FTYPE_RADHDR; + } +#endif + if (imtype == R_IMF_IMTYPE_PNG) { + r_options->quality = 15; + return IMB_FTYPE_PNG; + } +#ifdef WITH_DDS + if (imtype == R_IMF_IMTYPE_DDS) { + return IMB_FTYPE_DDS; + } +#endif + if (imtype == R_IMF_IMTYPE_BMP) { + return IMB_FTYPE_BMP; + } +#ifdef WITH_TIFF + if (imtype == R_IMF_IMTYPE_TIFF) { + return IMB_FTYPE_TIF; + } +#endif + if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + return IMB_FTYPE_OPENEXR; + } +#ifdef WITH_CINEON + if (imtype == R_IMF_IMTYPE_CINEON) { + return IMB_FTYPE_CINEON; + } + if (imtype == R_IMF_IMTYPE_DPX) { + return IMB_FTYPE_DPX; + } +#endif +#ifdef WITH_OPENJPEG + if (imtype == R_IMF_IMTYPE_JP2) { + r_options->flag |= JP2_JP2; + r_options->quality = 90; + return IMB_FTYPE_JP2; + } +#endif + + r_options->quality = 90; + return IMB_FTYPE_JPG; +} + +char BKE_image_ftype_to_imtype(const int ftype, const ImbFormatOptions *options) +{ + if (ftype == IMB_FTYPE_NONE) { + return R_IMF_IMTYPE_TARGA; + } + if (ftype == IMB_FTYPE_IMAGIC) { + return R_IMF_IMTYPE_IRIS; + } +#ifdef WITH_HDR + if (ftype == IMB_FTYPE_RADHDR) { + return R_IMF_IMTYPE_RADHDR; + } +#endif + if (ftype == IMB_FTYPE_PNG) { + return R_IMF_IMTYPE_PNG; + } +#ifdef WITH_DDS + if (ftype == IMB_FTYPE_DDS) { + return R_IMF_IMTYPE_DDS; + } +#endif + if (ftype == IMB_FTYPE_BMP) { + return R_IMF_IMTYPE_BMP; + } +#ifdef WITH_TIFF + if (ftype == IMB_FTYPE_TIF) { + return R_IMF_IMTYPE_TIFF; + } +#endif + if (ftype == IMB_FTYPE_OPENEXR) { + return R_IMF_IMTYPE_OPENEXR; + } +#ifdef WITH_CINEON + if (ftype == IMB_FTYPE_CINEON) { + return R_IMF_IMTYPE_CINEON; + } + if (ftype == IMB_FTYPE_DPX) { + return R_IMF_IMTYPE_DPX; + } +#endif + if (ftype == IMB_FTYPE_TGA) { + if (options && (options->flag & RAWTGA)) { + return R_IMF_IMTYPE_RAWTGA; + } + + return R_IMF_IMTYPE_TARGA; + } +#ifdef WITH_OPENJPEG + if (ftype == IMB_FTYPE_JP2) { + return R_IMF_IMTYPE_JP2; + } +#endif + + return R_IMF_IMTYPE_JPEG90; +} + +bool BKE_imtype_is_movie(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_AVIRAW: + case R_IMF_IMTYPE_AVIJPEG: + case R_IMF_IMTYPE_FFMPEG: + case R_IMF_IMTYPE_H264: + case R_IMF_IMTYPE_THEORA: + case R_IMF_IMTYPE_XVID: + return true; + } + return false; +} + +bool BKE_imtype_supports_zbuf(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_IRIZ: + case R_IMF_IMTYPE_OPENEXR: /* but not R_IMF_IMTYPE_MULTILAYER */ + return true; + } + return false; +} + +bool BKE_imtype_supports_compress(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_PNG: + return true; + } + return false; +} + +bool BKE_imtype_supports_quality(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_JPEG90: + case R_IMF_IMTYPE_JP2: + case R_IMF_IMTYPE_AVIJPEG: + return true; + } + return false; +} + +bool BKE_imtype_requires_linear_float(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_CINEON: + case R_IMF_IMTYPE_DPX: + case R_IMF_IMTYPE_RADHDR: + case R_IMF_IMTYPE_OPENEXR: + case R_IMF_IMTYPE_MULTILAYER: + return true; + } + return false; +} + +char BKE_imtype_valid_channels(const char imtype, bool write_file) +{ + char chan_flag = IMA_CHAN_FLAG_RGB; /* Assume all support RGB. */ + + /* Alpha. */ + switch (imtype) { + case R_IMF_IMTYPE_BMP: + if (write_file) { + break; + } + ATTR_FALLTHROUGH; + case R_IMF_IMTYPE_TARGA: + case R_IMF_IMTYPE_RAWTGA: + case R_IMF_IMTYPE_IRIS: + case R_IMF_IMTYPE_PNG: + case R_IMF_IMTYPE_TIFF: + case R_IMF_IMTYPE_OPENEXR: + case R_IMF_IMTYPE_MULTILAYER: + case R_IMF_IMTYPE_DDS: + case R_IMF_IMTYPE_JP2: + case R_IMF_IMTYPE_DPX: + chan_flag |= IMA_CHAN_FLAG_ALPHA; + break; + } + + /* BW. */ + switch (imtype) { + case R_IMF_IMTYPE_BMP: + case R_IMF_IMTYPE_PNG: + case R_IMF_IMTYPE_JPEG90: + case R_IMF_IMTYPE_TARGA: + case R_IMF_IMTYPE_RAWTGA: + case R_IMF_IMTYPE_TIFF: + case R_IMF_IMTYPE_IRIS: + chan_flag |= IMA_CHAN_FLAG_BW; + break; + } + + return chan_flag; +} + +char BKE_imtype_valid_depths(const char imtype) +{ + switch (imtype) { + case R_IMF_IMTYPE_RADHDR: + return R_IMF_CHAN_DEPTH_32; + case R_IMF_IMTYPE_TIFF: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_OPENEXR: + return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; + case R_IMF_IMTYPE_MULTILAYER: + return R_IMF_CHAN_DEPTH_16 | R_IMF_CHAN_DEPTH_32; + /* eeh, cineon does some strange 10bits per channel */ + case R_IMF_IMTYPE_DPX: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_10 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_CINEON: + return R_IMF_CHAN_DEPTH_10; + case R_IMF_IMTYPE_JP2: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_12 | R_IMF_CHAN_DEPTH_16; + case R_IMF_IMTYPE_PNG: + return R_IMF_CHAN_DEPTH_8 | R_IMF_CHAN_DEPTH_16; + /* most formats are 8bit only */ + default: + return R_IMF_CHAN_DEPTH_8; + } +} + +char BKE_imtype_from_arg(const char *imtype_arg) +{ + if (STREQ(imtype_arg, "TGA")) { + return R_IMF_IMTYPE_TARGA; + } + if (STREQ(imtype_arg, "IRIS")) { + return R_IMF_IMTYPE_IRIS; + } +#ifdef WITH_DDS + if (STREQ(imtype_arg, "DDS")) { + return R_IMF_IMTYPE_DDS; + } +#endif + if (STREQ(imtype_arg, "JPEG")) { + return R_IMF_IMTYPE_JPEG90; + } + if (STREQ(imtype_arg, "IRIZ")) { + return R_IMF_IMTYPE_IRIZ; + } + if (STREQ(imtype_arg, "RAWTGA")) { + return R_IMF_IMTYPE_RAWTGA; + } + if (STREQ(imtype_arg, "AVIRAW")) { + return R_IMF_IMTYPE_AVIRAW; + } + if (STREQ(imtype_arg, "AVIJPEG")) { + return R_IMF_IMTYPE_AVIJPEG; + } + if (STREQ(imtype_arg, "PNG")) { + return R_IMF_IMTYPE_PNG; + } + if (STREQ(imtype_arg, "BMP")) { + return R_IMF_IMTYPE_BMP; + } +#ifdef WITH_HDR + if (STREQ(imtype_arg, "HDR")) { + return R_IMF_IMTYPE_RADHDR; + } +#endif +#ifdef WITH_TIFF + if (STREQ(imtype_arg, "TIFF")) { + return R_IMF_IMTYPE_TIFF; + } +#endif +#ifdef WITH_OPENEXR + if (STREQ(imtype_arg, "OPEN_EXR")) { + return R_IMF_IMTYPE_OPENEXR; + } + if (STREQ(imtype_arg, "OPEN_EXR_MULTILAYER")) { + return R_IMF_IMTYPE_MULTILAYER; + } + if (STREQ(imtype_arg, "EXR")) { + return R_IMF_IMTYPE_OPENEXR; + } + if (STREQ(imtype_arg, "MULTILAYER")) { + return R_IMF_IMTYPE_MULTILAYER; + } +#endif + if (STREQ(imtype_arg, "FFMPEG")) { + return R_IMF_IMTYPE_FFMPEG; + } +#ifdef WITH_CINEON + if (STREQ(imtype_arg, "CINEON")) { + return R_IMF_IMTYPE_CINEON; + } + if (STREQ(imtype_arg, "DPX")) { + return R_IMF_IMTYPE_DPX; + } +#endif +#ifdef WITH_OPENJPEG + if (STREQ(imtype_arg, "JP2")) { + return R_IMF_IMTYPE_JP2; + } +#endif + + return R_IMF_IMTYPE_INVALID; +} + +static bool do_add_image_extension(char *string, + const char imtype, + const ImageFormatData *im_format) +{ + const char *extension = nullptr; + const char *extension_test; + (void)im_format; /* may be unused, depends on build options */ + + if (imtype == R_IMF_IMTYPE_IRIS) { + if (!BLI_path_extension_check(string, extension_test = ".rgb")) { + extension = extension_test; + } + } + else if (imtype == R_IMF_IMTYPE_IRIZ) { + if (!BLI_path_extension_check(string, extension_test = ".rgb")) { + extension = extension_test; + } + } +#ifdef WITH_HDR + else if (imtype == R_IMF_IMTYPE_RADHDR) { + if (!BLI_path_extension_check(string, extension_test = ".hdr")) { + extension = extension_test; + } + } +#endif + else if (ELEM(imtype, + R_IMF_IMTYPE_PNG, + R_IMF_IMTYPE_FFMPEG, + R_IMF_IMTYPE_H264, + R_IMF_IMTYPE_THEORA, + R_IMF_IMTYPE_XVID)) { + if (!BLI_path_extension_check(string, extension_test = ".png")) { + extension = extension_test; + } + } +#ifdef WITH_DDS + else if (imtype == R_IMF_IMTYPE_DDS) { + if (!BLI_path_extension_check(string, extension_test = ".dds")) { + extension = extension_test; + } + } +#endif + else if (ELEM(imtype, R_IMF_IMTYPE_TARGA, R_IMF_IMTYPE_RAWTGA)) { + if (!BLI_path_extension_check(string, extension_test = ".tga")) { + extension = extension_test; + } + } + else if (imtype == R_IMF_IMTYPE_BMP) { + if (!BLI_path_extension_check(string, extension_test = ".bmp")) { + extension = extension_test; + } + } +#ifdef WITH_TIFF + else if (imtype == R_IMF_IMTYPE_TIFF) { + if (!BLI_path_extension_check_n(string, extension_test = ".tif", ".tiff", nullptr)) { + extension = extension_test; + } + } +#endif +#ifdef WITH_OPENIMAGEIO + else if (imtype == R_IMF_IMTYPE_PSD) { + if (!BLI_path_extension_check(string, extension_test = ".psd")) { + extension = extension_test; + } + } +#endif +#ifdef WITH_OPENEXR + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + if (!BLI_path_extension_check(string, extension_test = ".exr")) { + extension = extension_test; + } + } +#endif +#ifdef WITH_CINEON + else if (imtype == R_IMF_IMTYPE_CINEON) { + if (!BLI_path_extension_check(string, extension_test = ".cin")) { + extension = extension_test; + } + } + else if (imtype == R_IMF_IMTYPE_DPX) { + if (!BLI_path_extension_check(string, extension_test = ".dpx")) { + extension = extension_test; + } + } +#endif +#ifdef WITH_OPENJPEG + else if (imtype == R_IMF_IMTYPE_JP2) { + if (im_format) { + if (im_format->jp2_codec == R_IMF_JP2_CODEC_JP2) { + if (!BLI_path_extension_check(string, extension_test = ".jp2")) { + extension = extension_test; + } + } + else if (im_format->jp2_codec == R_IMF_JP2_CODEC_J2K) { + if (!BLI_path_extension_check(string, extension_test = ".j2c")) { + extension = extension_test; + } + } + else { + BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); + } + } + else { + if (!BLI_path_extension_check(string, extension_test = ".jp2")) { + extension = extension_test; + } + } + } +#endif + else { // R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG, R_IMF_IMTYPE_JPEG90 etc + if (!(BLI_path_extension_check_n(string, extension_test = ".jpg", ".jpeg", nullptr))) { + extension = extension_test; + } + } + + if (extension) { + /* prefer this in many cases to avoid .png.tga, but in certain cases it breaks */ + /* remove any other known image extension */ + if (BLI_path_extension_check_array(string, imb_ext_image)) { + return BLI_path_extension_replace(string, FILE_MAX, extension); + } + + return BLI_path_extension_ensure(string, FILE_MAX, extension); + } + + return false; +} + +int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) +{ + return do_add_image_extension(string, im_format->imtype, im_format); +} + +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) +{ + return do_add_image_extension(string, imtype, nullptr); +} + +void BKE_imformat_defaults(ImageFormatData *im_format) +{ + memset(im_format, 0, sizeof(*im_format)); + im_format->planes = R_IMF_PLANES_RGBA; + im_format->imtype = R_IMF_IMTYPE_PNG; + im_format->depth = R_IMF_CHAN_DEPTH_8; + im_format->quality = 90; + im_format->compress = 15; + + BKE_color_managed_display_settings_init(&im_format->display_settings); + BKE_color_managed_view_settings_init_default(&im_format->view_settings, + &im_format->display_settings); +} + +void BKE_imbuf_to_image_format(struct ImageFormatData *im_format, const ImBuf *imbuf) +{ + int ftype = imbuf->ftype; + int custom_flags = imbuf->foptions.flag; + char quality = imbuf->foptions.quality; + + BKE_imformat_defaults(im_format); + + /* file type */ + + if (ftype == IMB_FTYPE_IMAGIC) { + im_format->imtype = R_IMF_IMTYPE_IRIS; + } +#ifdef WITH_HDR + else if (ftype == IMB_FTYPE_RADHDR) { + im_format->imtype = R_IMF_IMTYPE_RADHDR; + } +#endif + else if (ftype == IMB_FTYPE_PNG) { + im_format->imtype = R_IMF_IMTYPE_PNG; + + if (custom_flags & PNG_16BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + + im_format->compress = quality; + } + +#ifdef WITH_DDS + else if (ftype == IMB_FTYPE_DDS) { + im_format->imtype = R_IMF_IMTYPE_DDS; + } +#endif + else if (ftype == IMB_FTYPE_BMP) { + im_format->imtype = R_IMF_IMTYPE_BMP; + } +#ifdef WITH_TIFF + else if (ftype == IMB_FTYPE_TIF) { + im_format->imtype = R_IMF_IMTYPE_TIFF; + if (custom_flags & TIF_16BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + if (custom_flags & TIF_COMPRESS_NONE) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_NONE; + } + if (custom_flags & TIF_COMPRESS_DEFLATE) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_DEFLATE; + } + if (custom_flags & TIF_COMPRESS_LZW) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_LZW; + } + if (custom_flags & TIF_COMPRESS_PACKBITS) { + im_format->tiff_codec = R_IMF_TIFF_CODEC_PACKBITS; + } + } +#endif + +#ifdef WITH_OPENEXR + else if (ftype == IMB_FTYPE_OPENEXR) { + im_format->imtype = R_IMF_IMTYPE_OPENEXR; + if (custom_flags & OPENEXR_HALF) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + if (custom_flags & OPENEXR_COMPRESS) { + im_format->exr_codec = R_IMF_EXR_CODEC_ZIP; /* Can't determine compression */ + } + if (imbuf->zbuf_float) { + im_format->flag |= R_IMF_FLAG_ZBUF; + } + } +#endif + +#ifdef WITH_CINEON + else if (ftype == IMB_FTYPE_CINEON) { + im_format->imtype = R_IMF_IMTYPE_CINEON; + } + else if (ftype == IMB_FTYPE_DPX) { + im_format->imtype = R_IMF_IMTYPE_DPX; + } +#endif + else if (ftype == IMB_FTYPE_TGA) { + if (custom_flags & RAWTGA) { + im_format->imtype = R_IMF_IMTYPE_RAWTGA; + } + else { + im_format->imtype = R_IMF_IMTYPE_TARGA; + } + } +#ifdef WITH_OPENJPEG + else if (ftype == IMB_FTYPE_JP2) { + im_format->imtype = R_IMF_IMTYPE_JP2; + im_format->quality = quality; + + if (custom_flags & JP2_16BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_16; + } + else if (custom_flags & JP2_12BIT) { + im_format->depth = R_IMF_CHAN_DEPTH_12; + } + + if (custom_flags & JP2_YCC) { + im_format->jp2_flag |= R_IMF_JP2_FLAG_YCC; + } + + if (custom_flags & JP2_CINE) { + im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_PRESET; + if (custom_flags & JP2_CINE_48FPS) { + im_format->jp2_flag |= R_IMF_JP2_FLAG_CINE_48; + } + } + + if (custom_flags & JP2_JP2) { + im_format->jp2_codec = R_IMF_JP2_CODEC_JP2; + } + else if (custom_flags & JP2_J2K) { + im_format->jp2_codec = R_IMF_JP2_CODEC_J2K; + } + else { + BLI_assert_msg(0, "Unsupported jp2 codec was specified in file type"); + } + } +#endif + + else { + im_format->imtype = R_IMF_IMTYPE_JPEG90; + im_format->quality = quality; + } + + /* planes */ + im_format->planes = imbuf->planes; +} + +#define STAMP_NAME_SIZE ((MAX_ID_NAME - 2) + 16) +/* could allow access externally - 512 is for long names, + * STAMP_NAME_SIZE is for id names, allowing them some room for description */ +struct StampDataCustomField { + struct StampDataCustomField *next, *prev; + /* TODO(sergey): Think of better size here, maybe dynamically allocated even. */ + char key[512]; + char *value; + /* TODO(sergey): Support non-string values. */ +}; + +struct StampData { + char file[512]; + char note[512]; + char date[512]; + char marker[512]; + char time[512]; + char frame[512]; + char frame_range[512]; + char camera[STAMP_NAME_SIZE]; + char cameralens[STAMP_NAME_SIZE]; + char scene[STAMP_NAME_SIZE]; + char strip[STAMP_NAME_SIZE]; + char rendertime[STAMP_NAME_SIZE]; + char memory[STAMP_NAME_SIZE]; + char hostname[512]; + + /* Custom fields are used to put extra meta information header from render + * engine to the result image. + * + * NOTE: This fields are not stamped onto the image. At least for now. + */ + ListBase custom_fields; +}; +#undef STAMP_NAME_SIZE + +/** + * \param do_prefix: Include a label like "File ", "Date ", etc. in the stamp data strings. + * \param use_dynamic: Also include data that can change on a per-frame basis. + */ +static void stampdata( + const Scene *scene, Object *camera, StampData *stamp_data, int do_prefix, bool use_dynamic) +{ + char text[256]; + struct tm *tl; + time_t t; + + if (scene->r.stamp & R_STAMP_FILENAME) { + const char *blendfile_path = BKE_main_blendfile_path_from_global(); + SNPRINTF(stamp_data->file, + do_prefix ? "File %s" : "%s", + (blendfile_path[0] != '\0') ? blendfile_path : ""); + } + else { + stamp_data->file[0] = '\0'; + } + + if (scene->r.stamp & R_STAMP_NOTE) { + /* Never do prefix for Note */ + SNPRINTF(stamp_data->note, "%s", scene->r.stamp_udata); + } + else { + stamp_data->note[0] = '\0'; + } + + if (scene->r.stamp & R_STAMP_DATE) { + t = time(nullptr); + tl = localtime(&t); + SNPRINTF(text, + "%04d/%02d/%02d %02d:%02d:%02d", + tl->tm_year + 1900, + tl->tm_mon + 1, + tl->tm_mday, + tl->tm_hour, + tl->tm_min, + tl->tm_sec); + SNPRINTF(stamp_data->date, do_prefix ? "Date %s" : "%s", text); + } + else { + stamp_data->date[0] = '\0'; + } + + if (use_dynamic && scene->r.stamp & R_STAMP_MARKER) { + const char *name = BKE_scene_find_last_marker_name(scene, CFRA); + + if (name) { + STRNCPY(text, name); + } + else { + STRNCPY(text, ""); + } + + SNPRINTF(stamp_data->marker, do_prefix ? "Marker %s" : "%s", text); + } + else { + stamp_data->marker[0] = '\0'; + } + + if (use_dynamic && scene->r.stamp & R_STAMP_TIME) { + const short timecode_style = USER_TIMECODE_SMPTE_FULL; + BLI_timecode_string_from_time( + text, sizeof(text), 0, FRA2TIME(scene->r.cfra), FPS, timecode_style); + SNPRINTF(stamp_data->time, do_prefix ? "Timecode %s" : "%s", text); + } + else { + stamp_data->time[0] = '\0'; + } + + if (use_dynamic && scene->r.stamp & R_STAMP_FRAME) { + char fmtstr[32]; + int digits = 1; + + if (scene->r.efra > 9) { + digits = integer_digits_i(scene->r.efra); + } + + SNPRINTF(fmtstr, do_prefix ? "Frame %%0%di" : "%%0%di", digits); + SNPRINTF(stamp_data->frame, fmtstr, scene->r.cfra); + } + else { + stamp_data->frame[0] = '\0'; + } + + if (scene->r.stamp & R_STAMP_FRAME_RANGE) { + SNPRINTF(stamp_data->frame_range, + do_prefix ? "Frame Range %d:%d" : "%d:%d", + scene->r.sfra, + scene->r.efra); + } + else { + stamp_data->frame_range[0] = '\0'; + } + + if (use_dynamic && scene->r.stamp & R_STAMP_CAMERA) { + SNPRINTF(stamp_data->camera, + do_prefix ? "Camera %s" : "%s", + camera ? camera->id.name + 2 : ""); + } + else { + stamp_data->camera[0] = '\0'; + } + + if (use_dynamic && scene->r.stamp & R_STAMP_CAMERALENS) { + if (camera && camera->type == OB_CAMERA) { + SNPRINTF(text, "%.2f", ((Camera *)camera->data)->lens); + } + else { + STRNCPY(text, ""); + } + + SNPRINTF(stamp_data->cameralens, do_prefix ? "Lens %s" : "%s", text); + } + else { + stamp_data->cameralens[0] = '\0'; + } + + if (scene->r.stamp & R_STAMP_SCENE) { + SNPRINTF(stamp_data->scene, do_prefix ? "Scene %s" : "%s", scene->id.name + 2); + } + else { + stamp_data->scene[0] = '\0'; + } + + if (use_dynamic && scene->r.stamp & R_STAMP_SEQSTRIP) { + const Sequence *seq = SEQ_get_topmost_sequence(scene, scene->r.cfra); + + if (seq) { + STRNCPY(text, seq->name + 2); + } + else { + STRNCPY(text, ""); + } + + SNPRINTF(stamp_data->strip, do_prefix ? "Strip %s" : "%s", text); + } + else { + stamp_data->strip[0] = '\0'; + } + + { + Render *re = RE_GetSceneRender(scene); + RenderStats *stats = re ? RE_GetStats(re) : nullptr; + + if (use_dynamic && stats && (scene->r.stamp & R_STAMP_RENDERTIME)) { + BLI_timecode_string_from_time_simple(text, sizeof(text), stats->lastframetime); + + SNPRINTF(stamp_data->rendertime, do_prefix ? "RenderTime %s" : "%s", text); + } + else { + stamp_data->rendertime[0] = '\0'; + } + + if (use_dynamic && stats && (scene->r.stamp & R_STAMP_MEMORY)) { + SNPRINTF(stamp_data->memory, do_prefix ? "Peak Memory %.2fM" : "%.2fM", stats->mem_peak); + } + else { + stamp_data->memory[0] = '\0'; + } + } + if (scene->r.stamp & R_STAMP_FRAME_RANGE) { + SNPRINTF(stamp_data->frame_range, + do_prefix ? "Frame Range %d:%d" : "%d:%d", + scene->r.sfra, + scene->r.efra); + } + else { + stamp_data->frame_range[0] = '\0'; + } + + if (scene->r.stamp & R_STAMP_HOSTNAME) { + char hostname[500]; /* sizeof(stamp_data->hostname) minus some bytes for a label. */ + BLI_hostname_get(hostname, sizeof(hostname)); + SNPRINTF(stamp_data->hostname, do_prefix ? "Hostname %s" : "%s", hostname); + } + else { + stamp_data->hostname[0] = '\0'; + } +} + +static void stampdata_from_template(StampData *stamp_data, + const Scene *scene, + const StampData *stamp_data_template, + bool do_prefix) +{ + if (scene->r.stamp & R_STAMP_FILENAME) { + SNPRINTF(stamp_data->file, do_prefix ? "File %s" : "%s", stamp_data_template->file); + } + else { + stamp_data->file[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_NOTE) { + SNPRINTF(stamp_data->note, "%s", stamp_data_template->note); + } + else { + stamp_data->note[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_DATE) { + SNPRINTF(stamp_data->date, do_prefix ? "Date %s" : "%s", stamp_data_template->date); + } + else { + stamp_data->date[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_MARKER) { + SNPRINTF(stamp_data->marker, do_prefix ? "Marker %s" : "%s", stamp_data_template->marker); + } + else { + stamp_data->marker[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_TIME) { + SNPRINTF(stamp_data->time, do_prefix ? "Timecode %s" : "%s", stamp_data_template->time); + } + else { + stamp_data->time[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_FRAME) { + SNPRINTF(stamp_data->frame, do_prefix ? "Frame %s" : "%s", stamp_data_template->frame); + } + else { + stamp_data->frame[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_CAMERA) { + SNPRINTF(stamp_data->camera, do_prefix ? "Camera %s" : "%s", stamp_data_template->camera); + } + else { + stamp_data->camera[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_CAMERALENS) { + SNPRINTF( + stamp_data->cameralens, do_prefix ? "Lens %s" : "%s", stamp_data_template->cameralens); + } + else { + stamp_data->cameralens[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_SCENE) { + SNPRINTF(stamp_data->scene, do_prefix ? "Scene %s" : "%s", stamp_data_template->scene); + } + else { + stamp_data->scene[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_SEQSTRIP) { + SNPRINTF(stamp_data->strip, do_prefix ? "Strip %s" : "%s", stamp_data_template->strip); + } + else { + stamp_data->strip[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_RENDERTIME) { + SNPRINTF(stamp_data->rendertime, + do_prefix ? "RenderTime %s" : "%s", + stamp_data_template->rendertime); + } + else { + stamp_data->rendertime[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_MEMORY) { + SNPRINTF(stamp_data->memory, do_prefix ? "Peak Memory %s" : "%s", stamp_data_template->memory); + } + else { + stamp_data->memory[0] = '\0'; + } + if (scene->r.stamp & R_STAMP_HOSTNAME) { + SNPRINTF( + stamp_data->hostname, do_prefix ? "Hostname %s" : "%s", stamp_data_template->hostname); + } + else { + stamp_data->hostname[0] = '\0'; + } +} + +void BKE_image_stamp_buf(Scene *scene, + Object *camera, + const StampData *stamp_data_template, + unsigned char *rect, + float *rectf, + int width, + int height, + int channels) +{ + struct StampData stamp_data; + float w, h, pad; + int x, y, y_ofs; + float h_fixed; + const int mono = blf_mono_font_render; /* XXX */ + struct ColorManagedDisplay *display; + const char *display_device; + + /* vars for calculating wordwrap */ + struct { + struct ResultBLF info; + rctf rect; + } wrap; + + /* this could be an argument if we want to operate on non linear float imbuf's + * for now though this is only used for renders which use scene settings */ + +#define TEXT_SIZE_CHECK(str, w, h) \ + ((str[0]) && ((void)(h = h_fixed), (w = BLF_width(mono, str, sizeof(str))))) + + /* must enable BLF_WORD_WRAP before using */ +#define TEXT_SIZE_CHECK_WORD_WRAP(str, w, h) \ + ((str[0]) && (BLF_boundbox_ex(mono, str, sizeof(str), &wrap.rect, &wrap.info), \ + (void)(h = h_fixed * wrap.info.lines), \ + (w = BLI_rctf_size_x(&wrap.rect)))) + +#define BUFF_MARGIN_X 2 +#define BUFF_MARGIN_Y 1 + + if (!rect && !rectf) { + return; + } + + display_device = scene->display_settings.display_device; + display = IMB_colormanagement_display_get_named(display_device); + + bool do_prefix = (scene->r.stamp & R_STAMP_HIDE_LABELS) == 0; + if (stamp_data_template == nullptr) { + stampdata(scene, camera, &stamp_data, do_prefix, true); + } + else { + stampdata_from_template(&stamp_data, scene, stamp_data_template, do_prefix); + } + + /* TODO: do_versions. */ + if (scene->r.stamp_font_id < 8) { + scene->r.stamp_font_id = 12; + } + + /* set before return */ + BLF_size(mono, scene->r.stamp_font_id, 72); + BLF_wordwrap(mono, width - (BUFF_MARGIN_X * 2)); + + BLF_buffer(mono, rectf, rect, width, height, channels, display); + BLF_buffer_col(mono, scene->r.fg_stamp); + pad = BLF_width_max(mono); + + /* use 'h_fixed' rather than 'h', aligns better */ + h_fixed = BLF_height_max(mono); + y_ofs = -BLF_descender(mono); + + x = 0; + y = height; + + if (TEXT_SIZE_CHECK(stamp_data.file, w, h)) { + /* Top left corner */ + y -= h; + + /* also a little of space to the background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + /* and draw the text. */ + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.file, sizeof(stamp_data.file)); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File */ + if (TEXT_SIZE_CHECK(stamp_data.date, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + 0, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.date, sizeof(stamp_data.date)); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File, Date */ + if (TEXT_SIZE_CHECK(stamp_data.rendertime, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + 0, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.rendertime, sizeof(stamp_data.rendertime)); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File, Date, Rendertime */ + if (TEXT_SIZE_CHECK(stamp_data.memory, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + 0, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.memory, sizeof(stamp_data.memory)); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File, Date, Rendertime, Memory */ + if (TEXT_SIZE_CHECK(stamp_data.hostname, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + 0, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.hostname, sizeof(stamp_data.hostname)); + + /* the extra pixel for background. */ + y -= BUFF_MARGIN_Y * 2; + } + + /* Top left corner, below File, Date, Memory, Rendertime, Hostname */ + BLF_enable(mono, BLF_WORD_WRAP); + if (TEXT_SIZE_CHECK_WORD_WRAP(stamp_data.note, w, h)) { + y -= h; + + /* and space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + 0, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs + (h - h_fixed), 0.0); + BLF_draw_buffer(mono, stamp_data.note, sizeof(stamp_data.note)); + } + BLF_disable(mono, BLF_WORD_WRAP); + + x = 0; + y = 0; + + /* Bottom left corner, leaving space for timing */ + if (TEXT_SIZE_CHECK(stamp_data.marker, w, h)) { + + /* extra space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + /* and pad the text. */ + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.marker, sizeof(stamp_data.marker)); + + /* space width. */ + x += w + pad; + } + + /* Left bottom corner */ + if (TEXT_SIZE_CHECK(stamp_data.time, w, h)) { + + /* extra space for background */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y, + x + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + /* and pad the text. */ + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.time, sizeof(stamp_data.time)); + + /* space width. */ + x += w + pad; + } + + if (TEXT_SIZE_CHECK(stamp_data.frame, w, h)) { + + /* extra space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + x + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + /* and pad the text. */ + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.frame, sizeof(stamp_data.frame)); + + /* space width. */ + x += w + pad; + } + + if (TEXT_SIZE_CHECK(stamp_data.camera, w, h)) { + + /* extra space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + x + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.camera, sizeof(stamp_data.camera)); + + /* space width. */ + x += w + pad; + } + + if (TEXT_SIZE_CHECK(stamp_data.cameralens, w, h)) { + + /* extra space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + x + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.cameralens, sizeof(stamp_data.cameralens)); + } + + if (TEXT_SIZE_CHECK(stamp_data.scene, w, h)) { + + /* Bottom right corner, with an extra space because blenfont is too strict! */ + x = width - w - 2; + + /* extra space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + x + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + /* and pad the text. */ + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.scene, sizeof(stamp_data.scene)); + } + + if (TEXT_SIZE_CHECK(stamp_data.strip, w, h)) { + + /* Top right corner, with an extra space because blenfont is too strict! */ + x = width - w - pad; + y = height - h; + + /* extra space for background. */ + buf_rectfill_area(rect, + rectf, + width, + height, + scene->r.bg_stamp, + display, + x - BUFF_MARGIN_X, + y - BUFF_MARGIN_Y, + x + w + BUFF_MARGIN_X, + y + h + BUFF_MARGIN_Y); + + BLF_position(mono, x, y + y_ofs, 0.0); + BLF_draw_buffer(mono, stamp_data.strip, sizeof(stamp_data.strip)); + } + + /* cleanup the buffer. */ + BLF_buffer(mono, nullptr, nullptr, 0, 0, 0, nullptr); + BLF_wordwrap(mono, 0); + +#undef TEXT_SIZE_CHECK +#undef TEXT_SIZE_CHECK_WORD_WRAP +#undef BUFF_MARGIN_X +#undef BUFF_MARGIN_Y +} + +void BKE_render_result_stamp_info(Scene *scene, + Object *camera, + struct RenderResult *rr, + bool allocate_only) +{ + struct StampData *stamp_data; + + if (!(scene && (scene->r.stamp & R_STAMP_ALL)) && !allocate_only) { + return; + } + + if (!rr->stamp_data) { + stamp_data = MEM_cnew("RenderResult.stamp_data"); + } + else { + stamp_data = rr->stamp_data; + } + + if (!allocate_only) { + stampdata(scene, camera, stamp_data, 0, true); + } + + if (!rr->stamp_data) { + rr->stamp_data = stamp_data; + } +} + +struct StampData *BKE_stamp_info_from_scene_static(const Scene *scene) +{ + struct StampData *stamp_data; + + if (!(scene && (scene->r.stamp & R_STAMP_ALL))) { + return nullptr; + } + + /* Memory is allocated here (instead of by the caller) so that the caller + * doesn't have to know the size of the StampData struct. */ + stamp_data = MEM_cnew(__func__); + stampdata(scene, nullptr, stamp_data, 0, false); + + return stamp_data; +} + +static const char *stamp_metadata_fields[] = { + "File", + "Note", + "Date", + "Marker", + "Time", + "Frame", + "FrameRange", + "Camera", + "Lens", + "Scene", + "Strip", + "RenderTime", + "Memory", + "Hostname", + nullptr, +}; + +bool BKE_stamp_is_known_field(const char *field_name) +{ + int i = 0; + while (stamp_metadata_fields[i] != nullptr) { + if (STREQ(field_name, stamp_metadata_fields[i])) { + return true; + } + i++; + } + return false; +} + +void BKE_stamp_info_callback(void *data, + struct StampData *stamp_data, + StampCallback callback, + bool noskip) +{ + if ((callback == nullptr) || (stamp_data == nullptr)) { + return; + } + +#define CALL(member, value_str) \ + if (noskip || stamp_data->member[0]) { \ + callback(data, value_str, stamp_data->member, sizeof(stamp_data->member)); \ + } \ + ((void)0) + + /* TODO(sergey): Use stamp_metadata_fields somehow, or make it more generic + * meta information to avoid duplication. */ + CALL(file, "File"); + CALL(note, "Note"); + CALL(date, "Date"); + CALL(marker, "Marker"); + CALL(time, "Time"); + CALL(frame, "Frame"); + CALL(frame_range, "FrameRange"); + CALL(camera, "Camera"); + CALL(cameralens, "Lens"); + CALL(scene, "Scene"); + CALL(strip, "Strip"); + CALL(rendertime, "RenderTime"); + CALL(memory, "Memory"); + CALL(hostname, "Hostname"); + + LISTBASE_FOREACH (StampDataCustomField *, custom_field, &stamp_data->custom_fields) { + if (noskip || custom_field->value[0]) { + callback(data, custom_field->key, custom_field->value, strlen(custom_field->value) + 1); + } + } + +#undef CALL +} + +void BKE_render_result_stamp_data(RenderResult *rr, const char *key, const char *value) +{ + StampData *stamp_data; + if (rr->stamp_data == nullptr) { + rr->stamp_data = MEM_cnew("RenderResult.stamp_data"); + } + stamp_data = rr->stamp_data; + StampDataCustomField *field = static_cast( + MEM_mallocN(sizeof(StampDataCustomField), "StampData Custom Field")); + STRNCPY(field->key, key); + field->value = BLI_strdup(value); + BLI_addtail(&stamp_data->custom_fields, field); +} + +StampData *BKE_stamp_data_copy(const StampData *stamp_data) +{ + if (stamp_data == nullptr) { + return nullptr; + } + + StampData *stamp_datan = static_cast(MEM_dupallocN(stamp_data)); + BLI_duplicatelist(&stamp_datan->custom_fields, &stamp_data->custom_fields); + + LISTBASE_FOREACH (StampDataCustomField *, custom_fieldn, &stamp_datan->custom_fields) { + custom_fieldn->value = static_cast(MEM_dupallocN(custom_fieldn->value)); + } + + return stamp_datan; +} + +void BKE_stamp_data_free(StampData *stamp_data) +{ + if (stamp_data == nullptr) { + return; + } + LISTBASE_FOREACH (StampDataCustomField *, custom_field, &stamp_data->custom_fields) { + MEM_freeN(custom_field->value); + } + BLI_freelistN(&stamp_data->custom_fields); + MEM_freeN(stamp_data); +} + +/* wrap for callback only */ +static void metadata_set_field(void *data, const char *propname, char *propvalue, int UNUSED(len)) +{ + /* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */ + ImBuf *imbuf = static_cast(data); + IMB_metadata_set_field(imbuf->metadata, propname, propvalue); +} + +static void metadata_get_field(void *data, const char *propname, char *propvalue, int len) +{ + /* We know it is an ImBuf* because that's what we pass to BKE_stamp_info_callback. */ + ImBuf *imbuf = static_cast(data); + IMB_metadata_get_field(imbuf->metadata, propname, propvalue, len); +} + +void BKE_imbuf_stamp_info(RenderResult *rr, struct ImBuf *ibuf) +{ + struct StampData *stamp_data = rr->stamp_data; + IMB_metadata_ensure(&ibuf->metadata); + BKE_stamp_info_callback(ibuf, stamp_data, metadata_set_field, false); +} + +static void metadata_copy_custom_fields(const char *field, const char *value, void *rr_v) +{ + if (BKE_stamp_is_known_field(field)) { + return; + } + RenderResult *rr = (RenderResult *)rr_v; + BKE_render_result_stamp_data(rr, field, value); +} + +void BKE_stamp_info_from_imbuf(RenderResult *rr, struct ImBuf *ibuf) +{ + if (rr->stamp_data == nullptr) { + rr->stamp_data = MEM_cnew("RenderResult.stamp_data"); + } + struct StampData *stamp_data = rr->stamp_data; + IMB_metadata_ensure(&ibuf->metadata); + BKE_stamp_info_callback(ibuf, stamp_data, metadata_get_field, true); + /* Copy render engine specific settings. */ + IMB_metadata_foreach(ibuf, metadata_copy_custom_fields, rr); +} + +bool BKE_imbuf_alpha_test(ImBuf *ibuf) +{ + int tot; + if (ibuf->rect_float) { + const float *buf = ibuf->rect_float; + for (tot = ibuf->x * ibuf->y; tot--; buf += 4) { + if (buf[3] < 1.0f) { + return true; + } + } + } + else if (ibuf->rect) { + unsigned char *buf = (unsigned char *)ibuf->rect; + for (tot = ibuf->x * ibuf->y; tot--; buf += 4) { + if (buf[3] != 255) { + return true; + } + } + } + + return false; +} + +void BKE_imbuf_write_prepare(ImBuf *ibuf, const ImageFormatData *imf) +{ + char imtype = imf->imtype; + char compress = imf->compress; + char quality = imf->quality; + + /* initialize all from image format */ + ibuf->foptions.flag = 0; + + if (imtype == R_IMF_IMTYPE_IRIS) { + ibuf->ftype = IMB_FTYPE_IMAGIC; + } +#ifdef WITH_HDR + else if (imtype == R_IMF_IMTYPE_RADHDR) { + ibuf->ftype = IMB_FTYPE_RADHDR; + } +#endif + else if (ELEM(imtype, + R_IMF_IMTYPE_PNG, + R_IMF_IMTYPE_FFMPEG, + R_IMF_IMTYPE_H264, + R_IMF_IMTYPE_THEORA, + R_IMF_IMTYPE_XVID)) { + ibuf->ftype = IMB_FTYPE_PNG; + + if (imtype == R_IMF_IMTYPE_PNG) { + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= PNG_16BIT; + } + + ibuf->foptions.quality = compress; + } + } +#ifdef WITH_DDS + else if (imtype == R_IMF_IMTYPE_DDS) { + ibuf->ftype = IMB_FTYPE_DDS; + } +#endif + else if (imtype == R_IMF_IMTYPE_BMP) { + ibuf->ftype = IMB_FTYPE_BMP; + } +#ifdef WITH_TIFF + else if (imtype == R_IMF_IMTYPE_TIFF) { + ibuf->ftype = IMB_FTYPE_TIF; + + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= TIF_16BIT; + } + if (imf->tiff_codec == R_IMF_TIFF_CODEC_NONE) { + ibuf->foptions.flag |= TIF_COMPRESS_NONE; + } + else if (imf->tiff_codec == R_IMF_TIFF_CODEC_DEFLATE) { + ibuf->foptions.flag |= TIF_COMPRESS_DEFLATE; + } + else if (imf->tiff_codec == R_IMF_TIFF_CODEC_LZW) { + ibuf->foptions.flag |= TIF_COMPRESS_LZW; + } + else if (imf->tiff_codec == R_IMF_TIFF_CODEC_PACKBITS) { + ibuf->foptions.flag |= TIF_COMPRESS_PACKBITS; + } + } +#endif +#ifdef WITH_OPENEXR + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + ibuf->ftype = IMB_FTYPE_OPENEXR; + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= OPENEXR_HALF; + } + ibuf->foptions.flag |= (imf->exr_codec & OPENEXR_COMPRESS); + + if (!(imf->flag & R_IMF_FLAG_ZBUF)) { + /* Signal for exr saving. */ + IMB_freezbuffloatImBuf(ibuf); + } + } +#endif +#ifdef WITH_CINEON + else if (imtype == R_IMF_IMTYPE_CINEON) { + ibuf->ftype = IMB_FTYPE_CINEON; + if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { + ibuf->foptions.flag |= CINEON_LOG; + } + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= CINEON_16BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_12) { + ibuf->foptions.flag |= CINEON_12BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_10) { + ibuf->foptions.flag |= CINEON_10BIT; + } + } + else if (imtype == R_IMF_IMTYPE_DPX) { + ibuf->ftype = IMB_FTYPE_DPX; + if (imf->cineon_flag & R_IMF_CINEON_FLAG_LOG) { + ibuf->foptions.flag |= CINEON_LOG; + } + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= CINEON_16BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_12) { + ibuf->foptions.flag |= CINEON_12BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_10) { + ibuf->foptions.flag |= CINEON_10BIT; + } + } +#endif + else if (imtype == R_IMF_IMTYPE_TARGA) { + ibuf->ftype = IMB_FTYPE_TGA; + } + else if (imtype == R_IMF_IMTYPE_RAWTGA) { + ibuf->ftype = IMB_FTYPE_TGA; + ibuf->foptions.flag = RAWTGA; + } +#ifdef WITH_OPENJPEG + else if (imtype == R_IMF_IMTYPE_JP2) { + if (quality < 10) { + quality = 90; + } + ibuf->ftype = IMB_FTYPE_JP2; + ibuf->foptions.quality = quality; + + if (imf->depth == R_IMF_CHAN_DEPTH_16) { + ibuf->foptions.flag |= JP2_16BIT; + } + else if (imf->depth == R_IMF_CHAN_DEPTH_12) { + ibuf->foptions.flag |= JP2_12BIT; + } + + if (imf->jp2_flag & R_IMF_JP2_FLAG_YCC) { + ibuf->foptions.flag |= JP2_YCC; + } + + if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_PRESET) { + ibuf->foptions.flag |= JP2_CINE; + if (imf->jp2_flag & R_IMF_JP2_FLAG_CINE_48) { + ibuf->foptions.flag |= JP2_CINE_48FPS; + } + } + + if (imf->jp2_codec == R_IMF_JP2_CODEC_JP2) { + ibuf->foptions.flag |= JP2_JP2; + } + else if (imf->jp2_codec == R_IMF_JP2_CODEC_J2K) { + ibuf->foptions.flag |= JP2_J2K; + } + else { + BLI_assert_msg(0, "Unsupported jp2 codec was specified in im_format->jp2_codec"); + } + } +#endif + else { + /* R_IMF_IMTYPE_JPEG90, etc. default we save jpegs */ + if (quality < 10) { + quality = 90; + } + ibuf->ftype = IMB_FTYPE_JPG; + ibuf->foptions.quality = quality; + } +} + +int BKE_imbuf_write(ImBuf *ibuf, const char *name, const ImageFormatData *imf) +{ + BKE_imbuf_write_prepare(ibuf, imf); + + BLI_make_existing_file(name); + + const bool ok = IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat); + if (ok == 0) { + perror(name); + } + + return ok; +} + +int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, const bool save_copy) +{ + ImBuf ibuf_back = *ibuf; + int ok; + + /* All data is RGBA anyway, this just controls how to save for some formats. */ + ibuf->planes = imf->planes; + + ok = BKE_imbuf_write(ibuf, name, imf); + + if (save_copy) { + /* note that we are not restoring _all_ settings */ + ibuf->planes = ibuf_back.planes; + ibuf->ftype = ibuf_back.ftype; + ibuf->foptions = ibuf_back.foptions; + } + + return ok; +} + +int BKE_imbuf_write_stamp(Scene *scene, + struct RenderResult *rr, + ImBuf *ibuf, + const char *name, + const struct ImageFormatData *imf) +{ + if (scene && scene->r.stamp & R_STAMP_ALL) { + BKE_imbuf_stamp_info(rr, ibuf); + } + + return BKE_imbuf_write(ibuf, name, imf); +} + +static void do_makepicstring(char *string, + const char *base, + const char *relbase, + int frame, + const char imtype, + const ImageFormatData *im_format, + const bool use_ext, + const bool use_frames, + const char *suffix) +{ + if (string == nullptr) { + return; + } + BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ + BLI_path_abs(string, relbase); + + if (use_frames) { + BLI_path_frame(string, frame, 4); + } + + if (suffix) { + BLI_path_suffix(string, FILE_MAX, suffix, ""); + } + + if (use_ext) { + 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 char *suffix) +{ + 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 *suffix) +{ + do_makepicstring(string, base, relbase, frame, imtype, nullptr, use_ext, use_frames, suffix); +} + +struct anim *openanim_noload(const char *name, + int flags, + int streamindex, + char colorspace[IMA_MAX_SPACE]) +{ + struct anim *anim; + + anim = IMB_open_anim(name, flags, streamindex, colorspace); + return anim; +} + +struct anim *openanim(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) +{ + struct anim *anim; + struct ImBuf *ibuf; + + anim = IMB_open_anim(name, flags, streamindex, colorspace); + if (anim == nullptr) { + return nullptr; + } + + ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE); + if (ibuf == nullptr) { + if (BLI_exists(name)) { + printf("not an anim: %s\n", name); + } + else { + printf("anim file doesn't exist: %s\n", name); + } + IMB_free_anim(anim); + return nullptr; + } + IMB_freeImBuf(ibuf); + + return anim; +} + +/* ************************* New Image API *************** */ + +/* Notes about Image storage + * - packedfile + * -> written in .blend + * - filename + * -> written in .blend + * - movie + * -> comes from packedfile or filename + * - renderresult + * -> comes from packedfile or filename + * - listbase + * -> ibufs from exrhandle + * - flipbook array + * -> ibufs come from movie, temporary renderresult or sequence + * - ibuf + * -> comes from packedfile or filename or generated + */ + +Image *BKE_image_ensure_viewer(Main *bmain, int type, const char *name) +{ + Image *ima; + + for (ima = static_cast(bmain->images.first); ima; + ima = static_cast(ima->id.next)) { + if (ima->source == IMA_SRC_VIEWER) { + if (ima->type == type) { + break; + } + } + } + + if (ima == nullptr) { + ima = image_alloc(bmain, name, IMA_SRC_VIEWER, type); + } + + /* Happens on reload, imagewindow cannot be image user when hidden. */ + if (ima->id.us == 0) { + id_us_ensure_real(&ima->id); + } + + return ima; +} + +static void image_viewer_create_views(const RenderData *rd, Image *ima) +{ + if ((rd->scemode & R_MULTIVIEW) == 0) { + image_add_view(ima, "", ""); + } + else { + for (SceneRenderView *srv = static_cast(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, ""); + } + } +} + +void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser *iuser) +{ + bool do_reset; + const bool is_multiview = (rd->scemode & R_MULTIVIEW) != 0; + + BLI_thread_lock(LOCK_DRAW_IMAGE); + + if (!BKE_scene_multiview_is_stereo3d(rd)) { + 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)); + + /* multiview also needs to be sure all the views are synced */ + if (is_multiview && !do_reset) { + SceneRenderView *srv; + ImageView *iv; + + for (iv = static_cast(ima->views.first); iv; iv = iv->next) { + srv = static_cast( + BLI_findstring(&rd->views, iv->name, offsetof(SceneRenderView, name))); + if ((srv == nullptr) || (BKE_scene_multiview_is_render_view_active(rd, srv) == false)) { + do_reset = true; + break; + } + } + } + + if (do_reset) { + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + + image_free_cached_frames(ima); + BKE_image_free_views(ima); + + /* add new views */ + image_viewer_create_views(rd, ima); + + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); + } + + BLI_thread_unlock(LOCK_DRAW_IMAGE); +} + +static void image_walk_ntree_all_users( + bNodeTree *ntree, + ID *id, + void *customdata, + void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) +{ + switch (ntree->type) { + case NTREE_SHADER: + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id) { + if (node->type == SH_NODE_TEX_IMAGE) { + NodeTexImage *tex = static_cast(node->storage); + Image *ima = (Image *)node->id; + callback(ima, id, &tex->iuser, customdata); + } + if (node->type == SH_NODE_TEX_ENVIRONMENT) { + NodeTexImage *tex = static_cast(node->storage); + Image *ima = (Image *)node->id; + callback(ima, id, &tex->iuser, customdata); + } + } + } + break; + case NTREE_TEXTURE: + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id && node->type == TEX_NODE_IMAGE) { + Image *ima = (Image *)node->id; + ImageUser *iuser = static_cast(node->storage); + callback(ima, id, iuser, customdata); + } + } + break; + case NTREE_COMPOSIT: + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->id && node->type == CMP_NODE_IMAGE) { + Image *ima = (Image *)node->id; + ImageUser *iuser = static_cast(node->storage); + callback(ima, id, iuser, customdata); + } + } + break; + } +} + +static void image_walk_gpu_materials( + ID *id, + ListBase *gpu_materials, + void *customdata, + void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) +{ + LISTBASE_FOREACH (LinkData *, link, gpu_materials) { + GPUMaterial *gpu_material = (GPUMaterial *)link->data; + ListBase textures = GPU_material_textures(gpu_material); + LISTBASE_FOREACH (GPUMaterialTexture *, gpu_material_texture, &textures) { + if (gpu_material_texture->iuser_available) { + callback(gpu_material_texture->ima, id, &gpu_material_texture->iuser, customdata); + } + } + } +} + +static void image_walk_id_all_users( + ID *id, + bool skip_nested_nodes, + void *customdata, + void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) +{ + switch (GS(id->name)) { + case ID_OB: { + Object *ob = (Object *)id; + if (ob->empty_drawtype == OB_EMPTY_IMAGE && ob->data) { + callback(static_cast(ob->data), &ob->id, ob->iuser, customdata); + } + break; + } + case ID_MA: { + Material *ma = (Material *)id; + if (ma->nodetree && ma->use_nodes && !skip_nested_nodes) { + image_walk_ntree_all_users(ma->nodetree, &ma->id, customdata, callback); + } + image_walk_gpu_materials(id, &ma->gpumaterial, customdata, callback); + break; + } + case ID_LA: { + Light *light = (Light *)id; + if (light->nodetree && light->use_nodes && !skip_nested_nodes) { + image_walk_ntree_all_users(light->nodetree, &light->id, customdata, callback); + } + break; + } + case ID_WO: { + World *world = (World *)id; + if (world->nodetree && world->use_nodes && !skip_nested_nodes) { + image_walk_ntree_all_users(world->nodetree, &world->id, customdata, callback); + } + image_walk_gpu_materials(id, &world->gpumaterial, customdata, callback); + break; + } + case ID_TE: { + Tex *tex = (Tex *)id; + if (tex->type == TEX_IMAGE && tex->ima) { + callback(tex->ima, &tex->id, &tex->iuser, customdata); + } + if (tex->nodetree && tex->use_nodes && !skip_nested_nodes) { + image_walk_ntree_all_users(tex->nodetree, &tex->id, customdata, callback); + } + break; + } + case ID_NT: { + bNodeTree *ntree = (bNodeTree *)id; + image_walk_ntree_all_users(ntree, &ntree->id, customdata, callback); + break; + } + case ID_CA: { + Camera *cam = (Camera *)id; + LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { + callback(bgpic->ima, nullptr, &bgpic->iuser, customdata); + } + break; + } + case ID_WM: { + wmWindowManager *wm = (wmWindowManager *)id; + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { + const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); + + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_IMAGE) { + SpaceImage *sima = static_cast(area->spacedata.first); + callback(sima->image, nullptr, &sima->iuser, customdata); + } + } + } + break; + } + case ID_SCE: { + Scene *scene = (Scene *)id; + if (scene->nodetree && scene->use_nodes && !skip_nested_nodes) { + image_walk_ntree_all_users(scene->nodetree, &scene->id, customdata, callback); + } + break; + } + case ID_SIM: { + Simulation *simulation = (Simulation *)id; + image_walk_ntree_all_users(simulation->nodetree, &simulation->id, customdata, callback); + break; + } + default: + break; + } +} + +void BKE_image_walk_all_users( + const Main *mainp, + void *customdata, + void callback(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata)) +{ + for (Scene *scene = static_cast(mainp->scenes.first); scene; + scene = static_cast(scene->id.next)) { + image_walk_id_all_users(&scene->id, false, customdata, callback); + } + + for (Object *ob = static_cast(mainp->objects.first); ob; + ob = static_cast(ob->id.next)) { + image_walk_id_all_users(&ob->id, false, customdata, callback); + } + + for (bNodeTree *ntree = static_cast(mainp->nodetrees.first); ntree; + ntree = static_cast(ntree->id.next)) { + image_walk_id_all_users(&ntree->id, false, customdata, callback); + } + + for (Material *ma = static_cast(mainp->materials.first); ma; + ma = static_cast(ma->id.next)) { + image_walk_id_all_users(&ma->id, false, customdata, callback); + } + + for (Light *light = static_cast(mainp->materials.first); light; + light = static_cast(light->id.next)) { + image_walk_id_all_users(&light->id, false, customdata, callback); + } + + for (World *world = static_cast(mainp->materials.first); world; + world = static_cast(world->id.next)) { + image_walk_id_all_users(&world->id, false, customdata, callback); + } + + for (Tex *tex = static_cast(mainp->textures.first); tex; + tex = static_cast(tex->id.next)) { + image_walk_id_all_users(&tex->id, false, customdata, callback); + } + + for (Camera *cam = static_cast(mainp->cameras.first); cam; + cam = static_cast(cam->id.next)) { + image_walk_id_all_users(&cam->id, false, customdata, callback); + } + + for (wmWindowManager *wm = static_cast(mainp->wm.first); wm; + wm = static_cast(wm->id.next)) { /* only 1 wm */ + image_walk_id_all_users(&wm->id, false, customdata, callback); + } +} + +static void image_tag_frame_recalc(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata) +{ + Image *changed_image = static_cast(customdata); + + if (ima == changed_image && BKE_image_is_animated(ima)) { + iuser->flag |= IMA_NEED_FRAME_RECALC; + + if (iuser_id) { + /* Must copy image user changes to CoW data-block. */ + DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE); + } + } +} + +static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *customdata) +{ + Image *changed_image = static_cast(customdata); + + if (ima == changed_image) { + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + if (iuser_id) { + /* Must copy image user changes to CoW data-block. */ + DEG_id_tag_update(iuser_id, ID_RECALC_COPY_ON_WRITE); + } + BKE_image_partial_update_mark_full_update(ima); + } +} + +void BKE_imageuser_default(ImageUser *iuser) +{ + memset(iuser, 0, sizeof(ImageUser)); + iuser->frames = 100; + iuser->sfra = 1; +} + +void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) +{ + RenderResult *rr = ima->rr; + + iuser->multi_index = 0; + iuser->layer = iuser->pass = iuser->view = 0; + + if (rr) { + BKE_image_multilayer_index(rr, iuser); + } +} + +static void image_free_tile(Image *ima, ImageTile *tile) +{ + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* Only two textures depends on all tiles, so if this is a secondary tile we can keep the other + * two. */ + if (tile != ima->tiles.first && !(ELEM(i, TEXTARGET_2D_ARRAY, TEXTARGET_TILE_MAPPING))) { + continue; + } + + for (int eye = 0; eye < 2; eye++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + if (ima->gputexture[i][eye][resolution] != nullptr) { + GPU_texture_free(ima->gputexture[i][eye][resolution]); + ima->gputexture[i][eye][resolution] = nullptr; + } + } + } + } + BKE_image_partial_update_mark_full_update(ima); + + if (BKE_image_is_multiview(ima)) { + const int totviews = BLI_listbase_count(&ima->views); + for (int i = 0; i < totviews; i++) { + image_remove_ibuf(ima, i, tile->tile_number); + } + } + else { + image_remove_ibuf(ima, 0, tile->tile_number); + } +} + +void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) +{ + if (ima == nullptr) { + return; + } + + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + + switch (signal) { + case IMA_SIGNAL_FREE: + BKE_image_free_buffers(ima); + + if (iuser) { + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } + break; + case IMA_SIGNAL_SRC_CHANGE: + if (ima->type == IMA_TYPE_UV_TEST) { + if (ima->source != IMA_SRC_GENERATED) { + ima->type = IMA_TYPE_IMAGE; + } + } + + if (ima->source == IMA_SRC_GENERATED) { + if (ima->gen_x == 0 || ima->gen_y == 0) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); + if (ibuf) { + ima->gen_x = ibuf->x; + ima->gen_y = ibuf->y; + IMB_freeImBuf(ibuf); + } + } + + /* Changing source type to generated will likely change file format + * used by generated image buffer. Saving different file format to + * the old name might confuse other applications. + * + * Here we ensure original image path wouldn't be used when saving + * generated image. + */ + ima->filepath[0] = '\0'; + } + + if (ima->source != IMA_SRC_TILED) { + /* Free all but the first tile. */ + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + BLI_assert(base_tile == ima->tiles.first); + for (ImageTile *tile = base_tile->next, *tile_next; tile; tile = tile_next) { + tile_next = tile->next; + image_free_tile(ima, tile); + MEM_freeN(tile); + } + base_tile->next = nullptr; + ima->tiles.last = base_tile; + } + + /* image buffers for non-sequence multilayer will share buffers with RenderResult, + * however sequence multilayer will own buffers. Such logic makes switching from + * single multilayer file to sequence completely unstable + * since changes in nodes seems this workaround isn't needed anymore, all sockets + * are nicely detecting anyway, but freeing buffers always here makes multilayer + * sequences behave stable + */ + BKE_image_free_buffers(ima); + + if (iuser) { + image_tag_frame_recalc(ima, nullptr, iuser, ima); + } + BKE_image_walk_all_users(bmain, ima, image_tag_frame_recalc); + + break; + + case IMA_SIGNAL_RELOAD: + /* try to repack file */ + if (BKE_image_has_packedfile(ima)) { + const int totfiles = image_num_files(ima); + + if (totfiles != BLI_listbase_count_at_most(&ima->packedfiles, totfiles + 1)) { + /* in case there are new available files to be loaded */ + image_free_packedfiles(ima); + BKE_image_packfiles(nullptr, ima, ID_BLEND_PATH(bmain, &ima->id)); + } + else { + ImagePackedFile *imapf; + for (imapf = static_cast(ima->packedfiles.first); imapf; + imapf = imapf->next) { + PackedFile *pf; + pf = BKE_packedfile_new(nullptr, imapf->filepath, ID_BLEND_PATH(bmain, &ima->id)); + if (pf) { + BKE_packedfile_free(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 (ima->source == IMA_SRC_TILED) { + ListBase new_tiles = {nullptr, nullptr}; + int new_start, new_range; + + char filepath[FILE_MAX]; + BLI_strncpy(filepath, ima->filepath, sizeof(filepath)); + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + bool result = BKE_image_get_tile_info(filepath, &new_tiles, &new_start, &new_range); + if (result) { + /* Because the prior and new list of tiles are both sparse sequences, we need to be sure + * to account for how the two sets might or might not overlap. To be complete, we start + * the refresh process by clearing all existing tiles, stopping when there's only 1 tile + * left. */ + while (BKE_image_remove_tile(ima, static_cast(ima->tiles.last))) { + ; + } + + int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number; + bool needs_final_cleanup = true; + + /* Add in all the new tiles. */ + LISTBASE_FOREACH (LinkData *, new_tile, &new_tiles) { + int new_tile_number = POINTER_AS_INT(new_tile->data); + BKE_image_add_tile(ima, new_tile_number, nullptr); + if (new_tile_number == remaining_tile_number) { + needs_final_cleanup = false; + } + } + + /* Final cleanup if the prior remaining tile was never encountered in the new list. */ + if (needs_final_cleanup) { + BKE_image_remove_tile(ima, BKE_image_get_tile(ima, remaining_tile_number)); + } + } + BLI_freelistN(&new_tiles); + } + + if (iuser) { + image_tag_reload(ima, nullptr, iuser, ima); + } + BKE_image_walk_all_users(bmain, ima, image_tag_reload); + break; + case IMA_SIGNAL_USER_NEW_IMAGE: + if (iuser) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { + if (ima->type == IMA_TYPE_MULTILAYER) { + BKE_image_init_imageuser(ima, iuser); + } + } + } + break; + case IMA_SIGNAL_COLORMANAGE: + BKE_image_free_buffers(ima); + break; + } + + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); + + BKE_ntree_update_tag_id_changed(bmain, &ima->id); + BKE_ntree_update_main(bmain, nullptr); +} + +/* return renderpass for a given pass index and active view */ +/* fallback to available if there are missing passes for active view */ +static RenderPass *image_render_pass_get(RenderLayer *rl, + const int pass, + const int view, + int *r_passindex) +{ + RenderPass *rpass_ret = nullptr; + RenderPass *rpass; + + int rp_index = 0; + const char *rp_name = ""; + + for (rpass = static_cast(rl->passes.first); rpass; + rpass = rpass->next, rp_index++) { + if (rp_index == pass) { + rpass_ret = rpass; + if (view == 0) { + /* no multiview or left eye */ + break; + } + + rp_name = rpass->name; + } + /* multiview */ + else if (rp_name[0] && STREQ(rpass->name, rp_name) && (rpass->view_id == view)) { + rpass_ret = rpass; + break; + } + } + + /* fallback to the first pass in the layer */ + if (rpass_ret == nullptr) { + rp_index = 0; + rpass_ret = static_cast(rl->passes.first); + } + + if (r_passindex) { + *r_passindex = (rpass == rpass_ret ? rp_index : pass); + } + + return rpass_ret; +} + +void BKE_image_get_tile_label(Image *ima, ImageTile *tile, char *label, int len_label) +{ + label[0] = '\0'; + if (ima == nullptr || tile == nullptr) { + return; + } + + if (tile->label[0]) { + BLI_strncpy(label, tile->label, len_label); + } + else { + BLI_snprintf(label, len_label, "%d", tile->tile_number); + } +} + +bool BKE_image_get_tile_info(char *filepath, ListBase *tiles, int *tile_start, int *tile_range) +{ + char filename[FILE_MAXFILE], dirname[FILE_MAXDIR]; + BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); + + BKE_image_ensure_tile_token(filename); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(filename, &tile_format); + + bool is_udim = true; + int min_udim = IMA_UDIM_MAX + 1; + int max_udim = 0; + int id; + + struct direntry *dir; + uint totfile = BLI_filelist_dir_contents(dirname, &dir); + for (int i = 0; i < totfile; i++) { + if (!(dir[i].type & S_IFREG)) { + continue; + } + + if (!BKE_image_get_tile_number_from_filepath(dir[i].relname, udim_pattern, tile_format, &id)) { + continue; + } + + if (id < 1001 || id > IMA_UDIM_MAX) { + is_udim = false; + break; + } + + BLI_addtail(tiles, BLI_genericNodeN(POINTER_FROM_INT(id))); + min_udim = min_ii(min_udim, id); + max_udim = max_ii(max_udim, id); + } + BLI_filelist_free(dir, totfile); + MEM_SAFE_FREE(udim_pattern); + + if (is_udim && min_udim <= IMA_UDIM_MAX) { + BLI_join_dirfile(filepath, FILE_MAX, dirname, filename); + + *tile_start = min_udim; + *tile_range = max_udim - min_udim + 1; + return true; + } + return false; +} + +ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label) +{ + if (ima->source != IMA_SRC_TILED) { + return nullptr; + } + + if (tile_number < 1001 || tile_number > IMA_UDIM_MAX) { + return nullptr; + } + + /* Search the first tile that has a higher number. + * We then insert before that to keep the list sorted. */ + ImageTile *next_tile; + for (next_tile = static_cast(ima->tiles.first); next_tile; + next_tile = next_tile->next) { + if (next_tile->tile_number == tile_number) { + /* Tile already exists. */ + return nullptr; + } + if (next_tile->tile_number > tile_number) { + break; + } + } + + ImageTile *tile = MEM_cnew("image new tile"); + tile->tile_number = tile_number; + + if (next_tile) { + BLI_insertlinkbefore(&ima->tiles, next_tile, tile); + } + else { + BLI_addtail(&ima->tiles, tile); + } + + if (label) { + BLI_strncpy(tile->label, label, sizeof(tile->label)); + } + + for (int eye = 0; eye < 2; eye++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = nullptr; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = nullptr; + } + } + } + BKE_image_partial_update_mark_full_update(ima); + + return tile; +} + +bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) +{ + if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) { + return false; + } + + if (BLI_listbase_is_single(&ima->tiles)) { + /* Can't remove the last remaining tile. */ + return false; + } + + image_free_tile(ima, tile); + BLI_remlink(&ima->tiles, tile); + MEM_freeN(tile); + + return true; +} + +void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number) +{ + if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) { + return; + } + + if (new_tile_number < 1001 || new_tile_number > IMA_UDIM_MAX) { + return; + } + + const int old_tile_number = tile->tile_number; + tile->tile_number = new_tile_number; + + if (BKE_image_is_multiview(ima)) { + const int totviews = BLI_listbase_count(&ima->views); + for (int i = 0; i < totviews; i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number, nullptr); + image_remove_ibuf(ima, i, old_tile_number); + image_assign_ibuf(ima, ibuf, i, new_tile_number); + IMB_freeImBuf(ibuf); + } + } + else { + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number, nullptr); + image_remove_ibuf(ima, 0, old_tile_number); + image_assign_ibuf(ima, ibuf, 0, new_tile_number); + IMB_freeImBuf(ibuf); + } + + for (int eye = 0; eye < 2; eye++) { + for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + + /* Reallocate GPU tile array. */ + if (ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution]); + ima->gputexture[TEXTARGET_2D_ARRAY][eye][resolution] = nullptr; + } + if (ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] != nullptr) { + GPU_texture_free(ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution]); + ima->gputexture[TEXTARGET_TILE_MAPPING][eye][resolution] = nullptr; + } + } + } + BKE_image_partial_update_mark_full_update(ima); +} + +static int tile_sort_cb(const void *a, const void *b) +{ + const ImageTile *tile_a = static_cast(a); + const ImageTile *tile_b = static_cast(b); + return (tile_a->tile_number > tile_b->tile_number) ? 1 : 0; +} + +void BKE_image_sort_tiles(struct Image *ima) +{ + if (ima == nullptr || ima->source != IMA_SRC_TILED) { + return; + } + + BLI_listbase_sort(&ima->tiles, tile_sort_cb); +} + +bool BKE_image_fill_tile(struct Image *ima, + ImageTile *tile, + int width, + int height, + const float color[4], + int gen_type, + int planes, + bool is_float) +{ + if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) { + return false; + } + + image_free_tile(ima, tile); + + ImBuf *tile_ibuf = add_ibuf_size( + width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings); + + if (tile_ibuf != nullptr) { + image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); + BKE_image_release_ibuf(ima, tile_ibuf, nullptr); + return true; + } + return false; +} + +void BKE_image_ensure_tile_token(char *filename) +{ + BLI_assert_msg(BLI_path_slash_find(filename) == nullptr, + "Only the file-name component should be used!"); + + /* Is there a '<' character in the filename? Assume tokens already present. */ + if (strstr(filename, "<") != nullptr) { + return; + } + + /* Is there a sequence of digits in the filename? */ + ushort digits; + char head[FILE_MAX], tail[FILE_MAX]; + BLI_path_sequence_decode(filename, head, tail, &digits); + if (digits == 4) { + sprintf(filename, "%s%s", head, tail); + return; + } + + /* Is there a sequence like u##_v#### in the filename? */ + uint cur = 0; + uint name_end = strlen(filename); + uint u_digits = 0; + uint v_digits = 0; + uint u_start = (uint)-1; + bool u_found = false; + bool v_found = false; + bool sep_found = false; + while (cur < name_end) { + if (filename[cur] == 'u') { + u_found = true; + u_digits = 0; + u_start = cur; + } + else if (filename[cur] == 'v') { + v_found = true; + v_digits = 0; + } + else if (u_found && !v_found) { + if (isdigit(filename[cur]) && u_digits < 2) { + u_digits++; + } + else if (filename[cur] == '_') { + sep_found = true; + } + else { + u_found = false; + } + } + else if (u_found && u_digits > 0 && v_found) { + if (isdigit(filename[cur])) { + if (v_digits < 4) { + v_digits++; + } + else { + u_found = false; + v_found = false; + } + } + else if (v_digits > 0) { + break; + } + } + + cur++; + } + + if (u_found && sep_found && v_found && (u_digits + v_digits > 1)) { + const char *token = ""; + const size_t token_length = strlen(token); + memmove(filename + u_start + token_length, filename + cur, name_end - cur); + memcpy(filename + u_start, token, token_length); + filename[u_start + token_length + (name_end - cur)] = '\0'; + } +} + +bool BKE_image_tile_filepath_exists(const char *filepath) +{ + BLI_assert(!BLI_path_is_rel(filepath)); + + char dirname[FILE_MAXDIR]; + BLI_split_dir_part(filepath, dirname, sizeof(dirname)); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(filepath, &tile_format); + + bool found = false; + struct direntry *dir; + uint totfile = BLI_filelist_dir_contents(dirname, &dir); + for (int i = 0; i < totfile; i++) { + if (!(dir[i].type & S_IFREG)) { + continue; + } + + int id; + if (!BKE_image_get_tile_number_from_filepath(dir[i].path, udim_pattern, tile_format, &id)) { + continue; + } + + if (id < 1001 || id > IMA_UDIM_MAX) { + continue; + } + + found = true; + break; + } + BLI_filelist_free(dir, totfile); + MEM_SAFE_FREE(udim_pattern); + + return found; +} + +char *BKE_image_get_tile_strformat(const char *filepath, eUDIM_TILE_FORMAT *r_tile_format) +{ + if (filepath == nullptr || r_tile_format == nullptr) { + return nullptr; + } + + if (strstr(filepath, "") != nullptr) { + *r_tile_format = UDIM_TILE_FORMAT_UDIM; + return BLI_str_replaceN(filepath, "", "%d"); + } + if (strstr(filepath, "") != nullptr) { + *r_tile_format = UDIM_TILE_FORMAT_UVTILE; + return BLI_str_replaceN(filepath, "", "u%d_v%d"); + } + + *r_tile_format = UDIM_TILE_FORMAT_NONE; + return nullptr; +} + +bool BKE_image_get_tile_number_from_filepath(const char *filepath, + const char *pattern, + eUDIM_TILE_FORMAT tile_format, + int *r_tile_number) +{ + if (filepath == nullptr || pattern == nullptr || r_tile_number == nullptr) { + return false; + } + + int u, v; + bool result = false; + + if (tile_format == UDIM_TILE_FORMAT_UDIM) { + if (sscanf(filepath, pattern, &u) == 1) { + *r_tile_number = u; + result = true; + } + } + else if (tile_format == UDIM_TILE_FORMAT_UVTILE) { + if (sscanf(filepath, pattern, &u, &v) == 2) { + *r_tile_number = 1001 + (u - 1) + ((v - 1) * 10); + result = true; + } + } + + return result; +} + +void BKE_image_set_filepath_from_tile_number(char *filepath, + const char *pattern, + eUDIM_TILE_FORMAT tile_format, + int tile_number) +{ + if (filepath == nullptr || pattern == nullptr) { + return; + } + + if (tile_format == UDIM_TILE_FORMAT_UDIM) { + sprintf(filepath, pattern, tile_number); + } + else if (tile_format == UDIM_TILE_FORMAT_UVTILE) { + int u = ((tile_number - 1001) % 10); + int v = ((tile_number - 1001) / 10); + sprintf(filepath, pattern, u + 1, v + 1); + } +} + +/* if layer or pass changes, we need an index for the imbufs list */ +/* note it is called for rendered results, but it doesn't use the index! */ +RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) +{ + RenderLayer *rl; + RenderPass *rpass = nullptr; + + if (rr == nullptr) { + return nullptr; + } + + if (iuser) { + short index = 0, rv_index, rl_index = 0; + bool is_stereo = (iuser->flag & IMA_SHOW_STEREO) && RE_RenderResult_is_stereo(rr); + + rv_index = is_stereo ? iuser->multiview_eye : iuser->view; + if (RE_HasCombinedLayer(rr)) { + rl_index += 1; + } + + for (rl = static_cast(rr->layers.first); rl; rl = rl->next, rl_index++) { + if (iuser->layer == rl_index) { + int rp_index; + rpass = image_render_pass_get(rl, iuser->pass, rv_index, &rp_index); + iuser->multi_index = index + rp_index; + break; + } + + index += BLI_listbase_count(&rl->passes); + } + } + + return rpass; +} + +void BKE_image_multiview_index(Image *ima, ImageUser *iuser) +{ + if (iuser) { + bool is_stereo = BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO); + if (is_stereo) { + iuser->multi_index = iuser->multiview_eye; + } + else { + if ((iuser->view < 0) || + (iuser->view >= BLI_listbase_count_at_most(&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 doesn't use the index! */ +bool BKE_image_is_multilayer(Image *ima) +{ + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { + 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; +} + +bool BKE_image_is_multiview(Image *ima) +{ + ImageView *view = static_cast(ima->views.first); + return (view && (view->next || view->name[0])); +} + +bool BKE_image_is_stereo(Image *ima) +{ + return BKE_image_is_multiview(ima) && + (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) && + BLI_findstring(&ima->views, STEREO_RIGHT_NAME, offsetof(ImageView, name))); +} + +static void image_init_multilayer_multiview(Image *ima, RenderResult *rr) +{ + /* update image views from render views, but only if they actually changed, + * to avoid invalid memory access during render. ideally these should always + * be acquired with a mutex along with the render result, but there are still + * some places with just an image pointer that need to access views */ + if (rr && BLI_listbase_count(&ima->views) == BLI_listbase_count(&rr->views)) { + ImageView *iv = static_cast(ima->views.first); + RenderView *rv = static_cast(rr->views.first); + bool modified = false; + for (; rv; rv = rv->next, iv = iv->next) { + modified |= !STREQ(rv->name, iv->name); + } + if (!modified) { + return; + } + } + + BKE_image_free_views(ima); + + if (rr) { + LISTBASE_FOREACH (RenderView *, rv, &rr->views) { + ImageView *iv = MEM_cnew("Viewer Image View"); + STRNCPY(iv->name, rv->name); + BLI_addtail(&ima->views, iv); + } + } +} + +RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima) +{ + RenderResult *rr = nullptr; + if (ima->rr) { + rr = ima->rr; + } + else if (ima->type == IMA_TYPE_R_RESULT) { + if (ima->render_slot == ima->last_render_slot) { + rr = RE_AcquireResultRead(RE_GetSceneRender(scene)); + } + else { + rr = BKE_image_get_renderslot(ima, ima->render_slot)->render; + BKE_image_partial_update_mark_full_update(ima); + } + + /* set proper views */ + image_init_multilayer_multiview(ima, rr); + } + + return rr; +} + +void BKE_image_release_renderresult(Scene *scene, Image *ima) +{ + if (ima->rr) { + /* pass */ + } + else if (ima->type == IMA_TYPE_R_RESULT) { + if (ima->render_slot == ima->last_render_slot) { + RE_ReleaseResult(RE_GetSceneRender(scene)); + } + } +} + +bool BKE_image_is_openexr(struct Image *ima) +{ +#ifdef WITH_OPENEXR + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { + return BLI_path_extension_check(ima->filepath, ".exr"); + } +#else + UNUSED_VARS(ima); +#endif + return false; +} + +void BKE_image_backup_render(Scene *scene, Image *ima, bool free_current_slot) +{ + /* called right before rendering, ima->renderslots contains render + * result pointers for everything but the current render */ + Render *re = RE_GetSceneRender(scene); + + /* Ensure we always have a valid render slot. */ + if (!ima->renderslots.first) { + BKE_image_add_renderslot(ima, nullptr); + ima->render_slot = 0; + ima->last_render_slot = 0; + } + else if (ima->render_slot >= BLI_listbase_count(&ima->renderslots)) { + ima->render_slot = 0; + ima->last_render_slot = 0; + } + + RenderSlot *last_slot = BKE_image_get_renderslot(ima, ima->last_render_slot); + RenderSlot *cur_slot = BKE_image_get_renderslot(ima, ima->render_slot); + + if (last_slot && ima->render_slot != ima->last_render_slot) { + last_slot->render = nullptr; + RE_SwapResult(re, &last_slot->render); + + if (cur_slot->render) { + if (free_current_slot) { + BKE_image_clear_renderslot(ima, nullptr, ima->render_slot); + } + else { + RE_SwapResult(re, &cur_slot->render); + } + } + } + + ima->last_render_slot = ima->render_slot; +} + +/**************************** multiview load openexr *********************************/ + +static void image_add_view(Image *ima, const char *viewname, const char *filepath) +{ + ImageView *iv; + + iv = static_cast(MEM_mallocN(sizeof(ImageView), "Viewer Image View")); + STRNCPY(iv->name, viewname); + STRNCPY(iv->filepath, 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 = static_cast( + BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name))); + + if (left_iv == nullptr) { + BLI_addhead(&ima->views, iv); + } + else { + BLI_insertlinkafter(&ima->views, left_iv, iv); + } + } + else { + BLI_addtail(&ima->views, iv); + } +} + +/* 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; + bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL); + + /* only load rr once for multiview */ + if (!ima->rr) { + ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y); + } + + IMB_exr_close(ibuf->userdata); + + ibuf->userdata = nullptr; + if (ima->rr != nullptr) { + ima->rr->framenr = framenr; + BKE_stamp_info_from_imbuf(ima->rr, ibuf); + } + + /* set proper views */ + image_init_multilayer_multiview(ima, ima->rr); +} +#endif /* WITH_OPENEXR */ + +/* common stuff to do with images after loading */ +static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) +{ + /* Preview is null when it has never been used as an icon before. + * Never handle previews/icons outside of main thread. */ + if (G.background == 0 && ima->preview == nullptr && BLI_thread_is_main()) { + BKE_icon_changed(BKE_icon_id_ensure(&ima->id)); + } + + /* timer */ + BKE_image_tag_time(ima); + + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + /* Images should never get loaded if the corresponding tile does not exist, + * but we should at least not crash if it happens due to a bug elsewhere. */ + BLI_assert(tile != nullptr); + UNUSED_VARS_NDEBUG(tile); +} + +static int imbuf_alpha_flags_for_image(Image *ima) +{ + switch (ima->alpha_mode) { + case IMA_ALPHA_STRAIGHT: + return 0; + case IMA_ALPHA_PREMUL: + return IB_alphamode_premul; + case IMA_ALPHA_CHANNEL_PACKED: + return IB_alphamode_channel_packed; + case IMA_ALPHA_IGNORE: + return IB_alphamode_ignore; + } + + return 0; +} + +/* the number of files will vary according to the stereo format */ +static int image_num_files(Image *ima) +{ + const bool is_multiview = BKE_image_is_multiview(ima); + + if (!is_multiview) { + return 1; + } + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + + return BLI_listbase_count(&ima->views); +} + +static ImBuf *load_sequence_single( + Image *ima, ImageUser *iuser, int frame, const int view_id, bool *r_cache_ibuf) +{ + struct ImBuf *ibuf; + char name[FILE_MAX]; + int flag; + ImageUser iuser_t{}; + + *r_cache_ibuf = true; + + ima->lastframe = frame; + + if (iuser) { + iuser_t = *iuser; + } + else { + /* BKE_image_user_file_path() uses this value for file name for sequences. */ + iuser_t.framenr = frame; + /* TODO(sergey): Do we need to initialize something else here? */ + } + + iuser_t.view = view_id; + BKE_image_user_file_path(&iuser_t, ima, name); + + flag = IB_rect | IB_multilayer | IB_metadata; + flag |= imbuf_alpha_flags_for_image(ima); + + /* read ibuf */ + ibuf = IMB_loadiffname(name, flag, ima->colorspace_settings.name); + +#if 0 + if (ibuf) { + printf(AT " loaded %s\n", name); + } + else { + printf(AT " missed %s\n", name); + } +#endif + + if (ibuf) { +#ifdef WITH_OPENEXR + if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) { + /* Handle multilayer and multiview cases, don't assign ibuf here. + * will be set layer in BKE_image_acquire_ibuf from ima->rr. */ + if (IMB_exr_has_multilayer(ibuf->userdata)) { + image_create_multilayer(ima, ibuf, frame); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = nullptr; + /* Null ibuf in the cache means the image failed to load. However for multilayer we load + * pixels into RenderResult instead and intentionally leave ibuf null. */ + *r_cache_ibuf = false; + } + } + else { + image_init_after_load(ima, iuser, ibuf); + } +#else + image_init_after_load(ima, iuser, ibuf); +#endif + } + + return ibuf; +} + +static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, int frame) +{ + struct ImBuf *ibuf = nullptr; + const bool is_multiview = BKE_image_is_multiview(ima); + const int totfiles = image_num_files(ima); + + if (!is_multiview) { + bool put_in_cache; + ibuf = load_sequence_single(ima, iuser, frame, 0, &put_in_cache); + if (put_in_cache) { + image_assign_ibuf(ima, ibuf, 0, entry); + } + } + else { + const int totviews = BLI_listbase_count(&ima->views); + Array ibuf_arr(totviews); + Array cache_ibuf_arr(totviews); + + for (int i = 0; i < totfiles; i++) { + ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &cache_ibuf_arr[i]); + } + + if (BKE_image_is_stereo(ima) && 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)]; + + for (int i = 0; i < totviews; i++) { + if (cache_ibuf_arr[i]) { + image_assign_ibuf(ima, ibuf_arr[i], i, entry); + } + } + + /* "remove" the others (decrease their refcount) */ + for (int i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + return ibuf; +} + +static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int entry, int frame) +{ + struct ImBuf *ibuf = nullptr; + + /* either we load from RenderResult, or we have to load a new one */ + + /* check for new RenderResult */ + if (ima->rr == nullptr || frame != ima->rr->framenr) { + if (ima->rr) { + /* Cached image buffers shares pointers with render result, + * need to ensure there's no image buffers are hanging around + * with dead links after freeing the render result. + */ + image_free_cached_frames(ima); + RE_FreeRenderResult(ima->rr); + ima->rr = nullptr; + } + + ibuf = image_load_sequence_file(ima, iuser, entry, frame); + + if (ibuf) { /* actually an error */ + ima->type = IMA_TYPE_IMAGE; + printf("error, multi is normal image\n"); + } + } + if (ima->rr) { + RenderPass *rpass = BKE_image_multilayer_index(ima->rr, iuser); + + if (rpass) { + // printf("load from pass %s\n", rpass->name); + /* since we free render results, we copy the rect */ + ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); + ibuf->rect_float = static_cast(MEM_dupallocN(rpass->rect)); + ibuf->flags |= IB_rectfloat; + ibuf->mall = IB_rectfloat; + ibuf->channels = rpass->channels; + + BKE_imbuf_stamp_info(ima->rr, ibuf); + + image_init_after_load(ima, iuser, ibuf); + image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry); + } + // else printf("pass not found\n"); + } + + return ibuf; +} + +static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const int view_id) +{ + struct ImBuf *ibuf = nullptr; + ImageAnim *ia; + + ia = static_cast(BLI_findlink(&ima->anims, view_id)); + + if (ia->anim == nullptr) { + char str[FILE_MAX]; + int flags = IB_rect; + ImageUser iuser_t{}; + + if (ima->flag & IMA_DEINTERLACE) { + flags |= IB_animdeinterlace; + } + + 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. */ + ia->anim = openanim(str, flags, 0, ima->colorspace_settings.name); + + /* let's initialize this user */ + if (ia->anim && iuser && iuser->frames == 0) { + iuser->frames = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); + } + } + + 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(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); + + if (ibuf) { + image_init_after_load(ima, iuser, ibuf); + } + } + + return ibuf; +} + +static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = nullptr; + const bool is_multiview = BKE_image_is_multiview(ima); + const int totfiles = image_num_files(ima); + + if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) { + image_free_anims(ima); + + for (int i = 0; i < totfiles; i++) { + /* allocate the ImageAnim */ + ImageAnim *ia = MEM_cnew("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 { + const int totviews = BLI_listbase_count(&ima->views); + Array ibuf_arr(totviews); + + for (int i = 0; i < totfiles; i++) { + ibuf_arr[i] = load_movie_single(ima, iuser, frame, i); + } + + if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D) { + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + } + + for (int i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + /* "remove" the others (decrease their refcount) */ + for (int i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + return ibuf; +} + +static ImBuf *load_image_single(Image *ima, + ImageUser *iuser, + int cfra, + const int view_id, + const bool has_packed, + bool *r_cache_ibuf) +{ + char filepath[FILE_MAX]; + struct ImBuf *ibuf = nullptr; + int flag; + + *r_cache_ibuf = true; + + /* is there a PackedFile with this image ? */ + if (has_packed) { + ImagePackedFile *imapf; + + flag = IB_rect | IB_multilayer; + flag |= imbuf_alpha_flags_for_image(ima); + + imapf = static_cast(BLI_findlink(&ima->packedfiles, view_id)); + if (imapf->packedfile) { + ibuf = IMB_ibImageFromMemory((unsigned char *)imapf->packedfile->data, + imapf->packedfile->size, + flag, + ima->colorspace_settings.name, + ""); + } + } + else { + ImageUser iuser_t{}; + + flag = IB_rect | IB_multilayer | IB_metadata; + flag |= imbuf_alpha_flags_for_image(ima); + + /* get the correct filepath */ + BKE_image_user_frame_calc(ima, iuser, cfra); + + 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(filepath, flag, ima->colorspace_settings.name); + } + + if (ibuf) { +#ifdef WITH_OPENEXR + if (ibuf->ftype == IMB_FTYPE_OPENEXR && ibuf->userdata) { + /* Handle multilayer and multiview cases, don't assign ibuf here. + * will be set layer in BKE_image_acquire_ibuf from ima->rr. */ + if (IMB_exr_has_multilayer(ibuf->userdata)) { + image_create_multilayer(ima, ibuf, cfra); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = nullptr; + /* Null ibuf in the cache means the image failed to load. However for multilayer we load + * pixels into RenderResult instead and intentionally leave ibuf null. */ + *r_cache_ibuf = false; + } + } + else +#endif + { + image_init_after_load(ima, iuser, ibuf); + + /* Make packed file for auto-pack. */ + if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) { + ImagePackedFile *imapf = static_cast( + MEM_mallocN(sizeof(ImagePackedFile), "Image Pack-file")); + BLI_addtail(&ima->packedfiles, imapf); + + STRNCPY(imapf->filepath, filepath); + imapf->packedfile = BKE_packedfile_new( + nullptr, filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + } + } + } + + 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 = nullptr; + const bool is_multiview = BKE_image_is_multiview(ima); + const int 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_at_most(&ima->packedfiles, totfiles + 1)) { + image_free_packedfiles(ima); + has_packed = false; + } + } + + if (!is_multiview) { + bool put_in_cache; + ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &put_in_cache); + if (put_in_cache) { + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + } + } + else { + const int totviews = BLI_listbase_count(&ima->views); + BLI_assert(totviews > 0); + + Array ibuf_arr(totviews); + Array cache_ibuf_arr(totviews); + + for (int i = 0; i < totfiles; i++) { + ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &cache_ibuf_arr[i]); + } + + /* multi-views/multi-layers OpenEXR files directly populate ima, and return null ibuf... */ + if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D && ibuf_arr[0] && + totfiles == 1 && totviews >= 2) { + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + } + + /* return the original requested ImBuf */ + int i = (iuser && iuser->multi_index < totviews) ? iuser->multi_index : 0; + ibuf = ibuf_arr[i]; + + for (i = 0; i < totviews; i++) { + if (cache_ibuf_arr[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]); + } + } + } + + return ibuf; +} + +static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) +{ + ImBuf *ibuf = nullptr; + + if (ima->rr == nullptr) { + ibuf = image_load_image_file(ima, iuser, 0); + if (ibuf) { /* actually an error */ + ima->type = IMA_TYPE_IMAGE; + return ibuf; + } + } + if (ima->rr) { + RenderPass *rpass = BKE_image_multilayer_index(ima->rr, iuser); + + if (rpass) { + ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); + + image_init_after_load(ima, iuser, ibuf); + + ibuf->rect_float = rpass->rect; + ibuf->flags |= IB_rectfloat; + ibuf->channels = rpass->channels; + + BKE_imbuf_stamp_info(ima->rr, ibuf); + + image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); + } + } + + return ibuf; +} + +/* showing RGBA result itself (from compo/sequence) or + * like exr, using layers etc */ +/* always returns a single ibuf, also during render progress */ +static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_lock) +{ + Render *re; + RenderView *rv; + float *rectf, *rectz; + unsigned int *rect; + float dither; + int channels, layer, pass; + ImBuf *ibuf; + int from_render = (ima->render_slot == ima->last_render_slot); + int actview; + + if (!(iuser && iuser->scene)) { + return nullptr; + } + + /* if we the caller is not going to release the lock, don't give the image */ + if (!r_lock) { + return nullptr; + } + + re = RE_GetSceneRender(iuser->scene); + + channels = 4; + layer = iuser->layer; + pass = iuser->pass; + actview = iuser->view; + + if (BKE_image_is_stereo(ima) && (iuser->flag & IMA_SHOW_STEREO)) { + actview = iuser->multiview_eye; + } + + RenderResult rres{}; + RenderSlot *slot; + if (from_render) { + RE_AcquireResultImage(re, &rres, actview); + } + else if ((slot = BKE_image_get_renderslot(ima, ima->render_slot))->render) { + /* Unfortunately each field needs to be set individually because RenderResult + * contains volatile fields and using memcpy would invoke undefined behavior with c++. */ + rres.next = slot->render->next; + rres.prev = slot->render->prev; + rres.rectx = slot->render->rectx; + rres.recty = slot->render->recty; + rres.sample_nr = slot->render->sample_nr; + rres.rect32 = slot->render->rect32; + rres.rectf = slot->render->rectf; + rres.rectz = slot->render->rectz; + rres.tilerect = slot->render->tilerect; + rres.xof = slot->render->xof; + rres.yof = slot->render->yof; + rres.layers = slot->render->layers; + rres.views = slot->render->views; + rres.renrect.xmin = slot->render->renrect.xmin; + rres.renrect.xmax = slot->render->renrect.xmax; + rres.renrect.ymin = slot->render->renrect.ymin; + rres.renrect.ymax = slot->render->renrect.ymax; + rres.renlay = slot->render->renlay; + rres.framenr = slot->render->framenr; + rres.text = slot->render->text; + rres.error = slot->render->error; + rres.stamp_data = slot->render->stamp_data; + rres.passes_allocated = slot->render->passes_allocated; + rres.have_combined = ((RenderView *)rres.views.first)->rectf != nullptr; + } + + if (!(rres.rectx > 0 && rres.recty > 0)) { + if (from_render) { + RE_ReleaseResultImage(re); + } + return nullptr; + } + + /* release is done in BKE_image_release_ibuf using r_lock */ + if (from_render) { + BLI_thread_lock(LOCK_VIEWER); + *r_lock = re; + rv = nullptr; + } + else { + rv = static_cast(BLI_findlink(&rres.views, actview)); + if (rv == nullptr) { + rv = static_cast(rres.views.first); + } + } + + /* this gives active layer, composite or sequence result */ + if (rv == nullptr) { + rect = (unsigned int *)rres.rect32; + rectf = rres.rectf; + rectz = rres.rectz; + } + else { + rect = (unsigned int *)rv->rect32; + rectf = rv->rectf; + rectz = rv->rectz; + } + + dither = iuser->scene->r.dither_intensity; + + /* combined layer gets added as first layer */ + if (rres.have_combined && layer == 0) { + /* pass */ + } + else if (rect && layer == 0) { + /* rect32 is set when there's a Sequence pass, this pass seems + * to have layer=0 (this is from image_buttons.c) + * in this case we ignore float buffer, because it could have + * hung from previous pass which was float + */ + rectf = nullptr; + } + else if (rres.layers.first) { + RenderLayer *rl = static_cast( + BLI_findlink(&rres.layers, layer - (rres.have_combined ? 1 : 0))); + if (rl) { + RenderPass *rpass = image_render_pass_get(rl, pass, actview, nullptr); + if (rpass) { + rectf = rpass->rect; + if (pass != 0) { + channels = rpass->channels; + dither = 0.0f; /* don't dither passes */ + } + } + + for (rpass = static_cast(rl->passes.first); rpass; rpass = rpass->next) { + if (STREQ(rpass->name, RE_PASSNAME_Z) && rpass->view_id == actview) { + rectz = rpass->rect; + } + } + } + } + + ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); + + /* make ibuf if needed, and initialize it */ + if (ibuf == nullptr) { + ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, 0); + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + } + + /* Set color space settings for a byte buffer. + * + * This is mainly to make it so color management treats byte buffer + * from render result with Save Buffers enabled as final display buffer + * and doesn't apply any color management on it. + * + * For other cases we need to be sure it stays to default byte buffer space. + */ + if (ibuf->rect != rect) { + const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_DEFAULT_BYTE); + IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace); + } + + /* invalidate color managed buffers if render result changed */ + BLI_thread_lock(LOCK_COLORMANAGE); + if (ibuf->x != rres.rectx || ibuf->y != rres.recty || ibuf->rect_float != rectf) { + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + } + + ibuf->x = rres.rectx; + ibuf->y = rres.recty; + + if (rect) { + imb_freerectImBuf(ibuf); + ibuf->rect = rect; + } + else { + /* byte buffer of render result has been freed, make sure image buffers + * does not reference to this buffer anymore + * need check for whether byte buffer was allocated and owned by image itself + * or if it's reusing buffer from render result + */ + if ((ibuf->mall & IB_rect) == 0) { + ibuf->rect = nullptr; + } + } + + if (rectf) { + ibuf->rect_float = rectf; + ibuf->flags |= IB_rectfloat; + ibuf->channels = channels; + } + else { + ibuf->rect_float = nullptr; + ibuf->flags &= ~IB_rectfloat; + } + + if (rectz) { + ibuf->zbuf_float = rectz; + ibuf->flags |= IB_zbuffloat; + } + else { + ibuf->zbuf_float = nullptr; + ibuf->flags &= ~IB_zbuffloat; + } + + /* TODO(sergey): Make this faster by either simply referencing the stamp + * or by changing both ImBug and RenderResult to use same data type to + * store metadata. */ + if (ibuf->metadata != nullptr) { + IMB_metadata_free(ibuf->metadata); + ibuf->metadata = nullptr; + } + BKE_imbuf_stamp_info(&rres, ibuf); + + BLI_thread_unlock(LOCK_COLORMANAGE); + + ibuf->dither = dither; + + return ibuf; +} + +static int 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 == nullptr); + int index = BKE_image_has_multiple_ibufs(ima) ? 0 : IMA_NO_INDEX; + + if (is_multilayer) { + return iuser ? iuser->multi_index : index; + } + if (is_backdrop) { + if (BKE_image_is_stereo(ima)) { + /* backdrop hackaround (since there is no iuser */ + return ima->eye; + } + } + else if (BKE_image_is_multiview(ima)) { + return iuser ? iuser->multi_index : index; + } + + return index; +} + +static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry, int *r_index) +{ + int frame = 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; + } + else if (ima->source == IMA_SRC_SEQUENCE) { + if (ima->type == IMA_TYPE_IMAGE) { + frame = iuser ? iuser->framenr : ima->lastframe; + } + else if (ima->type == IMA_TYPE_MULTILAYER) { + frame = iuser ? iuser->framenr : ima->lastframe; + } + } + else if (ima->source == IMA_SRC_TILED) { + frame = image_get_tile_number_from_iuser(ima, iuser); + } + + *r_entry = frame; + *r_index = index; +} + +/* Get the ibuf from an image cache for a given image user. + * + * Returns referenced image buffer if it exists, callee is to + * call IMB_freeImBuf to de-reference the image buffer after + * it's done handling it. + */ +static ImBuf *image_get_cached_ibuf( + Image *ima, ImageUser *iuser, int *r_entry, int *r_index, bool *r_is_cached_empty) +{ + ImBuf *ibuf = nullptr; + int entry = 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) { + entry = iuser ? iuser->framenr : ima->lastframe; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); + ima->lastframe = entry; + } + else if (ima->source == IMA_SRC_SEQUENCE) { + if (ima->type == IMA_TYPE_IMAGE) { + entry = iuser ? iuser->framenr : ima->lastframe; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); + ima->lastframe = entry; + } + else if (ima->type == IMA_TYPE_MULTILAYER) { + entry = iuser ? iuser->framenr : ima->lastframe; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); + } + } + else if (ima->source == IMA_SRC_FILE) { + if (ima->type == IMA_TYPE_IMAGE) { + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); + } + else if (ima->type == IMA_TYPE_MULTILAYER) { + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); + } + } + else if (ima->source == IMA_SRC_GENERATED) { + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); + } + else if (ima->source == IMA_SRC_VIEWER) { + /* always verify entirely, not that this shouldn't happen + * as part of texture sampling in rendering anyway, so not + * a big bottleneck */ + } + else if (ima->source == IMA_SRC_TILED) { + if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { + entry = image_get_tile_number_from_iuser(ima, iuser); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); + } + } + + if (r_entry) { + *r_entry = entry; + } + + if (r_index) { + *r_index = index; + } + + return ibuf; +} + +BLI_INLINE bool image_quick_test(Image *ima, const ImageUser *iuser) +{ + if (ima == nullptr) { + return false; + } + + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + if (tile == nullptr) { + return false; + } + + return true; +} + +/** + * Checks optional #ImageUser and verifies/creates #ImBuf. + * + * \warning Not thread-safe, so callee should worry about thread locks. + */ +static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) +{ + ImBuf *ibuf = nullptr; + int entry = 0, index = 0; + + if (r_lock) { + *r_lock = nullptr; + } + + /* quick reject tests */ + if (!image_quick_test(ima, iuser)) { + return nullptr; + } + + bool is_cached_empty = false; + ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index, &is_cached_empty); + if (is_cached_empty) { + return nullptr; + } + + if (ibuf == nullptr) { + /* we are sure we have to load the ibuf, using source and type */ + if (ima->source == IMA_SRC_MOVIE) { + /* source is from single file, use flipbook to store ibuf */ + ibuf = image_load_movie_file(ima, iuser, entry); + } + else if (ima->source == IMA_SRC_SEQUENCE) { + if (ima->type == IMA_TYPE_IMAGE) { + /* regular files, ibufs in flipbook, allows saving */ + ibuf = image_load_sequence_file(ima, iuser, entry, entry); + } + /* no else; on load the ima type can change */ + if (ima->type == IMA_TYPE_MULTILAYER) { + /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ + ibuf = image_load_sequence_multilayer(ima, iuser, entry, entry); + } + } + else if (ima->source == IMA_SRC_TILED) { + if (ima->type == IMA_TYPE_IMAGE) { + /* regular files, ibufs in flipbook, allows saving */ + ibuf = image_load_sequence_file(ima, iuser, entry, 0); + } + /* no else; on load the ima type can change */ + if (ima->type == IMA_TYPE_MULTILAYER) { + /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ + ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); + } + } + else if (ima->source == IMA_SRC_FILE) { + + if (ima->type == IMA_TYPE_IMAGE) { + ibuf = image_load_image_file(ima, iuser, entry); /* cfra only for '#', this global is OK */ + } + /* no else; on load the ima type can change */ + if (ima->type == IMA_TYPE_MULTILAYER) { + /* keeps render result, stores ibufs in listbase, allows saving */ + ibuf = image_get_ibuf_multilayer(ima, iuser); + } + } + else if (ima->source == IMA_SRC_GENERATED) { + /* generated is: ibuf is allocated dynamically */ + /* UV testgrid or black or solid etc */ + if (ima->gen_x == 0) { + ima->gen_x = 1024; + } + if (ima->gen_y == 0) { + ima->gen_y = 1024; + } + if (ima->gen_depth == 0) { + ima->gen_depth = 24; + } + ibuf = add_ibuf_size(ima->gen_x, + ima->gen_y, + ima->filepath, + ima->gen_depth, + (ima->gen_flag & IMA_GEN_FLOAT) != 0, + ima->gen_type, + ima->gen_color, + &ima->colorspace_settings); + image_assign_ibuf(ima, ibuf, index, 0); + } + else if (ima->source == IMA_SRC_VIEWER) { + if (ima->type == IMA_TYPE_R_RESULT) { + /* always verify entirely, and potentially + * returns pointer to release later */ + ibuf = image_get_render_result(ima, iuser, r_lock); + } + else if (ima->type == IMA_TYPE_COMPOSITE) { + /* requires lock/unlock, otherwise don't return image */ + if (r_lock) { + /* unlock in BKE_image_release_ibuf */ + BLI_thread_lock(LOCK_VIEWER); + *r_lock = ima; + + /* XXX anim play for viewer nodes not yet supported */ + entry = 0; // XXX iuser ? iuser->framenr : 0; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, nullptr); + + if (!ibuf) { + /* Composite Viewer, all handled in compositor */ + /* fake ibuf, will be filled in compositor */ + ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat); + image_assign_ibuf(ima, ibuf, index, entry); + } + } + } + } + + /* We only want movies and sequences to be memory limited. */ + if (ibuf != nullptr && !ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + ibuf->userflags |= IB_PERSISTENT; + } + } + + BKE_image_tag_time(ima); + + return ibuf; +} + +ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) +{ + /* NOTE: same as #image_acquire_ibuf, but can be used to retrieve images being rendered in + * a thread safe way, always call both acquire and release. */ + + if (ima == nullptr) { + return nullptr; + } + + ImBuf *ibuf; + + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + + ibuf = image_acquire_ibuf(ima, iuser, r_lock); + + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); + + return ibuf; +} + +void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock) +{ + if (lock != nullptr) { + /* for getting image during threaded render / compositing, need to release */ + if (lock == ima) { + BLI_thread_unlock(LOCK_VIEWER); /* viewer image */ + } + else { + RE_ReleaseResultImage(static_cast(lock)); /* render result */ + BLI_thread_unlock(LOCK_VIEWER); /* view image imbuf */ + } + } + + if (ibuf) { + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + IMB_freeImBuf(ibuf); + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); + } +} + +bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) +{ + ImBuf *ibuf; + + /* quick reject tests */ + if (!image_quick_test(ima, iuser)) { + return false; + } + + BLI_mutex_lock(static_cast(ima->runtime.cache_mutex)); + + ibuf = image_get_cached_ibuf(ima, iuser, nullptr, nullptr, nullptr); + + if (!ibuf) { + ibuf = image_acquire_ibuf(ima, iuser, nullptr); + } + + BLI_mutex_unlock(static_cast(ima->runtime.cache_mutex)); + + IMB_freeImBuf(ibuf); + + return ibuf != nullptr; +} + +/* ******** Pool for image buffers ******** */ + +struct ImagePoolItem { + struct ImagePoolItem *next, *prev; + Image *image; + ImBuf *ibuf; + int index; + int entry; +}; + +struct ImagePool { + ListBase image_buffers; + BLI_mempool *memory_pool; + ThreadMutex mutex; +}; + +ImagePool *BKE_image_pool_new(void) +{ + ImagePool *pool = MEM_cnew("Image Pool"); + pool->memory_pool = BLI_mempool_create(sizeof(ImagePoolItem), 0, 128, BLI_MEMPOOL_NOP); + + BLI_mutex_init(&pool->mutex); + + return pool; +} + +void BKE_image_pool_free(ImagePool *pool) +{ + /* Use single lock to dereference all the image buffers. */ + BLI_mutex_lock(&pool->mutex); + for (ImagePoolItem *item = static_cast(pool->image_buffers.first); + item != nullptr; + item = item->next) { + if (item->ibuf != nullptr) { + BLI_mutex_lock(static_cast(item->image->runtime.cache_mutex)); + IMB_freeImBuf(item->ibuf); + BLI_mutex_unlock(static_cast(item->image->runtime.cache_mutex)); + } + } + BLI_mutex_unlock(&pool->mutex); + + BLI_mempool_destroy(pool->memory_pool); + + BLI_mutex_end(&pool->mutex); + + MEM_freeN(pool); +} + +BLI_INLINE ImBuf *image_pool_find_item( + ImagePool *pool, Image *image, int entry, int index, bool *found) +{ + ImagePoolItem *item; + + *found = false; + + for (item = static_cast(pool->image_buffers.first); item; item = item->next) { + if (item->image == image && item->entry == entry && item->index == index) { + *found = true; + return item->ibuf; + } + } + + return nullptr; +} + +ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool) +{ + ImBuf *ibuf; + int index, entry; + bool found; + + if (!image_quick_test(ima, iuser)) { + return nullptr; + } + + if (pool == nullptr) { + /* Pool could be null, in this case use general acquire function. */ + return BKE_image_acquire_ibuf(ima, iuser, nullptr); + } + + image_get_entry_and_index(ima, iuser, &entry, &index); + + /* Use double-checked locking, to avoid locking when the requested image buffer is already in the + * pool. */ + + ibuf = image_pool_find_item(pool, ima, entry, index, &found); + if (found) { + return ibuf; + } + + /* Lock the pool, to allow thread-safe modification of the content of the pool. */ + BLI_mutex_lock(&pool->mutex); + + ibuf = image_pool_find_item(pool, ima, entry, index, &found); + + /* Will also create item even in cases image buffer failed to load, + * prevents trying to load the same buggy file multiple times. */ + if (!found) { + ImagePoolItem *item; + + /* Thread-safe acquisition of an image buffer from the image. + * The acquisition does not use image pools, so there is no risk of recursive or out-of-order + * mutex locking. */ + ibuf = BKE_image_acquire_ibuf(ima, iuser, nullptr); + + item = static_cast(BLI_mempool_alloc(pool->memory_pool)); + item->image = ima; + item->entry = entry; + item->index = index; + item->ibuf = ibuf; + + BLI_addtail(&pool->image_buffers, item); + } + + BLI_mutex_unlock(&pool->mutex); + + return ibuf; +} + +void BKE_image_pool_release_ibuf(Image *ima, ImBuf *ibuf, ImagePool *pool) +{ + /* if pool wasn't actually used, use general release stuff, + * for pools image buffers will be dereferenced on pool free + */ + if (pool == nullptr) { + BKE_image_release_ibuf(ima, ibuf, nullptr); + } +} + +int BKE_image_user_frame_get(const ImageUser *iuser, int cfra, bool *r_is_in_range) +{ + const int len = iuser->frames; + + if (r_is_in_range) { + *r_is_in_range = false; + } + + if (len == 0) { + return 0; + } + + int framenr; + cfra = cfra - iuser->sfra + 1; + + /* cyclic */ + if (iuser->cycl) { + cfra = ((cfra) % len); + if (cfra < 0) { + cfra += len; + } + if (cfra == 0) { + cfra = len; + } + + if (r_is_in_range) { + *r_is_in_range = true; + } + } + + if (cfra < 0) { + cfra = 0; + } + else if (cfra > len) { + cfra = len; + } + else { + if (r_is_in_range) { + *r_is_in_range = true; + } + } + + /* transform to images space */ + framenr = cfra; + if (framenr > iuser->frames) { + framenr = iuser->frames; + } + + if (iuser->cycl) { + framenr = ((framenr) % len); + while (framenr < 0) { + framenr += len; + } + if (framenr == 0) { + framenr = len; + } + } + + /* important to apply after else we can't loop on frames 100 - 110 for eg. */ + framenr += iuser->offset; + + return framenr; +} + +void BKE_image_user_frame_calc(Image *ima, ImageUser *iuser, int cfra) +{ + if (iuser) { + if (ima && BKE_image_is_animated(ima)) { + /* Compute current frame for animated image. */ + bool is_in_range; + const int framenr = BKE_image_user_frame_get(iuser, cfra, &is_in_range); + + if (is_in_range) { + iuser->flag |= IMA_USER_FRAME_IN_RANGE; + } + else { + iuser->flag &= ~IMA_USER_FRAME_IN_RANGE; + } + + iuser->framenr = framenr; + } + else { + /* Set fixed frame number for still image. */ + iuser->framenr = 0; + iuser->flag |= IMA_USER_FRAME_IN_RANGE; + } + + if (ima && ima->gpuframenr != iuser->framenr) { + /* NOTE: a single texture and refresh doesn't really work when + * multiple image users may use different frames, this is to + * be improved with perhaps a GPU texture cache. */ + BKE_image_partial_update_mark_full_update(ima); + ima->gpuframenr = iuser->framenr; + } + + iuser->flag &= ~IMA_NEED_FRAME_RECALC; + } +} + +/* goes over all ImageUsers, and sets frame numbers if auto-refresh is set */ +static void image_editors_update_frame(Image *ima, + ID *UNUSED(iuser_id), + ImageUser *iuser, + void *customdata) +{ + int cfra = *(int *)customdata; + + if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC)) { + BKE_image_user_frame_calc(ima, iuser, cfra); + } +} + +void BKE_image_editors_update_frame(const Main *bmain, int cfra) +{ + /* This only updates images used by the user interface. For others the + * dependency graph will call BKE_image_user_id_eval_animation. */ + wmWindowManager *wm = static_cast(bmain->wm.first); + image_walk_id_all_users(&wm->id, false, &cfra, image_editors_update_frame); +} + +static void image_user_id_has_animation(Image *ima, + ID *UNUSED(iuser_id), + ImageUser *UNUSED(iuser), + void *customdata) +{ + if (ima && BKE_image_is_animated(ima)) { + *(bool *)customdata = true; + } +} + +bool BKE_image_user_id_has_animation(ID *id) +{ + /* For the dependency graph, this does not consider nested node + * trees as these are handled as their own data-block. */ + bool has_animation = false; + bool skip_nested_nodes = true; + image_walk_id_all_users(id, skip_nested_nodes, &has_animation, image_user_id_has_animation); + return has_animation; +} + +static void image_user_id_eval_animation(Image *ima, + ID *UNUSED(iduser_id), + ImageUser *iuser, + void *customdata) +{ + if (ima && BKE_image_is_animated(ima)) { + Depsgraph *depsgraph = (Depsgraph *)customdata; + + if ((iuser->flag & IMA_ANIM_ALWAYS) || (iuser->flag & IMA_NEED_FRAME_RECALC) || + (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER)) { + float cfra = DEG_get_ctime(depsgraph); + + BKE_image_user_frame_calc(ima, iuser, cfra); + } + } +} + +void BKE_image_user_id_eval_animation(Depsgraph *depsgraph, ID *id) +{ + /* This is called from the dependency graph to update the image + * users in data-blocks. It computes the current frame number + * and tags the image to be refreshed. + * This does not consider nested node trees as these are handled + * as their own data-block. */ + bool skip_nested_nodes = true; + image_walk_id_all_users(id, skip_nested_nodes, depsgraph, image_user_id_eval_animation); +} + +void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) +{ + BKE_image_user_file_path_ex(iuser, ima, filepath, true); +} + +void BKE_image_user_file_path_ex(ImageUser *iuser, Image *ima, char *filepath, bool resolve_udim) +{ + if (BKE_image_is_multiview(ima)) { + ImageView *iv = static_cast(BLI_findlink(&ima->views, iuser->view)); + if (iv->filepath[0]) { + BLI_strncpy(filepath, iv->filepath, FILE_MAX); + } + else { + BLI_strncpy(filepath, ima->filepath, FILE_MAX); + } + } + else { + BLI_strncpy(filepath, ima->filepath, FILE_MAX); + } + + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { + char head[FILE_MAX], tail[FILE_MAX]; + unsigned short numlen; + + int index; + if (ima->source == IMA_SRC_SEQUENCE) { + index = iuser ? iuser->framenr : ima->lastframe; + BLI_path_sequence_decode(filepath, head, tail, &numlen); + BLI_path_sequence_encode(filepath, head, tail, numlen, index); + } + else if (resolve_udim) { + index = image_get_tile_number_from_iuser(ima, iuser); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(filepath, &tile_format); + BKE_image_set_filepath_from_tile_number(filepath, udim_pattern, tile_format, index); + MEM_SAFE_FREE(udim_pattern); + } + } + + BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); +} + +bool BKE_image_has_alpha(Image *image) +{ + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + const int planes = (ibuf ? ibuf->planes : 0); + BKE_image_release_ibuf(image, ibuf, lock); + + if (planes == 32 || planes == 16) { + return true; + } + + return false; +} + +void BKE_image_get_size(Image *image, ImageUser *iuser, int *r_width, int *r_height) +{ + ImBuf *ibuf = nullptr; + void *lock; + + if (image != nullptr) { + ibuf = BKE_image_acquire_ibuf(image, iuser, &lock); + } + + if (ibuf && ibuf->x > 0 && ibuf->y > 0) { + *r_width = ibuf->x; + *r_height = ibuf->y; + } + else if (image != nullptr && image->type == IMA_TYPE_R_RESULT && iuser != nullptr && + iuser->scene != nullptr) { + Scene *scene = iuser->scene; + *r_width = (scene->r.xsch * scene->r.size) / 100; + *r_height = (scene->r.ysch * scene->r.size) / 100; + if ((scene->r.mode & R_BORDER) && (scene->r.mode & R_CROP)) { + *r_width *= BLI_rctf_size_x(&scene->r.border); + *r_height *= BLI_rctf_size_y(&scene->r.border); + } + } + else { + *r_width = IMG_SIZE_FALLBACK; + *r_height = IMG_SIZE_FALLBACK; + } + + if (image != nullptr) { + BKE_image_release_ibuf(image, ibuf, lock); + } +} + +void BKE_image_get_size_fl(Image *image, ImageUser *iuser, float r_size[2]) +{ + int width, height; + BKE_image_get_size(image, iuser, &width, &height); + + r_size[0] = (float)width; + r_size[1] = (float)height; +} + +void BKE_image_get_aspect(Image *image, float *r_aspx, float *r_aspy) +{ + *r_aspx = 1.0; + + /* x is always 1 */ + if (image) { + *r_aspy = image->aspy / image->aspx; + } + else { + *r_aspy = 1.0f; + } +} + +unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame, int tile) +{ + ImageUser iuser; + BKE_imageuser_default(&iuser); + void *lock; + ImBuf *ibuf; + unsigned char *pixels = nullptr; + + iuser.framenr = frame; + iuser.tile = tile; + + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf) { + pixels = (unsigned char *)ibuf->rect; + + if (pixels) { + pixels = static_cast(MEM_dupallocN(pixels)); + } + + BKE_image_release_ibuf(image, ibuf, lock); + } + + if (!pixels) { + return nullptr; + } + + return pixels; +} + +float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int tile) +{ + ImageUser iuser; + BKE_imageuser_default(&iuser); + void *lock; + ImBuf *ibuf; + float *pixels = nullptr; + + iuser.framenr = frame; + iuser.tile = tile; + + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); + + if (ibuf) { + pixels = ibuf->rect_float; + + if (pixels) { + pixels = static_cast(MEM_dupallocN(pixels)); + } + + BKE_image_release_ibuf(image, ibuf, lock); + } + + if (!pixels) { + return nullptr; + } + + return pixels; +} + +int BKE_image_sequence_guess_offset(Image *image) +{ + return BLI_path_sequence_decode(image->filepath, nullptr, nullptr, nullptr); +} + +bool BKE_image_has_anim(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->anims) == false); +} + +bool BKE_image_has_packedfile(const Image *ima) +{ + return (BLI_listbase_is_empty(&ima->packedfiles) == false); +} + +bool BKE_image_has_filepath(Image *ima) +{ + /* This could be improved to detect cases like //../../, currently path + * remapping empty file paths empty. */ + return ima->filepath[0] != '\0'; +} + +bool BKE_image_is_animated(Image *image) +{ + return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE); +} + +bool BKE_image_has_multiple_ibufs(Image *image) +{ + return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED); +} + +bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) +{ + bool is_dirty = false; + bool is_writable = false; + + BLI_mutex_lock(static_cast(image->runtime.cache_mutex)); + if (image->cache != nullptr) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + if (ibuf != nullptr && ibuf->userflags & IB_BITMAPDIRTY) { + is_writable = BKE_image_buffer_format_writable(ibuf); + is_dirty = true; + break; + } + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + BLI_mutex_unlock(static_cast(image->runtime.cache_mutex)); + + if (r_is_writable) { + *r_is_writable = is_writable; + } + + return is_dirty; +} + +bool BKE_image_is_dirty(Image *image) +{ + return BKE_image_is_dirty_writable(image, nullptr); +} + +void BKE_image_mark_dirty(Image *UNUSED(image), ImBuf *ibuf) +{ + ibuf->userflags |= IB_BITMAPDIRTY; +} + +bool BKE_image_buffer_format_writable(ImBuf *ibuf) +{ + ImageFormatData im_format; + ImbFormatOptions options_dummy; + BKE_imbuf_to_image_format(&im_format, ibuf); + return (BKE_image_imtype_to_ftype(im_format.imtype, &options_dummy) == ibuf->ftype); +} + +void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions *options) +{ + BLI_mutex_lock(static_cast(image->runtime.cache_mutex)); + if (image->cache != nullptr) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + if (ibuf != nullptr) { + ibuf->ftype = static_cast(ftype); + ibuf->foptions = *options; + } + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + BLI_mutex_unlock(static_cast(image->runtime.cache_mutex)); +} + +bool BKE_image_has_loaded_ibuf(Image *image) +{ + bool has_loaded_ibuf = false; + + BLI_mutex_lock(static_cast(image->runtime.cache_mutex)); + if (image->cache != nullptr) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + if (ibuf != nullptr) { + has_loaded_ibuf = true; + break; + } + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + BLI_mutex_unlock(static_cast(image->runtime.cache_mutex)); + + return has_loaded_ibuf; +} + +ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) +{ + ImBuf *ibuf = nullptr; + + BLI_mutex_lock(static_cast(image->runtime.cache_mutex)); + if (image->cache != nullptr) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *current_ibuf = IMB_moviecacheIter_getImBuf(iter); + if (current_ibuf != nullptr && STREQ(current_ibuf->name, name)) { + ibuf = current_ibuf; + IMB_refImBuf(ibuf); + break; + } + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + BLI_mutex_unlock(static_cast(image->runtime.cache_mutex)); + + return ibuf; +} + +ImBuf *BKE_image_get_first_ibuf(Image *image) +{ + ImBuf *ibuf = nullptr; + + BLI_mutex_lock(static_cast(image->runtime.cache_mutex)); + if (image->cache != nullptr) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ibuf = IMB_moviecacheIter_getImBuf(iter); + if (ibuf != nullptr) { + IMB_refImBuf(ibuf); + } + break; + } + IMB_moviecacheIter_free(iter); + } + BLI_mutex_unlock(static_cast(image->runtime.cache_mutex)); + + 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) { + /* nothing to do */ + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + for (int i = 0; i < 2; i++) { + image_add_view(ima, names[i], ima->filepath); + } + return; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + char prefix[FILE_MAX] = {'\0'}; + char *name = ima->filepath; + const char *ext = nullptr; + + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] == '\0') { + BKE_image_free_views(ima); + return; + } + + /* create all the image views */ + for (srv = static_cast(scene->r.views.first); srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + char filepath[FILE_MAX]; + SNPRINTF(filepath, "%s%s%s", prefix, srv->suffix, ext); + image_add_view(ima, srv->name, filepath); + } + } + + /* check if the files are all available */ + iv = static_cast(ima->views.last); + while (iv) { + int file; + char str[FILE_MAX]; + + STRNCPY(str, iv->filepath); + BLI_path_abs(str, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); + + /* 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 (!BKE_image_is_multiview(ima)) { + BKE_image_free_views(ima); + } + } +} + +/**************************** Render Slots ***************************/ + +RenderSlot *BKE_image_add_renderslot(Image *ima, const char *name) +{ + RenderSlot *slot = MEM_cnew("Image new Render Slot"); + if (name && name[0]) { + BLI_strncpy(slot->name, name, sizeof(slot->name)); + } + else { + int n = BLI_listbase_count(&ima->renderslots) + 1; + BLI_snprintf(slot->name, sizeof(slot->name), "Slot %d", n); + } + BLI_addtail(&ima->renderslots, slot); + return slot; +} + +bool BKE_image_remove_renderslot(Image *ima, ImageUser *iuser, int slot) +{ + if (slot == ima->last_render_slot) { + /* Don't remove render slot while rendering to it. */ + if (G.is_rendering) { + return false; + } + } + + int num_slots = BLI_listbase_count(&ima->renderslots); + if (slot >= num_slots || num_slots == 1) { + return false; + } + + RenderSlot *remove_slot = static_cast(BLI_findlink(&ima->renderslots, slot)); + RenderSlot *current_slot = static_cast( + BLI_findlink(&ima->renderslots, ima->render_slot)); + RenderSlot *current_last_slot = static_cast( + BLI_findlink(&ima->renderslots, ima->last_render_slot)); + + RenderSlot *next_slot; + if (current_slot == remove_slot) { + next_slot = static_cast( + BLI_findlink(&ima->renderslots, (slot == num_slots - 1) ? slot - 1 : slot + 1)); + } + else { + next_slot = current_slot; + } + + /* If the slot to be removed is the slot with the last render, + * make another slot the last render slot. */ + if (remove_slot == current_last_slot) { + /* Choose the currently selected slot unless that one is being removed, + * in that case take the next one. */ + RenderSlot *next_last_slot; + if (current_slot == remove_slot) { + next_last_slot = next_slot; + } + else { + next_last_slot = current_slot; + } + + if (!iuser) { + return false; + } + Render *re = RE_GetSceneRender(iuser->scene); + if (!re) { + return false; + } + RE_SwapResult(re, ¤t_last_slot->render); + RE_SwapResult(re, &next_last_slot->render); + current_last_slot = next_last_slot; + } + + current_slot = next_slot; + + BLI_remlink(&ima->renderslots, remove_slot); + + ima->render_slot = BLI_findindex(&ima->renderslots, current_slot); + ima->last_render_slot = BLI_findindex(&ima->renderslots, current_last_slot); + + if (remove_slot->render) { + RE_FreeRenderResult(remove_slot->render); + } + MEM_freeN(remove_slot); + + return true; +} + +bool BKE_image_clear_renderslot(Image *ima, ImageUser *iuser, int slot) +{ + if (slot == ima->last_render_slot) { + if (!iuser) { + return false; + } + if (G.is_rendering) { + return false; + } + Render *re = RE_GetSceneRender(iuser->scene); + if (!re) { + return false; + } + RE_ClearResult(re); + return true; + } + + RenderSlot *render_slot = static_cast(BLI_findlink(&ima->renderslots, slot)); + if (!slot) { + return false; + } + if (render_slot->render) { + RE_FreeRenderResult(render_slot->render); + render_slot->render = nullptr; + } + return true; +} + +RenderSlot *BKE_image_get_renderslot(Image *ima, int index) +{ + /* Can be null for images without render slots. */ + return static_cast(BLI_findlink(&ima->renderslots, index)); +} -- cgit v1.2.3 From 1829232598e6e94e6b0735d86ae8ea34a4bff0c8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Mar 2022 11:32:48 +1100 Subject: Cleanup: spelling in comments & some minor clarifications --- intern/cycles/scene/mesh.cpp | 2 +- source/blender/blenkernel/BKE_geometry_set.hh | 2 +- source/blender/blenkernel/BKE_lib_id.h | 14 ++--- .../blenkernel/intern/blendfile_link_append.c | 2 +- source/blender/blenkernel/intern/data_transfer.c | 4 +- source/blender/blenkernel/intern/image.cc | 2 +- source/blender/blenkernel/intern/key.c | 2 +- source/blender/blenkernel/intern/lib_id.c | 6 +- source/blender/blenkernel/intern/lib_override.c | 2 +- source/blender/blenkernel/intern/screen.c | 2 +- source/blender/blenkernel/intern/sound.c | 2 +- .../depsgraph/intern/builder/deg_builder_nodes.cc | 2 +- source/blender/depsgraph/intern/depsgraph_tag.cc | 2 +- source/blender/draw/intern/draw_common.c | 2 +- .../editors/animation/anim_channels_defines.c | 73 +++++++++++----------- source/blender/editors/animation/keyframing.c | 7 +-- source/blender/editors/include/ED_screen_types.h | 6 +- source/blender/editors/include/ED_view3d.h | 6 +- source/blender/editors/mask/mask_edit.c | 2 +- source/blender/editors/mask/mask_editaction.c | 4 +- source/blender/editors/mesh/meshtools.c | 38 +++++------ source/blender/editors/space_node/node_draw.cc | 2 +- .../intern/MOD_gpencil_ui_common.h | 4 +- source/blender/ikplugin/intern/iksolver_plugin.c | 6 +- source/blender/io/collada/AnimationImporter.cpp | 4 +- source/blender/makesdna/DNA_ID.h | 6 +- source/blender/makesdna/DNA_action_types.h | 18 +++--- source/blender/makesrna/intern/rna_key.c | 4 +- source/blender/modifiers/intern/MOD_ui_common.h | 4 +- source/blender/python/intern/bpy_rna.c | 2 +- source/blender/windowmanager/WM_api.h | 3 +- 31 files changed, 120 insertions(+), 115 deletions(-) diff --git a/intern/cycles/scene/mesh.cpp b/intern/cycles/scene/mesh.cpp index ebf1c3999b6..a459195efee 100644 --- a/intern/cycles/scene/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -68,7 +68,7 @@ void Mesh::Triangle::verts_for_step(const float3 *verts, r_verts[2] = verts[v[2]]; } else { - /* Center step not stored in the attribute array array. */ + /* Center step not stored in the attribute array. */ if (step > center_step) { step--; } diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index 169554b4453..0e121068cbc 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -678,7 +678,7 @@ class CurveComponentLegacy : public GeometryComponent { }; /** - * A geometry component that stores a group of curves, corresponding the the #Curves and + * A geometry component that stores a group of curves, corresponding the #Curves and * #CurvesGeometry types. */ class CurveComponent : public GeometryComponent { diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index c56cb616a9a..040be8d1280 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -150,11 +150,11 @@ enum { LIB_ID_COPY_NO_PREVIEW = 1 << 17, /** Copy runtime data caches. */ LIB_ID_COPY_CACHES = 1 << 18, - /** Don't copy id->adt, used by ID datablock localization routines. */ + /** Don't copy id->adt, used by ID data-block localization routines. */ LIB_ID_COPY_NO_ANIMDATA = 1 << 19, /** Mesh: Reference CD data layers instead of doing real copy - USE WITH CAUTION! */ LIB_ID_COPY_CD_REFERENCE = 1 << 20, - /** Do not copy id->override_library, used by ID datablock override routines. */ + /** Do not copy id->override_library, used by ID data-block override routines. */ LIB_ID_COPY_NO_LIB_OVERRIDE = 1 << 21, /** When copying local sub-data (like constraints or modifiers), do not set their "library * override local data" flag. */ @@ -162,11 +162,11 @@ enum { /* *** XXX Hackish/not-so-nice specific behaviors needed for some corner cases. *** */ /* *** Ideally we should not have those, but we need them for now... *** */ - /** EXCEPTION! Deep-copy actions used by animdata of copied ID. */ + /** EXCEPTION! Deep-copy actions used by animation-data of copied ID. */ LIB_ID_COPY_ACTIONS = 1 << 24, - /** Keep the library pointer when copying datablock outside of bmain. */ + /** Keep the library pointer when copying data-block outside of bmain. */ LIB_ID_COPY_KEEP_LIB = 1 << 25, - /** EXCEPTION! Deep-copy shapekeys used by copied obdata ID. */ + /** EXCEPTION! Deep-copy shape-keys used by copied obdata ID. */ LIB_ID_COPY_SHAPEKEY = 1 << 26, /** EXCEPTION! Specific deep-copy of node trees used e.g. for rendering purposes. */ LIB_ID_COPY_NODETREE_LOCALIZE = 1 << 27, @@ -177,7 +177,7 @@ enum { LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28, /* *** Helper 'defines' gathering most common flag sets. *** */ - /** Shapekeys are not real ID's, more like local data to geometry IDs... */ + /** Shape-keys are not real ID's, more like local data to geometry IDs. */ LIB_ID_COPY_DEFAULT = LIB_ID_COPY_SHAPEKEY, /** Create a local, outside of bmain, data-block to work on. */ @@ -412,7 +412,7 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id); * * There are exceptions though: * - Embedded IDs (root node trees and master collections) are always copied with their owner. - * - If #LIB_ID_COPY_ACTIONS is defined, actions used by animdata will be duplicated. + * - If #LIB_ID_COPY_ACTIONS is defined, actions used by anim-data will be duplicated. * - If #LIB_ID_COPY_SHAPEKEY is defined, shape-keys will be duplicated. * - If #LIB_ID_CREATE_LOCAL is defined, root node trees will be deep-duplicated recursively. * diff --git a/source/blender/blenkernel/intern/blendfile_link_append.c b/source/blender/blenkernel/intern/blendfile_link_append.c index ce36bfe81be..294fe57c923 100644 --- a/source/blender/blenkernel/intern/blendfile_link_append.c +++ b/source/blender/blenkernel/intern/blendfile_link_append.c @@ -926,7 +926,7 @@ static int foreach_libblock_link_append_callback(LibraryIDLinkCallbackData *cb_d * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be * processed, so we need to recursively deal with them here. */ /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it - * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of + * recursively, we need to take care of potential recursion cases ourselves (e.g.anim-data of * shape-key referencing the shape-key itself). * NOTE: in case both IDs (owner and 'used' ones) are non-linkable, we can assume we can break * the dependency here. Indeed, either they are both linked in another way (through their own diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 1818e5a9490..5be993ca1f7 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -973,8 +973,8 @@ static bool data_transfer_layersmapping_generate(ListBase *r_map, return ret; } if (cddata_type == CD_FAKE_SHAPEKEY) { - /* TODO: leaving shapekeys aside for now, quite specific case, - * since we can't access them from MVert :/ */ + /* TODO: leaving shape-keys aside for now, quite specific case, + * since we can't access them from #MVert :/ */ return false; } } diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 967f0f61e07..ff10f36cc22 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -198,7 +198,7 @@ static void image_free_data(ID *id) { Image *image = (Image *)id; - /* Also frees animdata. */ + /* Also frees animations (#Image.anims list). */ BKE_image_free_buffers(image); image_free_packedfiles(image); diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index e28094c0abc..bd6ffa2bc55 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -206,7 +206,7 @@ IDTypeInfo IDType_ID_KE = { .foreach_id = shapekey_foreach_id, .foreach_cache = NULL, .foreach_path = NULL, - /* A bit weird, due to shapekeys not being strictly speaking embedded data... But they also + /* A bit weird, due to shape-keys not being strictly speaking embedded data... But they also * share a lot with those (non linkable, only ever used by one owner ID, etc.). */ .owner_get = shapekey_owner_get, diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 7cd3204f3f1..28745f1d2c7 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -1875,7 +1875,7 @@ void BKE_library_make_local(Main *bmain, for (int a = set_listbasepointers(bmain, lbarray); a--;) { ID *id = lbarray[a]->first; - /* Do not explicitly make local non-linkable IDs (shapekeys, in fact), + /* Do not explicitly make local non-linkable IDs (shape-keys, in fact), * they are assumed to be handled by real data-blocks responsible of them. */ const bool do_skip = (id && !BKE_idtype_idcode_is_linkable(GS(id->name))); @@ -1902,8 +1902,8 @@ void BKE_library_make_local(Main *bmain, * to discover all your links are lost after appending). * Also, never ever make proxified objects local, would not make any sense. */ /* Some more notes: - * - Shapekeys are never tagged here (since they are not linkable). - * - Nodetrees used in materials etc. have to be tagged manually, + * - Shape-keys are never tagged here (since they are not linkable). + * - Node-trees used in materials etc. have to be tagged manually, * since they do not exist in Main (!). * This is ok-ish on 'make local' side of things * (since those are handled by their 'owner' IDs), diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 9b7f9d4f36d..7f34ccfc36f 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -3420,7 +3420,7 @@ ID *BKE_lib_override_library_operations_store_start(Main *bmain, { if (ID_IS_OVERRIDE_LIBRARY_TEMPLATE(local) || ID_IS_OVERRIDE_LIBRARY_VIRTUAL(local)) { /* This is actually purely local data with an override template, or one of those embedded IDs - * (root node trees, master collections or shapekeys) that cannot have their own override. + * (root node trees, master collections or shape-keys) that cannot have their own override. * Nothing to do here! */ return NULL; } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index b1b9a24ebaa..ebc87c6ccc0 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -58,7 +58,7 @@ static void screen_free_data(ID *id) { bScreen *screen = (bScreen *)id; - /* No animdata here. */ + /* No animation-data here. */ LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { BKE_area_region_free(NULL, region); diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index 8b72fd05057..b991805fae8 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -87,7 +87,7 @@ static void sound_free_data(ID *id) { bSound *sound = (bSound *)id; - /* No animdata here. */ + /* No animation-data here. */ if (sound->packedfile) { BKE_packedfile_free(sound->packedfile); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 77597e0db06..49e850b1979 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1408,7 +1408,7 @@ void DepsgraphNodeBuilder::build_particle_settings(ParticleSettings *particle_se } } -/* Shapekeys */ +/* Shape-keys. */ void DepsgraphNodeBuilder::build_shapekeys(Key *key) { if (built_map_.checkIsBuiltAndTag(key)) { diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 9551a00cf95..f945e9b6fbc 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -865,7 +865,7 @@ void DEG_ids_clear_recalc(Depsgraph *depsgraph, const bool backup) if (!DEG_id_type_any_updated(depsgraph)) { return; } - /* Go over all ID nodes nodes, clearing tags. */ + /* Go over all ID nodes, clearing tags. */ for (deg::IDNode *id_node : deg_graph->id_nodes) { if (backup) { id_node->id_cow_recalc_backup |= id_node->id_cow->recalc; diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index 2897234f4dc..c7edf003346 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -135,7 +135,7 @@ void DRW_globals_update(void) UI_GetThemeColor4fv(TH_CFRAME, gb->colorCurrentFrame); - /* Metaball */ + /* Meta-ball. */ UI_COLOR_RGBA_FROM_U8(0xA0, 0x30, 0x30, 0xFF, gb->colorMballRadius); UI_COLOR_RGBA_FROM_U8(0xF0, 0xA0, 0xA0, 0xFF, gb->colorMballRadiusSelect); UI_COLOR_RGBA_FROM_U8(0x30, 0xA0, 0x30, 0xFF, gb->colorMballStiffness); diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index f4412ea837d..445d3715658 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -525,7 +525,7 @@ static void *acf_summary_setting_ptr(bAnimListElem *ale, return NULL; } -/* all animation summary (DopeSheet only) type define */ +/** All animation summary (dope-sheet only) type define. */ static bAnimChannelType ACF_SUMMARY = { "Summary", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -634,7 +634,7 @@ static void *acf_scene_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* scene type define */ +/** Scene type define. */ static bAnimChannelType ACF_SCENE = { "Scene", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -810,7 +810,7 @@ static void *acf_object_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se } } -/* object type define */ +/** Object type define. */ static bAnimChannelType ACF_OBJECT = { "Object", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -987,7 +987,7 @@ static void *acf_group_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(agrp->flag, type); } -/* group type define */ +/** Group type define. */ static bAnimChannelType ACF_GROUP = { "Group", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ @@ -1111,7 +1111,7 @@ static void *acf_fcurve_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(fcu->flag, type); } -/* fcurve type define */ +/** F-Curve type define. */ static bAnimChannelType ACF_FCURVE = { "F-Curve", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ @@ -1231,7 +1231,7 @@ static int acf_nla_controls_icon(bAnimListElem *UNUSED(ale)) return ICON_NLA; } -/* NLA Control FCurves Expander type define */ +/** NLA Control F-Curves expander type define. */ static bAnimChannelType ACF_NLACONTROLS = { "NLA Controls Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1271,7 +1271,7 @@ static void acf_nla_curve_name(bAnimListElem *ale, char *name) } } -/* NLA Control F-Curve type define */ +/** NLA Control F-Curve type define. */ static bAnimChannelType ACF_NLACURVE = { "NLA Control F-Curve", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ @@ -1361,7 +1361,7 @@ static void *acf_fillactd_setting_ptr(bAnimListElem *ale, } } -/* object action expander type define */ +/** Object action expander type define. */ static bAnimChannelType ACF_FILLACTD = { "Ob-Action Filler", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1446,7 +1446,7 @@ static void *acf_filldrivers_setting_ptr(bAnimListElem *ale, } } -/* drivers expander type define */ +/** Drivers expander type define. */ static bAnimChannelType ACF_FILLDRIVERS = { "Drivers Filler", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1525,7 +1525,7 @@ static void *acf_dsmat_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* material expander type define */ +/** Material expander type define. */ static bAnimChannelType ACF_DSMAT = { "Material Data Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1606,7 +1606,7 @@ static void *acf_dslight_setting_ptr(bAnimListElem *ale, } } -/* light expander type define */ +/** Light expander type define. */ static bAnimChannelType ACF_DSLIGHT = { "Light Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1692,7 +1692,7 @@ static void *acf_dstex_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* texture expander type define */ +/** Texture expander type define. */ static bAnimChannelType ACF_DSTEX = { "Texture Data Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1775,7 +1775,7 @@ static void *acf_dscachefile_setting_ptr(bAnimListElem *ale, } } -/* CacheFile expander type define. */ +/** CacheFile expander type define.. */ static bAnimChannelType ACF_DSCACHEFILE = { "Cache File Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1858,7 +1858,7 @@ static void *acf_dscam_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* camera expander type define */ +/** Camera expander type define. */ static bAnimChannelType ACF_DSCAM = { "Camera Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -1947,7 +1947,7 @@ static void *acf_dscur_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* curve expander type define */ +/** Curve expander type define. */ static bAnimChannelType ACF_DSCUR = { "Curve Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2045,7 +2045,7 @@ static void *acf_dsskey_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se } } -/* shapekey expander type define */ +/** Shape-key expander type define. */ static bAnimChannelType ACF_DSSKEY = { "Shape Key Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2124,7 +2124,7 @@ static void *acf_dswor_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* world expander type define */ +/** World expander type define. */ static bAnimChannelType ACF_DSWOR = { "World Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2203,7 +2203,7 @@ static void *acf_dspart_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se } } -/* particle expander type define */ +/** Particle expander type define. */ static bAnimChannelType ACF_DSPART = { "Particle Data Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2284,7 +2284,7 @@ static void *acf_dsmball_setting_ptr(bAnimListElem *ale, } } -/* metaball expander type define */ +/** Meta-ball expander type define. */ static bAnimChannelType ACF_DSMBALL = { "Metaball Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2363,7 +2363,7 @@ static void *acf_dsarm_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* metaball expander type define */ +/** Armature expander type define. */ static bAnimChannelType ACF_DSARM = { "Armature Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2455,7 +2455,7 @@ static void *acf_dsntree_setting_ptr(bAnimListElem *ale, } } -/* node tree expander type define */ +/** Node tree expander type define. */ static bAnimChannelType ACF_DSNTREE = { "Node Tree Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2536,7 +2536,7 @@ static void *acf_dslinestyle_setting_ptr(bAnimListElem *ale, } } -/* node tree expander type define */ +/** Line Style expander type define. */ static bAnimChannelType ACF_DSLINESTYLE = { "Line Style Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2615,7 +2615,7 @@ static void *acf_dsmesh_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings se } } -/* node tree expander type define */ +/** Mesh expander type define. */ static bAnimChannelType ACF_DSMESH = { "Mesh Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2695,7 +2695,7 @@ static void *acf_dslat_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* node tree expander type define */ +/** Lattice expander type define. */ static bAnimChannelType ACF_DSLAT = { "Lattice Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2775,7 +2775,7 @@ static void *acf_dsspk_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings set } } -/* speaker expander type define */ +/** Speaker expander type define. */ static bAnimChannelType ACF_DSSPK = { "Speaker Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2856,7 +2856,7 @@ static void *acf_dscurves_setting_ptr(bAnimListElem *ale, } } -/* Curves expander type define */ +/** Curves expander type define. */ static bAnimChannelType ACF_DSHAIR = { "Curves Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -2937,7 +2937,7 @@ static void *acf_dspointcloud_setting_ptr(bAnimListElem *ale, } } -/* pointcloud expander type define */ +/** Point-cloud expander type define. */ static bAnimChannelType ACF_DSPOINTCLOUD = { "PointCloud Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3018,7 +3018,7 @@ static void *acf_dsvolume_setting_ptr(bAnimListElem *ale, } } -/* volume expander type define */ +/** Volume expander type define. */ static bAnimChannelType ACF_DSVOLUME = { "Volume Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3097,6 +3097,7 @@ static void *acf_dssimulation_setting_ptr(bAnimListElem *ale, } } +/** Simulation expander type define. */ static bAnimChannelType ACF_DSSIMULATION = { "Simulation Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3177,7 +3178,7 @@ static void *acf_dsgpencil_setting_ptr(bAnimListElem *ale, } } -/* grease pencil expander type define */ +/** Grease-pencil expander type define. */ static bAnimChannelType ACF_DSGPENCIL = { "GPencil DS Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3258,7 +3259,7 @@ static void *acf_dsmclip_setting_ptr(bAnimListElem *ale, } } -/* world expander type define */ +/** Movie-clip expander type define. */ static bAnimChannelType ACF_DSMCLIP = { "Movieclip Expander", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3373,7 +3374,7 @@ static void *acf_shapekey_setting_ptr(bAnimListElem *ale, } } -/* shapekey expander type define */ +/** Shape-key expander type define. */ static bAnimChannelType ACF_SHAPEKEY = { "Shape Key", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ @@ -3453,7 +3454,7 @@ static void *acf_gpd_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(gpd->flag, type); } -/* gpencil datablock type define */ +/** Grease-pencil data-block type define. */ static bAnimChannelType ACF_GPD = { "GPencil Datablock", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3552,7 +3553,7 @@ static void *acf_gpl_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(gpl->flag, type); } -/* grease pencil layer type define */ +/** Grease-pencil layer type define. */ static bAnimChannelType ACF_GPL = { "GPencil Layer", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ @@ -3634,7 +3635,7 @@ static void *acf_mask_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(mask->flag, type); } -/* mask datablock type define */ +/** Mask data-block type define. */ static bAnimChannelType ACF_MASKDATA = { "Mask Datablock", /* type name */ ACHANNEL_ROLE_EXPANDER, /* role */ @@ -3730,7 +3731,7 @@ static void *acf_masklay_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(masklay->flag, type); } -/* grease pencil layer type define */ +/** Mask layer type define. */ static bAnimChannelType ACF_MASKLAYER = { "Mask Layer", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ @@ -3870,7 +3871,7 @@ static void *acf_nlatrack_setting_ptr(bAnimListElem *ale, return GET_ACF_FLAG_PTR(nlt->flag, type); } -/* nla track type define */ +/** NLA track type define. */ static bAnimChannelType ACF_NLATRACK = { "NLA Track", /* type name */ ACHANNEL_ROLE_CHANNEL, /* role */ diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 90c127b620b..0f93d728c8c 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -2848,13 +2848,12 @@ static bool object_frame_has_keyframe(Object *ob, float frame, short filter) } } - /* try shapekey keyframes (if available, and allowed by filter) */ + /* Try shape-key keyframes (if available, and allowed by filter). */ if (!(filter & ANIMFILTER_KEYS_LOCAL) && !(filter & ANIMFILTER_KEYS_NOSKEY)) { Key *key = BKE_key_from_object(ob); - /* shapekeys can have keyframes ('Relative Shape Keys') - * or depend on time (old 'Absolute Shape Keys') - */ + /* Shape-keys can have keyframes ('Relative Shape Keys') + * or depend on time (old 'Absolute Shape Keys'). */ /* 1. test for relative (with keyframes) */ if (id_frame_has_keyframe((ID *)key, frame, filter)) { diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h index 86fb5251ff3..21bb412d072 100644 --- a/source/blender/editors/include/ED_screen_types.h +++ b/source/blender/editors/include/ED_screen_types.h @@ -13,7 +13,9 @@ extern "C" { /* ----------------------------------------------------- */ -/* for animplayer */ +/** + * For animation playback operator, stored in #bScreen.animtimer.customdata. + */ typedef struct ScreenAnimData { ARegion *region; /* do not read from this, only for comparing if region exists */ short redraws; @@ -24,7 +26,7 @@ typedef struct ScreenAnimData { bool from_anim_edit; /* playback was invoked from animation editor */ } ScreenAnimData; -/* for animplayer */ +/** #ScreenAnimData.flag */ enum { /* user-setting - frame range is played backwards */ ANIMPLAY_FLAG_REVERSE = (1 << 0), diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index b1435e76eb2..d2ff5637a13 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -646,7 +646,7 @@ bool ED_view3d_win_to_3d_on_plane_int(const struct ARegion *region, * * \param region: The region (used for the window width and height). * \param xy_delta: 2D difference (in pixels) such as `event->mval[0] - other_x`. - * \param zfac: The depth result typically calculated by by #ED_view3d_calc_zfac + * \param zfac: The depth result typically calculated by #ED_view3d_calc_zfac * (see it's doc-string for details). * \param r_out: The resulting world-space delta. */ @@ -661,7 +661,7 @@ void ED_view3d_win_to_delta(const struct ARegion *region, * the origin in this case is close to zero coordinate. * * \param region: The region (used for the window width and height). - * \param mval: The area relative 2d location (such as event->mval converted to floats). + * \param mval: The area relative 2d location (such as `event->mval` converted to float). * \param r_out: The resulting normalized world-space direction vector. */ void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], float r_out[3]); @@ -675,7 +675,7 @@ void ED_view3d_win_to_origin(const struct ARegion *region, const float mval[2], * the mouse cursor as a normalized vector. * * \param region: The region (used for the window width and height). - * \param mval: The area relative 2d location (such as event->mval converted to floats). + * \param mval: The area relative 2d location (such as `event->mval` converted to float). * \param r_out: The resulting normalized world-space direction vector. */ void ED_view3d_win_to_vector(const struct ARegion *region, const float mval[2], float r_out[3]); diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c index 819ffd96b1c..b2d49bcc642 100644 --- a/source/blender/editors/mask/mask_edit.c +++ b/source/blender/editors/mask/mask_edit.c @@ -111,7 +111,7 @@ void ED_operatortypes_mask(void) WM_operatortype_append(MASK_OT_parent_set); WM_operatortype_append(MASK_OT_parent_clear); - /* shapekeys */ + /* Shape-keys. */ WM_operatortype_append(MASK_OT_shape_key_insert); WM_operatortype_append(MASK_OT_shape_key_clear); WM_operatortype_append(MASK_OT_shape_key_feather_reset); diff --git a/source/blender/editors/mask/mask_editaction.c b/source/blender/editors/mask/mask_editaction.c index 8bb6e8a71db..8a23a53a5d2 100644 --- a/source/blender/editors/mask/mask_editaction.c +++ b/source/blender/editors/mask/mask_editaction.c @@ -30,8 +30,8 @@ /* ***************************************** */ /* NOTE ABOUT THIS FILE: * This file contains code for editing Mask data in the Action Editor - * as a 'keyframes', so that a user can adjust the timing of Mask shapekeys. - * Therefore, this file mostly contains functions for selecting Mask frames (shapekeys). + * as a 'keyframes', so that a user can adjust the timing of Mask shape-keys. + * Therefore, this file mostly contains functions for selecting Mask frames (shape-keys). */ /* ***************************************** */ /* Generics - Loopers */ diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index f3782c17845..d57471b658c 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -137,22 +137,22 @@ static void join_mesh_single(Depsgraph *depsgraph, mul_m4_v3(cmat, mvert->co); } - /* For each shapekey in destination mesh: + /* For each shape-key in destination mesh: * - if there's a matching one, copy it across * (will need to transform vertices into new space...). * - otherwise, just copy own coordinates of mesh * (no need to transform vertex coordinates into new space). */ if (key) { - /* if this mesh has any shapekeys, check first, otherwise just copy coordinates */ + /* if this mesh has any shape-keys, check first, otherwise just copy coordinates */ LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { - /* get pointer to where to write data for this mesh in shapekey's data array */ + /* get pointer to where to write data for this mesh in shape-key's data array */ float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; - /* check if this mesh has such a shapekey */ + /* Check if this mesh has such a shape-key. */ KeyBlock *okb = me->key ? BKE_keyblock_find_name(me->key, kb->name) : NULL; if (okb) { - /* copy this mesh's shapekey to the destination shapekey + /* copy this mesh's shape-key to the destination shape-key * (need to transform first) */ float(*ocos)[3] = okb->data; for (a = 0; a < me->totvert; a++, cos++, ocos++) { @@ -161,7 +161,7 @@ static void join_mesh_single(Depsgraph *depsgraph, } } else { - /* copy this mesh's vertex coordinates to the destination shapekey */ + /* Copy this mesh's vertex coordinates to the destination shape-key. */ for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, cos++, mvert++) { copy_v3_v3(*cos, mvert->co); } @@ -170,26 +170,26 @@ static void join_mesh_single(Depsgraph *depsgraph, } } else { - /* for each shapekey in destination mesh: + /* for each shape-key in destination mesh: * - if it was an 'original', copy the appropriate data from nkey * - otherwise, copy across plain coordinates (no need to transform coordinates) */ if (key) { LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { - /* get pointer to where to write data for this mesh in shapekey's data array */ + /* get pointer to where to write data for this mesh in shape-key's data array */ float(*cos)[3] = ((float(*)[3])kb->data) + *vertofs; - /* check if this was one of the original shapekeys */ + /* Check if this was one of the original shape-keys. */ KeyBlock *okb = nkey ? BKE_keyblock_find_name(nkey, kb->name) : NULL; if (okb) { - /* copy this mesh's shapekey to the destination shapekey */ + /* copy this mesh's shape-key to the destination shape-key */ float(*ocos)[3] = okb->data; for (a = 0; a < me->totvert; a++, cos++, ocos++) { copy_v3_v3(*cos, *ocos); } } else { - /* copy base-coordinates to the destination shapekey */ + /* Copy base-coordinates to the destination shape-key. */ for (a = 0, mvert = *mvert_pp; a < me->totvert; a++, cos++, mvert++) { copy_v3_v3(*cos, mvert->co); } @@ -365,7 +365,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) join_parent = true; } - /* check for shapekeys */ + /* Check for shape-keys. */ if (me->key) { haskey++; } @@ -428,10 +428,10 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* increase id->us : will be lowered later */ } - /* - if destination mesh had shapekeys, move them somewhere safe, and set up placeholders - * with arrays that are large enough to hold shapekey data for all meshes - * - if destination mesh didn't have shapekeys, but we encountered some in the meshes we're - * joining, set up a new keyblock and assign to the mesh + /* - If destination mesh had shape-keys, move them somewhere safe, and set up placeholders + * with arrays that are large enough to hold shape-key data for all meshes. + * - If destination mesh didn't have shape-keys, but we encountered some in the meshes we're + * joining, set up a new key-block and assign to the mesh. */ if (key) { /* make a duplicate copy that will only be used here... (must remember to free it!) */ @@ -518,8 +518,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) } } - /* if this mesh has shapekeys, - * check if destination mesh already has matching entries too */ + /* If this mesh has shape-keys, + * check if destination mesh already has matching entries too. */ if (me->key && key) { /* for remapping KeyBlock.relative */ int *index_map = MEM_mallocN(sizeof(int) * me->key->totkey, __func__); @@ -713,7 +713,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) /* other mesh users */ BKE_objects_materials_test_all(bmain, (ID *)me); - /* free temp copy of destination shapekeys (if applicable) */ + /* Free temporary copy of destination shape-keys (if applicable). */ if (nkey) { /* We can assume nobody is using that ID currently. */ BKE_id_free_ex(bmain, nkey, LIB_ID_FREE_NO_UI_USER, false); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 1286f6a818c..410572159bf 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -720,7 +720,7 @@ static void node_socket_draw_multi_input(const float color[4], { /* The other sockets are drawn with the keyframe shader. There, the outline has a base thickness * that can be varied but always scales with the size the socket is drawn at. Using `U.dpi_fac` - * has the the same effect here. It scales the outline correctly across different screen DPIs + * has the same effect here. It scales the outline correctly across different screen DPI's * and UI scales without being affected by the 'line-width'. */ const float outline_width = NODE_SOCK_OUTLINE_SCALE * U.dpi_fac; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h index bbc0a3d26b6..57eac4d77bf 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.h @@ -41,8 +41,8 @@ PanelType *gpencil_modifier_panel_register(struct ARegionType *region_type, /** * Add a child panel to the parent. * - * \note To create the panel type's idname, it appends the \a name argument to the \a parent's - * idname. + * \note To create the panel type's #PanelType.idname, + * it appends the \a name argument to the \a parent's `idname`. */ struct PanelType *gpencil_modifier_subpanel_register(struct ARegionType *region_type, const char *name, diff --git a/source/blender/ikplugin/intern/iksolver_plugin.c b/source/blender/ikplugin/intern/iksolver_plugin.c index d0cb7f00df8..6d99fde9df6 100644 --- a/source/blender/ikplugin/intern/iksolver_plugin.c +++ b/source/blender/ikplugin/intern/iksolver_plugin.c @@ -255,8 +255,10 @@ static void where_is_ik_bone(bPoseChannel *pchan, pchan->flag |= POSE_DONE; } -/* called from within the core BKE_pose_where_is loop, all animsystems and constraints - * were executed & assigned. Now as last we do an IK pass */ +/** + * Called from within the core #BKE_pose_where_is loop, all animation-systems and constraints + * were executed & assigned. Now as last we do an IK pass. + */ static void execute_posetree(struct Depsgraph *depsgraph, struct Scene *scene, Object *ob, diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp index b14de8be7df..8f5d2742b0e 100644 --- a/source/blender/io/collada/AnimationImporter.cpp +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -719,7 +719,7 @@ void AnimationImporter::Assign_float_animations(const COLLADAFW::UniqueId &listi fcurve_deg_to_rad(fcu); } } - /** XXX What About animtype "rotation" ? */ + /** XXX What About animation-type "rotation" ? */ BLI_addtail(AnimCurves, fcu); fcurve_is_used(fcu); @@ -1938,7 +1938,7 @@ bool AnimationImporter::evaluate_animation(COLLADAFW::Transformation *tm, return false; } - /* TODO: support other animclasses. */ + /* TODO: support other animation-classes. */ if (animclass != COLLADAFW::AnimationList::ANGLE) { report_class_type_unsupported(path, animclass, type); return false; diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index e3be31d3afe..c3132eeab3d 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -609,7 +609,7 @@ typedef struct PreviewImage { /** id->flag (persistent). */ enum { - /** Don't delete the datablock even if unused. */ + /** Don't delete the data-block even if unused. */ LIB_FAKEUSER = 1 << 9, /** * The data-block is a sub-data of another one. @@ -617,14 +617,14 @@ enum { */ LIB_EMBEDDED_DATA = 1 << 10, /** - * Datablock is from a library and linked indirectly, with LIB_TAG_INDIRECT + * Data-block is from a library and linked indirectly, with LIB_TAG_INDIRECT * tag set. But the current .blend file also has a weak pointer to it that * we want to restore if possible, and silently drop if it's missing. */ LIB_INDIRECT_WEAK_LINK = 1 << 11, /** * The data-block is a sub-data of another one, which is an override. - * Note that this also applies to shapekeys, even though they are not 100% embedded data... + * Note that this also applies to shape-keys, even though they are not 100% embedded data. */ LIB_EMBEDDED_DATA_LIB_OVERRIDE = 1 << 12, /** diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index fa0898e6ea5..516d3ce94f9 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -875,27 +875,27 @@ typedef enum eSAction_Flag { SACTION_SHOW_MARKERS = (1 << 14), } eSAction_Flag; -/* SpaceAction_Runtime.flag */ +/** #SpaceAction_Runtime.flag */ typedef enum eSAction_Runtime_Flag { /** Temporary flag to force channel selections to be synced with main */ SACTION_RUNTIME_FLAG_NEED_CHAN_SYNC = (1 << 0), } eSAction_Runtime_Flag; -/* SpaceAction Mode Settings */ +/** #SpaceAction.mode */ typedef enum eAnimEdit_Context { - /* action on the active object */ + /** Action on the active object. */ SACTCONT_ACTION = 0, - /* list of all shapekeys on the active object, linked with their F-Curves */ + /** List of all shape-keys on the active object, linked with their F-Curves. */ SACTCONT_SHAPEKEY = 1, - /* editing of gpencil data */ + /** Editing of grease-pencil data. */ SACTCONT_GPENCIL = 2, - /* dopesheet (default) */ + /** Dope-sheet (default). */ SACTCONT_DOPESHEET = 3, - /* mask */ + /** Mask. */ SACTCONT_MASK = 4, - /* cache file */ + /** Cache file */ SACTCONT_CACHEFILE = 5, - /* timeline - replacement for the standalone "timeline editor" */ + /** Timeline - replacement for the standalone "timeline editor". */ SACTCONT_TIMELINE = 6, } eAnimEdit_Context; diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 7687dcbb11f..50b25157989 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -153,10 +153,10 @@ static void rna_ShapeKey_slider_max_set(PointerRNA *ptr, float value) # undef SHAPEKEY_SLIDER_TOL -/* ***** Normals accessors for shapekeys. ***** */ +/* ***** Normals accessors for shape-keys. ***** */ /* NOTE: with this we may recompute several times the same data, should we want to access verts, * then polys, then loops normals... However, - * such case looks rather unlikely - and not worth adding some kind of caching in KeyBlocks. + * such case looks rather unlikely - and not worth adding some kind of caching in key-blocks. */ static Mesh *rna_KeyBlock_normals_get_mesh(PointerRNA *ptr, ID *id) diff --git a/source/blender/modifiers/intern/MOD_ui_common.h b/source/blender/modifiers/intern/MOD_ui_common.h index 16e9dd25253..6548a897be9 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.h +++ b/source/blender/modifiers/intern/MOD_ui_common.h @@ -51,8 +51,8 @@ struct PanelType *modifier_panel_register(struct ARegionType *region_type, /** * Add a child panel to the parent. * - * \note To create the panel type's idname, it appends the \a name argument to the \a parent's - * idname. + * \note To create the panel type's #PanelType.idname, + * it appends the \a name argument to the \a parent's `idname`. */ struct PanelType *modifier_subpanel_register(struct ARegionType *region_type, const char *name, diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 316cfe4d8b1..ab119377e7e 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -6229,7 +6229,7 @@ static PyObject *pyrna_func_call(BPy_FunctionRNA *self, PyObject *args, PyObject arg_name = PyUnicode_AsUTF8(key); found = false; - if (arg_name == NULL) { /* Unlikely the argname is not a string, but ignore if it is. */ + if (arg_name == NULL) { /* Unlikely the `arg_name` is not a string, but ignore if it is. */ PyErr_Clear(); } else { diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index bc51f7d065a..6f8233c153e 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -615,7 +615,8 @@ bool WM_operator_poll_context(struct bContext *C, struct wmOperatorType *ot, sho /** * For running operators with frozen context (modal handlers, menus). * - * \param store: Store settings for re-use. + * \param store: Store properties for re-use when an operator has finished + * (unless #PROP_SKIP_SAVE is set). * * \warning do not use this within an operator to call itself! T29537. */ -- cgit v1.2.3 From 36dd3f42bc70cdaacca1e1f78c0dd9641432eb20 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Mar 2022 11:33:31 +1100 Subject: Cleanup: use 'WM_file_' prefix for file operations --- source/blender/makesrna/intern/rna_userdef.c | 2 +- source/blender/windowmanager/WM_api.h | 7 ++++--- source/blender/windowmanager/intern/wm.c | 2 +- source/blender/windowmanager/intern/wm_files.c | 6 +++--- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 339d0bf380c..3b9632342ec 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -638,7 +638,7 @@ static void rna_userdef_autosave_update(Main *bmain, Scene *scene, PointerRNA *p wmWindowManager *wm = bmain->wm.first; if (wm) { - WM_autosave_init(wm); + WM_file_autosave_init(wm); } rna_userdef_update(bmain, scene, ptr); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 6f8233c153e..2f9062959c7 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -242,11 +242,12 @@ void WM_window_set_dpi(const wmWindow *win); bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test); -/* files */ +/* wm_files.c */ + void WM_file_autoexec_init(const char *filepath); bool WM_file_read(struct bContext *C, const char *filepath, struct ReportList *reports); -void WM_autosave_init(struct wmWindowManager *wm); -bool WM_recover_last_session(struct bContext *C, struct ReportList *reports); +void WM_file_autosave_init(struct wmWindowManager *wm); +bool WM_file_recover_last_session(struct bContext *C, struct ReportList *reports); void WM_file_tag_modified(void); /** diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index a55f5af03d1..487eafc736c 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -484,7 +484,7 @@ void WM_check(bContext *C) /* Case: file-read. */ if ((wm->initialized & WM_WINDOW_IS_INIT) == 0) { WM_keyconfig_init(C); - WM_autosave_init(wm); + WM_file_autosave_init(wm); } /* Case: no open windows at all, for old file reads. */ diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 7471d41b44c..5bf5780b93f 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1966,7 +1966,7 @@ void wm_autosave_timer_end(wmWindowManager *wm) } } -void WM_autosave_init(wmWindowManager *wm) +void WM_file_autosave_init(wmWindowManager *wm) { wm_autosave_timer_begin(wm); } @@ -2860,7 +2860,7 @@ void WM_OT_revert_mainfile(wmOperatorType *ot) /** \name Recover Last Session Operator * \{ */ -bool WM_recover_last_session(bContext *C, ReportList *reports) +bool WM_file_recover_last_session(bContext *C, ReportList *reports) { char filepath[FILE_MAX]; BLI_join_dirfile(filepath, sizeof(filepath), BKE_tempdir_base(), BLENDER_QUIT_FILE); @@ -2874,7 +2874,7 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op) { wm_open_init_use_scripts(op, true); SET_FLAG_FROM_TEST(G.f, RNA_boolean_get(op->ptr, "use_scripts"), G_FLAG_SCRIPT_AUTOEXEC); - if (WM_recover_last_session(C, op->reports)) { + if (WM_file_recover_last_session(C, op->reports)) { if (!G.background) { wmOperatorType *ot = op->type; PointerRNA *props_ptr = MEM_callocN(sizeof(PointerRNA), __func__); -- cgit v1.2.3 From 8c3aee2fafb5a7cd3292d23bdc76ca92a35d0799 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Mar 2022 13:18:02 +1100 Subject: Cleanup: use SPDX license header --- .../blender/draw/engines/image/image_buffer_cache.hh | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/source/blender/draw/engines/image/image_buffer_cache.hh b/source/blender/draw/engines/image/image_buffer_cache.hh index ef11551c879..470e9f225b4 100644 --- a/source/blender/draw/engines/image/image_buffer_cache.hh +++ b/source/blender/draw/engines/image/image_buffer_cache.hh @@ -1,20 +1,5 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Copyright 2022, Blender Foundation. - */ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ /** \file * \ingroup draw_engine -- cgit v1.2.3 From b33d38ebd90b90e6c9d13f3f2cf002873272639e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Mar 2022 16:38:28 +1100 Subject: Cleanup: de-duplicate event customdata freeing logic --- .../blender/windowmanager/intern/wm_event_system.c | 46 +++++++++++++--------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f6e71ba5df3..14a4a618bb0 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -161,6 +161,29 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) return event; } +static void wm_event_custom_free(wmEvent *event) +{ + if ((event->customdata && event->customdata_free) == 0) { + return; + } + + /* NOTE: pointer to #ListBase struct elsewhere. */ + if (event->custom == EVT_DATA_DRAGDROP) { + ListBase *lb = event->customdata; + WM_drag_free_list(lb); + } + else { + MEM_freeN(event->customdata); + } +} + +static void wm_event_custom_clear(wmEvent *event) +{ + event->custom = 0; + event->customdata = NULL; + event->customdata_free = false; +} + void wm_event_free(wmEvent *event) { #ifndef NDEBUG @@ -172,18 +195,7 @@ void wm_event_free(wmEvent *event) } #endif - if (event->customdata) { - if (event->customdata_free) { - /* NOTE: pointer to #ListBase struct elsewhere. */ - if (event->custom == EVT_DATA_DRAGDROP) { - ListBase *lb = event->customdata; - WM_drag_free_list(lb); - } - else { - MEM_freeN(event->customdata); - } - } - } + wm_event_custom_free(event); MEM_freeN(event); } @@ -3062,8 +3074,7 @@ static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, Lis WM_drag_free_list(lb); WM_drag_free_list(&single_lb); - event->customdata = NULL; - event->custom = 0; + wm_event_custom_clear(event); wm_drop_end(C, drag, drop); @@ -3405,11 +3416,8 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv event->type = EVT_DROP; /* Create custom-data, first free existing. */ - if (event->customdata) { - if (event->customdata_free) { - MEM_freeN(event->customdata); - } - } + wm_event_custom_free(event); + wm_event_custom_clear(event); event->custom = EVT_DATA_DRAGDROP; event->customdata = &wm->drags; -- cgit v1.2.3 From ef6d108b759711102a04ac5f9ab9dafffae473bc Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Mar 2022 20:47:28 +1100 Subject: Fix T96279: 'Emulate 3 button mouse' fails to emulate MMB Regression in 08d8eee006f7e2a7ac05ef691bbaee230cbfbe5a caused emulate-middle mouse to work once, clearing the modifier key. Now the modifier key from emulated mouse events is never stored in the windows event-state. --- source/blender/windowmanager/intern/wm_event_system.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 14a4a618bb0..47f1f36dceb 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4775,7 +4775,9 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d /** * Update the event-state for any kind of event that supports #KM_PRESS / #KM_RELEASE. */ -static void wm_event_state_update_and_click_set(wmEvent *event, wmEvent *event_state) +static void wm_event_state_update_and_click_set(const GHOST_TEventType type, + wmEvent *event, + wmEvent *event_state) { BLI_assert(ISKEYBOARD_OR_BUTTON(event->type)); BLI_assert(ELEM(event->val, KM_PRESS, KM_RELEASE)); @@ -4788,7 +4790,12 @@ static void wm_event_state_update_and_click_set(wmEvent *event, wmEvent *event_s /* Copy to event state. */ event_state->val = event->val; event_state->type = event->type; - event_state->modifier = event->modifier; + /* It's important only to write into the `event_state` modifier for keyboard + * events because emulate MMB clears one of the modifiers in `event->modifier`, + * making the second press not behave as if the modifier is pressed, see T96279. */ + if (ELEM(type, GHOST_kEventKeyDown, GHOST_kEventKeyUp)) { + event_state->modifier = event->modifier; + } event_state->flag = (event->flag & event_state_flag_mask); /* NOTE: It's important that `keymodifier` is handled in the keyboard event handling logic * since the `event_state` and the `event` are not kept in sync. */ @@ -4962,7 +4969,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wm_tablet_data_from_ghost(&bd->tablet, &event.tablet); wm_eventemulation(&event, false); - wm_event_state_update_and_click_set(&event, event_state); + wm_event_state_update_and_click_set(type, &event, event_state); /* Add to other window if event is there (not to both!). */ wmWindow *win_other = wm_event_cursor_other_windows(wm, win, &event); @@ -5082,7 +5089,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void } /* It's important `event.modifier` has been initialized first. */ - wm_event_state_update_and_click_set(&event, event_state); + wm_event_state_update_and_click_set(type, &event, event_state); /* If test_break set, it catches this. Do not set with modifier presses. * Exclude modifiers because MS-Windows uses these to bring up the task manager. @@ -5156,7 +5163,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.custom = 0; event.customdata = NULL; - wm_event_state_update_and_click_set(&event, event_state); + wm_event_state_update_and_click_set(type, &event, event_state); wm_event_add(win, &event); -- cgit v1.2.3 From 1f1dcf41d51a03150ee38f220c590f8715b11e88 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 10 Mar 2022 16:23:24 +1100 Subject: Event System: key-press while dragging now activates drag first When dragging with a large threshold (using a tablet for example), it's possible to press another key before the drag threshold is reached. So tweaking then pressing X would show the delete popup instead of transforming along the X-axis. Now key presses while dragging cause the drag event to be evaluated before the key press. Note that to properly base the mouse-move event on the previous state the last handled event is now stored in the window. Without this the inserted mouse-move event may contain invalid values from the next event (it's modifier state or other `prev_*` values). Requested by @JulienKaspar. --- .../editors/interface/interface_region_popup.c | 2 + source/blender/makesdna/DNA_windowmanager_types.h | 2 + source/blender/windowmanager/WM_types.h | 5 ++ source/blender/windowmanager/intern/wm.c | 1 + .../blender/windowmanager/intern/wm_event_system.c | 77 ++++++++++++++++++++-- source/blender/windowmanager/intern/wm_files.c | 2 + source/blender/windowmanager/intern/wm_window.c | 3 + 7 files changed, 88 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index c37d8ec7a2b..2f2556225b5 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -557,6 +557,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, #ifdef DEBUG wmEvent *event_back = window->eventstate; + wmEvent *event_last_back = window->event_last_handled; #endif /* create ui block */ @@ -740,6 +741,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, #ifdef DEBUG window->eventstate = event_back; + window->event_last_handled = event_last_back; #endif return block; diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index 42c94832a43..ade0fcdb13f 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -294,6 +294,8 @@ typedef struct wmWindow { /** Storage for event system. */ struct wmEvent *eventstate; + /** Keep the last handled event in `event_queue` here (owned and must be freed). */ + struct wmEvent *event_last_handled; /* Input Method Editor data - complex character input (especially for Asian character input) * Currently WIN32 and APPLE, runtime-only data. */ diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index 8934f714c21..9edbafafdd3 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -610,6 +610,11 @@ typedef enum eWM_EventFlag { * See #KMI_REPEAT_IGNORE for details on how key-map handling uses this. */ WM_EVENT_IS_REPEAT = (1 << 1), + /** + * Mouse-move events may have this flag set to force creating a click-drag event + * even when the threshold has not been met. + */ + WM_EVENT_FORCE_DRAG_THRESHOLD = (1 << 2), } eWM_EventFlag; typedef struct wmTabletData { diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 487eafc736c..40d9b0b9a35 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -155,6 +155,7 @@ static void window_manager_blend_read_data(BlendDataReader *reader, ID *id) win->ghostwin = NULL; win->gpuctx = NULL; win->eventstate = NULL; + win->event_last_handled = NULL; win->cursor_keymap_status = NULL; #if defined(WIN32) || defined(__APPLE__) win->ime_data = NULL; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 47f1f36dceb..3fe8e6cd1b0 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -100,6 +100,7 @@ static int wm_operator_call_internal(bContext *C, wmEvent *event); static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); +static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win); /* -------------------------------------------------------------------- */ /** \name Event Management @@ -200,6 +201,30 @@ void wm_event_free(wmEvent *event) MEM_freeN(event); } +/** A version of #wm_event_free that holds the last handled event. */ +static void wm_event_free_last_handled(wmWindow *win, wmEvent *event) +{ + /* Don't rely on this pointer being valid, + * callers should behave as if the memory has been freed. + * As this function should be interchangeable with #wm_event_free. */ +#ifndef NDEBUG + { + wmEvent *event_copy = MEM_dupallocN(event); + MEM_freeN(event); + event = event_copy; + } +#endif + + if (win->event_last_handled) { + wm_event_free(win->event_last_handled); + } + /* Don't store custom data in the last handled event as we don't have control how long this event + * will be stored and the referenced data may become invalid (also it's not needed currently). */ + wm_event_custom_free(event); + wm_event_custom_clear(event); + win->event_last_handled = event; +} + static void wm_event_free_last(wmWindow *win) { wmEvent *event = BLI_poptail(&win->event_queue); @@ -3176,7 +3201,8 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) * in `action` setting #WM_HANDLER_HANDLED, but not #WM_HANDLER_BREAK. */ if ((action & WM_HANDLER_BREAK) == 0 || wm_action_not_handled(action)) { if (win->event_queue_check_drag) { - if (WM_event_drag_test(event, event->prev_press_xy)) { + if ((event->flag & WM_EVENT_FORCE_DRAG_THRESHOLD) || + WM_event_drag_test(event, event->prev_press_xy)) { win->event_queue_check_drag_handled = true; const int direction = WM_event_drag_direction(event); @@ -3660,6 +3686,17 @@ void wm_event_do_handlers(bContext *C) while ((event = win->event_queue.first)) { int action = WM_HANDLER_CONTINUE; + /* Force handling drag if a key is pressed even if the drag threshold has not been met. + * Needed so tablet actions (which typically use a larger threshold) can click-drag + * then press keys - activating the drag action early. */ + if (win->event_queue_check_drag) { + if ((event->val == KM_PRESS) && ((event->flag & WM_EVENT_IS_REPEAT) == 0) && + ISKEYBOARD_OR_BUTTON(event->type)) { + event = wm_event_add_mousemove_to_head(win); + event->flag |= WM_EVENT_FORCE_DRAG_THRESHOLD; + } + } + /* Active screen might change during handlers, update pointer. */ screen = WM_window_get_active_screen(win); @@ -3675,7 +3712,7 @@ void wm_event_do_handlers(bContext *C) CLOG_INFO(WM_LOG_HANDLERS, 1, "event filtered due to pie button pressed"); } BLI_remlink(&win->event_queue, event); - wm_event_free(event); + wm_event_free_last_handled(win, event); continue; } @@ -3685,7 +3722,7 @@ void wm_event_do_handlers(bContext *C) if (event->type == EVT_XR_ACTION) { wm_event_handle_xrevent(C, wm, win, event); BLI_remlink(&win->event_queue, event); - wm_event_free(event); + wm_event_free_last_handled(win, event); /* Skip mouse event handling below, which is unnecessary for XR events. */ continue; } @@ -3823,7 +3860,7 @@ void wm_event_do_handlers(bContext *C) /* Un-link and free here, Blender-quit then frees all. */ BLI_remlink(&win->event_queue, event); - wm_event_free(event); + wm_event_free_last_handled(win, event); } /* Only add mouse-move when the event queue was read entirely. */ @@ -4752,6 +4789,38 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) return event_new; } +static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win) +{ + /* Use the last handled event instead of `win->eventstate` because the state of the modifiers + * and previous values should be set based on the last state, not using values from the future. + * So this gives an accurate simulation of mouse motion before the next event is handled. */ + const wmEvent *event_last = win->event_last_handled; + + wmEvent tevent; + if (event_last) { + tevent = *event_last; + + tevent.flag = 0; + tevent.ascii = '\0'; + tevent.utf8_buf[0] = '\0'; + + wm_event_custom_clear(&tevent); + } + else { + memset(&tevent, 0x0, sizeof(tevent)); + } + + tevent.type = MOUSEMOVE; + copy_v2_v2_int(tevent.prev_xy, tevent.xy); + + wmEvent *event_new = wm_event_add(win, &tevent); + BLI_remlink(&win->event_queue, event_new); + BLI_addhead(&win->event_queue, event_new); + + copy_v2_v2_int(event_new->prev_xy, event_last->xy); + return event_new; +} + static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int deltax, int deltay) { /* Ignore in between track-pad events for performance, we only need high accuracy diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 5bf5780b93f..4ff96b82fd2 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -246,7 +246,9 @@ static void wm_window_substitute_old(wmWindowManager *oldwm, oldwin->gpuctx = NULL; win->eventstate = oldwin->eventstate; + win->event_last_handled = oldwin->event_last_handled; oldwin->eventstate = NULL; + oldwin->event_last_handled = NULL; /* Ensure proper screen re-scaling. */ win->sizex = oldwin->sizex; diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 6c90e0603a5..d6f85e3795e 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -209,6 +209,9 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) if (win->eventstate) { MEM_freeN(win->eventstate); } + if (win->event_last_handled) { + MEM_freeN(win->event_last_handled); + } if (win->cursor_keymap_status) { MEM_freeN(win->cursor_keymap_status); -- cgit v1.2.3 From 7b62203fc794b5facda195bccea5981b54256044 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 7 Mar 2022 17:27:06 +0100 Subject: IK: make Ik chain length non-animatable Mark the chain length of regular and spline IK constraints non-animatable. Changing the IK chain length requires a rebuild of depsgraph relations. This makes it unsuitable for animation. It's better to simply avoid having this property animatable than to allow animation but produce unstable results. Ref: T96203 --- source/blender/makesrna/intern/rna_constraint.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 114fe30acf7..63d8876ec8b 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -1209,6 +1209,9 @@ static void rna_def_constraint_kinematic(BlenderRNA *brna) prop = RNA_def_property(srna, "chain_count", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "rootbone"); + /* Changing the IK chain length requires a rebuild of depsgraph relations. This makes it + * unsuitable for animation. */ + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_range(prop, 0, 255); RNA_def_property_ui_text( prop, "Chain Length", "How many bones are included in the IK effect - 0 uses all bones"); @@ -3020,6 +3023,9 @@ static void rna_def_constraint_spline_ik(BlenderRNA *brna) prop = RNA_def_property(srna, "chain_count", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "chainlen"); + /* Changing the IK chain length requires a rebuild of depsgraph relations. This makes it + * unsuitable for animation. */ + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); /* TODO: this should really check the max length of the chain the constraint is attached to */ RNA_def_property_range(prop, 1, 255); RNA_def_property_ui_text(prop, "Chain Length", "How many bones are included in the chain"); -- cgit v1.2.3 From 34d8ef8fa47f38e44088f34338d9dc7b126bce1c Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Thu, 10 Mar 2022 11:36:16 +0100 Subject: Fix: closest image texture interpolation inconsistent in geometry nodes The `-0.5f` offset is inconsistent with shader nodes. See D14235 for an example file. Differential Revision: https://developer.blender.org/D14235 --- source/blender/nodes/geometry/nodes/node_geo_image_texture.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc index a87aef358e1..33802d00d2b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -257,8 +257,8 @@ class ImageFieldsFunction : public fn::MultiFunction { const int width = ibuf->x; const int height = ibuf->y; int ix, iy; - const float tx = frac(px * (float)width - 0.5f, &ix); - const float ty = frac(py * (float)height - 0.5f, &iy); + const float tx = frac(px * (float)width, &ix); + const float ty = frac(py * (float)height, &iy); switch (extension) { case SHD_IMAGE_EXTENSION_REPEAT: { -- cgit v1.2.3 From 8cdee3a6d4306fa7cae73b9f7bcd761e90f39937 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Thu, 10 Mar 2022 11:55:43 +0100 Subject: Fix T93710: Artifacts denoising hi-res images using OPtiX Caused by an integer overflow in the tiling utilities of OptiX SDK. Seems for now it's easier to copy and modify code to our sources so that we don't need to bump SDK version requirement (which might lead to an increased driver requirement as well). There are still some fixes needed from a newer driver to have such denoising to work properly: Windows requires 511.79, Linux 510.54. Thanks Patrick for investigation! Differential Revision: https://developer.blender.org/D14300 --- intern/cycles/device/optix/device_impl.cpp | 213 +++++++++++++++++++++++++++-- 1 file changed, 199 insertions(+), 14 deletions(-) diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp index cbe823c26ad..8830d8c44ac 100644 --- a/intern/cycles/device/optix/device_impl.cpp +++ b/intern/cycles/device/optix/device_impl.cpp @@ -33,6 +33,191 @@ CCL_NAMESPACE_BEGIN +// A minimal copy of functionality `optix_denoiser_tiling.h` which allows to fix integer overflow +// issues without bumping SDK or driver requirement. +// +// The original code is Copyright NVIDIA Corporation, BSD-3-Clause. +namespace { + +static OptixResult optixUtilDenoiserSplitImage(const OptixImage2D &input, + const OptixImage2D &output, + unsigned int overlapWindowSizeInPixels, + unsigned int tileWidth, + unsigned int tileHeight, + std::vector &tiles) +{ + if (tileWidth == 0 || tileHeight == 0) + return OPTIX_ERROR_INVALID_VALUE; + + unsigned int inPixelStride = optixUtilGetPixelStride(input); + unsigned int outPixelStride = optixUtilGetPixelStride(output); + + int inp_w = std::min(tileWidth + 2 * overlapWindowSizeInPixels, input.width); + int inp_h = std::min(tileHeight + 2 * overlapWindowSizeInPixels, input.height); + int inp_y = 0, copied_y = 0; + + do { + int inputOffsetY = inp_y == 0 ? 0 : + std::max((int)overlapWindowSizeInPixels, + inp_h - ((int)input.height - inp_y)); + int copy_y = inp_y == 0 ? std::min(input.height, tileHeight + overlapWindowSizeInPixels) : + std::min(tileHeight, input.height - copied_y); + + int inp_x = 0, copied_x = 0; + do { + int inputOffsetX = inp_x == 0 ? 0 : + std::max((int)overlapWindowSizeInPixels, + inp_w - ((int)input.width - inp_x)); + int copy_x = inp_x == 0 ? std::min(input.width, tileWidth + overlapWindowSizeInPixels) : + std::min(tileWidth, input.width - copied_x); + + OptixUtilDenoiserImageTile tile; + tile.input.data = input.data + (size_t)(inp_y - inputOffsetY) * input.rowStrideInBytes + + +(size_t)(inp_x - inputOffsetX) * inPixelStride; + tile.input.width = inp_w; + tile.input.height = inp_h; + tile.input.rowStrideInBytes = input.rowStrideInBytes; + tile.input.pixelStrideInBytes = input.pixelStrideInBytes; + tile.input.format = input.format; + + tile.output.data = output.data + (size_t)inp_y * output.rowStrideInBytes + + (size_t)inp_x * outPixelStride; + tile.output.width = copy_x; + tile.output.height = copy_y; + tile.output.rowStrideInBytes = output.rowStrideInBytes; + tile.output.pixelStrideInBytes = output.pixelStrideInBytes; + tile.output.format = output.format; + + tile.inputOffsetX = inputOffsetX; + tile.inputOffsetY = inputOffsetY; + tiles.push_back(tile); + + inp_x += inp_x == 0 ? tileWidth + overlapWindowSizeInPixels : tileWidth; + copied_x += copy_x; + } while (inp_x < static_cast(input.width)); + + inp_y += inp_y == 0 ? tileHeight + overlapWindowSizeInPixels : tileHeight; + copied_y += copy_y; + } while (inp_y < static_cast(input.height)); + + return OPTIX_SUCCESS; +} + +static OptixResult optixUtilDenoiserInvokeTiled(OptixDenoiser denoiser, + CUstream stream, + const OptixDenoiserParams *params, + CUdeviceptr denoiserState, + size_t denoiserStateSizeInBytes, + const OptixDenoiserGuideLayer *guideLayer, + const OptixDenoiserLayer *layers, + unsigned int numLayers, + CUdeviceptr scratch, + size_t scratchSizeInBytes, + unsigned int overlapWindowSizeInPixels, + unsigned int tileWidth, + unsigned int tileHeight) +{ + if (!guideLayer || !layers) + return OPTIX_ERROR_INVALID_VALUE; + + std::vector> tiles(numLayers); + std::vector> prevTiles(numLayers); + for (unsigned int l = 0; l < numLayers; l++) { + if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(layers[l].input, + layers[l].output, + overlapWindowSizeInPixels, + tileWidth, + tileHeight, + tiles[l])) + return res; + + if (layers[l].previousOutput.data) { + OptixImage2D dummyOutput = layers[l].previousOutput; + if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(layers[l].previousOutput, + dummyOutput, + overlapWindowSizeInPixels, + tileWidth, + tileHeight, + prevTiles[l])) + return res; + } + } + + std::vector albedoTiles; + if (guideLayer->albedo.data) { + OptixImage2D dummyOutput = guideLayer->albedo; + if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(guideLayer->albedo, + dummyOutput, + overlapWindowSizeInPixels, + tileWidth, + tileHeight, + albedoTiles)) + return res; + } + + std::vector normalTiles; + if (guideLayer->normal.data) { + OptixImage2D dummyOutput = guideLayer->normal; + if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(guideLayer->normal, + dummyOutput, + overlapWindowSizeInPixels, + tileWidth, + tileHeight, + normalTiles)) + return res; + } + std::vector flowTiles; + if (guideLayer->flow.data) { + OptixImage2D dummyOutput = guideLayer->flow; + if (const OptixResult res = ccl::optixUtilDenoiserSplitImage(guideLayer->flow, + dummyOutput, + overlapWindowSizeInPixels, + tileWidth, + tileHeight, + flowTiles)) + return res; + } + + for (size_t t = 0; t < tiles[0].size(); t++) { + std::vector tlayers; + for (unsigned int l = 0; l < numLayers; l++) { + OptixDenoiserLayer layer = {}; + layer.input = (tiles[l])[t].input; + layer.output = (tiles[l])[t].output; + if (layers[l].previousOutput.data) + layer.previousOutput = (prevTiles[l])[t].input; + tlayers.push_back(layer); + } + + OptixDenoiserGuideLayer gl = {}; + if (guideLayer->albedo.data) + gl.albedo = albedoTiles[t].input; + + if (guideLayer->normal.data) + gl.normal = normalTiles[t].input; + + if (guideLayer->flow.data) + gl.flow = flowTiles[t].input; + + if (const OptixResult res = optixDenoiserInvoke(denoiser, + stream, + params, + denoiserState, + denoiserStateSizeInBytes, + &gl, + &tlayers[0], + numLayers, + (tiles[0])[t].inputOffsetX, + (tiles[0])[t].inputOffsetY, + scratch, + scratchSizeInBytes)) + return res; + } + return OPTIX_SUCCESS; +} + +} // namespace + OptiXDevice::Denoiser::Denoiser(OptiXDevice *device) : device(device), queue(device), state(device, "__denoiser_state", true) { @@ -1075,20 +1260,20 @@ bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) /* Finally run denoising. */ OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */ - optix_assert(optixUtilDenoiserInvokeTiled(denoiser_.optix_denoiser, - denoiser_.queue.stream(), - ¶ms, - denoiser_.state.device_pointer, - denoiser_.sizes.stateSizeInBytes, - &guide_layers, - &image_layers, - 1, - denoiser_.state.device_pointer + - denoiser_.sizes.stateSizeInBytes, - denoiser_.sizes.withOverlapScratchSizeInBytes, - denoiser_.sizes.overlapWindowSizeInPixels, - denoiser_.configured_size.x, - denoiser_.configured_size.y)); + optix_assert(ccl::optixUtilDenoiserInvokeTiled(denoiser_.optix_denoiser, + denoiser_.queue.stream(), + ¶ms, + denoiser_.state.device_pointer, + denoiser_.sizes.stateSizeInBytes, + &guide_layers, + &image_layers, + 1, + denoiser_.state.device_pointer + + denoiser_.sizes.stateSizeInBytes, + denoiser_.sizes.withOverlapScratchSizeInBytes, + denoiser_.sizes.overlapWindowSizeInPixels, + denoiser_.configured_size.x, + denoiser_.configured_size.y)); return true; } -- cgit v1.2.3 From 338fe9e2b5360705b9edd9c526e76dece2477610 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 10 Mar 2022 09:37:42 -0600 Subject: Cleanup: Reduce duplication in realize instances code Realizing and copying attributes of meshes, curves, and points are very similar processes, but currently the logic is duplicated three times in the realize instances code. This commit combines the implementation for copying generic attributes and creating the result id attribute. The functions for threaded copying and filling should ideally be in some file elsewhere, since they're not just useful here. But it's not clear where they would go yet. Differential Revision: https://developer.blender.org/D14294 --- .../blender/geometry/intern/realize_instances.cc | 323 +++++++++------------ 1 file changed, 132 insertions(+), 191 deletions(-) diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 94c75e8dbb1..100146bbf0c 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -265,6 +265,81 @@ static void copy_transformed_positions(const Span src, }); } +static void threaded_copy(const GSpan src, GMutableSpan dst) +{ + BLI_assert(src.size() == dst.size()); + BLI_assert(src.type() == dst.type()); + threading::parallel_for(IndexRange(src.size()), 1024, [&](const IndexRange range) { + src.type().copy_construct_n(src.slice(range).data(), dst.slice(range).data(), range.size()); + }); +} + +static void threaded_fill(const fn::GPointer value, GMutableSpan dst) +{ + BLI_assert(*value.type() == dst.type()); + threading::parallel_for(IndexRange(dst.size()), 1024, [&](const IndexRange range) { + value.type()->fill_construct_n(value.get(), dst.slice(range).data(), range.size()); + }); +} + +static void copy_generic_attributes_to_result( + const Span> src_attributes, + const AttributeFallbacksArray &attribute_fallbacks, + const OrderedAttributes &ordered_attributes, + const FunctionRef &range_fn, + MutableSpan dst_attributes) +{ + threading::parallel_for(dst_attributes.index_range(), 10, [&](const IndexRange attribute_range) { + for (const int attribute_index : attribute_range) { + const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; + const IndexRange element_slice = range_fn(domain); + + GMutableSpan dst_span = dst_attributes[attribute_index].slice(element_slice); + if (src_attributes[attribute_index].has_value()) { + threaded_copy(*src_attributes[attribute_index], dst_span); + } + else { + const CPPType &cpp_type = dst_span.type(); + const void *fallback = attribute_fallbacks.array[attribute_index] == nullptr ? + cpp_type.default_value() : + attribute_fallbacks.array[attribute_index]; + threaded_fill({cpp_type, fallback}, dst_span); + } + } + }); +} + +static void create_result_ids(const RealizeInstancesOptions &options, + Span stored_ids, + const int task_id, + MutableSpan dst_ids) +{ + if (options.keep_original_ids) { + if (stored_ids.is_empty()) { + dst_ids.fill(0); + } + else { + dst_ids.copy_from(stored_ids); + } + } + else { + if (stored_ids.is_empty()) { + threading::parallel_for(dst_ids.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + dst_ids[i] = noise::hash(task_id, i); + } + }); + } + else { + threading::parallel_for(dst_ids.index_range(), 1024, [&](const IndexRange range) { + for (const int i : range) { + dst_ids[i] = noise::hash(task_id, stored_ids[i]); + } + }); + } + } +} + /* -------------------------------------------------------------------- */ /** \name Gather Realize Tasks * \{ */ @@ -595,6 +670,7 @@ static AllPointCloudsInfo preprocess_pointclouds(const GeometrySet &geometry_set static void execute_realize_pointcloud_task(const RealizeInstancesOptions &options, const RealizePointCloudTask &task, + const OrderedAttributes &ordered_attributes, PointCloud &dst_pointcloud, MutableSpan dst_attribute_spans, MutableSpan all_dst_ids) @@ -605,64 +681,25 @@ static void execute_realize_pointcloud_task(const RealizeInstancesOptions &optio const IndexRange point_slice{task.start_index, pointcloud.totpoint}; MutableSpan dst_positions{(float3 *)dst_pointcloud.co + task.start_index, pointcloud.totpoint}; - MutableSpan dst_ids = all_dst_ids.slice(task.start_index, pointcloud.totpoint); copy_transformed_positions(src_positions, task.transform, dst_positions); /* Create point ids. */ if (!all_dst_ids.is_empty()) { - if (options.keep_original_ids) { - if (pointcloud_info.stored_ids.is_empty()) { - dst_ids.fill(0); - } - else { - dst_ids.copy_from(pointcloud_info.stored_ids); - } - } - else { - threading::parallel_for(IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - if (pointcloud_info.stored_ids.is_empty()) { - for (const int i : range) { - dst_ids[i] = noise::hash(task.id, i); - } - } - else { - for (const int i : range) { - dst_ids[i] = noise::hash(task.id, pointcloud_info.stored_ids[i]); - } - } - }); - } + create_result_ids( + options, pointcloud_info.stored_ids, task.id, all_dst_ids.slice(point_slice)); } - /* Copy generic attributes. */ - threading::parallel_for( - dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) { - for (const int attribute_index : attribute_range) { - GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(point_slice); - const CPPType &cpp_type = dst_span.type(); - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - if (pointcloud_info.attributes[attribute_index].has_value()) { - /* Copy attribute from the original point cloud. */ - const GSpan src_span = *pointcloud_info.attributes[attribute_index]; - threading::parallel_for( - IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - cpp_type.copy_construct_n( - src_span.slice(range).data(), dst_span.slice(range).data(), range.size()); - }); - } - else { - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); - } - /* As the fallback value for the attribute. */ - threading::parallel_for( - IndexRange(pointcloud.totpoint), 1024, [&](const IndexRange range) { - cpp_type.fill_construct_n( - attribute_fallback, dst_span.slice(range).data(), range.size()); - }); - } - } - }); + + copy_generic_attributes_to_result( + pointcloud_info.attributes, + task.attribute_fallbacks, + ordered_attributes, + [&](const AttributeDomain domain) { + BLI_assert(domain == ATTR_DOMAIN_POINT); + UNUSED_VARS_NDEBUG(domain); + return point_slice; + }, + dst_attribute_spans); } static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &options, @@ -710,7 +747,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti for (const int task_index : task_range) { const RealizePointCloudTask &task = tasks[task_index]; execute_realize_pointcloud_task( - options, task, *dst_pointcloud, dst_attribute_spans, point_ids_span); + options, task, ordered_attributes, *dst_pointcloud, dst_attribute_spans, point_ids_span); } }); @@ -842,9 +879,6 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, MutableSpan dst_loops{dst_mesh.mloop + task.start_indices.loop, mesh.totloop}; MutableSpan dst_polys{dst_mesh.mpoly + task.start_indices.poly, mesh.totpoly}; - MutableSpan dst_vertex_ids = all_dst_vertex_ids.slice(task.start_indices.vertex, - mesh.totvert); - const Span material_index_map = mesh_info.material_index_map; threading::parallel_for(IndexRange(mesh.totvert), 1024, [&](const IndexRange vert_range) { @@ -888,79 +922,34 @@ static void execute_realize_mesh_task(const RealizeInstancesOptions &options, } } }); - /* Create id attribute. */ + if (!all_dst_vertex_ids.is_empty()) { - if (options.keep_original_ids) { - if (mesh_info.stored_vertex_ids.is_empty()) { - dst_vertex_ids.fill(0); - } - else { - dst_vertex_ids.copy_from(mesh_info.stored_vertex_ids); - } - } - else { - threading::parallel_for(IndexRange(mesh.totvert), 1024, [&](const IndexRange vert_range) { - if (mesh_info.stored_vertex_ids.is_empty()) { - for (const int i : vert_range) { - dst_vertex_ids[i] = noise::hash(task.id, i); - } - } - else { - for (const int i : vert_range) { - const int original_id = mesh_info.stored_vertex_ids[i]; - dst_vertex_ids[i] = noise::hash(task.id, original_id); - } - } - }); - } + create_result_ids(options, + mesh_info.stored_vertex_ids, + task.id, + all_dst_vertex_ids.slice(task.start_indices.vertex, mesh.totvert)); } - /* Copy generic attributes. */ - threading::parallel_for( - dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) { - for (const int attribute_index : attribute_range) { - const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - IndexRange element_slice; - switch (domain) { - case ATTR_DOMAIN_POINT: - element_slice = IndexRange(task.start_indices.vertex, mesh.totvert); - break; - case ATTR_DOMAIN_EDGE: - element_slice = IndexRange(task.start_indices.edge, mesh.totedge); - break; - case ATTR_DOMAIN_CORNER: - element_slice = IndexRange(task.start_indices.loop, mesh.totloop); - break; - case ATTR_DOMAIN_FACE: - element_slice = IndexRange(task.start_indices.poly, mesh.totpoly); - break; - default: - BLI_assert_unreachable(); - break; - } - GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(element_slice); - const CPPType &cpp_type = dst_span.type(); - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - if (mesh_info.attributes[attribute_index].has_value()) { - const GSpan src_span = *mesh_info.attributes[attribute_index]; - threading::parallel_for( - IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.copy_construct_n(src_span.slice(sub_range).data(), - dst_span.slice(sub_range).data(), - sub_range.size()); - }); - } - else { - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); - } - threading::parallel_for( - IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.fill_construct_n( - attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size()); - }); - } + + copy_generic_attributes_to_result( + mesh_info.attributes, + task.attribute_fallbacks, + ordered_attributes, + [&](const AttributeDomain domain) { + switch (domain) { + case ATTR_DOMAIN_POINT: + return IndexRange(task.start_indices.vertex, mesh.totvert); + case ATTR_DOMAIN_EDGE: + return IndexRange(task.start_indices.edge, mesh.totedge); + case ATTR_DOMAIN_CORNER: + return IndexRange(task.start_indices.loop, mesh.totloop); + case ATTR_DOMAIN_FACE: + return IndexRange(task.start_indices.poly, mesh.totpoly); + default: + BLI_assert_unreachable(); + return IndexRange(); } - }); + }, + dst_attribute_spans); } static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, @@ -1200,75 +1189,27 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, } }); - /* Create id attribute. */ if (!all_dst_ids.is_empty()) { - MutableSpan dst_ids = all_dst_ids.slice(task.start_indices.point, curves.points_size()); - if (options.keep_original_ids) { - if (curves_info.stored_ids.is_empty()) { - dst_ids.fill(0); - } - else { - dst_ids.copy_from(curves_info.stored_ids); - } - } - else { - threading::parallel_for(curves.points_range(), 1024, [&](const IndexRange vert_range) { - if (curves_info.stored_ids.is_empty()) { - for (const int i : vert_range) { - dst_ids[i] = noise::hash(task.id, i); - } - } - else { - for (const int i : vert_range) { - const int original_id = curves_info.stored_ids[i]; - dst_ids[i] = noise::hash(task.id, original_id); - } - } - }); - } + create_result_ids( + options, curves_info.stored_ids, task.id, all_dst_ids.slice(dst_point_range)); } - /* Copy generic attributes. */ - threading::parallel_for( - dst_attribute_spans.index_range(), 10, [&](const IndexRange attribute_range) { - for (const int attribute_index : attribute_range) { - const AttributeDomain domain = ordered_attributes.kinds[attribute_index].domain; - IndexRange element_slice; - switch (domain) { - case ATTR_DOMAIN_POINT: - element_slice = IndexRange(task.start_indices.point, curves.points_size()); - break; - case ATTR_DOMAIN_CURVE: - element_slice = IndexRange(task.start_indices.curve, curves.curves_size()); - break; - default: - BLI_assert_unreachable(); - break; - } - GMutableSpan dst_span = dst_attribute_spans[attribute_index].slice(element_slice); - const CPPType &cpp_type = dst_span.type(); - const void *attribute_fallback = task.attribute_fallbacks.array[attribute_index]; - if (curves_info.attributes[attribute_index].has_value()) { - const GSpan src_span = *curves_info.attributes[attribute_index]; - threading::parallel_for( - IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.copy_construct_n(src_span.slice(sub_range).data(), - dst_span.slice(sub_range).data(), - sub_range.size()); - }); - } - else { - if (attribute_fallback == nullptr) { - attribute_fallback = cpp_type.default_value(); - } - threading::parallel_for( - IndexRange(element_slice.size()), 1024, [&](const IndexRange sub_range) { - cpp_type.fill_construct_n( - attribute_fallback, dst_span.slice(sub_range).data(), sub_range.size()); - }); - } + copy_generic_attributes_to_result( + curves_info.attributes, + task.attribute_fallbacks, + ordered_attributes, + [&](const AttributeDomain domain) { + switch (domain) { + case ATTR_DOMAIN_POINT: + return IndexRange(task.start_indices.point, curves.points_size()); + case ATTR_DOMAIN_CURVE: + return IndexRange(task.start_indices.curve, curves.curves_size()); + default: + BLI_assert_unreachable(); + return IndexRange(); } - }); + }, + dst_attribute_spans); } static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, -- cgit v1.2.3 From 22a341d9d8d3d337f79df228ab2e4e0726f81430 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 10 Mar 2022 18:04:07 +0100 Subject: Fix T96292: unable to set active material output using Python Differential Revision: https://developer.blender.org/D14301 --- source/blender/makesrna/intern/rna_nodetree.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index cc97dca9387..640fe3b919b 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -4520,6 +4520,25 @@ static void rna_CompositorNodeScale_update(Main *bmain, Scene *scene, PointerRNA rna_Node_update(bmain, scene, ptr); } +static void rna_ShaderNode_is_active_output_set(PointerRNA *ptr, bool value) +{ + bNodeTree *ntree = (bNodeTree *)ptr->owner_id; + bNode *node = ptr->data; + if (value) { + /* If this node becomes the active output, the others of the same type can't be the active + * output anymore. */ + LISTBASE_FOREACH (bNode *, other_node, &ntree->nodes) { + if (other_node->type == node->type) { + other_node->flag &= ~NODE_DO_OUTPUT; + } + } + node->flag |= NODE_DO_OUTPUT; + } + else { + node->flag &= ~NODE_DO_OUTPUT; + } +} + static PointerRNA rna_ShaderNodePointDensity_psys_get(PointerRNA *ptr) { bNode *node = ptr->data; @@ -5288,6 +5307,7 @@ static void def_sh_output(StructRNA *srna) RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_DO_OUTPUT); RNA_def_property_ui_text( prop, "Active Output", "True if this node is used as the active output"); + RNA_def_property_boolean_funcs(prop, NULL, "rna_ShaderNode_is_active_output_set"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); prop = RNA_def_property(srna, "target", PROP_ENUM, PROP_NONE); -- cgit v1.2.3 From 3f91e7d7792ed5315e8b0c80de4355f064db4a6d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 10 Mar 2022 18:06:43 +0100 Subject: Curves: actually delete curves with Delete brush Previously, the position was just set to zero as part of the prototype. Differential Revision: https://developer.blender.org/D14291 --- source/blender/blenkernel/BKE_curves.hh | 3 + .../blender/blenkernel/intern/curves_geometry.cc | 135 +++++++++++++++++++++ .../editors/sculpt_paint/curves_sculpt_ops.cc | 11 +- 3 files changed, 141 insertions(+), 8 deletions(-) diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index f3d9090d16b..6cab9c8ff90 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -95,6 +95,7 @@ class CurvesGeometry : public ::CurvesGeometry { * Access a range of indices of point data for a specific curve. */ IndexRange range_for_curve(int index) const; + IndexRange range_for_curves(IndexRange curves) const; /** The type (#CurveType) of each curve, or potentially a single if all are the same type. */ VArray curve_types() const; @@ -147,6 +148,8 @@ class CurvesGeometry : public ::CurvesGeometry { void update_customdata_pointers(); + void remove_curves(IndexMask curves_to_delete); + /* -------------------------------------------------------------------- * Attributes. */ diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 0ae5654546b..afa0e6e452d 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -129,6 +129,13 @@ IndexRange CurvesGeometry::range_for_curve(const int index) const return {offset, offset_next - offset}; } +IndexRange CurvesGeometry::range_for_curves(const IndexRange curves) const +{ + const int offset = this->curve_offsets[curves.start()]; + const int offset_next = this->curve_offsets[curves.one_after_last()]; + return {offset, offset_next - offset}; +} + static int domain_size(const CurvesGeometry &curves, const AttributeDomain domain) { return domain == ATTR_DOMAIN_POINT ? curves.points_size() : curves.curves_size(); @@ -305,6 +312,134 @@ void CurvesGeometry::update_customdata_pointers() &this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str()); } +static void *ensure_customdata_layer(CustomData &custom_data, + const StringRefNull name, + const CustomDataType data_type, + const int tot_elements) +{ + for (const int other_layer_i : IndexRange(custom_data.totlayer)) { + CustomDataLayer &new_layer = custom_data.layers[other_layer_i]; + if (name == StringRef(new_layer.name)) { + return new_layer.data; + } + } + return CustomData_add_layer_named( + &custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str()); +} + +static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, + const IndexMask curves_to_delete) +{ + const Span old_offsets = curves.offsets(); + const Vector old_curve_ranges = curves_to_delete.extract_ranges_invert( + curves.curves_range(), nullptr); + Vector new_curve_ranges; + Vector old_point_ranges; + Vector new_point_ranges; + int new_tot_points = 0; + int new_tot_curves = 0; + for (const IndexRange &curve_range : old_curve_ranges) { + new_curve_ranges.append(IndexRange(new_tot_curves, curve_range.size())); + new_tot_curves += curve_range.size(); + + const IndexRange old_point_range = curves.range_for_curves(curve_range); + old_point_ranges.append(old_point_range); + new_point_ranges.append(IndexRange(new_tot_points, old_point_range.size())); + new_tot_points += old_point_range.size(); + } + + CurvesGeometry new_curves{new_tot_points, new_tot_curves}; + + threading::parallel_invoke( + /* Initialize curve offsets. */ + [&]() { + MutableSpan new_offsets = new_curves.offsets(); + new_offsets.last() = new_tot_points; + threading::parallel_for( + old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { + for (const int range_i : ranges_range) { + const IndexRange old_curve_range = old_curve_ranges[range_i]; + const IndexRange new_curve_range = new_curve_ranges[range_i]; + const IndexRange old_point_range = old_point_ranges[range_i]; + const IndexRange new_point_range = new_point_ranges[range_i]; + const int offset_shift = new_point_range.start() - old_point_range.start(); + const int curves_in_range = old_curve_range.size(); + threading::parallel_for( + IndexRange(curves_in_range), 512, [&](const IndexRange range) { + for (const int i : range) { + const int old_curve_i = old_curve_range[i]; + const int new_curve_i = new_curve_range[i]; + const int old_offset = old_offsets[old_curve_i]; + const int new_offset = old_offset + offset_shift; + new_offsets[new_curve_i] = new_offset; + } + }); + } + }); + }, + /* Copy over point attributes. */ + [&]() { + const CustomData &old_point_data = curves.point_data; + CustomData &new_point_data = new_curves.point_data; + for (const int layer_i : IndexRange(old_point_data.totlayer)) { + const CustomDataLayer &old_layer = old_point_data.layers[layer_i]; + const CustomDataType data_type = static_cast(old_layer.type); + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + const void *src_buffer = old_layer.data; + void *dst_buffer = ensure_customdata_layer( + new_point_data, old_layer.name, data_type, new_tot_points); + + threading::parallel_for( + old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { + for (const int range_i : ranges_range) { + const IndexRange old_point_range = old_point_ranges[range_i]; + const IndexRange new_point_range = new_point_ranges[range_i]; + + type.copy_construct_n( + POINTER_OFFSET(src_buffer, type.size() * old_point_range.start()), + POINTER_OFFSET(dst_buffer, type.size() * new_point_range.start()), + old_point_range.size()); + } + }); + } + }, + /* Copy over curve attributes. */ + [&]() { + const CustomData &old_curve_data = curves.curve_data; + CustomData &new_curve_data = new_curves.curve_data; + for (const int layer_i : IndexRange(old_curve_data.totlayer)) { + const CustomDataLayer &old_layer = old_curve_data.layers[layer_i]; + const CustomDataType data_type = static_cast(old_layer.type); + const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type); + + const void *src_buffer = old_layer.data; + void *dst_buffer = ensure_customdata_layer( + new_curve_data, old_layer.name, data_type, new_tot_points); + + threading::parallel_for( + old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) { + for (const int range_i : ranges_range) { + const IndexRange old_curve_range = old_curve_ranges[range_i]; + const IndexRange new_curve_range = new_curve_ranges[range_i]; + + type.copy_construct_n( + POINTER_OFFSET(src_buffer, type.size() * old_curve_range.start()), + POINTER_OFFSET(dst_buffer, type.size() * new_curve_range.start()), + old_curve_range.size()); + } + }); + } + }); + + return new_curves; +} + +void CurvesGeometry::remove_curves(const IndexMask curves_to_delete) +{ + *this = copy_with_removed_curves(*this, curves_to_delete); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc index 1411f30bffd..47bf7a28352 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_ops.cc @@ -7,6 +7,7 @@ #include "BKE_bvhutils.h" #include "BKE_context.h" #include "BKE_curves.hh" +#include "BKE_geometry_set.hh" #include "BKE_lib_id.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -67,6 +68,7 @@ bool CURVES_SCULPT_mode_poll_view3d(bContext *C) namespace blender::ed::sculpt_paint { using blender::bke::CurvesGeometry; +using blender::fn::CPPType; /* -------------------------------------------------------------------- */ /** \name * SCULPT_CURVES_OT_brush_stroke @@ -134,14 +136,7 @@ class DeleteOperation : public CurvesSculptStrokeOperation { return false; }); - /* Just reset positions instead of actually removing the curves. This is just a prototype. */ - threading::parallel_for(curves_to_remove.index_range(), 512, [&](const IndexRange range) { - for (const int curve_i : curves_to_remove.slice(range)) { - for (const int point_i : curves.range_for_curve(curve_i)) { - positions[point_i] = {0.0f, 0.0f, 0.0f}; - } - } - }); + curves.remove_curves(curves_to_remove); curves.tag_positions_changed(); DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY); -- cgit v1.2.3 From fe3a9fcce1e6cfce84139d53efe6ed17a71bcad7 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Thu, 10 Mar 2022 20:22:02 +0100 Subject: Fix incorrect meta strip background color When viewing meta strip, it had orange color. This was caused by overflow because of hard-coded offset. Theme got darker, and background was also set again further in code, but redundant drawing was removed in f4492629ea0. --- source/blender/editors/space_sequencer/sequencer_draw.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index bdad08fe20a..0d0042c4656 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -2690,12 +2690,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region) GPU_depth_test(GPU_DEPTH_NONE); UI_GetThemeColor3fv(TH_BACK, col); - if (ed && ed->metastack.first) { - GPU_clear_color(col[0], col[1], col[2] - 0.1f, 0.0f); - } - else { - GPU_clear_color(col[0], col[1], col[2], 0.0f); - } + GPU_clear_color(col[0], col[1], col[2], 0.0f); UI_view2d_view_ortho(v2d); draw_seq_timeline_channels(v2d); -- cgit v1.2.3 From edcb2ad7b3a07f9cd09243cc5fbf5e563e853964 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Thu, 10 Mar 2022 15:42:22 -0600 Subject: Fix: Handle handles reset when realizing with other curve types The result handle attributes for non-bezier types are zeroed. By mistake though, the entire array was zeroed, not just the area corresponding to that curves source. --- source/blender/geometry/intern/realize_instances.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 100146bbf0c..32bad07de4f 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -1155,14 +1155,14 @@ static void execute_realize_curve_task(const RealizeInstancesOptions &options, /* Copy and transform handle positions if necessary. */ if (all_curves_info.create_handle_postion_attributes) { if (curves_info.handle_left.is_empty()) { - all_handle_left.fill(float3(0)); + all_handle_left.slice(dst_point_range).fill(float3(0)); } else { copy_transformed_positions( curves_info.handle_left, task.transform, all_handle_left.slice(dst_point_range)); } if (curves_info.handle_right.is_empty()) { - all_handle_right.fill(float3(0)); + all_handle_right.slice(dst_point_range).fill(float3(0)); } else { copy_transformed_positions( -- cgit v1.2.3 From e3de755ae31503284277681b52947c02aef5d411 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Thu, 10 Mar 2022 18:33:04 -0300 Subject: Transform/UI: individualize the option to use snap per editor type `3DView`'s `use_snap` option has little or nothing to do with using snapping in `UV`, `Nodes` or `Sequencer`. So there are no real advantages to keeping these options in sync. Therefore, individualize the option to use snap for each "spacetype". Reviewed By: brecht Differential Revision: https://developer.blender.org/D13310 --- .../keyconfig/keymap_data/blender_default.py | 4 +- .../keymap_data/industry_compatible_data.py | 2 +- release/scripts/startup/bl_ui/space_image.py | 2 +- release/scripts/startup/bl_ui/space_node.py | 2 +- source/blender/blenloader/intern/versioning_300.c | 15 ++++++++ source/blender/editors/animation/anim_ops.c | 2 +- source/blender/editors/transform/transform.c | 36 ++++++++++++++++-- source/blender/editors/transform/transform.h | 3 +- source/blender/editors/transform/transform_snap.c | 43 +++++++++++++--------- source/blender/makesdna/DNA_scene_types.h | 16 ++++---- source/blender/makesrna/intern/rna_scene.c | 26 +++++++++---- 11 files changed, 109 insertions(+), 42 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 94454ad50e8..5a12d5c79f9 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -1224,7 +1224,7 @@ def km_uv_editor(params): ("transform.shear", {"type": 'S', "value": 'PRESS', "shift": True, "ctrl": True, "alt": True}, None), ("transform.mirror", {"type": 'M', "value": 'PRESS', "ctrl": True}, None), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, - {"properties": [("data_path", 'tool_settings.use_snap')]}), + {"properties": [("data_path", 'tool_settings.use_snap_uv')]}), ("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("data_path", 'tool_settings.snap_uv_element')]}), *_template_items_context_menu("IMAGE_MT_uvs_context_menu", params.context_menu_event), @@ -2089,7 +2089,7 @@ def km_node_editor(params): {"type": params.select_mouse, "value": 'CLICK_DRAG', "alt": True}, {"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}), ("wm.context_toggle", {"type": 'TAB', "value": 'PRESS', "shift": True}, - {"properties": [("data_path", 'tool_settings.use_snap')]}), + {"properties": [("data_path", 'tool_settings.use_snap_node')]}), ("wm.context_menu_enum", {"type": 'TAB', "value": 'PRESS', "shift": True, "ctrl": True}, {"properties": [("data_path", 'tool_settings.snap_node_element')]}), ("wm.context_toggle", {"type": 'Z', "value": 'PRESS', "alt": True, "shift": True}, diff --git a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py index 3469dbdc175..55ee91af7cd 100644 --- a/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py +++ b/release/scripts/presets/keyconfig/keymap_data/industry_compatible_data.py @@ -1152,7 +1152,7 @@ def km_node_editor(params): ("node.move_detach_links_release", {"type": params.action_mouse, "value": 'CLICK_DRAG', "alt": True}, None), ("node.move_detach_links", {"type": 'LEFTMOUSE', "value": 'CLICK_DRAG', "alt": True}, None), ("wm.context_toggle", {"type": 'X', "value": 'PRESS'}, - {"properties": [("data_path", 'tool_settings.use_snap')]}), + {"properties": [("data_path", 'tool_settings.use_snap_node')]}), ]) return keymap diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 65d624a253d..785a841a0e6 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -724,7 +724,7 @@ class IMAGE_HT_header(Header): act_snap_uv_element = tool_settings.bl_rna.properties['snap_uv_element'].enum_items[snap_uv_element] row = layout.row(align=True) - row.prop(tool_settings, "use_snap", text="") + row.prop(tool_settings, "use_snap_uv", text="") sub = row.row(align=True) sub.popover( diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index a89b201d648..7c88006a4d6 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -185,7 +185,7 @@ class NODE_HT_header(Header): # Snap row = layout.row(align=True) - row.prop(tool_settings, "use_snap", text="") + row.prop(tool_settings, "use_snap_node", text="") row.prop(tool_settings, "snap_node_element", icon_only=True) if tool_settings.snap_node_element != 'GRID': row.prop(tool_settings, "snap_target", text="") diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 32dcb977110..37356e58132 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1481,6 +1481,10 @@ static void version_liboverride_rnacollections_insertion_animdata(ID *id) /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { + /* The #SCE_SNAP_SEQ flag has been removed in favor of the #SCE_SNAP which can be used for each + * snap_flag member individually. */ + const int SCE_SNAP_SEQ = (1 << 7); + if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) { /* Set default value for the new bisect_threshold parameter in the mirror modifier. */ if (!DNA_struct_elem_find(fd->filesdna, "MirrorModifierData", "float", "bisect_threshold")) { @@ -2585,5 +2589,16 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) ts->uv_relax_method = UV_SCULPT_TOOL_RELAX_LAPLACIAN; } } + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + tool_settings->snap_flag_seq = tool_settings->snap_flag & ~(SCE_SNAP | SCE_SNAP_SEQ); + if (tool_settings->snap_flag & SCE_SNAP_SEQ) { + tool_settings->snap_flag_seq |= SCE_SNAP; + tool_settings->snap_flag &= ~SCE_SNAP_SEQ; + } + + tool_settings->snap_flag_node = tool_settings->snap_flag; + tool_settings->snap_uv_flag |= tool_settings->snap_flag & SCE_SNAP; + } } } diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 1e8f46fd490..5b0c5eac11b 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -233,7 +233,7 @@ static bool use_sequencer_snapping(bContext *C) Scene *scene = CTX_data_scene(C); short snap_flag = SEQ_tool_settings_snap_flag_get(scene); - return (scene->toolsettings->snap_flag & SCE_SNAP_SEQ) && + return (scene->toolsettings->snap_flag_seq & SCE_SNAP) && (snap_flag & SEQ_SNAP_CURRENT_FRAME_TO_STRIPS); } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index bb80109d8c9..92d312cebce 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -1513,14 +1513,42 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) if (t->flag & T_MODAL) { /* do we check for parameter? */ if (transformModeUseSnap(t)) { - if (!(t->modifiers & MOD_SNAP) != !(ts->snap_flag & SCE_SNAP)) { + if (!(t->modifiers & MOD_SNAP) != !(t->tsnap.flag & SCE_SNAP)) { + char *snap_flag_ptr; + + wmMsgParams_RNA msg_key_params = {{0}}; + RNA_pointer_create(&t->scene->id, &RNA_ToolSettings, ts, &msg_key_params.ptr); + + _WM_MESSAGE_EXTERN_BEGIN; + extern PropertyRNA rna_ToolSettings_use_snap; + extern PropertyRNA rna_ToolSettings_use_snap_node; + extern PropertyRNA rna_ToolSettings_use_snap_sequencer; + extern PropertyRNA rna_ToolSettings_use_snap_uv; + _WM_MESSAGE_EXTERN_END; + if (t->spacetype == SPACE_NODE) { + snap_flag_ptr = &ts->snap_flag_node; + msg_key_params.prop = &rna_ToolSettings_use_snap_node; + } + else if (t->spacetype == SPACE_IMAGE) { + snap_flag_ptr = &ts->snap_uv_flag; + msg_key_params.prop = &rna_ToolSettings_use_snap_uv; + } + else if (t->spacetype == SPACE_SEQ) { + snap_flag_ptr = &ts->snap_flag_seq; + msg_key_params.prop = &rna_ToolSettings_use_snap_sequencer; + } + else { + snap_flag_ptr = &ts->snap_flag; + msg_key_params.prop = &rna_ToolSettings_use_snap; + } + if (t->modifiers & MOD_SNAP) { - ts->snap_flag |= SCE_SNAP; + *snap_flag_ptr |= SCE_SNAP; } else { - ts->snap_flag &= ~SCE_SNAP; + *snap_flag_ptr &= ~SCE_SNAP; } - WM_msg_publish_rna_prop(t->mbus, &t->scene->id, ts, ToolSettings, use_snap); + WM_msg_publish_rna_params(t->mbus, &msg_key_params); } } } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 21592032af2..713cf487ac7 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -295,7 +295,8 @@ typedef struct TransSnapPoint { } TransSnapPoint; typedef struct TransSnap { - short mode; + char flag; + char mode; short target; short modePoint; short modeSelect; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 2f3c021ba38..7ace6343985 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -561,7 +561,22 @@ static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data)) return true; } -static short snap_mode_from_scene(TransInfo *t) +static char snap_flag_from_spacetype(TransInfo *t) +{ + ToolSettings *ts = t->settings; + if (t->spacetype == SPACE_NODE) { + return ts->snap_flag_node; + } + else if (t->spacetype == SPACE_IMAGE) { + return ts->snap_uv_flag; + } + else if (t->spacetype == SPACE_SEQ) { + return ts->snap_flag_seq; + } + return ts->snap_flag; +} + +static short snap_mode_from_spacetype(TransInfo *t) { ToolSettings *ts = t->settings; short r_snap_mode = SCE_SNAP_MODE_INCREMENT; @@ -580,7 +595,7 @@ static short snap_mode_from_scene(TransInfo *t) else if (t->spacetype == SPACE_SEQ) { r_snap_mode = SEQ_tool_settings_snap_mode_get(t->scene); } - else if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE) && !(t->options & CTX_CAMERA)) { + else if ((t->spacetype == SPACE_VIEW3D) && !(t->options & CTX_CAMERA)) { /* All obedit types will match. */ const int obedit_type = t->obedit_type; if ((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR | CTX_OBMODE_XFORM_OBDATA)) || @@ -652,7 +667,7 @@ static short snap_select_type_get(TransInfo *t) static void initSnappingMode(TransInfo *t) { ToolSettings *ts = t->settings; - t->tsnap.mode = snap_mode_from_scene(t); + t->tsnap.mode = snap_mode_from_spacetype(t); t->tsnap.modeSelect = snap_select_type_get(t); if ((t->spacetype != SPACE_VIEW3D) || !(ts->snap_mode & SCE_SNAP_MODE_FACE)) { @@ -701,10 +716,9 @@ static void initSnappingMode(TransInfo *t) void initSnapping(TransInfo *t, wmOperator *op) { - ToolSettings *ts = t->settings; - short snap_target = t->settings->snap_target; - resetSnapping(t); + t->tsnap.flag = snap_flag_from_spacetype(t); + short snap_target = t->settings->snap_target; /* if snap property exists */ PropertyRNA *prop; @@ -745,19 +759,14 @@ void initSnapping(TransInfo *t, wmOperator *op) } /* use scene defaults only when transform is modal */ else if (t->flag & T_MODAL) { - if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { - if (transformModeUseSnap(t) && (ts->snap_flag & SCE_SNAP)) { - t->modifiers |= MOD_SNAP; - } - - t->tsnap.align = ((t->settings->snap_flag & SCE_SNAP_ROTATE) != 0); - t->tsnap.project = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0); - t->tsnap.snap_self = !((t->settings->snap_flag & SCE_SNAP_NO_SELF) != 0); - t->tsnap.peel = ((t->settings->snap_flag & SCE_SNAP_PROJECT) != 0); - } - else if ((t->spacetype == SPACE_SEQ) && (ts->snap_flag & SCE_SNAP_SEQ)) { + if (transformModeUseSnap(t) && (t->tsnap.flag & SCE_SNAP)) { t->modifiers |= MOD_SNAP; } + + t->tsnap.align = ((t->tsnap.flag & SCE_SNAP_ROTATE) != 0); + t->tsnap.project = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0); + t->tsnap.snap_self = !((t->tsnap.flag & SCE_SNAP_NO_SELF) != 0); + t->tsnap.peel = ((t->tsnap.flag & SCE_SNAP_PROJECT) != 0); } t->tsnap.target = snap_target; diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 512be41ce2c..2f8d1c5eafe 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1469,13 +1469,18 @@ typedef struct ToolSettings { /* Transform */ char transform_pivot_point; char transform_flag; + /** Snap elements (per spacetype). */ char snap_mode; char snap_node_mode; char snap_uv_mode; + /** Generic flags (per spacetype). */ char snap_flag; - /** UV equivalent of `snap_flag`, limited to: #SCE_SNAP_ABS_GRID. */ + char snap_flag_node; + char snap_flag_seq; char snap_uv_flag; + /** Default snap source. */ char snap_target; + /** Snap mask for transform modes. */ char snap_transform_mode_flag; char proportional_edit, prop_mode; @@ -1505,16 +1510,14 @@ typedef struct ToolSettings { char gpencil_selectmode_vertex; /* UV painting */ - char _pad2[1]; char uv_sculpt_settings; char uv_relax_method; - /* XXX: these sculpt_paint_* fields are deprecated, use the - * unified_paint_settings field instead! */ - short sculpt_paint_settings DNA_DEPRECATED; char workspace_tool_type; - char _pad5[1]; + /* XXX: these sculpt_paint_* fields are deprecated, use the + * unified_paint_settings field instead! */ + short sculpt_paint_settings DNA_DEPRECATED; int sculpt_paint_unified_size DNA_DEPRECATED; float sculpt_paint_unified_unprojected_radius DNA_DEPRECATED; float sculpt_paint_unified_alpha DNA_DEPRECATED; @@ -2055,7 +2058,6 @@ enum { #define SCE_SNAP_NO_SELF (1 << 4) #define SCE_SNAP_ABS_GRID (1 << 5) #define SCE_SNAP_BACKFACE_CULLING (1 << 6) -#define SCE_SNAP_SEQ (1 << 7) /** #ToolSettings.snap_target */ #define SCE_SNAP_TARGET_CLOSEST 0 diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 209331065ac..d398dadbec7 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3149,6 +3149,25 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_node", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag_node", SCE_SNAP); + RNA_def_property_ui_text(prop, "Snap", "Snap Node during transform"); + RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + + prop = RNA_def_property(srna, "use_snap_sequencer", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_flag_seq", SCE_SNAP); + RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame"); + RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* Publish message-bus. */ + + prop = RNA_def_property(srna, "use_snap_uv", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_uv_flag", SCE_SNAP); + RNA_def_property_ui_text(prop, "Snap", "Snap UV during transform"); + RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_align_rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_ROTATE); RNA_def_property_ui_text( @@ -3163,13 +3182,6 @@ static void rna_def_tool_settings(BlenderRNA *brna) "Absolute grid alignment while translating (based on the pivot center)"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ - prop = RNA_def_property(srna, "use_snap_sequencer", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "snap_flag", SCE_SNAP_SEQ); - RNA_def_property_ui_text(prop, "Use Snapping", "Snap to strip edges or current frame"); - RNA_def_property_ui_icon(prop, ICON_SNAP_OFF, 1); - RNA_def_property_boolean_default(prop, true); - RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* Publish message-bus. */ - prop = RNA_def_property(srna, "snap_elements", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "snap_mode"); RNA_def_property_enum_items(prop, rna_enum_snap_element_items); -- cgit v1.2.3 From d449deec21355426b6e1b487cd87067da28009a7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 09:27:37 +1100 Subject: Cleanup: use enum for local versioning enum --- source/blender/blenloader/intern/versioning_260.c | 2 +- source/blender/blenloader/intern/versioning_280.c | 6 +++--- source/blender/blenloader/intern/versioning_290.c | 6 ++++-- source/blender/blenloader/intern/versioning_300.c | 2 +- source/blender/blenloader/intern/versioning_cycles.c | 12 +++++++----- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 28b934843af..655dc297c35 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1838,7 +1838,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) Image *image = blo_do_versions_newlibadr(fd, tex->id.lib, tex->ima); if (image && (image->flag & IMA_DO_PREMUL) == 0) { - const int IMA_IGNORE_ALPHA = (1 << 12); + enum { IMA_IGNORE_ALPHA = (1 << 12) }; image->flag |= IMA_IGNORE_ALPHA; } } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 32dbabf2643..3700ead92f0 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1550,7 +1550,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) if (!MAIN_VERSION_ATLEAST(bmain, 280, 69)) { /* Unify DOF settings (EEVEE part only) */ - const int SCE_EEVEE_DOF_ENABLED = (1 << 7); + enum { SCE_EEVEE_DOF_ENABLED = (1 << 7) }; LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { if (STREQ(scene->r.engine, RE_engine_id_BLENDER_EEVEE)) { if (scene->eevee.flag & SCE_EEVEE_DOF_ENABLED) { @@ -2312,7 +2312,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } \ } \ ((void)0) - const int SCE_EEVEE_DOF_ENABLED = (1 << 7); + enum { SCE_EEVEE_DOF_ENABLED = (1 << 7) }; IDProperty *props = IDP_GetPropertyFromGroup(scene->layer_properties, RE_engine_id_BLENDER_EEVEE); // EEVEE_GET_BOOL(props, volumetric_enable, SCE_EEVEE_VOLUMETRIC_ENABLED); @@ -4032,7 +4032,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 280, 70)) { /* New image alpha modes. */ LISTBASE_FOREACH (Image *, image, &bmain->images) { - const int IMA_IGNORE_ALPHA = (1 << 12); + enum { IMA_IGNORE_ALPHA = (1 << 12) }; if (image->flag & IMA_IGNORE_ALPHA) { image->alpha_mode = IMA_ALPHA_IGNORE; image->flag &= ~IMA_IGNORE_ALPHA; diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index ada011f191d..3ae26dea767 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -701,8 +701,10 @@ static void panels_remove_x_closed_flag_recursive(Panel *panel) static void do_versions_point_attributes(CustomData *pdata) { /* Change to generic named float/float3 attributes. */ - const int CD_LOCATION = 43; - const int CD_RADIUS = 44; + enum { + CD_LOCATION = 43, + CD_RADIUS = 44, + }; for (int i = 0; i < pdata->totlayer; i++) { CustomDataLayer *layer = &pdata->layers[i]; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 37356e58132..df4ae9ea2ce 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -1483,7 +1483,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { /* The #SCE_SNAP_SEQ flag has been removed in favor of the #SCE_SNAP which can be used for each * snap_flag member individually. */ - const int SCE_SNAP_SEQ = (1 << 7); + enum { SCE_SNAP_SEQ = (1 << 7) }; if (!MAIN_VERSION_ATLEAST(bmain, 300, 1)) { /* Set default value for the new bisect_threshold parameter in the mirror modifier. */ diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index c654972730f..e1ceed82ba7 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -330,7 +330,7 @@ static void image_node_colorspace(bNode *node) return; } - const int SHD_COLORSPACE_NONE = 0; + enum { SHD_COLORSPACE_NONE = 0 }; Image *image = (Image *)node->id; if (color_space == SHD_COLORSPACE_NONE) { STRNCPY(image->colorspace_settings.name, @@ -1362,10 +1362,12 @@ void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bm void do_versions_after_linking_cycles(Main *bmain) { - const int DENOISER_AUTO = 0; - const int DENOISER_NLM = 1; - const int DENOISER_OPTIX = 2; - const int DENOISER_OPENIMAGEDENOISE = 4; + enum { + DENOISER_AUTO = 0, + DENOISER_NLM = 1, + DENOISER_OPTIX = 2, + DENOISER_OPENIMAGEDENOISE = 4, + }; if (!MAIN_VERSION_ATLEAST(bmain, 280, 66)) { /* Shader node tree changes. After lib linking so we have all the typeinfo -- cgit v1.2.3 From 4a8bd318255e6fe829a93e8cb8039e8e1896fc72 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 09:41:48 +1100 Subject: Cleanup: use doxy sections in image.cc Also minor improvements & clarifications to comments. --- source/blender/blenkernel/intern/image.cc | 105 ++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index ff10f36cc22..b4644b84f1e 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -109,7 +109,11 @@ static void image_init(Image *ima, short source, short type); static void image_free_packedfiles(Image *ima); static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src); -/* Reset runtime image fields when data-block is being initialized. */ +/* -------------------------------------------------------------------- */ +/** \name Image #IDTypeInfo API + * \{ */ + +/** Reset runtime image fields when data-block is being initialized. */ static void image_runtime_reset(struct Image *image) { memset(&image->runtime, 0, sizeof(image->runtime)); @@ -117,7 +121,7 @@ static void image_runtime_reset(struct Image *image) BLI_mutex_init(static_cast(image->runtime.cache_mutex)); } -/* Reset runtime image fields when data-block is being copied. */ +/** Reset runtime image fields when data-block is being copied. */ static void image_runtime_reset_on_copy(struct Image *image) { image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); @@ -479,7 +483,11 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat # define IMA_INDEX_PASS(index) (index & ~1023) #endif -/* ******** IMAGE CACHE ************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Cache + * \{ */ typedef struct ImageCacheKey { int index; @@ -548,7 +556,11 @@ static struct ImBuf *imagecache_get(Image *image, int index, bool *r_is_cached_e return nullptr; } -/* ***************** ALLOC & FREE, DATA MANAGING *************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Allocate & Free, Data Managing + * \{ */ static void image_free_cached_frames(Image *image) { @@ -666,12 +678,12 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ return ima; } -/* Get the ibuf from an image cache by its index and entry. +/** + * Get the ibuf from an image cache by its index and entry. * Local use here only. * - * Returns referenced image buffer if it exists, callee is to - * call IMB_freeImBuf to de-reference the image buffer after - * it's done handling it. + * \returns referenced image buffer if it exists, callee is to call #IMB_freeImBuf + * to de-reference the image buffer after it's done handling it. */ static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, int index, @@ -1127,7 +1139,8 @@ Image *BKE_image_add_generated(Main *bmain, int view_id; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; - // STRNCPY(ima->filepath, name); /* don't do this, this writes in ain invalid filepath! */ + /* NOTE: leave `ima->filepath` unset, + * setting it to a dummy value may write to an invalid file-path. */ ima->gen_x = width; ima->gen_y = height; ima->gen_type = gen_type; @@ -1179,7 +1192,7 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) return ima; } -/* Pack image buffer to memory as PNG or EXR. */ +/** Pack image buffer to memory as PNG or EXR. */ static bool image_memorypack_imbuf(Image *ima, ImBuf *ibuf, const char *filepath) { ibuf->ftype = (ibuf->rect_float) ? IMB_FTYPE_OPENEXR : IMB_FTYPE_PNG; @@ -1472,7 +1485,11 @@ void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) } } -/* *********** READ AND WRITE ************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Read and Write + * \{ */ int BKE_image_imtype_to_ftype(const char imtype, ImbFormatOptions *r_options) { @@ -1607,7 +1624,7 @@ bool BKE_imtype_supports_zbuf(const char imtype) { switch (imtype) { case R_IMF_IMTYPE_IRIZ: - case R_IMF_IMTYPE_OPENEXR: /* but not R_IMF_IMTYPE_MULTILAYER */ + case R_IMF_IMTYPE_OPENEXR: /* But not #R_IMF_IMTYPE_MULTILAYER. */ return true; } return false; @@ -3131,7 +3148,7 @@ void BKE_imbuf_write_prepare(ImBuf *ibuf, const ImageFormatData *imf) } #endif else { - /* R_IMF_IMTYPE_JPEG90, etc. default we save jpegs */ + /* #R_IMF_IMTYPE_JPEG90, etc. fallback to JPEG image. */ if (quality < 10) { quality = 90; } @@ -3278,7 +3295,11 @@ struct anim *openanim(const char *name, int flags, int streamindex, char colorsp return anim; } -/* ************************* New Image API *************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Image API + * \{ */ /* Notes about Image storage * - packedfile @@ -3290,8 +3311,8 @@ struct anim *openanim(const char *name, int flags, int streamindex, char colorsp * - renderresult * -> comes from packedfile or filename * - listbase - * -> ibufs from exrhandle - * - flipbook array + * -> ibufs from EXR-handle. + * - flip-book array * -> ibufs come from movie, temporary renderresult or sequence * - ibuf * -> comes from packedfile or filename or generated @@ -3834,8 +3855,10 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) BKE_ntree_update_main(bmain, nullptr); } -/* return renderpass for a given pass index and active view */ -/* fallback to available if there are missing passes for active view */ +/** + * \return render-pass for a given pass index and active view. + * fallback to available if there are missing passes for active view. + */ static RenderPass *image_render_pass_get(RenderLayer *rl, const int pass, const int view, @@ -4477,7 +4500,11 @@ void BKE_image_backup_render(Scene *scene, Image *ima, bool free_current_slot) ima->last_render_slot = ima->render_slot; } -/**************************** multiview load openexr *********************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Multiview Load OpenEXR + * \{ */ static void image_add_view(Image *ima, const char *viewname, const char *filepath) { @@ -4510,8 +4537,8 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat } } -/* after imbuf load, openexr type can return with a exrhandle open */ -/* in that case we have to build a render-result */ +/* After imbuf load, OpenEXR type can return with a EXR-handle 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) { @@ -4536,7 +4563,7 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) } #endif /* WITH_OPENEXR */ -/* common stuff to do with images after loading */ +/** Common stuff to do with images after loading. */ static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) { /* Preview is null when it has never been used as an icon before. @@ -4571,7 +4598,9 @@ static int imbuf_alpha_flags_for_image(Image *ima) return 0; } -/* the number of files will vary according to the stereo format */ +/** + * \return the number of files will vary according to the stereo format. + */ static int image_num_files(Image *ima) { const bool is_multiview = BKE_image_is_multiview(ima); @@ -5402,30 +5431,30 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) } if (ibuf == nullptr) { - /* we are sure we have to load the ibuf, using source and type */ + /* We are sure we have to load the ibuf, using source and type. */ if (ima->source == IMA_SRC_MOVIE) { - /* source is from single file, use flipbook to store ibuf */ + /* Source is from single file, use flip-book to store ibuf. */ ibuf = image_load_movie_file(ima, iuser, entry); } else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { - /* regular files, ibufs in flipbook, allows saving */ + /* Regular files, ibufs in flip-book, allows saving. */ ibuf = image_load_sequence_file(ima, iuser, entry, entry); } /* no else; on load the ima type can change */ if (ima->type == IMA_TYPE_MULTILAYER) { - /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ + /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */ ibuf = image_load_sequence_multilayer(ima, iuser, entry, entry); } } else if (ima->source == IMA_SRC_TILED) { if (ima->type == IMA_TYPE_IMAGE) { - /* regular files, ibufs in flipbook, allows saving */ + /* Regular files, ibufs in flip-book, allows saving */ ibuf = image_load_sequence_file(ima, iuser, entry, 0); } /* no else; on load the ima type can change */ if (ima->type == IMA_TYPE_MULTILAYER) { - /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ + /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */ ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); } } @@ -5441,8 +5470,8 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) } } else if (ima->source == IMA_SRC_GENERATED) { - /* generated is: ibuf is allocated dynamically */ - /* UV testgrid or black or solid etc */ + /* Generated is: `ibuf` is allocated dynamically. */ + /* UV test-grid or black or solid etc. */ if (ima->gen_x == 0) { ima->gen_x = 1024; } @@ -5564,7 +5593,11 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) return ibuf != nullptr; } -/* ******** Pool for image buffers ******** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pool for Image Buffers + * \{ */ struct ImagePoolItem { struct ImagePoolItem *next, *prev; @@ -6274,7 +6307,11 @@ static void image_update_views_format(Image *ima, ImageUser *iuser) } } -/**************************** Render Slots ***************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render Slots + * \{ */ RenderSlot *BKE_image_add_renderslot(Image *ima, const char *name) { @@ -6392,3 +6429,5 @@ RenderSlot *BKE_image_get_renderslot(Image *ima, int index) /* Can be null for images without render slots. */ return static_cast(BLI_findlink(&ima->renderslots, index)); } + +/** \} */ -- cgit v1.2.3 From 27fb63381e9b0963976237c58ca2cfc9bc5ddfd8 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 10:08:25 +1100 Subject: Fix T94121: PyAPI: ID property group returns wrong type with iter() Regression in 265d97556aa0f0f2a0e4dd7584e3b8573bbddd54. Where iterating directly on a property group failed, e.g.: `iter(group)`, tests missed this since only `group.keys()` was checked. --- source/blender/python/generic/idprop_py_api.c | 11 ++++++++++- tests/python/bl_pyapi_idprop.py | 8 ++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index c05c003b176..2fbb6b8ee05 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -756,7 +756,16 @@ static int BPy_IDGroup_Map_SetItem(BPy_IDProperty *self, PyObject *key, PyObject static PyObject *BPy_IDGroup_iter(BPy_IDProperty *self) { - return BPy_IDGroup_ViewKeys_CreatePyObject(self); + PyObject *iterable = BPy_IDGroup_ViewKeys_CreatePyObject(self); + PyObject *ret; + if (iterable) { + ret = PyObject_GetIter(iterable); + Py_DECREF(iterable); + } + else { + ret = NULL; + } + return ret; } PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py index 0e93ba0b690..ac1bd618570 100644 --- a/tests/python/bl_pyapi_idprop.py +++ b/tests/python/bl_pyapi_idprop.py @@ -172,6 +172,14 @@ class TestIdPropertyGroupView(TestHelper, unittest.TestCase): self.assertEqual(list(self.id.items()), [(k, v) for v, k in enumerate(text)]) self.assertEqual(list(reversed(self.id.items())), list(reversed([(k, v) for v, k in enumerate(text)]))) + # Check direct iteration is working as expected. + self.id["group"] = {ch: i for i, ch in enumerate(text)} + group = self.id["group"] + + self.assertEqual(len(group), len(text)) + self.assertEqual(list(iter(group)), text) + + def test_contains(self): # Check `idprop.types.IDPropertyGroupView{Keys/Values/Items}.__contains__` text = ["A", "B", "C"] -- cgit v1.2.3 From 0602852860dda7dfc0ea20c72e03b7f96c981f1a Mon Sep 17 00:00:00 2001 From: Laurynas Duburas Date: Thu, 10 Mar 2022 18:34:27 -0600 Subject: Curve: Improve NURBS knot generation modes This patch enables all 8 combinations of Nurbs modes: Cyclic, Bezier and Endpoint. Also removes restriction on Bezier Nurbs order. The most significant changes are mode combinations bringing new meaning. In D13891 is a scheme showing NURBS with same control points in a modes, and also further description of each possible case. Differential Revision: https://developer.blender.org/D13891 --- .../scripts/startup/bl_ui/properties_data_curve.py | 14 ++- source/blender/blenkernel/BKE_blender_version.h | 2 +- source/blender/blenkernel/BKE_curve.h | 8 ++ source/blender/blenkernel/BKE_spline.hh | 1 + source/blender/blenkernel/intern/curve.cc | 110 ++++++++++----------- source/blender/blenkernel/intern/curve_eval.cc | 2 + source/blender/blenkernel/intern/spline_nurbs.cc | 27 ++--- source/blender/blenloader/intern/versioning_300.c | 60 ++++++++--- source/blender/editors/curve/editcurve.c | 2 +- source/blender/editors/curve/editcurve_add.c | 4 +- source/blender/makesrna/intern/rna_curve.c | 63 +++++++++--- .../geometry/nodes/node_geo_curve_spline_type.cc | 1 + 12 files changed, 195 insertions(+), 99 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index 26e5cfaae60..2ad6e5bae8a 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -289,7 +289,6 @@ class DATA_PT_active_spline(CurveButtonsPanelActive, Panel): if is_surf: subsub = sub.column() - subsub.active = (not act_spline.use_cyclic_v) subsub.prop(act_spline, "use_bezier_v", text="V") sub = col.column(heading="Endpoint", align=True) @@ -297,7 +296,6 @@ class DATA_PT_active_spline(CurveButtonsPanelActive, Panel): if is_surf: subsub = sub.column() - subsub.active = (not act_spline.use_cyclic_v) subsub.prop(act_spline, "use_endpoint_v", text="V") sub = col.column(align=True) @@ -322,6 +320,18 @@ class DATA_PT_active_spline(CurveButtonsPanelActive, Panel): col.prop(act_spline, "radius_interpolation", text="Radius") layout.prop(act_spline, "use_smooth") + if act_spline.type == 'NURBS': + messages = [act_spline.valid_message_u] + if is_surf and act_spline.point_count_v > 1: + messages.append(act_spline.valid_message_v) + + messages = list(filter(None, messages)) + + if len(messages) > 0: + layout.separator() + col = layout.column(align=True) + for message in messages: + col.label(text=message, icon='INFO') class DATA_PT_font(CurveButtonsPanelText, Panel): diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index a8a851bb228..1639a564508 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -25,7 +25,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 5 +#define BLENDER_FILE_SUBVERSION 6 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h index 3f65ce83b3f..394d97223e3 100644 --- a/source/blender/blenkernel/BKE_curve.h +++ b/source/blender/blenkernel/BKE_curve.h @@ -269,6 +269,14 @@ void BKE_nurb_knot_calc_v(struct Nurb *nu); bool BKE_nurb_check_valid_u(const struct Nurb *nu); bool BKE_nurb_check_valid_v(const struct Nurb *nu); bool BKE_nurb_check_valid_uv(const struct Nurb *nu); +bool BKE_nurb_valid_message(int pnts, + short order, + short flag, + short type, + bool is_surf, + const char *dir, + char *message_dst, + size_t maxncpy); bool BKE_nurb_order_clamp_u(struct Nurb *nu); bool BKE_nurb_order_clamp_v(struct Nurb *nu); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 42b4702ee44..ed9b743b524 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -453,6 +453,7 @@ class NURBSpline final : public Spline { Normal, EndPoint, Bezier, + EndPointBezier, }; /** Method used to recalculate the knots vector when points are added or removed. */ diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 6b7f7af44e8..0b619c1a969 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -17,8 +17,8 @@ #include "BLI_index_range.hh" #include "BLI_math.h" #include "BLI_math_vec_types.hh" +#include "BLI_string.h" #include "BLI_utildefines.h" - #include "BLT_translation.h" /* Allow using deprecated functionality for .blend file I/O. */ @@ -1156,12 +1156,13 @@ void BKE_nurb_bpoint_calc_plane(struct Nurb *nu, BPoint *bp, float r_plane[3]) static void calcknots(float *knots, const int pnts, const short order, const short flag) { const bool is_cyclic = flag & CU_NURB_CYCLIC; - const bool is_bezier = flag & CU_NURB_BEZIER && !(flag & CU_NURB_ENDPOINT); - const bool is_end_point = flag & CU_NURB_ENDPOINT && !(flag & CU_NURB_BEZIER); + const bool is_bezier = flag & CU_NURB_BEZIER; + const bool is_end_point = flag & CU_NURB_ENDPOINT; /* Inner knots are always repeated once except on Bezier case. */ const int repeat_inner = is_bezier ? order - 1 : 1; /* How many times to repeat 0.0 at the beginning of knot. */ - const int head = is_end_point && !is_cyclic ? order : (is_bezier ? order / 2 : 1); + const int head = is_end_point ? (order - (is_cyclic ? 1 : 0)) : + (is_bezier ? min_ii(2, repeat_inner) : 1); /* Number of knots replicating widths of the starting knots. * Covers both Cyclic and EndPoint cases. */ const int tail = is_cyclic ? 2 * order - 1 : (is_end_point ? order : 0); @@ -1171,11 +1172,17 @@ static void calcknots(float *knots, const int pnts, const short order, const sho int r = head; float current = 0.0f; - for (const int i : IndexRange(knot_count - tail)) { + const int offset = is_end_point && is_cyclic ? 1 : 0; + if (offset) { + knots[0] = current; + current += 1.0f; + } + + for (const int i : IndexRange(offset, knot_count - offset - tail)) { knots[i] = current; r--; if (r == 0) { - current += 1.0; + current += 1.0f; r = repeat_inner; } } @@ -4693,59 +4700,56 @@ void BKE_curve_nurbs_key_vert_tilts_apply(ListBase *lb, const float *key) } } -bool BKE_nurb_check_valid_u(const Nurb *nu) +bool BKE_nurb_valid_message(const int pnts, + const short order, + const short flag, + const short type, + const bool is_surf, + const char *dir, + char *message_dst, + const size_t maxncpy) { - if (nu->pntsu <= 1) { - return false; - } - if (nu->type != CU_NURBS) { - return true; /* not a nurb, lets assume its valid */ - } + const char *msg_template = ""; + uint16_t points_needed = 0; - if (nu->pntsu < nu->orderu) { - return false; + if (pnts <= 1) { + msg_template = TIP_("At least two points required."); } - if (((nu->flagu & CU_NURB_CYCLIC) == 0) && (nu->flagu & CU_NURB_BEZIER)) { - /* Bezier U Endpoints */ - if (nu->orderu == 4) { - if (nu->pntsu < 5) { - return false; /* bezier with 4 orderu needs 5 points */ - } + else if (type == CU_NURBS) { + if (pnts < order) { + msg_template = TIP_("Must have more control points than Order"); } - else { - if (nu->orderu != 3) { - return false; /* order must be 3 or 4 */ + else if (flag & CU_NURB_BEZIER) { + if (flag & CU_NURB_CYCLIC) { + const uint16_t remainder = pnts % (order - 1); + points_needed = remainder > 0 ? order - 1 - remainder : 0; + } + else if (((flag & CU_NURB_ENDPOINT) == 0) && pnts <= order) { + points_needed = order + 1 - pnts; + } + if (points_needed) { + msg_template = is_surf ? TIP_("%d more %s row(s) needed for Bezier") : + TIP_("%d more point(s) needed for Bezier"); } } } - return true; + + if (message_dst) { + BLI_snprintf(message_dst, maxncpy, msg_template, points_needed, dir); + } + return msg_template[0]; } -bool BKE_nurb_check_valid_v(const Nurb *nu) + +bool BKE_nurb_check_valid_u(const Nurb *nu) { - if (nu->pntsv <= 1) { - return false; - } - if (nu->type != CU_NURBS) { - return true; /* not a nurb, lets assume its valid */ - } + return !BKE_nurb_valid_message( + nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, "U", nullptr, 0); +} - if (nu->pntsv < nu->orderv) { - return false; - } - if (((nu->flagv & CU_NURB_CYCLIC) == 0) && (nu->flagv & CU_NURB_BEZIER)) { - /* Bezier V Endpoints */ - if (nu->orderv == 4) { - if (nu->pntsv < 5) { - return false; /* bezier with 4 orderu needs 5 points */ - } - } - else { - if (nu->orderv != 3) { - return false; /* order must be 3 or 4 */ - } - } - } - return true; +bool BKE_nurb_check_valid_v(const Nurb *nu) +{ + return !BKE_nurb_valid_message( + nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, "V", nullptr, 0); } bool BKE_nurb_check_valid_uv(const Nurb *nu) @@ -4767,10 +4771,6 @@ bool BKE_nurb_order_clamp_u(struct Nurb *nu) nu->orderu = max_ii(2, nu->pntsu); changed = true; } - if (((nu->flagu & CU_NURB_CYCLIC) == 0) && (nu->flagu & CU_NURB_BEZIER)) { - CLAMP(nu->orderu, 3, 4); - changed = true; - } return changed; } @@ -4781,10 +4781,6 @@ bool BKE_nurb_order_clamp_v(struct Nurb *nu) nu->orderv = max_ii(2, nu->pntsv); changed = true; } - if (((nu->flagv & CU_NURB_CYCLIC) == 0) && (nu->flagv & CU_NURB_BEZIER)) { - CLAMP(nu->orderv, 3, 4); - changed = true; - } return changed; } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 1ffbed39216..59c2155255b 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -210,6 +210,8 @@ static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) return NURBSpline::KnotsMode::EndPoint; case CU_NURB_BEZIER: return NURBSpline::KnotsMode::Bezier; + case CU_NURB_ENDPOINT | CU_NURB_BEZIER: + return NURBSpline::KnotsMode::EndPointBezier; default: return NURBSpline::KnotsMode::Normal; } diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index c2479e9e595..1e0fb874d11 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -142,15 +142,11 @@ bool NURBSpline::check_valid_size_and_order() const return false; } - if (!is_cyclic_ && this->knots_mode == KnotsMode::Bezier) { - if (order_ == 4) { - if (this->size() < 5) { - return false; - } - } - else if (order_ != 3) { + if (ELEM(this->knots_mode, KnotsMode::Bezier, KnotsMode::EndPointBezier)) { + if (this->knots_mode == KnotsMode::Bezier && this->size() <= order_) { return false; } + return (!is_cyclic_ || this->size() % (order_ - 1) == 0); } return true; @@ -166,12 +162,15 @@ void NURBSpline::calculate_knots() const { const KnotsMode mode = this->knots_mode; const int order = order_; - const bool is_bezier = mode == NURBSpline::KnotsMode::Bezier; - const bool is_end_point = mode == NURBSpline::KnotsMode::EndPoint; + const bool is_bezier = ELEM( + mode, NURBSpline::KnotsMode::Bezier, NURBSpline::KnotsMode::EndPointBezier); + const bool is_end_point = ELEM( + mode, NURBSpline::KnotsMode::EndPoint, NURBSpline::KnotsMode::EndPointBezier); /* Inner knots are always repeated once except on Bezier case. */ const int repeat_inner = is_bezier ? order - 1 : 1; /* How many times to repeat 0.0 at the beginning of knot. */ - const int head = is_end_point && !is_cyclic_ ? order : (is_bezier ? order / 2 : 1); + const int head = is_end_point ? (order - (is_cyclic_ ? 1 : 0)) : + (is_bezier ? min_ii(2, repeat_inner) : 1); /* Number of knots replicating widths of the starting knots. * Covers both Cyclic and EndPoint cases. */ const int tail = is_cyclic_ ? 2 * order - 1 : (is_end_point ? order : 0); @@ -182,7 +181,13 @@ void NURBSpline::calculate_knots() const int r = head; float current = 0.0f; - for (const int i : IndexRange(knots.size() - tail)) { + const int offset = is_end_point && is_cyclic_ ? 1 : 0; + if (offset) { + knots[0] = current; + current += 1.0f; + } + + for (const int i : IndexRange(offset, knots.size() - offset - tail)) { knots[i] = current; r--; if (r == 0) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index df4ae9ea2ce..accb3e521e1 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -42,6 +42,7 @@ #include "BKE_armature.h" #include "BKE_asset.h" #include "BKE_collection.h" +#include "BKE_curve.h" #include "BKE_deform.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" @@ -2571,18 +2572,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 302, 6)) { LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { ToolSettings *ts = scene->toolsettings; if (ts->uv_relax_method == 0) { @@ -2600,5 +2590,51 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) tool_settings->snap_flag_node = tool_settings->snap_flag; tool_settings->snap_uv_flag |= tool_settings->snap_flag & SCE_SNAP; } + + /* Alter NURBS knot mode flags to fit new modes. */ + LISTBASE_FOREACH (Curve *, curve, &bmain->curves) { + LISTBASE_FOREACH (Nurb *, nurb, &curve->nurb) { + /* Previously other flags were ignored if CU_NURB_CYCLIC is set. */ + if (nurb->flagu & CU_NURB_CYCLIC) { + nurb->flagu = CU_NURB_CYCLIC; + } + /* CU_NURB_BEZIER and CU_NURB_ENDPOINT were ignored if combined. */ + else if (nurb->flagu & CU_NURB_BEZIER && nurb->flagu & CU_NURB_ENDPOINT) { + nurb->flagu &= ~(CU_NURB_BEZIER | CU_NURB_ENDPOINT); + BKE_nurb_knot_calc_u(nurb); + } + /* Bezier NURBS of order 3 were clamped to first control point. */ + else if (nurb->orderu == 3 && (nurb->flagu & CU_NURB_BEZIER)) { + nurb->flagu |= CU_NURB_ENDPOINT; + } + + /* Previously other flags were ignored if CU_NURB_CYCLIC is set. */ + if (nurb->flagv & CU_NURB_CYCLIC) { + nurb->flagv = CU_NURB_CYCLIC; + } + /* CU_NURB_BEZIER and CU_NURB_ENDPOINT were ignored if used together. */ + else if (nurb->flagv & CU_NURB_BEZIER && nurb->flagv & CU_NURB_ENDPOINT) { + nurb->flagv &= ~(CU_NURB_BEZIER | CU_NURB_ENDPOINT); + BKE_nurb_knot_calc_v(nurb); + } + /* Bezier NURBS of order 3 were clamped to first control point. */ + else if (nurb->orderv == 3 && (nurb->flagv & CU_NURB_BEZIER)) { + nurb->flagv |= CU_NURB_ENDPOINT; + } + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index ba9502b80bf..f992309ed00 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -4958,7 +4958,7 @@ bool ed_editnurb_spin( /* It is challenging to create a good approximation of a circle with uniform knots vector * (which is forced in Blender for cyclic NURBS curves). Here a NURBS circle is constructed * by connecting four Bezier arcs. */ - nu->flagv |= CU_NURB_CYCLIC | CU_NURB_BEZIER; + nu->flagv |= CU_NURB_CYCLIC | CU_NURB_BEZIER | CU_NURB_ENDPOINT; BKE_nurb_knot_calc_v(nu); } } diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index d7201495f75..ba5a7409ba7 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -292,7 +292,7 @@ Nurb *ED_curve_add_nurbs_primitive( nu->pntsv = 1; nu->orderu = 3; nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "addNurbprim6"); - nu->flagu = CU_NURB_CYCLIC | CU_NURB_BEZIER; + nu->flagu = CU_NURB_CYCLIC | CU_NURB_BEZIER | CU_NURB_ENDPOINT; bp = nu->bp; for (a = 0; a < 8; a++) { @@ -407,7 +407,7 @@ Nurb *ED_curve_add_nurbs_primitive( mul_m4_v3(mat, bp->vec); bp++; } - nu->flagu = CU_NURB_BEZIER; + nu->flagu = CU_NURB_BEZIER | CU_NURB_ENDPOINT; BKE_nurb_knot_calc_u(nu); BLI_addtail(editnurb, nu); /* temporal for spin */ diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 891de95a2a2..2c1ed483de6 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -562,6 +562,38 @@ static void rna_Curve_offset_set(PointerRNA *ptr, float value) cu->offset = 1.0f + value; } +static int rna_Nurb_valid_message_u_length(PointerRNA *ptr) +{ + char buff[64]; + Nurb *nu = (Nurb *)ptr->data; + BKE_nurb_valid_message( + nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, "U", buff, sizeof(buff)); + return strlen(buff); +} + +static void rna_Nurb_valid_message_u(PointerRNA *ptr, char *value) +{ + Nurb *nu = (Nurb *)ptr->data; + BKE_nurb_valid_message( + nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, "U", value, 64); +} + +static int rna_Nurb_valid_message_v_length(PointerRNA *ptr) +{ + char buff[64]; + Nurb *nu = (Nurb *)ptr->data; + BKE_nurb_valid_message( + nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, "V", buff, sizeof(buff)); + return strlen(buff); +} + +static void rna_Nurb_valid_message_v(PointerRNA *ptr, char *value) +{ + Nurb *nu = (Nurb *)ptr->data; + BKE_nurb_valid_message( + nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, "V", value, 64); +} + static int rna_Curve_body_length(PointerRNA *ptr); static void rna_Curve_body_get(PointerRNA *ptr, char *value) { @@ -1995,24 +2027,20 @@ static void rna_def_curve_nurb(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Cyclic V", "Make this surface a closed loop in the V direction"); RNA_def_property_update(prop, 0, "rna_Nurb_update_cyclic_v"); - /* NOTE: endpoint and bezier flags should never be on at the same time! */ prop = RNA_def_property(srna, "use_endpoint_u", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flagu", CU_NURB_ENDPOINT); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_ui_text( prop, "Endpoint U", - "Make this nurbs curve or surface meet the endpoints in the U direction " - "(Cyclic U must be disabled)"); + "Make this nurbs curve or surface meet the endpoints in the U direction"); RNA_def_property_update(prop, 0, "rna_Nurb_update_knot_u"); prop = RNA_def_property(srna, "use_endpoint_v", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flagv", CU_NURB_ENDPOINT); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, - "Endpoint V", - "Make this nurbs surface meet the endpoints in the V direction " - "(Cyclic V must be disabled)"); + RNA_def_property_ui_text( + prop, "Endpoint V", "Make this nurbs surface meet the endpoints in the V direction "); RNA_def_property_update(prop, 0, "rna_Nurb_update_knot_v"); prop = RNA_def_property(srna, "use_bezier_u", PROP_BOOLEAN, PROP_NONE); @@ -2021,19 +2049,28 @@ static void rna_def_curve_nurb(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Bezier U", - "Make this nurbs curve or surface act like a Bezier spline in the U direction " - "(Order U must be 3 or 4, Cyclic U must be disabled)"); + "Make this nurbs curve or surface act like a Bezier spline in the U direction"); RNA_def_property_update(prop, 0, "rna_Nurb_update_knot_u"); prop = RNA_def_property(srna, "use_bezier_v", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flagv", CU_NURB_BEZIER); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, - "Bezier V", - "Make this nurbs surface act like a Bezier spline in the V direction " - "(Order V must be 3 or 4, Cyclic V must be disabled)"); + RNA_def_property_ui_text( + prop, "Bezier V", "Make this nurbs surface act like a Bezier spline in the V direction"); RNA_def_property_update(prop, 0, "rna_Nurb_update_knot_v"); + prop = RNA_def_property(srna, "valid_message_u", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_Nurb_valid_message_u", "rna_Nurb_valid_message_u_length", NULL); + RNA_def_property_ui_text(prop, "Valid U", "Validation message for NURBS definition in U"); + + prop = RNA_def_property(srna, "valid_message_v", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_Nurb_valid_message_v", "rna_Nurb_valid_message_v_length", NULL); + RNA_def_property_ui_text(prop, "Valid V", "Validation message for NURBS definition in V"); + prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_SMOOTH); RNA_def_property_ui_text(prop, "Smooth", "Smooth the normals of the surface or beveled curve"); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 92c0cafa551..6928cde638c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -65,6 +65,7 @@ static void nurbs_to_bezier_assign(const Span input, switch (knotsMode) { case NURBSpline::KnotsMode::Bezier: + case NURBSpline::KnotsMode::EndPointBezier: scale_input_assign(input, 3, 1, r_output); break; case NURBSpline::KnotsMode::Normal: -- cgit v1.2.3 From 0327a464ba40db4c555831520c8a5dd2248e47b0 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 10:08:25 +1100 Subject: Fix dragging items in the outliner Regression in b8960267dd51f9108b3b49e9b762e6b4d35ae1dc, it's important for box-select to use the drag start to check if the cursor is over an icon. --- source/blender/editors/space_outliner/outliner_select.cc | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index c6b9d9577b5..7cdfa441eaa 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -1662,10 +1662,15 @@ static int outliner_item_do_activate_from_cursor(bContext *C, /* Event can enter-key, then it opens/closes. */ static int outliner_item_activate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + ARegion *region = CTX_wm_region(C); + const bool extend = RNA_boolean_get(op->ptr, "extend"); const bool use_range = RNA_boolean_get(op->ptr, "extend_range"); const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); - return outliner_item_do_activate_from_cursor(C, event->mval, extend, use_range, deselect_all); + + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + return outliner_item_do_activate_from_cursor(C, mval, extend, use_range, deselect_all); } void OUTLINER_OT_item_activate(wmOperatorType *ot) @@ -1756,8 +1761,9 @@ static int outliner_box_select_invoke(bContext *C, wmOperator *op, const wmEvent float view_mval[2]; const bool tweak = RNA_boolean_get(op->ptr, "tweak"); - UI_view2d_region_to_view( - ®ion->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]); + int mval[2]; + WM_event_drag_start_mval(event, region, mval); + UI_view2d_region_to_view(®ion->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]); /* Find element clicked on */ TreeElement *te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]); -- cgit v1.2.3 From d73726dbbe82709ea11995f6be0d28f6290fec26 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 11:59:53 +1100 Subject: Cleanup: use doxy sections for the outliner --- .../editors/space_outliner/outliner_dragdrop.cc | 58 ++++++++++++++++++---- .../blender/editors/space_outliner/outliner_ops.cc | 6 ++- .../editors/space_outliner/outliner_select.cc | 26 +++++++--- .../editors/space_outliner/outliner_tree.cc | 33 +++++++++--- 4 files changed, 96 insertions(+), 27 deletions(-) diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 7a2f56eaaaa..ba4c3b0e595 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -46,7 +46,9 @@ static Collection *collection_parent_from_ID(ID *id); -/* ******************** Drop Target Find *********************** */ +/* -------------------------------------------------------------------- */ +/** \name Drop Target Find + * \{ */ static TreeElement *outliner_dropzone_element(TreeElement *te, const float fmval[2], @@ -254,7 +256,11 @@ static int outliner_get_insert_index(TreeElement *drag_te, return BLI_findindex(listbase, drop_te->directdata); } -/* ******************** Parent Drop Operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Parent Drop Operator + * \{ */ static bool parent_drop_allowed(TreeElement *te, Object *potential_child) { @@ -443,7 +449,11 @@ void OUTLINER_OT_parent_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/* ******************** Parent Clear Operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Parent Clear Operator + * \{ */ static bool parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -527,7 +537,11 @@ void OUTLINER_OT_parent_clear(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/* ******************** Scene Drop Operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Scene Drop Operator + * \{ */ static bool scene_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -592,7 +606,11 @@ void OUTLINER_OT_scene_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/* ******************** Material Drop Operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Drop Operator + * \{ */ static bool material_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -641,15 +659,19 @@ void OUTLINER_OT_material_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/* ******************** Data Stack Drop Operator *********************** */ +/** \} */ -/* A generic operator to allow drag and drop for modifiers, constraints, +/* -------------------------------------------------------------------- */ +/** \name Data Stack Drop Operator + * + * A generic operator to allow drag and drop for modifiers, constraints, * and shader effects which all share the same UI stack layout. * * The following operations are allowed: * - Reordering within an object. * - Copying a single modifier/constraint/effect to another object. - * - Copying (linking) an object's modifiers/constraints/effects to another. */ + * - Copying (linking) an object's modifiers/constraints/effects to another. + * \{ */ enum eDataStackDropAction { DATA_STACK_DROP_REORDER, @@ -1069,7 +1091,11 @@ void OUTLINER_OT_datastack_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/* ******************** Collection Drop Operator *********************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Collection Drop Operator + * \{ */ struct CollectionDrop { Collection *from; @@ -1350,7 +1376,11 @@ void OUTLINER_OT_collection_drop(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/* ********************* Outliner Drag Operator ******************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Outliner Drag Operator + * \{ */ #define OUTLINER_DRAG_SCOLL_OUTSIDE_PAD 7 /* In UI units */ @@ -1528,7 +1558,11 @@ void OUTLINER_OT_item_drag_drop(wmOperatorType *ot) #undef OUTLINER_DRAG_SCOLL_OUTSIDE_PAD -/* *************************** Drop Boxes ************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drop Boxes + * \{ */ void outliner_dropboxes(void) { @@ -1551,3 +1585,5 @@ void outliner_dropboxes(void) nullptr, collection_drop_tooltip); } + +/** \} */ diff --git a/source/blender/editors/space_outliner/outliner_ops.cc b/source/blender/editors/space_outliner/outliner_ops.cc index 69a691aeb50..8baac45666e 100644 --- a/source/blender/editors/space_outliner/outliner_ops.cc +++ b/source/blender/editors/space_outliner/outliner_ops.cc @@ -11,7 +11,9 @@ #include "outliner_intern.hh" -/* ************************** registration **********************************/ +/* -------------------------------------------------------------------- */ +/** \name Registration + * \{ */ void outliner_operatortypes(void) { @@ -97,3 +99,5 @@ void outliner_keymap(wmKeyConfig *keyconf) { WM_keymap_ensure(keyconf, "Outliner", SPACE_OUTLINER, 0); } + +/** \} */ diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index 7cdfa441eaa..233051f31ff 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -68,6 +68,10 @@ using namespace blender::ed::outliner; +/* -------------------------------------------------------------------- */ +/** \name Internal Utilities + * \{ */ + /** * \note changes to selection are by convention and not essential. * @@ -199,8 +203,11 @@ void outliner_item_mode_toggle(bContext *C, } } -/* ****************************************************** */ -/* Outliner Element Selection/Activation on Click */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Outliner Element Selection/Activation on Click Operator + * \{ */ static void tree_element_viewlayer_activate(bContext *C, TreeElement *te) { @@ -1700,9 +1707,12 @@ void OUTLINER_OT_item_activate(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ****************************************************** */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Box Select Operator + * \{ */ -/* **************** Box Select Tool ****************** */ static void outliner_item_box_select(bContext *C, SpaceOutliner *space_outliner, Scene *scene, @@ -1809,9 +1819,11 @@ void OUTLINER_OT_select_box(wmOperatorType *ot) WM_operator_properties_select_operation_simple(ot); } -/* ****************************************************** */ +/** \} */ -/* **************** Walk Select Tool ****************** */ +/* -------------------------------------------------------------------- */ +/** \name Walk Select Operator + * \{ */ /* Given a tree element return the rightmost child that is visible in the outliner */ static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *space_outliner, @@ -2036,4 +2048,4 @@ void OUTLINER_OT_select_walk(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } -/* ****************************************************** */ +/** \} */ diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 06a5918f25c..1a772287dfa 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -74,8 +74,9 @@ using namespace blender::ed::outliner; /* prototypes */ static int outliner_exclude_filter_get(const SpaceOutliner *space_outliner); -/* ********************************************************* */ -/* Persistent Data */ +/* -------------------------------------------------------------------- */ +/** \name Persistent Data + * \{ */ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) { @@ -175,8 +176,11 @@ static void check_persistent( BKE_outliner_treehash_add_element(space_outliner->runtime->treehash, tselem); } -/* ********************************************************* */ -/* Tree Management */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tree Management + * \{ */ void outliner_free_tree(ListBase *tree) { @@ -960,10 +964,14 @@ TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outliner, return ten; } +/** \} */ + /* ======================================================= */ /* Generic Tree Building helpers - order these are called is top to bottom */ -/* Sorting ------------------------------------------------------ */ +/* -------------------------------------------------------------------- */ +/** \name Tree Sorting Helper + * \{ */ struct tTreeSort { TreeElement *te; @@ -1195,7 +1203,11 @@ static void outliner_collections_children_sort(ListBase *lb) } } -/* Filtering ----------------------------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Tree Filtering Helper + * \{ */ struct OutlinerTreeElementFocus { TreeStoreElem *tselem; @@ -1644,8 +1656,11 @@ static void outliner_clear_newid_from_main(Main *bmain) FOREACH_MAIN_ID_END; } -/* ======================================================= */ -/* Main Tree Building API */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Tree Building API + * \{ */ void outliner_build_tree(Main *mainvar, Scene *scene, @@ -1707,3 +1722,5 @@ void outliner_build_tree(Main *mainvar, * as this expects valid IDs in this pointer, not random unknown data. */ outliner_clear_newid_from_main(mainvar); } + +/** \} */ -- cgit v1.2.3 From f527d6582b2998df1ed2f68772941b1638adbc53 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 12:38:34 +1100 Subject: Cleanup: remove unused variable --- source/blender/python/intern/bpy_rna.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ab119377e7e..f6ad105f173 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -5897,7 +5897,6 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat break; case PROP_STRING: { const char *data_ch; - PyObject *value_coerce = NULL; const int subtype = RNA_property_subtype(prop); if (flag & PROP_THICK_WRAP) { @@ -5926,10 +5925,6 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat } #endif -#ifdef USE_STRING_COERCE - Py_XDECREF(value_coerce); -#endif - break; } case PROP_ENUM: { -- cgit v1.2.3 From 1032f111d087e7ba77c16a4af78704019714bd6a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 14:42:39 +1100 Subject: RNA: support functions returning allocated strings Now it's possible for a C/RNA function to return a dynamic string, The PROP_NEVER_NULL flag is also supported so a NULL string returns None in Python. --- source/blender/makesrna/intern/makesrna.c | 31 ++++++++++++++++++++++++++----- source/blender/python/intern/bpy_rna.c | 26 ++++++++++++++++++-------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/source/blender/makesrna/intern/makesrna.c b/source/blender/makesrna/intern/makesrna.c index 20261fb9eeb..3ea7f8e0df6 100644 --- a/source/blender/makesrna/intern/makesrna.c +++ b/source/blender/makesrna/intern/makesrna.c @@ -2362,7 +2362,12 @@ static void rna_def_struct_function_prototype_cpp(FILE *f, pout = (flag_parameter & PARM_OUTPUT); if (flag & PROP_DYNAMIC) { - ptrstr = pout ? "**" : "*"; + if (type == PROP_STRING) { + ptrstr = pout ? "*" : ""; + } + else { + ptrstr = pout ? "**" : "*"; + } } else if (type == PROP_POINTER) { ptrstr = pout ? "*" : ""; @@ -2853,7 +2858,12 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA /* XXX only arrays and strings are allowed to be dynamic, is this checked anywhere? */ } else if (cptr || (flag & PROP_DYNAMIC)) { - ptrstr = pout ? "**" : "*"; + if (type == PROP_STRING) { + ptrstr = pout ? "*" : ""; + } + else { + ptrstr = pout ? "**" : "*"; + } /* Fixed size arrays and RNA pointers are pre-allocated on the ParameterList stack, * pass a pointer to it. */ } @@ -2933,8 +2943,14 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA else { const char *data_str; if (cptr || (flag & PROP_DYNAMIC)) { - ptrstr = "**"; - valstr = "*"; + if (type == PROP_STRING) { + ptrstr = "*"; + valstr = ""; + } + else { + ptrstr = "**"; + valstr = "*"; + } } else if ((type == PROP_POINTER) && !(flag & PROP_THICK_WRAP)) { ptrstr = "**"; @@ -3531,7 +3547,12 @@ static void rna_generate_static_parameter_prototypes(FILE *f, } if (cptr || (flag & PROP_DYNAMIC)) { - ptrstr = pout ? "**" : "*"; + if (type == PROP_STRING) { + ptrstr = pout ? "*" : ""; + } + else { + ptrstr = pout ? "**" : "*"; + } } else if (type == PROP_POINTER || dparm->prop->arraydimension) { ptrstr = "*"; diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index f6ad105f173..2a52253eece 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -5898,26 +5898,36 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat case PROP_STRING: { const char *data_ch; const int subtype = RNA_property_subtype(prop); + size_t data_ch_len; - if (flag & PROP_THICK_WRAP) { - data_ch = (char *)data; + if (flag & PROP_DYNAMIC) { + ParameterDynAlloc *data_alloc = data; + data_ch = data_alloc->array; + data_ch_len = data_alloc->array_tot; + BLI_assert((data_ch == NULL) || strlen(data_ch) == data_ch_len); } else { - data_ch = *(char **)data; + data_ch = (flag & PROP_THICK_WRAP) ? (char *)data : *(char **)data; + data_ch_len = data_ch ? 0 : strlen(data_ch); } + if (UNLIKELY(data_ch == NULL)) { + BLI_assert((flag & PROP_NEVER_NULL) == 0); + ret = Py_None; + Py_INCREF(ret); + } #ifdef USE_STRING_COERCE - if (subtype == PROP_BYTESTRING) { - ret = PyBytes_FromString(data_ch); + else if (subtype == PROP_BYTESTRING) { + ret = PyBytes_FromStringAndSize(data_ch, data_ch_len); } else if (ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME)) { - ret = PyC_UnicodeFromByte(data_ch); + ret = PyC_UnicodeFromByteAndSize(data_ch, data_ch_len); } else { - ret = PyUnicode_FromString(data_ch); + ret = PyUnicode_FromStringAndSize(data_ch, data_ch_len); } #else - if (subtype == PROP_BYTESTRING) { + else if (subtype == PROP_BYTESTRING) { ret = PyBytes_FromString(buf); } else { -- cgit v1.2.3 From 231eac160ee394d41c84e0cc36845facb7594ba5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 14:44:02 +1100 Subject: PyAPI: use C/RNA API for Text.from_string/to_string Use faster C code for getting the buffer from text. --- release/scripts/modules/bpy_types.py | 9 --------- source/blender/makesrna/intern/rna_text_api.c | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 54fcc8faf46..e0e20d0f8c9 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -580,15 +580,6 @@ class MeshPolygon(StructRNA): class Text(bpy_types.ID): __slots__ = () - def as_string(self): - """Return the text as a string.""" - return "\n".join(line.body for line in self.lines) - - def from_string(self, string): - """Replace text with this string.""" - self.clear() - self.write(string) - def as_module(self): import bpy from os.path import splitext, join diff --git a/source/blender/makesrna/intern/rna_text_api.c b/source/blender/makesrna/intern/rna_text_api.c index 67e93add549..0c0b8a85023 100644 --- a/source/blender/makesrna/intern/rna_text_api.c +++ b/source/blender/makesrna/intern/rna_text_api.c @@ -32,6 +32,17 @@ static void rna_Text_write(Text *text, const char *str) WM_main_add_notifier(NC_TEXT | NA_EDITED, text); } +static void rna_Text_from_string(Text *text, const char *str) +{ + BKE_text_clear(text); + BKE_text_write(text, str); +} + +static void rna_Text_as_string(Text *text, int *r_result_len, const char **result) +{ + *result = txt_to_buf(text, r_result_len); +} + static void rna_Text_select_set(Text *text, int startl, int startc, int endl, int endc) { txt_sel_set(text, startl, startc, endl, endc); @@ -60,6 +71,16 @@ void RNA_api_text(StructRNA *srna) parm = RNA_def_string(func, "text", "Text", 0, "", "New text for this data-block"); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + func = RNA_def_function(srna, "from_string", "rna_Text_from_string"); + RNA_def_function_ui_description(func, "Replace text with this string."); + parm = RNA_def_string(func, "text", "Text", 0, "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, "as_string", "rna_Text_as_string"); + RNA_def_function_ui_description(func, "Return the text as a string"); + parm = RNA_def_string(func, "text", "Text", 0, "", ""); + RNA_def_parameter_flags(parm, PROP_DYNAMIC, PARM_OUTPUT); + func = RNA_def_function( srna, "is_syntax_highlight_supported", "ED_text_is_syntax_highlight_supported"); RNA_def_function_return(func, -- cgit v1.2.3 From 8cc5483331d1a3d5c6eba055ae303788ba843526 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 15:09:55 +1100 Subject: Text: use simplified logic for txt_to_buf This function was copied from txt_sel_to_buf, including unnecessary complexity to support selection as well as checks for the cursor which don't make sense when copying the whole buffer. Use a simple loop to copy all text into the destination buffer. --- source/blender/blenkernel/BKE_text.h | 10 ++- source/blender/blenkernel/intern/text.c | 82 ++++-------------------- source/blender/python/intern/bpy_interface_run.c | 3 +- 3 files changed, 21 insertions(+), 74 deletions(-) diff --git a/source/blender/blenkernel/BKE_text.h b/source/blender/blenkernel/BKE_text.h index e833b4a14bb..bc6df18ce25 100644 --- a/source/blender/blenkernel/BKE_text.h +++ b/source/blender/blenkernel/BKE_text.h @@ -14,6 +14,8 @@ struct Main; struct Text; struct TextLine; +#include "BLI_compiler_attrs.h" + /** * \note caller must handle `compiled` member. */ @@ -55,7 +57,8 @@ void BKE_text_write(struct Text *text, const char *str); int BKE_text_file_modified_check(struct Text *text); void BKE_text_file_modified_ignore(struct Text *text); -char *txt_to_buf(struct Text *text, int *r_buf_strlen); +char *txt_to_buf(struct Text *text, int *r_buf_strlen) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; void txt_clean_text(struct Text *text); void txt_order_cursors(struct Text *text, bool reverse); int txt_find_string(struct Text *text, const char *findstr, int wrap, int match_case); @@ -135,11 +138,12 @@ enum { /** * Create a buffer, the only requirement is #txt_from_buf_for_undo can decode it. */ -char *txt_to_buf_for_undo(struct Text *text, int *r_buf_len); +char *txt_to_buf_for_undo(struct Text *text, int *r_buf_len) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; /** * Decode a buffer from #txt_to_buf_for_undo. */ -void txt_from_buf_for_undo(struct Text *text, const char *buf, int buf_len); +void txt_from_buf_for_undo(struct Text *text, const char *buf, int buf_len) ATTR_NONNULL(1, 2); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 5d0e515040d..486449c3f86 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -1436,78 +1436,20 @@ void txt_from_buf_for_undo(Text *text, const char *buf, int buf_len) char *txt_to_buf(Text *text, int *r_buf_strlen) { - int length; - TextLine *tmp, *linef, *linel; - int charf, charl; - char *buf; - - if (r_buf_strlen) { - *r_buf_strlen = 0; - } - - if (!text->curl) { - return NULL; - } - if (!text->sell) { - return NULL; - } - if (!text->lines.first) { - return NULL; - } - - linef = text->lines.first; - charf = 0; - - linel = text->lines.last; - charl = linel->len; - - if (linef == text->lines.last) { - length = charl - charf; - - buf = MEM_mallocN(length + 2, "text buffer"); - - BLI_strncpy(buf, linef->line + charf, length + 1); - buf[length] = 0; - } - else { - length = linef->len - charf; - length += charl; - length += 2; /* For the 2 '\n' */ - - tmp = linef->next; - while (tmp && tmp != linel) { - length += tmp->len + 1; - tmp = tmp->next; - } - - buf = MEM_mallocN(length + 1, "cut buffer"); - - strncpy(buf, linef->line + charf, linef->len - charf); - length = linef->len - charf; - - buf[length++] = '\n'; - - tmp = linef->next; - while (tmp && tmp != linel) { - strncpy(buf + length, tmp->line, tmp->len); - length += tmp->len; - - buf[length++] = '\n'; - - tmp = tmp->next; - } - strncpy(buf + length, linel->line, charl); - length += charl; - - /* python compiler wants an empty end line */ - buf[length++] = '\n'; - buf[length] = 0; + /* Identical to #txt_to_buf_for_undo except that the string is nil terminated. */ + int buf_len = 0; + LISTBASE_FOREACH (const TextLine *, l, &text->lines) { + buf_len += l->len + 1; } - - if (r_buf_strlen) { - *r_buf_strlen = length; + char *buf = MEM_mallocN(buf_len + 1, __func__); + char *buf_step = buf; + LISTBASE_FOREACH (const TextLine *, l, &text->lines) { + memcpy(buf_step, l->line, l->len); + buf_step += l->len; + *buf_step++ = '\n'; } - + *buf_step = '\0'; + *r_buf_strlen = buf_len; return buf; } diff --git a/source/blender/python/intern/bpy_interface_run.c b/source/blender/python/intern/bpy_interface_run.c index 01db0efecfd..500221c3c50 100644 --- a/source/blender/python/intern/bpy_interface_run.c +++ b/source/blender/python/intern/bpy_interface_run.c @@ -102,7 +102,8 @@ static bool python_script_exec( fn_dummy_py = PyC_UnicodeFromByte(fn_dummy); - buf = txt_to_buf(text, NULL); + int buf_len_dummy; + buf = txt_to_buf(text, &buf_len_dummy); text->compiled = Py_CompileStringObject(buf, fn_dummy_py, Py_file_input, NULL, -1); MEM_freeN(buf); -- cgit v1.2.3 From d2222d5b2cac203f4ddaae5c99b96701587231e7 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 15:18:14 +1100 Subject: RNA: use a function to access the nurbs error message It makes more sense to use a function in this case as this creates an error message which is not data associated with the NURBS curve. --- .../scripts/startup/bl_ui/properties_data_curve.py | 21 +++++----- source/blender/makesrna/intern/rna_curve.c | 44 ------------------- source/blender/makesrna/intern/rna_curve_api.c | 49 ++++++++++++++++++++++ 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/release/scripts/startup/bl_ui/properties_data_curve.py b/release/scripts/startup/bl_ui/properties_data_curve.py index 2ad6e5bae8a..d6e21053e3b 100644 --- a/release/scripts/startup/bl_ui/properties_data_curve.py +++ b/release/scripts/startup/bl_ui/properties_data_curve.py @@ -321,17 +321,16 @@ class DATA_PT_active_spline(CurveButtonsPanelActive, Panel): layout.prop(act_spline, "use_smooth") if act_spline.type == 'NURBS': - messages = [act_spline.valid_message_u] - if is_surf and act_spline.point_count_v > 1: - messages.append(act_spline.valid_message_v) - - messages = list(filter(None, messages)) - - if len(messages) > 0: - layout.separator() - col = layout.column(align=True) - for message in messages: - col.label(text=message, icon='INFO') + col = None + for direction in range(2): + message = act_spline.valid_message(direction) + if not message: + continue + if col is None: + layout.separator() + col = layout.column(align=True) + col.label(text=message, icon='INFO') + del col class DATA_PT_font(CurveButtonsPanelText, Panel): diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 2c1ed483de6..fb911725836 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -562,38 +562,6 @@ static void rna_Curve_offset_set(PointerRNA *ptr, float value) cu->offset = 1.0f + value; } -static int rna_Nurb_valid_message_u_length(PointerRNA *ptr) -{ - char buff[64]; - Nurb *nu = (Nurb *)ptr->data; - BKE_nurb_valid_message( - nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, "U", buff, sizeof(buff)); - return strlen(buff); -} - -static void rna_Nurb_valid_message_u(PointerRNA *ptr, char *value) -{ - Nurb *nu = (Nurb *)ptr->data; - BKE_nurb_valid_message( - nu->pntsu, nu->orderu, nu->flagu, nu->type, nu->pntsv > 1, "U", value, 64); -} - -static int rna_Nurb_valid_message_v_length(PointerRNA *ptr) -{ - char buff[64]; - Nurb *nu = (Nurb *)ptr->data; - BKE_nurb_valid_message( - nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, "V", buff, sizeof(buff)); - return strlen(buff); -} - -static void rna_Nurb_valid_message_v(PointerRNA *ptr, char *value) -{ - Nurb *nu = (Nurb *)ptr->data; - BKE_nurb_valid_message( - nu->pntsv, nu->orderv, nu->flagv, nu->type, nu->pntsv > 1, "V", value, 64); -} - static int rna_Curve_body_length(PointerRNA *ptr); static void rna_Curve_body_get(PointerRNA *ptr, char *value) { @@ -2059,18 +2027,6 @@ static void rna_def_curve_nurb(BlenderRNA *brna) prop, "Bezier V", "Make this nurbs surface act like a Bezier spline in the V direction"); RNA_def_property_update(prop, 0, "rna_Nurb_update_knot_v"); - prop = RNA_def_property(srna, "valid_message_u", PROP_STRING, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_string_funcs( - prop, "rna_Nurb_valid_message_u", "rna_Nurb_valid_message_u_length", NULL); - RNA_def_property_ui_text(prop, "Valid U", "Validation message for NURBS definition in U"); - - prop = RNA_def_property(srna, "valid_message_v", PROP_STRING, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_string_funcs( - prop, "rna_Nurb_valid_message_v", "rna_Nurb_valid_message_v_length", NULL); - RNA_def_property_ui_text(prop, "Valid V", "Validation message for NURBS definition in V"); - prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CU_SMOOTH); RNA_def_property_ui_text(prop, "Smooth", "Smooth the normals of the surface or beveled curve"); diff --git a/source/blender/makesrna/intern/rna_curve_api.c b/source/blender/makesrna/intern/rna_curve_api.c index d0086a425a2..72c1182ada1 100644 --- a/source/blender/makesrna/intern/rna_curve_api.c +++ b/source/blender/makesrna/intern/rna_curve_api.c @@ -36,6 +36,39 @@ static float rna_Nurb_calc_length(Nurb *nu, int resolution_u) return BKE_nurb_calc_length(nu, resolution_u); } +static void rna_Nurb_valid_message(Nurb *nu, int direction, int *result_len, const char **r_result) +{ + const bool is_surf = nu->pntsv > 1; + const short type = nu->type; + + int pnts; + short order, flag; + const char *dir; + if (direction) { + pnts = nu->pntsu; + order = nu->orderu; + flag = nu->flagu; + dir = "U"; + } + else { + pnts = nu->pntsv; + order = nu->orderv; + flag = nu->flagv; + dir = "V"; + } + + char buf[64]; + if (BKE_nurb_valid_message(pnts, order, flag, type, is_surf, dir, buf, sizeof(buf))) { + const int buf_len = strlen(buf); + *r_result = BLI_strdupn(buf, buf_len); + *result_len = buf_len; + } + else { + *r_result = NULL; + *result_len = 0; + } +} + #else void RNA_api_curve(StructRNA *srna) @@ -86,6 +119,22 @@ void RNA_api_curve_nurb(StructRNA *srna) 0.0f, FLT_MAX); RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "valid_message", "rna_Nurb_valid_message"); + RNA_def_function_ui_description(func, "Return the message"); + parm = RNA_def_int( + func, "direction", 0, 0, 1, "Direction", "The direction where 0-1 maps to U-V", 0, 1); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + /* return value */ + parm = RNA_def_string(func, + "result", + "nothing", + 64, + "Return value", + "The message or an empty string when there is no error"); + + RNA_def_parameter_flags(parm, PROP_DYNAMIC, PARM_OUTPUT); + RNA_def_property_clear_flag(parm, PROP_NEVER_NULL); } #endif -- cgit v1.2.3 From cf586484094979d0d08b6154c8b2d4d57b916126 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 15:27:34 +1100 Subject: Correct error in d2222d5b2cac203f4ddaae5c99b96701587231e7 --- source/blender/makesrna/intern/rna_curve_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/makesrna/intern/rna_curve_api.c b/source/blender/makesrna/intern/rna_curve_api.c index 72c1182ada1..f31e72ce652 100644 --- a/source/blender/makesrna/intern/rna_curve_api.c +++ b/source/blender/makesrna/intern/rna_curve_api.c @@ -44,7 +44,7 @@ static void rna_Nurb_valid_message(Nurb *nu, int direction, int *result_len, con int pnts; short order, flag; const char *dir; - if (direction) { + if (direction == 0) { pnts = nu->pntsu; order = nu->orderu; flag = nu->flagu; -- cgit v1.2.3 From f6564df3515fcabfdb18cbff9b2e1a1579430848 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 15:29:11 +1100 Subject: Cleanup: remove duplicate compiler attribute --- source/blender/blenlib/BLI_memiter.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/source/blender/blenlib/BLI_memiter.h b/source/blender/blenlib/BLI_memiter.h index 727092706c4..bf54b0ea14d 100644 --- a/source/blender/blenlib/BLI_memiter.h +++ b/source/blender/blenlib/BLI_memiter.h @@ -34,11 +34,12 @@ BLI_memiter *BLI_memiter_create(unsigned int chunk_size_min) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; void *BLI_memiter_alloc(BLI_memiter *mi, unsigned int size) /* WARNING: `ATTR_MALLOC` attribute on #BLI_memiter_alloc causes crash, see: D2756. */ - ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); + ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); void BLI_memiter_alloc_from(BLI_memiter *mi, uint elem_size, const void *data_from) ATTR_NONNULL(1, 3); -void *BLI_memiter_calloc(BLI_memiter *mi, unsigned int size) - ATTR_RETURNS_NONNULL ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1); +void *BLI_memiter_calloc(BLI_memiter *mi, + unsigned int size) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL + ATTR_NONNULL(1); void BLI_memiter_destroy(BLI_memiter *mi) ATTR_NONNULL(1); void BLI_memiter_clear(BLI_memiter *mi) ATTR_NONNULL(1); unsigned int BLI_memiter_count(const BLI_memiter *mi) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -- cgit v1.2.3 From f4c8b4eb3e309ebc043cd998faa6d8d2bada14ac Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Fri, 11 Mar 2022 12:32:30 +0100 Subject: Fix threading conflict with movie cache line It was possible that a render thread will be freeing cache while the interface is iterating over cache items to build cache line. Found while looking into T94738. It might be a fix, but I am unable to reproduce the original issue, so can not know for sure whether there is something else going or or not. --- source/blender/blenkernel/intern/movieclip.c | 2 ++ source/blender/editors/space_image/image_draw.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index a53714c8a98..c23110b4703 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -1609,8 +1609,10 @@ void BKE_movieclip_get_cache_segments(MovieClip *clip, if (clip->cache) { int proxy = rendersize_to_proxy(user, clip->flag); + BLI_thread_lock(LOCK_MOVIECLIP); IMB_moviecache_get_cache_segments( clip->cache->moviecache, proxy, user->render_flag, r_totseg, r_points); + BLI_thread_unlock(LOCK_MOVIECLIP); } } diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index cc86852e0d1..342f2df0020 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -541,7 +541,10 @@ void draw_image_cache(const bContext *C, ARegion *region) int num_segments = 0; int *points = NULL; + BLI_mutex_lock(image->runtime.cache_mutex); IMB_moviecache_get_cache_segments(image->cache, IMB_PROXY_NONE, 0, &num_segments, &points); + BLI_mutex_unlock(image->runtime.cache_mutex); + ED_region_cache_draw_cached_segments( region, num_segments, points, sfra + sima->iuser.offset, efra + sima->iuser.offset); } -- cgit v1.2.3 From e20fe187066cfb9b1847e656f0f9cef5c5bf7e73 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 22:34:59 +1100 Subject: Correct error in 1032f111d087e7ba77c16a4af78704019714bd6a Thanks to Jacques Lucke for spotting. --- source/blender/python/intern/bpy_rna.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 2a52253eece..16e90a55844 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -5908,7 +5908,7 @@ static PyObject *pyrna_param_to_py(PointerRNA *ptr, PropertyRNA *prop, void *dat } else { data_ch = (flag & PROP_THICK_WRAP) ? (char *)data : *(char **)data; - data_ch_len = data_ch ? 0 : strlen(data_ch); + data_ch_len = data_ch ? strlen(data_ch) : 0; } if (UNLIKELY(data_ch == NULL)) { -- cgit v1.2.3 From 789b1617f70e07f1c9bcb5253f1233acacbf6c8a Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 22:49:47 +1100 Subject: Fix out of order event handling when calling operators from gizmos Activating a gizmo used the windows eventstate which may have values newer than the event used to activate the gizmo. This meant transforms check for the key that activated transform could be incorrect. Support passing an event when calling operators to avoid this problem. --- source/blender/editors/animation/drivers.c | 2 +- source/blender/editors/asset/intern/asset_ops.cc | 2 +- source/blender/editors/gpencil/annotate_paint.c | 2 +- source/blender/editors/gpencil/gpencil_edit.c | 2 +- .../blender/editors/gpencil/gpencil_vertex_ops.c | 2 +- source/blender/editors/interface/interface_anim.c | 8 ++--- .../blender/editors/interface/interface_handlers.c | 3 +- source/blender/editors/interface/interface_ops.c | 2 +- .../interface/interface_template_search_menu.cc | 2 +- .../interface/interface_template_search_operator.c | 2 +- .../editors/interface/interface_templates.c | 4 +-- source/blender/editors/interface/view2d_ops.c | 2 +- source/blender/editors/mesh/editmesh_rip.c | 2 +- source/blender/editors/object/object_modes.c | 4 +-- source/blender/editors/sculpt_paint/paint_curve.c | 2 +- .../blender/editors/sculpt_paint/sculpt_detail.c | 2 +- source/blender/editors/space_buttons/buttons_ops.c | 2 +- source/blender/editors/space_console/console_ops.c | 6 ++-- .../blender/editors/space_console/space_console.c | 2 +- source/blender/editors/space_file/file_ops.c | 4 +-- source/blender/editors/space_image/image_ops.c | 4 +-- source/blender/editors/space_info/info_report.c | 2 +- source/blender/editors/space_nla/nla_edit.c | 4 +-- source/blender/editors/space_nla/nla_select.c | 4 +-- .../blender/editors/space_node/link_drag_search.cc | 2 +- source/blender/editors/space_node/node_draw.cc | 2 +- source/blender/editors/space_node/node_edit.cc | 2 +- .../editors/space_outliner/outliner_dragdrop.cc | 2 +- .../editors/space_outliner/outliner_edit.cc | 6 ++-- .../editors/space_outliner/outliner_tools.cc | 7 +++-- source/blender/editors/space_text/text_ops.c | 8 ++--- source/blender/editors/space_view3d/space_view3d.c | 2 +- source/blender/editors/space_view3d/view3d_edit.c | 8 ++--- .../editors/space_view3d/view3d_gizmo_ruler.c | 3 +- .../editors/space_view3d/view3d_navigate_dolly.c | 4 +-- .../editors/space_view3d/view3d_navigate_move.c | 4 +-- .../editors/space_view3d/view3d_navigate_roll.c | 4 +-- .../editors/space_view3d/view3d_navigate_rotate.c | 4 +-- .../editors/space_view3d/view3d_navigate_zoom.c | 4 +-- .../editors/space_view3d/view3d_placement.c | 2 +- .../blender/editors/space_view3d/view3d_select.c | 4 +-- source/blender/editors/transform/transform_ops.c | 6 ++-- .../intern/MOD_gpencil_ui_common.c | 2 +- source/blender/modifiers/intern/MOD_ui_common.c | 2 +- source/blender/python/intern/bpy_operator.c | 2 +- source/blender/shader_fx/intern/FX_ui_common.c | 2 +- source/blender/windowmanager/WM_api.h | 17 +++++++++-- source/blender/windowmanager/gizmo/WM_gizmo_api.h | 5 +++- .../blender/windowmanager/gizmo/intern/wm_gizmo.c | 6 ++-- .../windowmanager/gizmo/intern/wm_gizmo_group.c | 2 +- .../windowmanager/gizmo/intern/wm_gizmo_map.c | 2 +- .../blender/windowmanager/intern/wm_event_system.c | 35 ++++++++++++++-------- source/blender/windowmanager/intern/wm_files.c | 16 +++++----- source/blender/windowmanager/intern/wm_init_exit.c | 2 +- source/blender/windowmanager/intern/wm_operators.c | 7 +++-- .../blender/windowmanager/intern/wm_toolsystem.c | 2 +- source/blender/windowmanager/intern/wm_window.c | 2 +- 57 files changed, 137 insertions(+), 112 deletions(-) diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 6ae82a754aa..e1e974ff119 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -979,7 +979,7 @@ static int add_driver_button_menu_exec(bContext *C, wmOperator *op) /* XXX: We assume that it's fine to use the same set of properties, * since they're actually the same. */ - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, op->ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, op->ptr, NULL); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index 1d484f9ce85..f0768ebc907 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -754,7 +754,7 @@ static int asset_bundle_install_exec(bContext *C, wmOperator *op) cat_service->prepare_to_merge_on_write(); const int operator_result = WM_operator_name_call( - C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, op->ptr); + C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, op->ptr, nullptr); WM_cursor_wait(false); if (operator_result != OPERATOR_FINISHED) { diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index b33b676c078..338f6be4c89 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -2484,7 +2484,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve * - Since this operator is non-modal, we can just call it here, and keep going... * - This operator is especially useful when animating */ - WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL, event); estate = OPERATOR_RUNNING_MODAL; } else { diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index d734fb2678e..52ce7a7b8e1 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1928,7 +1928,7 @@ static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op) if (ELEM(NULL, gpd, active_gpl)) { /* Let's just be lazy, and call the "Add New Layer" operator, * which sets everything up as required. */ - WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL, NULL); } /* Go through each layer, adding a frame after the active one diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c index f49523925bc..c0888968a2d 100644 --- a/source/blender/editors/gpencil/gpencil_vertex_ops.c +++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c @@ -959,7 +959,7 @@ static int gpencil_material_to_vertex_exec(bContext *C, wmOperator *op) /* Clean unused materials. */ if (remove) { WM_operator_name_call( - C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL); + C, "OBJECT_OT_material_slot_remove_unused", WM_OP_INVOKE_REGION_WIN, NULL, NULL); } return OPERATOR_FINISHED; diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 46baf3033ff..e838ce37d8e 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -299,13 +299,13 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) void ui_but_anim_copy_driver(bContext *C) { /* this operator calls UI_context_active_but_prop_get */ - WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, NULL, NULL); } void ui_but_anim_paste_driver(bContext *C) { /* this operator calls UI_context_active_but_prop_get */ - WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL, NULL); } void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)) @@ -331,7 +331,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy) wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false); WM_operator_properties_create_ptr(&props_ptr, ot); RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } else { @@ -339,7 +339,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy) wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false); WM_operator_properties_create_ptr(&props_ptr, ot); RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2386b388a41..a8b21bebb2b 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1007,7 +1007,7 @@ static void ui_apply_but_funcs_after(bContext *C) if (after.optype) { WM_operator_name_call_ptr_with_depends_on_cursor( - C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL, after.drawstr); + C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL, NULL, after.drawstr); } if (after.opptr) { @@ -4190,6 +4190,7 @@ static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtra op_icon->optype_params->optype, op_icon->optype_params->opcontext, op_icon->optype_params->opptr, + NULL, NULL); /* Force recreation of extra operator icons (pseudo update). */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 0722584c7d8..1d1bb85bd36 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1673,7 +1673,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); - const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); /* Clean up */ if (but_label.strinfo) { diff --git a/source/blender/editors/interface/interface_template_search_menu.cc b/source/blender/editors/interface/interface_template_search_menu.cc index 160cc909036..8e4b2baed53 100644 --- a/source/blender/editors/interface/interface_template_search_menu.cc +++ b/source/blender/editors/interface/interface_template_search_menu.cc @@ -951,7 +951,7 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) case MenuSearch_Item::Type::Operator: { CTX_store_set(C, item->op.context); WM_operator_name_call_ptr_with_depends_on_cursor( - C, item->op.type, item->op.opcontext, item->op.opptr, item->drawstr); + C, item->op.type, item->op.opcontext, item->op.opptr, nullptr, item->drawstr); CTX_store_set(C, nullptr); break; } diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c index 4783a3f2ac6..41de2ab197d 100644 --- a/source/blender/editors/interface/interface_template_search_operator.c +++ b/source/blender/editors/interface/interface_template_search_operator.c @@ -38,7 +38,7 @@ static void operator_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) wmOperatorType *ot = arg2; if (ot) { - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, NULL, NULL); } } diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 43968e2c986..09cccbd6b4b 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -2013,7 +2013,7 @@ static void constraint_reorder(bContext *C, Panel *panel, int new_index) RNA_int_set(&props_ptr, "index", new_index); /* Set owner to #EDIT_CONSTRAINT_OWNER_OBJECT or #EDIT_CONSTRAINT_OWNER_BONE. */ RNA_enum_set(&props_ptr, "owner", constraint_from_bone ? 1 : 0); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } @@ -5653,7 +5653,7 @@ static void do_running_jobs(bContext *C, void *UNUSED(arg), int event) WM_jobs_stop(CTX_wm_manager(C), CTX_wm_screen(C), NULL); break; case B_STOPANIM: - WM_operator_name_call(C, "SCREEN_OT_animation_play", WM_OP_INVOKE_SCREEN, NULL); + WM_operator_name_call(C, "SCREEN_OT_animation_play", WM_OP_INVOKE_SCREEN, NULL, NULL); break; case B_STOPCOMPO: WM_jobs_stop(CTX_wm_manager(C), CTX_data_scene(C), NULL); diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 28a025ee581..0b4d00a7def 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -270,7 +270,7 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) view_pan_exit(op); WM_cursor_modal_restore(CTX_wm_window(C)); - WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW2D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL, event); return OPERATOR_FINISHED; } #endif diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index a8d62d90698..6b4edea498e 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -1017,7 +1017,7 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (bm->totfacesel) { /* highly nifty but hard to support since the operator can fail and we're left * with modified selection */ - // WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, NULL); + // WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, NULL, event); continue; } error_face_selected = false; diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 8e9e8558016..cc6aa34d39d 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -158,7 +158,7 @@ bool ED_object_mode_compat_set(bContext *C, Object *ob, eObjectMode mode, Report if (!ELEM(ob->mode, mode, OB_MODE_OBJECT)) { const char *opstring = object_mode_op_string(ob->mode); - WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL); + WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL, NULL); ok = ELEM(ob->mode, mode, OB_MODE_OBJECT); if (!ok) { wmOperatorType *ot = WM_operatortype_find(opstring, false); @@ -209,7 +209,7 @@ bool ED_object_mode_set_ex(bContext *C, eObjectMode mode, bool use_undo, ReportL if (!use_undo) { wm->op_undo_depth++; } - WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_REGION_WIN, NULL); + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_REGION_WIN, NULL, NULL); if (!use_undo) { wm->op_undo_depth--; } diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 57c8bd58549..22d6626ab16 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -680,7 +680,7 @@ static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_PASS_THROUGH; } - return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL); + return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL, NULL); } void PAINTCURVE_OT_draw(wmOperatorType *ot) diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 0f1f7c57287..dd8921d575f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -408,7 +408,7 @@ static void sculpt_detail_size_set_radial_control(bContext *C) RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); } - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index e215b7c7992..3de181fa540 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -295,7 +295,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_operator_properties_create_ptr(&props_ptr, ot); RNA_string_set(&props_ptr, "filepath", str); - WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); MEM_freeN(str); diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index a24eae6a0ce..17fbef23eac 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -469,10 +469,10 @@ static int console_indent_or_autocomplete_exec(bContext *C, wmOperator *UNUSED(o } if (text_before_cursor) { - WM_operator_name_call(C, "CONSOLE_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "CONSOLE_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL, NULL); } else { - WM_operator_name_call(C, "CONSOLE_OT_indent", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "CONSOLE_OT_indent", WM_OP_EXEC_DEFAULT, NULL, NULL); } return OPERATOR_FINISHED; } @@ -1070,7 +1070,7 @@ static int console_paste_exec(bContext *C, wmOperator *UNUSED(op)) } if (buf_next != buf_str) { - WM_operator_name_call(C, "CONSOLE_OT_execute", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "CONSOLE_OT_execute", WM_OP_EXEC_DEFAULT, NULL, NULL); ci = console_history_verify(C); } diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index d051f14d264..8e33f7fa97f 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -189,7 +189,7 @@ static void console_main_region_draw(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; if (BLI_listbase_is_empty(&sc->scrollback)) { - WM_operator_name_call((bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call((bContext *)C, "CONSOLE_OT_banner", WM_OP_EXEC_DEFAULT, NULL, NULL); } /* clear and setup matrix */ diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index c859cad1c27..578288ca289 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -2221,7 +2221,7 @@ static int file_smoothscroll_invoke(bContext *C, wmOperator *UNUSED(op), const w RNA_int_set(&op_ptr, "deltax", deltax); RNA_int_set(&op_ptr, "deltay", deltay); - WM_operator_name_call(C, "VIEW2D_OT_pan", WM_OP_EXEC_DEFAULT, &op_ptr); + WM_operator_name_call(C, "VIEW2D_OT_pan", WM_OP_EXEC_DEFAULT, &op_ptr, event); WM_operator_properties_free(&op_ptr); ED_region_tag_redraw(region); @@ -2580,7 +2580,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN BLI_strncpy(params->dir, lastdir, sizeof(params->dir)); } - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); WM_operator_properties_free(&ptr); } } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index aed4a2ccabd..6a16efdb413 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -2155,7 +2155,7 @@ static int image_save_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int image_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int image_save_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Image *ima = image_from_context(C); ImageUser *iuser = image_user_from_context(C); @@ -2163,7 +2163,7 @@ static int image_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( /* Not writable formats or images without a file-path will go to "Save As". */ if (!BKE_image_has_packedfile(ima) && (!BKE_image_has_filepath(ima) || !image_file_format_writable(ima, iuser))) { - WM_operator_name_call(C, "IMAGE_OT_save_as", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "IMAGE_OT_save_as", WM_OP_INVOKE_DEFAULT, NULL, event); return OPERATOR_CANCELLED; } return image_save_exec(C, op); diff --git a/source/blender/editors/space_info/info_report.c b/source/blender/editors/space_info/info_report.c index 603c7c9ad37..ba11c5b2958 100644 --- a/source/blender/editors/space_info/info_report.c +++ b/source/blender/editors/space_info/info_report.c @@ -101,7 +101,7 @@ static int report_replay_exec(bContext *C, wmOperator *UNUSED(op)) if ((report->type & report_mask) && (report->type & RPT_OPERATOR_ALL | RPT_PROPERTY_ALL) && (report->flag & SELECT)) { console_history_add_str(sc, report->message, 0); - WM_operator_name_call(C, "CONSOLE_OT_execute", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "CONSOLE_OT_execute", WM_OP_EXEC_DEFAULT, NULL, NULL); ED_area_tag_redraw(CTX_wm_area(C)); } diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index e94221fab04..eeeadb6e60a 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1192,12 +1192,12 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int nlaedit_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *event) { nlaedit_duplicate_exec(C, op); RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION); - WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr); + WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr, event); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c index 3000b4a1109..1efb91bc99f 100644 --- a/source/blender/editors/space_nla/nla_select.c +++ b/source/blender/editors/space_nla/nla_select.c @@ -439,7 +439,7 @@ static void nlaedit_select_leftright(bContext *C, /* if currently in tweak-mode, exit tweak-mode first */ if (scene->flag & SCE_NLA_EDIT_ON) { - WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL, NULL); } /* if select mode is replace, deselect all keyframes (and channels) first */ @@ -600,7 +600,7 @@ static int mouse_nla_strips(bContext *C, * now that we've found our target... */ if (scene->flag & SCE_NLA_EDIT_ON) { - WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "NLA_OT_tweakmode_exit", WM_OP_EXEC_DEFAULT, NULL, NULL); } if (select_mode != SELECT_REPLACE) { diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index 2484318e612..ccd3333fcc5 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -235,7 +235,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, ot); RNA_boolean_set(&ptr, "view2d_edge_pan", true); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr); WM_operator_properties_free(&ptr); } diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 410572159bf..9976ecf7700 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -1193,7 +1193,7 @@ static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_ /* Select & activate only the button's node. */ node_select_single(*C, *node); - WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, nullptr); + WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, nullptr, nullptr); } static void node_draw_shadow(const SpaceNode &snode, diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc index ab1f509af47..974e4c2e1cf 100644 --- a/source/blender/editors/space_node/node_edit.cc +++ b/source/blender/editors/space_node/node_edit.cc @@ -1507,7 +1507,7 @@ int node_render_changed_exec(bContext *C, wmOperator *UNUSED(op)) /* To keep keyframe positions. */ sce->r.scemode |= R_NO_FRAME_UPDATE; - WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr); + WM_operator_name_call(C, "RENDER_OT_render", WM_OP_INVOKE_DEFAULT, &op_ptr, nullptr); WM_operator_properties_free(&op_ptr); diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index ba4c3b0e595..30b81b2ecb2 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -1434,7 +1434,7 @@ static int outliner_item_drag_drop_invoke(bContext *C, PointerRNA op_ptr; WM_operator_properties_create_ptr(&op_ptr, ot); RNA_float_set(&op_ptr, "outside_padding", OUTLINER_DRAG_SCOLL_OUTSIDE_PAD); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr, event); WM_operator_properties_free(&op_ptr); } diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 8a02c3b64f2..a60e082f6a5 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -729,7 +729,7 @@ void id_remap_fn(bContext *C, RNA_enum_set(&op_props, "id_type", GS(tselem->id->name)); RNA_enum_set_identifier(C, &op_props, "old_id", tselem->id->name + 2); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr); WM_operator_properties_free(&op_props); } @@ -875,10 +875,10 @@ static int lib_relocate( RNA_string_set(&op_props, "directory", dir); RNA_string_set(&op_props, "filename", filename); - ret = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + ret = WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props, nullptr); } else { - ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props); + ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_props, nullptr); } WM_operator_properties_free(&op_props); diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index c58c13b2fb7..3b14c8542bd 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -2225,14 +2225,14 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) } case OUTLINER_IDOP_COPY: { wm->op_undo_depth++; - WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, nullptr); + WM_operator_name_call(C, "OUTLINER_OT_id_copy", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); wm->op_undo_depth--; /* No need for undo, this operation does not change anything... */ break; } case OUTLINER_IDOP_PASTE: { wm->op_undo_depth++; - WM_operator_name_call(C, "OUTLINER_OT_id_paste", WM_OP_INVOKE_DEFAULT, nullptr); + WM_operator_name_call(C, "OUTLINER_OT_id_paste", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); wm->op_undo_depth--; ED_outliner_select_sync_from_all_tag(C); ED_undo_push(C, "Paste"); @@ -2604,7 +2604,8 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) case OUTLINER_ANIMOP_SET_ACT: /* delegate once again... */ wm->op_undo_depth++; - WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, nullptr); + WM_operator_name_call( + C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, nullptr, nullptr); wm->op_undo_depth--; ED_undo_push(C, "Set active action"); break; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 3c29b32c2fa..15fea301b1c 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -649,13 +649,13 @@ static int text_save_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int text_save_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int text_save_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Text *text = CTX_data_edit_text(C); /* Internal and texts without a filepath will go to "Save As". */ if (text->filepath == NULL || (text->flags & TXT_ISMEM)) { - WM_operator_name_call(C, "TEXT_OT_save_as", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "TEXT_OT_save_as", WM_OP_INVOKE_DEFAULT, NULL, event); return OPERATOR_CANCELLED; } return text_save_exec(C, op); @@ -1098,10 +1098,10 @@ static int text_indent_or_autocomplete_exec(bContext *C, wmOperator *UNUSED(op)) TextLine *line = text->curl; bool text_before_cursor = text->curc != 0 && !ELEM(line->line[text->curc - 1], ' ', '\t'); if (text_before_cursor && (txt_has_sel(text) == false)) { - WM_operator_name_call(C, "TEXT_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "TEXT_OT_autocomplete", WM_OP_INVOKE_DEFAULT, NULL, NULL); } else { - WM_operator_name_call(C, "TEXT_OT_indent", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "TEXT_OT_indent", WM_OP_EXEC_DEFAULT, NULL, NULL); } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 4656540c19b..e2a027837a7 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -801,7 +801,7 @@ static void view3d_lightcache_update(bContext *C) RNA_int_set(&op_ptr, "delay", 200); RNA_enum_set_identifier(C, &op_ptr, "subset", "DIRTY"); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr, NULL); WM_operator_properties_free(&op_ptr); } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index b4cf08f69fe..e52ff062302 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -507,19 +507,17 @@ void VIEW3D_OT_view_persportho(wmOperatorType *ot) * Wraps walk/fly modes. * \{ */ -static int view3d_navigate_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *UNUSED(event)) +static int view3d_navigate_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { eViewNavigation_Method mode = U.navigation_mode; switch (mode) { case VIEW_NAVIGATION_FLY: - WM_operator_name_call(C, "VIEW3D_OT_fly", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_fly", WM_OP_INVOKE_DEFAULT, NULL, event); break; case VIEW_NAVIGATION_WALK: default: - WM_operator_name_call(C, "VIEW3D_OT_walk", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_walk", WM_OP_INVOKE_DEFAULT, NULL, event); break; } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 224c80bdd48..3a8a28dc2a1 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -1321,7 +1321,8 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e /* This is a little weak, but there is no real good way to tweak directly. */ WM_gizmo_highlight_set(gzmap, &ruler_item->gz); - if (WM_operator_name_call(C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_REGION_WIN, NULL) == + if (WM_operator_name_call( + C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_REGION_WIN, NULL, event) == OPERATOR_RUNNING_MODAL) { RulerInfo *ruler_info = gzgroup->customdata; RulerInteraction *inter = ruler_item->gz.interaction_data; diff --git a/source/blender/editors/space_view3d/view3d_navigate_dolly.c b/source/blender/editors/space_view3d/view3d_navigate_dolly.c index 7b6b119294d..29ccc352950 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_dolly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_dolly.c @@ -134,11 +134,11 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_move.c b/source/blender/editors/space_view3d/view3d_navigate_move.c index 071643e9314..af8419894b1 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_move.c +++ b/source/blender/editors/space_view3d/view3d_navigate_move.c @@ -76,11 +76,11 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_ZOOM: - WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_roll.c b/source/blender/editors/space_view3d/view3d_navigate_roll.c index 9c070fb0341..ea21eed6445 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_roll.c +++ b/source/blender/editors/space_view3d/view3d_navigate_roll.c @@ -99,11 +99,11 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_rotate.c b/source/blender/editors/space_view3d/view3d_navigate_rotate.c index 11de5463cdb..a53c71bd823 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_rotate.c +++ b/source/blender/editors/space_view3d/view3d_navigate_rotate.c @@ -327,11 +327,11 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_APPLY; break; case VIEWROT_MODAL_SWITCH_ZOOM: - WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_zoom", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; } diff --git a/source/blender/editors/space_view3d/view3d_navigate_zoom.c b/source/blender/editors/space_view3d/view3d_navigate_zoom.c index 5f6f9fde324..c744ef85236 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_zoom.c +++ b/source/blender/editors/space_view3d/view3d_navigate_zoom.c @@ -370,11 +370,11 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_MOVE: - WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; case VIEWROT_MODAL_SWITCH_ROTATE: - WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event); event_code = VIEW_CONFIRM; break; } diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 1603539cf94..05d4372723b 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -1153,7 +1153,7 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve RNA_float_set(&op_props, "radius2", 0.0f); } - WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props, NULL); WM_operator_properties_free(&op_props); } else { diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index e380a08dcc7..5d3168908d6 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1628,7 +1628,7 @@ static Base *object_mouse_select_menu(bContext *C, RNA_boolean_set(&ptr, "extend", extend); RNA_boolean_set(&ptr, "deselect", deselect); RNA_boolean_set(&ptr, "toggle", toggle); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); WM_operator_properties_free(&ptr); BLI_linklist_free(linklist.list, NULL); @@ -1851,7 +1851,7 @@ static bool bone_mouse_select_menu(bContext *C, RNA_boolean_set(&ptr, "extend", extend); RNA_boolean_set(&ptr, "deselect", deselect); RNA_boolean_set(&ptr, "toggle", toggle); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); WM_operator_properties_free(&ptr); BLI_linklist_free(base_list.list, NULL); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 936aca7d2e0..efbb7375af4 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -1311,9 +1311,7 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot) P_ALIGN_SNAP | P_GPENCIL_EDIT | P_CENTER); } -static int transform_from_gizmo_invoke(bContext *C, - wmOperator *UNUSED(op), - const wmEvent *UNUSED(event)) +static int transform_from_gizmo_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { bToolRef *tref = WM_toolsystem_ref_from_context(C); if (tref) { @@ -1343,7 +1341,7 @@ static int transform_from_gizmo_invoke(bContext *C, PointerRNA op_ptr; WM_operator_properties_create_ptr(&op_ptr, ot); RNA_boolean_set(&op_ptr, "release_confirm", true); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr, event); WM_operator_properties_free(&op_ptr); return OPERATOR_FINISHED; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index 45c7bd5b4e0..288ae9c0c5f 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -59,7 +59,7 @@ static void gpencil_modifier_reorder(bContext *C, Panel *panel, int new_index) WM_operator_properties_create_ptr(&props_ptr, ot); RNA_string_set(&props_ptr, "modifier", md->name); RNA_int_set(&props_ptr, "index", new_index); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 90652c180c6..eddf4bd03e1 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -63,7 +63,7 @@ static void modifier_reorder(bContext *C, Panel *panel, int new_index) WM_operator_properties_create_ptr(&props_ptr, ot); RNA_string_set(&props_ptr, "modifier", md->name); RNA_int_set(&props_ptr, "index", new_index); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } diff --git a/source/blender/python/intern/bpy_operator.c b/source/blender/python/intern/bpy_operator.c index 61de15b07f5..159ac23bf95 100644 --- a/source/blender/python/intern/bpy_operator.c +++ b/source/blender/python/intern/bpy_operator.c @@ -313,7 +313,7 @@ static PyObject *pyop_call(PyObject *UNUSED(self), PyObject *args) return NULL; } - WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, opname, WM_OP_EXEC_DEFAULT, NULL, NULL); } #endif } diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c index 3770b796c4d..4c9eaa11ab6 100644 --- a/source/blender/shader_fx/intern/FX_ui_common.c +++ b/source/blender/shader_fx/intern/FX_ui_common.c @@ -50,7 +50,7 @@ static void shaderfx_reorder(bContext *C, Panel *panel, int new_index) WM_operator_properties_create_ptr(&props_ptr, ot); RNA_string_set(&props_ptr, "shaderfx", fx->name); RNA_int_set(&props_ptr, "index", new_index); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 2f9062959c7..7959c3d6f0b 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -646,19 +646,29 @@ bool WM_operator_is_repeat(const struct bContext *C, const struct wmOperator *op bool WM_operator_name_poll(struct bContext *C, const char *opstring); /** * Invokes operator in context. + * + * \param event: Optionally pass in an event to use when context uses one of the + * `WM_OP_INVOKE_*` values. When left unset the #wmWindow.eventstate will be used, + * this can cause problems for operators that read the events type - for example, + * storing the key that was pressed so as to be able to detect it's release. + * In these cases it's necessary to forward the current event being handled. */ int WM_operator_name_call_ptr(struct bContext *C, struct wmOperatorType *ot, wmOperatorCallContext context, - struct PointerRNA *properties); + struct PointerRNA *properties, + const wmEvent *event); +/** See #WM_operator_name_call_ptr */ int WM_operator_name_call(struct bContext *C, const char *opstring, wmOperatorCallContext context, - struct PointerRNA *properties); + struct PointerRNA *properties, + const wmEvent *event); int WM_operator_name_call_with_properties(struct bContext *C, const char *opstring, wmOperatorCallContext context, - struct IDProperty *properties); + struct IDProperty *properties, + const wmEvent *event); /** * Similar to #WM_operator_name_call called with #WM_OP_EXEC_DEFAULT context. * @@ -677,6 +687,7 @@ void WM_operator_name_call_ptr_with_depends_on_cursor(struct bContext *C, wmOperatorType *ot, wmOperatorCallContext opcontext, PointerRNA *properties, + const wmEvent *event, const char *drawstr); /** diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_api.h b/source/blender/windowmanager/gizmo/WM_gizmo_api.h index 6b497c7462a..fe73daeed29 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_api.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_api.h @@ -92,7 +92,10 @@ struct PointerRNA *WM_gizmo_operator_set(struct wmGizmo *gz, int part_index, struct wmOperatorType *ot, struct IDProperty *properties); -int WM_gizmo_operator_invoke(struct bContext *C, struct wmGizmo *gz, struct wmGizmoOpElem *gzop); +int WM_gizmo_operator_invoke(struct bContext *C, + struct wmGizmo *gz, + struct wmGizmoOpElem *gzop, + const struct wmEvent *event); /* Callbacks. */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c index 05564f3bd0a..db4926f3430 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo.c @@ -225,7 +225,7 @@ PointerRNA *WM_gizmo_operator_set(wmGizmo *gz, return &gzop->ptr; } -int WM_gizmo_operator_invoke(bContext *C, wmGizmo *gz, wmGizmoOpElem *gzop) +int WM_gizmo_operator_invoke(bContext *C, wmGizmo *gz, wmGizmoOpElem *gzop, const wmEvent *event) { if (gz->flag & WM_GIZMO_OPERATOR_TOOL_INIT) { /* Merge toolsettings into the gizmo properties. */ @@ -239,7 +239,7 @@ int WM_gizmo_operator_invoke(bContext *C, wmGizmo *gz, wmGizmoOpElem *gzop) IDP_MergeGroup(gzop->ptr.data, tref_ptr.data, false); } } - return WM_operator_name_call_ptr(C, gzop->type, WM_OP_INVOKE_DEFAULT, &gzop->ptr); + return WM_operator_name_call_ptr(C, gzop->type, WM_OP_INVOKE_DEFAULT, &gzop->ptr, event); } static void wm_gizmo_set_matrix_rotation_from_z_axis__internal(float matrix[4][4], @@ -425,7 +425,7 @@ void WM_gizmo_modal_set_from_setup(struct wmGizmoMap *gzmap, } else { /* WEAK: but it works. */ - WM_operator_name_call(C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "GIZMOGROUP_OT_gizmo_tweak", WM_OP_INVOKE_DEFAULT, NULL, event); } } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index beef74ce2e2..00615d9d662 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -441,7 +441,7 @@ static bool gizmo_tweak_start_and_finish( gz->parent_gzgroup->type->invoke_prepare(C, gz->parent_gzgroup, gz, event); } /* Allow for 'button' gizmos, single click to run an action. */ - WM_gizmo_operator_invoke(C, gz, gzop); + WM_gizmo_operator_invoke(C, gz, gzop, event); } return true; } diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 44b7001e054..d9f4050c8f1 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -1157,7 +1157,7 @@ void wm_gizmomap_modal_set( struct wmGizmoOpElem *gzop = WM_gizmo_operator_get(gz, gz->highlight_part); if (gzop && gzop->type) { - const int retval = WM_gizmo_operator_invoke(C, gz, gzop); + const int retval = WM_gizmo_operator_invoke(C, gz, gzop, event); if ((retval & OPERATOR_RUNNING_MODAL) == 0) { wm_gizmomap_modal_set(gzmap, C, gz, event, false); } diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 3fe8e6cd1b0..44c9ffab92c 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -97,7 +97,7 @@ static int wm_operator_call_internal(bContext *C, ReportList *reports, const wmOperatorCallContext context, const bool poll_only, - wmEvent *event); + const wmEvent *event); static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot); static wmEvent *wm_event_add_mousemove_to_head(wmWindow *win); @@ -1313,7 +1313,7 @@ static void wm_region_mouse_co(bContext *C, wmEvent *event) */ static int wm_operator_invoke(bContext *C, wmOperatorType *ot, - wmEvent *event, + const wmEvent *event, PointerRNA *properties, ReportList *reports, const bool poll_only, @@ -1354,7 +1354,9 @@ static int wm_operator_invoke(bContext *C, } if (op->type->invoke && event) { - wm_region_mouse_co(C, event); + /* Temporarily write into `mval` (not technically `const` correct) but this is restored. */ + int mval_prev[2] = {UNPACK2(event->mval)}; + wm_region_mouse_co(C, (wmEvent *)event); if (op->type->flag & OPTYPE_UNDO) { wm->op_undo_depth++; @@ -1366,6 +1368,8 @@ static int wm_operator_invoke(bContext *C, if (op->type->flag & OPTYPE_UNDO && CTX_wm_manager(C) == wm) { wm->op_undo_depth--; } + + copy_v2_v2_int(((wmEvent *)event)->mval, mval_prev); } else if (op->type->exec) { if (op->type->flag & OPTYPE_UNDO) { @@ -1477,7 +1481,7 @@ static int wm_operator_call_internal(bContext *C, ReportList *reports, const wmOperatorCallContext context, const bool poll_only, - wmEvent *event) + const wmEvent *event) { int retval; @@ -1609,19 +1613,21 @@ static int wm_operator_call_internal(bContext *C, int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, - PointerRNA *properties) + PointerRNA *properties, + const wmEvent *event) { BLI_assert(ot == WM_operatortype_find(ot->idname, true)); - return wm_operator_call_internal(C, ot, properties, NULL, context, false, NULL); + return wm_operator_call_internal(C, ot, properties, NULL, context, false, event); } int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, - PointerRNA *properties) + PointerRNA *properties, + const wmEvent *event) { wmOperatorType *ot = WM_operatortype_find(opstring, 0); if (ot) { - return WM_operator_name_call_ptr(C, ot, context, properties); + return WM_operator_name_call_ptr(C, ot, context, properties, event); } return 0; @@ -1640,12 +1646,13 @@ bool WM_operator_name_poll(bContext *C, const char *opstring) int WM_operator_name_call_with_properties(struct bContext *C, const char *opstring, wmOperatorCallContext context, - struct IDProperty *properties) + struct IDProperty *properties, + const wmEvent *event) { PointerRNA props_ptr; wmOperatorType *ot = WM_operatortype_find(opstring, false); RNA_pointer_create(G_MAIN->wm.first, ot->srna, properties, &props_ptr); - return WM_operator_name_call_ptr(C, ot, context, &props_ptr); + return WM_operator_name_call_ptr(C, ot, context, &props_ptr, event); } void WM_menu_name_call(bContext *C, const char *menu_name, short context) @@ -1654,7 +1661,7 @@ void WM_menu_name_call(bContext *C, const char *menu_name, short context) PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, "name", menu_name); - WM_operator_name_call_ptr(C, ot, context, &ptr); + WM_operator_name_call_ptr(C, ot, context, &ptr, NULL); WM_operator_properties_free(&ptr); } @@ -1766,7 +1773,8 @@ static int ui_handler_wait_for_input(bContext *C, const wmEvent *event, void *us WM_operator_name_call_ptr(C, opwait->optype_params.optype, opwait->optype_params.opcontext, - opwait->optype_params.opptr); + opwait->optype_params.opptr, + event); CTX_store_set(C, NULL); } @@ -1788,6 +1796,7 @@ void WM_operator_name_call_ptr_with_depends_on_cursor(bContext *C, wmOperatorType *ot, wmOperatorCallContext opcontext, PointerRNA *properties, + const wmEvent *event, const char *drawstr) { int flag = ot->flag; @@ -1800,7 +1809,7 @@ void WM_operator_name_call_ptr_with_depends_on_cursor(bContext *C, } if ((flag & OPTYPE_DEPENDS_ON_CURSOR) == 0) { - WM_operator_name_call_ptr(C, ot, opcontext, properties); + WM_operator_name_call_ptr(C, ot, opcontext, properties, event); return; } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 4ff96b82fd2..04ce7bcb520 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -2424,7 +2424,7 @@ static int wm_homefile_read_exec(bContext *C, wmOperator *op) static void wm_homefile_read_after_dialog_callback(bContext *C, void *user_data) { WM_operator_name_call_with_properties( - C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data); + C, "WM_OT_read_homefile", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, NULL); } static int wm_homefile_read_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) @@ -2577,7 +2577,7 @@ static int wm_open_mainfile_dispatch(bContext *C, wmOperator *op); static void wm_open_mainfile_after_dialog_callback(bContext *C, void *user_data) { WM_operator_name_call_with_properties( - C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data); + C, "WM_OT_open_mainfile", WM_OP_INVOKE_DEFAULT, (IDProperty *)user_data, NULL); } static int wm_open_mainfile__discard_changes(bContext *C, wmOperator *op) @@ -2892,7 +2892,7 @@ static int wm_recover_last_session_exec(bContext *C, wmOperator *op) static void wm_recover_last_session_after_dialog_callback(bContext *C, void *user_data) { WM_operator_name_call_with_properties( - C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data); + C, "WM_OT_recover_last_session", WM_OP_EXEC_DEFAULT, (IDProperty *)user_data, NULL); } static int wm_recover_last_session_invoke(bContext *C, @@ -3290,7 +3290,7 @@ static void wm_block_autorun_warning_reload_with_scripts(bContext *C, /* Save user preferences for permanent execution. */ if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { - WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL, NULL); } /* Load file again with scripts enabled. @@ -3309,7 +3309,7 @@ static void wm_block_autorun_warning_enable_scripts(bContext *C, /* Save user preferences for permanent execution. */ if ((U.flag & USER_SCRIPT_AUTOEXEC_DISABLE) == 0) { - WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL); + WM_operator_name_call(C, "WM_OT_save_userpref", WM_OP_EXEC_DEFAULT, NULL, NULL); } /* Force a full refresh, but without reloading the file. */ @@ -3478,7 +3478,7 @@ void wm_test_autorun_revert_action_exec(bContext *C) wm_test_autorun_revert_action_set(ot, ptr); } - WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, ptr, NULL); wm_test_autorun_revert_action_set(NULL, NULL); } @@ -3559,13 +3559,13 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0'; if (file_has_been_saved_before) { - if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) & + if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL, NULL) & OPERATOR_CANCELLED) { execute_callback = false; } } else { - WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL, NULL); execute_callback = false; } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 70e54e3e69d..6a9776c6933 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -365,7 +365,7 @@ void WM_init_splash(bContext *C) if (wm->windows.first) { CTX_wm_window_set(C, wm->windows.first); - WM_operator_name_call(C, "WM_OT_splash", WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call(C, "WM_OT_splash", WM_OP_INVOKE_DEFAULT, NULL, NULL); CTX_wm_window_set(C, prevwin); } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 2f5eef652f2..84cd8dce6cf 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3597,8 +3597,11 @@ static int doc_view_manual_ui_context_exec(bContext *C, wmOperator *UNUSED(op)) WM_operator_properties_create(&ptr_props, "WM_OT_doc_view_manual"); RNA_string_set(&ptr_props, "doc_id", buf); - retval = WM_operator_name_call_ptr( - C, WM_operatortype_find("WM_OT_doc_view_manual", false), WM_OP_EXEC_DEFAULT, &ptr_props); + retval = WM_operator_name_call_ptr(C, + WM_operatortype_find("WM_OT_doc_view_manual", false), + WM_OP_EXEC_DEFAULT, + &ptr_props, + NULL); WM_operator_properties_free(&ptr_props); } diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index ae1024eafbc..bc4bbc86367 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -611,7 +611,7 @@ bToolRef *WM_toolsystem_ref_set_by_id_ex( RNA_enum_set(&op_props, "space_type", tkey->space_type); RNA_boolean_set(&op_props, "cycle", cycle); - WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props); + WM_operator_name_call_ptr(C, ot, WM_OP_EXEC_DEFAULT, &op_props, NULL); WM_operator_properties_free(&op_props); bToolRef *tref = WM_toolsystem_ref_find(workspace, tkey); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index d6f85e3795e..cbebb856660 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1328,7 +1328,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr WM_operator_properties_create_ptr(&props_ptr, ot); RNA_string_set(&props_ptr, "filepath", path); RNA_boolean_set(&props_ptr, "display_file_selector", false); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); WM_operator_properties_free(&props_ptr); CTX_wm_window_set(C, NULL); -- cgit v1.2.3 From c87c12b2a4c9166ebc021cbf254da3b63e779269 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 23:04:11 +1100 Subject: Event System: only set press values in win->eventstate Only set press events in the windows eventstate, not the current event since it's not useful for these to be set current events press values. This makes it possible for a press event to access values for the previous press. --- source/blender/windowmanager/intern/wm_event_system.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index 44c9ffab92c..f407bb38d22 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -4767,14 +4767,14 @@ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) event->prev_type = event_state->prev_type = event_state->type; } -static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) +static void wm_event_prev_click_set(wmEvent *event_state) { - event->prev_press_time = event_state->prev_press_time = PIL_check_seconds_timer(); - event->prev_press_type = event_state->prev_press_type = event_state->type; - event->prev_press_modifier = event_state->prev_press_modifier = event_state->modifier; - event->prev_press_keymodifier = event_state->prev_press_keymodifier = event_state->keymodifier; - event->prev_press_xy[0] = event_state->prev_press_xy[0] = event_state->xy[0]; - event->prev_press_xy[1] = event_state->prev_press_xy[1] = event_state->xy[1]; + event_state->prev_press_time = PIL_check_seconds_timer(); + event_state->prev_press_type = event_state->type; + event_state->prev_press_modifier = event_state->modifier; + event_state->prev_press_keymodifier = event_state->keymodifier; + event_state->prev_press_xy[0] = event_state->xy[0]; + event_state->prev_press_xy[1] = event_state->xy[1]; } static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) @@ -4885,7 +4885,7 @@ static void wm_event_state_update_and_click_set(const GHOST_TEventType type, } else if (event->val == KM_PRESS) { if ((event->flag & WM_EVENT_IS_REPEAT) == 0) { - wm_event_prev_click_set(event, event_state); + wm_event_prev_click_set(event_state); } } } -- cgit v1.2.3 From 9bee8e46a1dce4d0ae4b930d91690cc9dcf3fca3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 11 Mar 2022 23:06:45 +1100 Subject: Event System: limit early evaluation of drag actions to mouse buttons Change early drag evaluation added in 1f1dcf41d51a03150ee38f220c590f8715b11e88 to only apply to drag events from mouse buttons. Otherwise pressing two keyboard keys at one would create a drag event for the first pressed key. While this didn't cause any bugs as far as I know, this behavior makes most sense for drags that come from cursor input. --- source/blender/windowmanager/intern/wm_event_system.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index f407bb38d22..62aeb8a2928 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -3697,10 +3697,12 @@ void wm_event_do_handlers(bContext *C) /* Force handling drag if a key is pressed even if the drag threshold has not been met. * Needed so tablet actions (which typically use a larger threshold) can click-drag - * then press keys - activating the drag action early. */ + * then press keys - activating the drag action early. + * Limit to mouse-buttons drag actions interrupted by pressing any non-mouse button. + * Otherwise pressing two keys on the keyboard will interpret this as a drag action. */ if (win->event_queue_check_drag) { if ((event->val == KM_PRESS) && ((event->flag & WM_EVENT_IS_REPEAT) == 0) && - ISKEYBOARD_OR_BUTTON(event->type)) { + ISKEYBOARD_OR_BUTTON(event->type) && ISMOUSE_BUTTON(event->prev_press_type)) { event = wm_event_add_mousemove_to_head(win); event->flag |= WM_EVENT_FORCE_DRAG_THRESHOLD; } -- cgit v1.2.3 From 9097c9776798a56b48d148cf614750ecedfdfe3d Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 11 Mar 2022 13:51:50 +0100 Subject: Fix T96243: Workbench Curvature not rendering in background. When rendering using the command line the curvature wasn't rendered. The reason was that the ui_scale wasn't initialized and therefore the same pixels where sampled to detect the curvature. This is fixed by setting the ui_scale to 1 for any image render. --- source/blender/draw/engines/workbench/workbench_data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index bf197986687..010f424b9da 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -275,7 +275,7 @@ void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd) copy_v2_v2(wd.viewport_size_inv, DRW_viewport_invert_size_get()); copy_v3_v3(wd.object_outline_color, wpd->shading.object_outline_color); wd.object_outline_color[3] = 1.0f; - wd.ui_scale = G_draw.block.sizePixel; + wd.ui_scale = DRW_state_is_image_render() ? 1.0f : G_draw.block.sizePixel; wd.matcap_orientation = (wpd->shading.flag & V3D_SHADING_MATCAP_FLIP_X) != 0; workbench_studiolight_data_update(wpd, &wd); -- cgit v1.2.3 From 28434dab0c9b59ccee0ac1f55425580a75f43703 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 11 Mar 2022 10:46:48 -0600 Subject: Cleanup: Use helper variable For a NURBS curve, the order is just the degree plus one. So just pass around the degree instead of always subtracting one. --- source/blender/blenkernel/intern/spline_nurbs.cc | 25 ++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 1e0fb874d11..2146a4309ab 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -224,17 +224,17 @@ Span NURBSpline::knots() const static void calculate_basis_for_point(const float parameter, const int size, - const int order, + const int degree, Span knots, MutableSpan basis_buffer, NURBSpline::BasisCache &basis_cache) { /* Clamp parameter due to floating point inaccuracy. */ - const float t = std::clamp(parameter, knots[0], knots[size + order - 1]); + const float t = std::clamp(parameter, knots[0], knots[size + degree]); int start = 0; int end = 0; - for (const int i : IndexRange(size + order - 1)) { + for (const int i : IndexRange(size + degree)) { const bool knots_equal = knots[i] == knots[i + 1]; if (knots_equal || t < knots[i] || t > knots[i + 1]) { basis_buffer[i] = 0.0f; @@ -242,16 +242,16 @@ static void calculate_basis_for_point(const float parameter, } basis_buffer[i] = 1.0f; - start = std::max(i - order - 1, 0); + start = std::max(i - degree, 0); end = i; - basis_buffer.slice(i + 1, size + order - 1 - i).fill(0.0f); + basis_buffer.slice(i + 1, size + degree - i).fill(0.0f); break; } - basis_buffer[size + order - 1] = 0.0f; + basis_buffer[size + degree] = 0.0f; - for (const int i_order : IndexRange(2, order - 1)) { - if (end + i_order >= size + order) { - end = size + order - 1 - i_order; + for (const int i_order : IndexRange(2, degree)) { + if (end + i_order >= size + degree + 1) { + end = size + degree - i_order; } for (const int i : IndexRange(start, end - start + 1)) { float new_basis = 0.0f; @@ -301,6 +301,7 @@ Span NURBSpline::calculate_basis_cache() const basis_cache_.resize(eval_size); const int order = this->order(); + const int degree = order - 1; Span control_weights = this->weights(); Span knots = this->knots(); @@ -310,14 +311,14 @@ Span NURBSpline::calculate_basis_cache() const * Theoretically it could be optimized away in the future. */ Array basis_buffer(this->knots_size()); - const float start = knots[order - 1]; - const float end = is_cyclic_ ? knots[size + order - 1] : knots[size]; + const float start = knots[degree]; + const float end = is_cyclic_ ? knots[size + degree] : knots[size]; const float step = (end - start) / this->evaluated_edges_size(); float parameter = start; for (const int i : IndexRange(eval_size)) { BasisCache &basis = basis_cache[i]; calculate_basis_for_point( - parameter, size + (is_cyclic_ ? order - 1 : 0), order, knots, basis_buffer, basis); + parameter, size + (is_cyclic_ ? degree : 0), degree, knots, basis_buffer, basis); BLI_assert(basis.weights.size() <= order); for (const int j : basis.weights.index_range()) { -- cgit v1.2.3 From 5c86f0369c6fc458c828cf60268774ac0c4cf32a Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 11 Mar 2022 11:25:59 -0600 Subject: Fix: Deleting vertex group attribute can change original mesh There was an error with the attribute API implementation for vertex groups. If the vertex group layer referenced an original mesh, it wasn't properly duplicated for writing. --- source/blender/blenkernel/intern/geometry_component_mesh.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 104166df913..cbd7ec9155a 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -1049,6 +1049,11 @@ class VertexGroupsAttributeProvider final : public DynamicAttributesProvider { if (mesh->dvert == nullptr) { return true; } + + /* Copy the data layer if it is shared with some other mesh. */ + mesh->dvert = (MDeformVert *)CustomData_duplicate_referenced_layer( + &mesh->vdata, CD_MDEFORMVERT, mesh->totvert); + for (MDeformVert &dvert : MutableSpan(mesh->dvert, mesh->totvert)) { MDeformWeight *weight = BKE_defvert_find_index(&dvert, index); BKE_defvert_remove_group(&dvert, weight); -- cgit v1.2.3 From ae3c8bc9f098b1be19da19f9d555bc7b22c2d03c Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 11 Mar 2022 09:25:45 -0800 Subject: Fix T85689: Replace SHGetFileInfoW for Drive Name Win32: Replace SHGetFileInfoW as means to get friendly display names for volumes because it causes long pauses for disconnected remote drives. See D14305 for more details. Differential Revision: https://developer.blender.org/D14305 Reviewed by Brecht Van Lommel --- source/blender/editors/space_file/fsmenu.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 988ba7728f1..847bf89bba8 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -29,6 +29,7 @@ * because 'near' is disabled through BLI_windstuff. */ # include "BLI_winstuff.h" # include +# include #endif #include "UI_interface_icons.h" @@ -642,14 +643,29 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) tmps[3] = '\0'; name = NULL; - /* Flee from horrible win querying hover floppy drives! */ + /* Skip over floppy disks A & B. */ if (i > 1) { - /* Try to get a friendly drive description. */ - SHFILEINFOW shFile = {0}; + /* Friendly volume descriptions without using SHGetFileInfoW (T85689). */ BLI_strncpy_wchar_from_utf8(wline, tmps, 4); - if (SHGetFileInfoW(wline, 0, &shFile, sizeof(SHFILEINFOW), SHGFI_DISPLAYNAME)) { - BLI_strncpy_wchar_as_utf8(line, shFile.szDisplayName, FILE_MAXDIR); - name = line; + IShellFolder *desktop; + if (SHGetDesktopFolder(&desktop) == S_OK) { + PIDLIST_RELATIVE volume; + if (desktop->lpVtbl->ParseDisplayName( + desktop, NULL, NULL, wline, NULL, &volume, NULL) == S_OK) { + STRRET volume_name; + volume_name.uType = STRRET_WSTR; + if (desktop->lpVtbl->GetDisplayNameOf( + desktop, volume, SHGDN_FORADDRESSBAR, &volume_name) == S_OK) { + wchar_t *volume_name_wchar; + if (StrRetToStrW(&volume_name, volume, &volume_name_wchar) == S_OK) { + BLI_strncpy_wchar_as_utf8(line, volume_name_wchar, FILE_MAXDIR); + name = line; + CoTaskMemFree(volume_name_wchar); + } + } + CoTaskMemFree(volume); + } + desktop->lpVtbl->Release(desktop); } } if (name == NULL) { -- cgit v1.2.3 From dbd9472ef58174e8af011735b0b1bd87a7447348 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 11 Mar 2022 11:27:18 -0600 Subject: Fix: Assert in set spline type node after recent commit My own error when committing 0602852860dda. It appears that the "Endpoint" knots modes should be handled together, otherwise out of bounds array access is possible. --- source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 6928cde638c..1b4429cc527 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -65,7 +65,6 @@ static void nurbs_to_bezier_assign(const Span input, switch (knotsMode) { case NURBSpline::KnotsMode::Bezier: - case NURBSpline::KnotsMode::EndPointBezier: scale_input_assign(input, 3, 1, r_output); break; case NURBSpline::KnotsMode::Normal: @@ -73,6 +72,7 @@ static void nurbs_to_bezier_assign(const Span input, r_output[i] = input[(i + 1) % input_size]; } break; + case NURBSpline::KnotsMode::EndPointBezier: case NURBSpline::KnotsMode::EndPoint: for (const int i : IndexRange(1, output_size - 2)) { r_output[i] = input[i + 1]; -- cgit v1.2.3 From e1a1dc868b30be80e59b747557830d507600bd1d Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 11 Mar 2022 14:23:11 +0100 Subject: Cleanup: fix source comment typos Contributed by luzpaz. Differential Revision: https://developer.blender.org/D14307 --- GNUmakefile | 2 +- intern/itasc/Cache.hpp | 2 +- source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl | 2 +- source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl | 2 +- source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl | 2 +- source/blender/draw/intern/shaders/common_math_lib.glsl | 2 +- source/blender/gpu/intern/gpu_shader_create_info.hh | 2 +- source/blender/gpu/opengl/gl_shader.cc | 2 +- source/blender/windowmanager/intern/wm_operators.c | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/GNUmakefile b/GNUmakefile index 575f3e904df..8dc2a2e2a9a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -32,7 +32,7 @@ Other Convenience Targets * config: Run cmake configuration tool to set build options. * deps: Build library dependencies (intended only for platform maintainers). - The existance of locally build dependencies overrides the pre-built dependencies from subversion. + The existence of locally build dependencies overrides the pre-built dependencies from subversion. These must be manually removed from '../lib/' to go back to using the pre-compiled libraries. Project Files diff --git a/intern/itasc/Cache.hpp b/intern/itasc/Cache.hpp index a533d75b53a..d461bb32fa8 100644 --- a/intern/itasc/Cache.hpp +++ b/intern/itasc/Cache.hpp @@ -17,7 +17,7 @@ namespace iTaSC { #define CACHE_CHANNEL_EXTEND_SIZE 10 #define CACHE_MAX_ITEM_SIZE 0x3FFF0 -/* macro to get the alignement gap after an item header */ +/* macro to get the alignment gap after an item header */ #define CACHE_ITEM_GAPB(item) (unsigned int)(((size_t)item+sizeof(CacheItem))&(sizeof(void*)-1)) /* macro to get item data position, item=CacheItem pointer */ #define CACHE_ITEM_DATA_POINTER(item) (void*)((unsigned char*)item+sizeof(CacheItem)+CACHE_ITEM_GAPB(item)) diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl index f5c45d147e6..fe1ab395a54 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_gather_frag.glsl @@ -12,7 +12,7 @@ #pragma BLENDER_REQUIRE(common_utiltex_lib.glsl) #pragma BLENDER_REQUIRE(effect_dof_lib.glsl) -/* Mipmapped input buffers, halfres but with padding to ensure mipmap alignement. */ +/* Mipmapped input buffers, halfres but with padding to ensure mipmap alignment. */ uniform sampler2D colorBuffer; uniform sampler2D cocBuffer; diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl index 8ef39a55921..ce455123987 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl @@ -4,7 +4,7 @@ * Adapted from http://rastergrid.com/blog/2010/10/hierarchical-z-map-based-occlusion-culling/ * * Major simplification has been made since we pad the buffer to always be bigger than input to - * avoid mipmapping misalignement. + * avoid mipmapping misalignment. */ #ifdef LAYERED diff --git a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl index 36a52e05a4a..b4a26ec8103 100644 --- a/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl +++ b/source/blender/draw/engines/gpencil/shaders/gpencil_common_lib.glsl @@ -442,7 +442,7 @@ void stroke_vertex() if (is_dot) { # ifdef GP_MATERIAL_BUFFER_LEN int alignement = GP_FLAG(m) & GP_STROKE_ALIGNMENT; - /* For one point strokes use object aligment. */ + /* For one point strokes use object alignment. */ if (ma.x == -1 && ma2.x == -1 && alignement == GP_STROKE_ALIGNMENT_STROKE) { alignement = GP_STROKE_ALIGNMENT_OBJECT; } diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 479f9cd1827..eb7a98788c3 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -93,7 +93,7 @@ vec2 sqr(vec2 a) { return a * a; } vec3 sqr(vec3 a) { return a * a; } vec4 sqr(vec4 a) { return a * a; } -/* Use manual powers for fixed powers. pow() can have unpredicatble results on some implementations. +/* Use manual powers for fixed powers. pow() can have unpredictable results on some implementations. * (see T87369, T87541) */ float pow6(float x) { return sqr(sqr(x) * x); } float pow8(float x) { return sqr(sqr(sqr(x))); } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index bf74d44d9a7..da63d372669 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -22,7 +22,7 @@ namespace blender::gpu::shader { #ifndef GPU_SHADER_CREATE_INFO -/* Helps intelisense / auto-completion. */ +/* Helps intellisense / auto-completion. */ # define GPU_SHADER_INTERFACE_INFO(_interface, _inst_name) \ StageInterfaceInfo _interface(#_interface, _inst_name); \ _interface diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index 31f657a63b2..256702b60c5 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -175,7 +175,7 @@ static const char *to_string(const eGPUTextureFormat &type) case GPU_R16: return "r16"; default: - return "unkown"; + return "unknown"; } } diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 84cd8dce6cf..7b18d7659e2 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -383,7 +383,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr * since there is no convenient way to calculate partial RNA paths. * * \note While the path to the ID is typically sufficient to calculate the remainder of the path, - * in practice this would cause #WM_context_path_resolve_property_full to crate a path such as: + * in practice this would cause #WM_context_path_resolve_property_full to create a path such as: * `object.data.bones["Bones"].use_deform` such paths are not useful for key-shortcuts, * so this function supports returning data-paths directly to context members that aren't ID types. */ -- cgit v1.2.3 From 62a0984d7290cbd6fd3acfa79d53765f97f93b0a Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 11 Mar 2022 14:24:22 +0100 Subject: Cleanup: fix source typos homogenous->homogeneous Contributed by luzpaz. Differential Revision: https://developer.blender.org/D14306 --- intern/cycles/integrator/denoiser.cpp | 2 +- intern/cycles/scene/alembic_read.cpp | 8 ++++---- source/blender/blenlib/tests/BLI_task_test.cc | 2 +- source/blender/draw/engines/eevee/eevee_depth_of_field.c | 2 +- .../blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl | 2 +- source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl | 4 ++-- .../gpu/shaders/material/gpu_shader_material_tex_environment.glsl | 4 ++-- .../shaders/material/gpu_shader_material_texture_coordinates.glsl | 8 ++++---- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 0d3e49b7f21..62a6fd3361b 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -80,7 +80,7 @@ static bool is_single_supported_device(Device *device, DenoiserType type) if (!device->info.multi_devices.empty()) { /* Some configurations will use multi_devices, but keep the type of an individual device. - * This does simplify checks for homogenous setups, but here we really need a single device. */ + * This does simplify checks for homogeneous setups, but here we really need a single device. */ return false; } diff --git a/intern/cycles/scene/alembic_read.cpp b/intern/cycles/scene/alembic_read.cpp index be652e9bcd6..26a4153a825 100644 --- a/intern/cycles/scene/alembic_read.cpp +++ b/intern/cycles/scene/alembic_read.cpp @@ -543,7 +543,7 @@ static void read_subd_geometry(CachedData &cached_data, const SubDSchemaData &da add_positions(data.positions.getValue(iss), time, cached_data); - if (data.topology_variance != kHomogenousTopology || cached_data.shader.size() == 0) { + if (data.topology_variance != kHomogeneousTopology || cached_data.shader.size() == 0) { add_subd_polygons(cached_data, data, time); add_subd_edge_creases(cached_data, data, time); add_subd_vertex_creases(cached_data, data, time); @@ -582,7 +582,7 @@ static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &da array curve_first_key; array curve_shader; - const bool is_homogenous = data.topology_variance == kHomogenousTopology; + const bool is_homogeneous = data.topology_variance == kHomogeneousTopology; curve_keys.reserve(position->size()); curve_radius.reserve(position->size()); @@ -605,7 +605,7 @@ static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &da curve_radius.push_back_slow(radius * data.radius_scale); } - if (!is_homogenous || cached_data.curve_first_key.size() == 0) { + if (!is_homogeneous || cached_data.curve_first_key.size() == 0) { curve_first_key.push_back_reserved(offset); curve_shader.push_back_reserved(0); } @@ -616,7 +616,7 @@ static void read_curves_data(CachedData &cached_data, const CurvesSchemaData &da cached_data.curve_keys.add_data(curve_keys, time); cached_data.curve_radius.add_data(curve_radius, time); - if (!is_homogenous || cached_data.curve_first_key.size() == 0) { + if (!is_homogeneous || cached_data.curve_first_key.size() == 0) { cached_data.curve_first_key.add_data(curve_first_key, time); cached_data.curve_shader.add_data(curve_shader, time); } diff --git a/source/blender/blenlib/tests/BLI_task_test.cc b/source/blender/blenlib/tests/BLI_task_test.cc index 6a12ff10415..52852873a06 100644 --- a/source/blender/blenlib/tests/BLI_task_test.cc +++ b/source/blender/blenlib/tests/BLI_task_test.cc @@ -91,7 +91,7 @@ TEST(task, MempoolIter) int i; - /* 'Randomly' add and remove some items from mempool, to create a non-homogenous one. */ + /* 'Randomly' add and remove some items from mempool, to create a non-homogeneous one. */ int num_items = 0; for (i = 0; i < NUM_ITEMS; i++) { data[i] = (int *)BLI_mempool_alloc(mempool); diff --git a/source/blender/draw/engines/eevee/eevee_depth_of_field.c b/source/blender/draw/engines/eevee/eevee_depth_of_field.c index ef4d88bd521..0d14a0c5f61 100644 --- a/source/blender/draw/engines/eevee/eevee_depth_of_field.c +++ b/source/blender/draw/engines/eevee/eevee_depth_of_field.c @@ -62,7 +62,7 @@ static float circle_to_polygon_radius(float sides_count, float theta) cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI))); } -/* Remap input angle to have homogenous spacing of points along a polygon edge. +/* Remap input angle to have homogeneous spacing of points along a polygon edge. * Expect theta to be in [0..2pi] range. */ static float circle_to_polygon_angle(float sides_count, float theta) { diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl index 051a08d25e6..d724b4d4609 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_bokeh_frag.glsl @@ -35,7 +35,7 @@ float circle_to_polygon_radius(float sides_count, float theta) cos(theta - side_angle * floor((sides_count * theta + M_PI) / M_2PI)); } -/* Remap input angle to have homogenous spacing of points along a polygon edge. +/* Remap input angle to have homogeneous spacing of points along a polygon edge. * Expect theta to be in [0..2pi] range. */ float circle_to_polygon_angle(float sides_count, float theta) { diff --git a/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl index 9077b414026..7ab532ea0fb 100644 --- a/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/lookdev_world_frag.glsl @@ -16,9 +16,9 @@ out vec4 FragColor; vec3 background_transform_to_world(vec3 viewvec) { vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); + vec4 co_homogeneous = (ProjectionMatrixInverse * v); - vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); + vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0); return (ViewMatrixInverse * co).xyz; } diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl index 20a65f23c05..434e07e7b86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_environment.glsl @@ -4,9 +4,9 @@ void node_tex_environment_texco(vec3 viewvec, out vec3 worldvec) worldvec = worldPosition; #else vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(viewvec, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); + vec4 co_homogeneous = (ProjectionMatrixInverse * v); - vec3 co = co_homogenous.xyz / co_homogenous.w; + vec3 co = co_homogeneous.xyz / co_homogeneous.w; # if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) worldvec = mat3(ViewMatrixInverse) * co; # else diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl index 08d566224bf..a8ef9687b0a 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_texture_coordinates.glsl @@ -19,8 +19,8 @@ void generated_from_orco(vec3 orco, out vec3 generated) void generated_texco(vec3 I, vec3 attr_orco, out vec3 generated) { vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); - vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); + vec4 co_homogeneous = (ProjectionMatrixInverse * v); + vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0); co.xyz = normalize(co.xyz); #if defined(WORLD_BACKGROUND) || defined(PROBE_CAPTURE) generated = (ViewMatrixInverse * co).xyz; @@ -68,9 +68,9 @@ void node_tex_coord_background(vec3 I, out vec3 reflection) { vec4 v = (ProjectionMatrix[3][3] == 0.0) ? vec4(I, 1.0) : vec4(0.0, 0.0, 1.0, 1.0); - vec4 co_homogenous = (ProjectionMatrixInverse * v); + vec4 co_homogeneous = (ProjectionMatrixInverse * v); - vec4 co = vec4(co_homogenous.xyz / co_homogenous.w, 0.0); + vec4 co = vec4(co_homogeneous.xyz / co_homogeneous.w, 0.0); co = normalize(co); -- cgit v1.2.3 From 3da84d8b086bd2d09beb5bdb89ba6beddf1e1dd6 Mon Sep 17 00:00:00 2001 From: Hallam Roberts Date: Fri, 11 Mar 2022 16:14:05 +0100 Subject: Cleanup: use M_PI_2 and M_PI_4 where possible The constant M_PI_4 is added to GLSL to ensure it works there too. Differential Revision: https://developer.blender.org/D14288 --- intern/ghost/intern/GHOST_Wintab.cpp | 4 ++-- intern/iksolver/intern/IK_QJacobian.cpp | 2 +- source/blender/blenkernel/intern/armature_test.cc | 2 +- source/blender/blenkernel/intern/mask.c | 4 ++-- source/blender/blenkernel/intern/particle_child.c | 2 +- source/blender/blenlib/intern/math_base_inline.c | 8 ++++---- source/blender/blenlib/intern/math_vector.c | 4 ++-- source/blender/blenloader/intern/versioning_280.c | 2 +- source/blender/bmesh/tools/bmesh_bevel.c | 2 +- source/blender/draw/engines/eevee/eevee_lightprobes.c | 2 +- source/blender/draw/engines/eevee/eevee_lookdev.c | 2 +- .../draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl | 2 +- .../draw/engines/eevee/shaders/effect_translucency_frag.glsl | 2 +- source/blender/draw/engines/gpencil/gpencil_draw_data.c | 2 +- source/blender/draw/engines/workbench/workbench_effect_dof.c | 8 ++++---- source/blender/draw/intern/shaders/common_math_lib.glsl | 1 + source/blender/editors/curve/editcurve.c | 2 +- source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c | 2 +- source/blender/editors/space_view3d/view3d_navigate.c | 8 ++++---- source/blender/editors/uvedit/uvedit_parametrizer.c | 10 +++++----- source/blender/freestyle/intern/geometry/matrix_util.cpp | 2 +- source/blender/freestyle/intern/winged_edge/Curvature.cpp | 4 ++-- source/blender/freestyle/intern/winged_edge/WEdge.h | 4 ++-- source/blender/gpu/intern/gpu_immediate_util.c | 4 ++-- source/blender/io/collada/collada_internal.cpp | 4 ++-- source/blender/io/usd/intern/usd_capi_import.cc | 2 +- source/blender/makesdna/DNA_modifier_defaults.h | 2 +- source/blender/makesrna/intern/rna_brush.c | 2 +- source/blender/makesrna/intern/rna_camera.c | 4 ++-- source/blender/makesrna/intern/rna_cloth.c | 2 +- 30 files changed, 51 insertions(+), 50 deletions(-) diff --git a/intern/ghost/intern/GHOST_Wintab.cpp b/intern/ghost/intern/GHOST_Wintab.cpp index aaf9503b294..2547a38c0d1 100644 --- a/intern/ghost/intern/GHOST_Wintab.cpp +++ b/intern/ghost/intern/GHOST_Wintab.cpp @@ -326,7 +326,7 @@ void GHOST_Wintab::getInput(std::vector &outWintabInfo) ORIENTATION ort = pkt.pkOrientation; /* Convert raw fixed point data to radians. */ - float altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI / 2.0); + float altRad = (float)((fabs((float)ort.orAltitude) / (float)m_maxAltitude) * M_PI_2); float azmRad = (float)(((float)ort.orAzimuth / (float)m_maxAzimuth) * M_PI * 2.0); /* Find length of the stylus' projected vector on the XY plane. */ @@ -334,7 +334,7 @@ void GHOST_Wintab::getInput(std::vector &outWintabInfo) /* From there calculate X and Y components based on azimuth. */ out.tabletData.Xtilt = sin(azmRad) * vecLen; - out.tabletData.Ytilt = (float)(sin(M_PI / 2.0 - azmRad) * vecLen); + out.tabletData.Ytilt = (float)(sin(M_PI_2 - azmRad) * vecLen); } out.time = pkt.pkTime; diff --git a/intern/iksolver/intern/IK_QJacobian.cpp b/intern/iksolver/intern/IK_QJacobian.cpp index 39c0e6eb348..ea659414b73 100644 --- a/intern/iksolver/intern/IK_QJacobian.cpp +++ b/intern/iksolver/intern/IK_QJacobian.cpp @@ -193,7 +193,7 @@ void IK_QJacobian::InvertSDLS() // DLS. The SDLS damps individual singular values, instead of using a single // damping term. - double max_angle_change = M_PI / 4.0; + double max_angle_change = M_PI_4; double epsilon = 1e-10; int i, j; diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 7475e6f36d4..4d266c8493a 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -285,7 +285,7 @@ TEST(vec_roll_to_mat3_normalized, Roll1) const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, -0.788675129f}, {0.577350259f, 0.577350259f, 0.577350259f}, {0.788675129f, -0.577350259f, -0.211324856f}}; - test_vec_roll_to_mat3_normalized(input, float(M_PI * 0.5), expected_roll_mat); + test_vec_roll_to_mat3_normalized(input, float(M_PI_2), expected_roll_mat); } /** Test that the matrix is orthogonal for an input close to -Y. */ diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index da0f899001c..42e65a95404 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -609,7 +609,7 @@ float BKE_mask_spline_project_co(MaskSpline *spline, if (len_squared_v2(v1) > proj_eps_sq) { ang1 = angle_v2v2(v1, n1); - if (ang1 > (float)M_PI / 2.0f) { + if (ang1 > (float)M_PI_2) { ang1 = (float)M_PI - ang1; } @@ -635,7 +635,7 @@ float BKE_mask_spline_project_co(MaskSpline *spline, if (len_squared_v2(v2) > proj_eps_sq) { ang2 = angle_v2v2(v2, n2); - if (ang2 > (float)M_PI / 2.0f) { + if (ang2 > (float)M_PI_2) { ang2 = (float)M_PI - ang2; } diff --git a/source/blender/blenkernel/intern/particle_child.c b/source/blender/blenkernel/intern/particle_child.c index 7a2e4eb4ac9..8106ae8b302 100644 --- a/source/blender/blenkernel/intern/particle_child.c +++ b/source/blender/blenkernel/intern/particle_child.c @@ -85,7 +85,7 @@ static void do_kink_spiral_deform(ParticleKey *state, * and goes up to the Golden Spiral for 1.0 * https://en.wikipedia.org/wiki/Golden_spiral */ - const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI * 0.25f; + const float b = shape * (1.0f + sqrtf(5.0f)) / (float)M_PI_4; /* angle of the spiral against the curve (rotated opposite to make a smooth transition) */ const float start_angle = ((b != 0.0f) ? atanf(1.0f / b) : (float)-M_PI_2) + (b > 0.0f ? -(float)M_PI_2 : (float)M_PI_2); diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 90a91cbe26f..bb6bc0db00d 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -95,10 +95,10 @@ MINLINE float saacos(float fac) MINLINE float saasin(float fac) { if (UNLIKELY(fac <= -1.0f)) { - return (float)-M_PI / 2.0f; + return (float)-M_PI_2; } else if (UNLIKELY(fac >= 1.0f)) { - return (float)M_PI / 2.0f; + return (float)M_PI_2; } else { return asinf(fac); @@ -131,10 +131,10 @@ MINLINE float saacosf(float fac) MINLINE float saasinf(float fac) { if (UNLIKELY(fac <= -1.0f)) { - return (float)-M_PI / 2.0f; + return (float)-M_PI_2; } else if (UNLIKELY(fac >= 1.0f)) { - return (float)M_PI / 2.0f; + return (float)M_PI_2; } else { return asinf(fac); diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index e71b1150d8f..da6a6dff16f 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -289,7 +289,7 @@ void mid_v3_v3v3_angle_weighted(float r[3], const float a[3], const float b[3]) BLI_ASSERT_UNIT_V3(b); add_v3_v3v3(r, a, b); - angle = ((float)(1.0 / (M_PI / 2.0)) * + angle = ((float)M_2_PI * /* normally we would only multiply by 2, * but instead of an angle make this 0-1 factor */ 2.0f) * @@ -305,7 +305,7 @@ void mid_v3_angle_weighted(float r[3]) /* double check they are normalized */ BLI_assert(len_squared_v3(r) <= 1.0f + FLT_EPSILON); - angle = ((float)(1.0 / (M_PI / 2.0)) * + angle = ((float)M_2_PI * /* normally we would only multiply by 2, * but instead of an angle make this 0-1 factor */ 2.0f) * diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 3700ead92f0..b1c982649d2 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -4497,7 +4497,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) clmd->sim_parms->max_internal_tension = 15.0f; clmd->sim_parms->internal_compression = 15.0f; clmd->sim_parms->max_internal_compression = 15.0f; - clmd->sim_parms->internal_spring_max_diversion = M_PI / 4.0f; + clmd->sim_parms->internal_spring_max_diversion = M_PI_4; } } } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index a550b638e46..5790d76936f 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -7106,7 +7106,7 @@ static void find_even_superellipse_chords(int n, float r, double *xvals, double return; } if (r == PRO_CIRCLE_R) { - double temp = (M_PI / 2) / n; + double temp = M_PI_2 / n; /* Angle spacing. */ for (int i = 0; i <= n; i++) { xvals[i] = sin(i * temp); diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index a20fe156eda..7029d015b22 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -613,7 +613,7 @@ void EEVEE_lightprobes_planar_data_from_object(Object *ob, eplanar->clip_edge_y_neg = dot_v3v3(eplanar->clip_vec_y, vec); /* Facing factors */ - float max_angle = max_ff(1e-2f, 1.0f - probe->falloff) * M_PI * 0.5f; + float max_angle = max_ff(1e-2f, 1.0f - probe->falloff) * M_PI_2; float min_angle = 0.0f; eplanar->facing_scale = 1.0f / max_ff(1e-8f, cosf(min_angle) - cosf(max_angle)); eplanar->facing_bias = -min_ff(1.0f - 1e-8f, cosf(max_angle)) * eplanar->facing_scale; diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index 0ef78d97a79..874c2815b8b 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -217,7 +217,7 @@ void EEVEE_lookdev_cache_init(EEVEE_Data *vedata, float x_rot_matrix[3][3]; DRW_view_viewmat_get(NULL, view_matrix, false); copy_m3_m4(view_rot_matrix, view_matrix); - axis_angle_to_mat3_single(x_rot_matrix, 'X', M_PI / 2.0f); + axis_angle_to_mat3_single(x_rot_matrix, 'X', M_PI_2); mul_m3_m3m3(view_rot_matrix, x_rot_matrix, view_rot_matrix); mul_m3_m3m3(view_rot_matrix, g_data->studiolight_matrix, view_rot_matrix); copy_m3_m3(studiolight_matrix, view_rot_matrix); diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl index 59564890d7e..b05223e755d 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_vert.glsl @@ -103,7 +103,7 @@ void main() color4 = colors[3] * weights[3]; /* Extend to cover at least the unit circle */ - const float extend = (cos(M_PI / 4.0) + 1.0) * 2.0; + const float extend = (cos(M_PI_4) + 1.0) * 2.0; /* Crappy diagram * ex 1 * | \ diff --git a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl index ba90f5ae531..831ed0a119a 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_translucency_frag.glsl @@ -40,7 +40,7 @@ float light_translucent_power_with_falloff(LightData ld, vec3 N, vec4 l_vector) if (ld.l_type >= AREA_RECT) { power = (ld.l_sizex * ld.l_sizey * 4.0 * M_PI) * (1.0 / 80.0); if (ld.l_type == AREA_ELLIPSE) { - power *= M_PI * 0.25; + power *= M_PI_4; } power *= 0.3 * 20.0 * max(0.0, dot(-ld.l_forward, l_vector.xyz / l_vector.w)); /* XXX ad hoc, empirical */ diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 6c904745709..c3f96ecd0a2 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -387,7 +387,7 @@ void gpencil_light_pool_populate(GPENCIL_LightPool *lightpool, Object *ob) normalize_m4_m4(mat, ob->obmat); invert_m4(mat); gp_light->type = GP_LIGHT_TYPE_SPOT; - gp_light->spotsize = cosf(M_PI * 0.5f); + gp_light->spotsize = cosf(M_PI_2); gp_light->spotblend = (1.0f - gp_light->spotsize) * 1.0f; } else if (la->type == LA_SUN) { diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c index 6b64a9d7f09..58d49cf226e 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_dof.c +++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c @@ -31,22 +31,22 @@ static void square_to_circle(float x, float y, float *r, float *T) if (x > -y) { if (x > y) { *r = x; - *T = (M_PI / 4.0f) * (y / x); + *T = M_PI_4 * (y / x); } else { *r = y; - *T = (M_PI / 4.0f) * (2 - (x / y)); + *T = M_PI_4 * (2 - (x / y)); } } else { if (x < y) { *r = -x; - *T = (M_PI / 4.0f) * (4 + (y / x)); + *T = M_PI_4 * (4 + (y / x)); } else { *r = -y; if (y != 0) { - *T = (M_PI / 4.0f) * (6 - (x / y)); + *T = M_PI_4 * (6 - (x / y)); } else { *T = 0.0f; diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index eb7a98788c3..bc31649fd0f 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -6,6 +6,7 @@ #define M_PI 3.14159265358979323846 /* pi */ #define M_2PI 6.28318530717958647692 /* 2*pi */ #define M_PI_2 1.57079632679489661923 /* pi/2 */ +#define M_PI_4 0.78539816339744830962 /* pi/4 */ #define M_1_PI 0.318309886183790671538 /* 1/pi */ #define M_1_2PI 0.159154943091895335768 /* 1/(2*pi) */ #define M_1_PI2 0.101321183642337771443 /* 1/(pi^2) */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index f992309ed00..2dcddd01670 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -4906,7 +4906,7 @@ bool ed_editnurb_spin( copy_m3_m4(bmat, obedit->obmat); invert_m3_m3(imat, bmat); - axis_angle_to_mat3(cmat, axis, M_PI / 4.0); + axis_angle_to_mat3(cmat, axis, M_PI_4); mul_m3_m3m3(tmat, cmat, bmat); mul_m3_m3m3(rotmat, imat, tmat); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index 8d75e2aaf37..687f06c8dcf 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -44,7 +44,7 @@ # include "BKE_editmesh.h" #endif -static const float dial_angle_partial = M_PI / 2; +static const float dial_angle_partial = M_PI_2; static const float dial_angle_partial_margin = 0.92f; #define ORTHO_AXIS_OFFSET 2 diff --git a/source/blender/editors/space_view3d/view3d_navigate.c b/source/blender/editors/space_view3d/view3d_navigate.c index daf534ce0ba..50d7626a57d 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.c +++ b/source/blender/editors/space_view3d/view3d_navigate.c @@ -1153,16 +1153,16 @@ static int view_axis_exec(bContext *C, wmOperator *op) float quat_test[4]; if (viewnum == RV3D_VIEW_LEFT) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], -M_PI / 2.0f); + axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], -M_PI_2); } else if (viewnum == RV3D_VIEW_RIGHT) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], M_PI / 2.0f); + axis_angle_to_quat(quat_rotate, rv3d->viewinv[1], M_PI_2); } else if (viewnum == RV3D_VIEW_TOP) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], -M_PI / 2.0f); + axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], -M_PI_2); } else if (viewnum == RV3D_VIEW_BOTTOM) { - axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI / 2.0f); + axis_angle_to_quat(quat_rotate, rv3d->viewinv[0], M_PI_2); } else if (viewnum == RV3D_VIEW_FRONT) { unit_qt(quat_rotate); diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index f19b32e3ad4..3f7c7745bff 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -2059,7 +2059,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) a1 = a1 - M_PI / 3.0; a2 = a2 - M_PI / 3.0; a3 = a3 - M_PI / 3.0; - shapeold = (a1 * a1 + a2 * a2 + a3 * a3) / ((M_PI / 2) * (M_PI / 2)); + shapeold = (a1 * a1 + a2 * a2 + a3 * a3) / (M_PI_2 * M_PI_2); nshapeold++; } @@ -2068,7 +2068,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) a1 = a1 - M_PI / 3.0; a2 = a2 - M_PI / 3.0; a3 = a3 - M_PI / 3.0; - shapenew = (a1 * a1 + a2 * a2 + a3 * a3) / ((M_PI / 2) * (M_PI / 2)); + shapenew = (a1 * a1 + a2 * a2 + a3 * a3) / (M_PI_2 * M_PI_2); nshapenew++; } @@ -3730,7 +3730,7 @@ static float p_chart_minimum_area_angle(PChart *chart) minarea = 1e10; minangle = 0.0; - while (rotated <= (float)(M_PI / 2.0)) { /* INVESTIGATE: how far to rotate? */ + while (rotated <= (float)M_PI_2) { /* INVESTIGATE: how far to rotate? */ /* rotate with the smallest angle */ i_min = 0; mina = 1e10; @@ -3777,8 +3777,8 @@ static float p_chart_minimum_area_angle(PChart *chart) } /* try keeping rotation as small as possible */ - if (minangle > (float)(M_PI / 4)) { - minangle -= (float)(M_PI / 2.0); + if (minangle > (float)M_PI_4) { + minangle -= (float)M_PI_2; } MEM_freeN(angles); diff --git a/source/blender/freestyle/intern/geometry/matrix_util.cpp b/source/blender/freestyle/intern/geometry/matrix_util.cpp index ecf5443a957..35503a37685 100644 --- a/source/blender/freestyle/intern/geometry/matrix_util.cpp +++ b/source/blender/freestyle/intern/geometry/matrix_util.cpp @@ -106,7 +106,7 @@ void semi_definite_symmetric_eigen(const double *mat, int n, double *eigen_vec, delta = a_ll - a_mm; if (delta == 0.0) { - x = -M_PI / 4; + x = -M_PI_4; } else { x = -atan((a_lm + a_lm) / delta) / 2.0; diff --git a/source/blender/freestyle/intern/winged_edge/Curvature.cpp b/source/blender/freestyle/intern/winged_edge/Curvature.cpp index fba3110907a..9086593a945 100644 --- a/source/blender/freestyle/intern/winged_edge/Curvature.cpp +++ b/source/blender/freestyle/intern/winged_edge/Curvature.cpp @@ -474,10 +474,10 @@ inline static real angle(WOEdge *h) const Vec3r v = h->GetVec(); real sine = (n1 ^ n2) * v / v.norm(); if (sine >= 1.0) { - return M_PI / 2.0; + return M_PI_2; } if (sine <= -1.0) { - return -M_PI / 2.0; + return -M_PI_2; } return ::asin(sine); } diff --git a/source/blender/freestyle/intern/winged_edge/WEdge.h b/source/blender/freestyle/intern/winged_edge/WEdge.h index b77381bd4e5..6d39446056f 100644 --- a/source/blender/freestyle/intern/winged_edge/WEdge.h +++ b/source/blender/freestyle/intern/winged_edge/WEdge.h @@ -1400,11 +1400,11 @@ inline void WOEdge::setVecAndAngle() if (_paFace && _pbFace) { float sine = (_pbFace->GetNormal() ^ _paFace->GetNormal()) * _vec / _vec.norm(); if (sine >= 1.0) { - _angle = M_PI / 2.0; + _angle = M_PI_2; return; } if (sine <= -1.0) { - _angle = -M_PI / 2.0; + _angle = -M_PI_2; return; } _angle = ::asin(sine); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index b2fd60a62e5..cf8837ab26e 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -181,7 +181,7 @@ static void imm_draw_circle_partial(GPUPrimType prim_type, float sweep) { /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ - const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2); + const float angle_start = -(DEG2RADF(start)) + (float)M_PI_2; const float angle_end = -(DEG2RADF(sweep) - angle_start); nsegments += 1; immBegin(prim_type, nsegments); @@ -215,7 +215,7 @@ static void imm_draw_disk_partial(GPUPrimType prim_type, CLAMP(sweep, -max_angle, max_angle); /* shift & reverse angle, increase 'nsegments' to match gluPartialDisk */ - const float angle_start = -(DEG2RADF(start)) + (float)(M_PI / 2); + const float angle_start = -(DEG2RADF(start)) + (float)M_PI_2; const float angle_end = -(DEG2RADF(sweep) - angle_start); nsegments += 1; immBegin(prim_type, nsegments * 2); diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp index db27236b885..da9a4cd4a9b 100644 --- a/source/blender/io/collada/collada_internal.cpp +++ b/source/blender/io/collada/collada_internal.cpp @@ -14,8 +14,8 @@ UnitConverter::UnitConverter() : up_axis(COLLADAFW::FileInfo::Z_UP) { - axis_angle_to_mat4_single(x_up_mat4, 'Y', -0.5 * M_PI); - axis_angle_to_mat4_single(y_up_mat4, 'X', 0.5 * M_PI); + axis_angle_to_mat4_single(x_up_mat4, 'Y', -M_PI_2); + axis_angle_to_mat4_single(y_up_mat4, 'X', M_PI_2); unit_m4(z_up_mat4); unit_m4(scale_mat4); diff --git a/source/blender/io/usd/intern/usd_capi_import.cc b/source/blender/io/usd/intern/usd_capi_import.cc index 6186e2bb61f..49d5251198a 100644 --- a/source/blender/io/usd/intern/usd_capi_import.cc +++ b/source/blender/io/usd/intern/usd_capi_import.cc @@ -102,7 +102,7 @@ static void convert_to_z_up(pxr::UsdStageRefPtr stage, ImportSettings *r_setting /* Rotate 90 degrees about the X-axis. */ float rmat[3][3]; float axis[3] = {1.0f, 0.0f, 0.0f}; - axis_angle_normalized_to_mat3(rmat, axis, M_PI / 2.0f); + axis_angle_normalized_to_mat3(rmat, axis, M_PI_2); unit_m4(r_settings->conversion_mat); copy_m4_m3(r_settings->conversion_mat, rmat); diff --git a/source/blender/makesdna/DNA_modifier_defaults.h b/source/blender/makesdna/DNA_modifier_defaults.h index 41cc8048de1..299ac4e2fbe 100644 --- a/source/blender/makesdna/DNA_modifier_defaults.h +++ b/source/blender/makesdna/DNA_modifier_defaults.h @@ -145,7 +145,7 @@ .compression_damp = 5.0f, \ .shear_damp = 5.0f, \ .internal_spring_max_length = 0.0f, \ - .internal_spring_max_diversion = M_PI / 4.0f, \ + .internal_spring_max_diversion = M_PI_4, \ .vgroup_intern = 0, \ .internal_tension = 15.0f, \ .internal_compression = 15.0f, \ diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 39e1609e305..6052a742ad5 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2984,7 +2984,7 @@ static void rna_def_brush(BlenderRNA *brna) prop = RNA_def_property(srna, "falloff_angle", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "falloff_angle"); - RNA_def_property_range(prop, 0, M_PI / 2); + RNA_def_property_range(prop, 0, M_PI_2); RNA_def_property_ui_text( prop, "Falloff Angle", diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 23f6bb768d6..118589332ea 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -392,13 +392,13 @@ static void rna_def_camera_stereo_data(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); prop = RNA_def_property(srna, "pole_merge_angle_from", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_range(prop, 0.0f, M_PI / 2.0); + RNA_def_property_range(prop, 0.0f, M_PI_2); RNA_def_property_ui_text( prop, "Pole Merge Start Angle", "Angle at which interocular distance starts to fade to 0"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); prop = RNA_def_property(srna, "pole_merge_angle_to", PROP_FLOAT, PROP_ANGLE); - RNA_def_property_range(prop, 0.0f, M_PI / 2.0); + RNA_def_property_range(prop, 0.0f, M_PI_2); RNA_def_property_ui_text( prop, "Pole Merge End Angle", "Angle at which interocular distance is 0"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); diff --git a/source/blender/makesrna/intern/rna_cloth.c b/source/blender/makesrna/intern/rna_cloth.c index 4ee57017841..3ad901e5397 100644 --- a/source/blender/makesrna/intern/rna_cloth.c +++ b/source/blender/makesrna/intern/rna_cloth.c @@ -922,7 +922,7 @@ static void rna_def_cloth_sim_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "internal_spring_max_diversion", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "internal_spring_max_diversion"); - RNA_def_property_range(prop, 0.0f, M_PI / 4.0f); + RNA_def_property_range(prop, 0.0f, M_PI_4); RNA_def_property_ui_text(prop, "Internal Spring Max Diversion", "How much the rays used to connect the internal points can diverge " -- cgit v1.2.3 From 1842cef210a9cfd916044e1e5d7b68994af7806f Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 11 Mar 2022 14:39:14 +0100 Subject: Fix wrong line width when using Show Lines in Histogram scope By not resetting the line width, other scopes were using the wrong line thickness. Contributed by RedMser. Differential Revision: https://developer.blender.org/D12030 --- source/blender/editors/interface/interface_draw.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 9a51aa5cb9d..cb59680c062 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -411,6 +411,8 @@ static void histogram_draw_one(float r, immVertex2f(pos_attr, x2, y + (data[i] * h)); } immEnd(); + + GPU_line_width(1.0f); } else { /* under the curve */ -- cgit v1.2.3 From 3902bebf1809e4243b2184852aeb82e57ca68421 Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Fri, 11 Mar 2022 14:26:01 +0100 Subject: Cycles: make smart interpolation fallback to cubic for GPU Matching CPU and Eevee behavior. Differential Revision: https://developer.blender.org/D14296 --- intern/cycles/kernel/device/gpu/image.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/intern/cycles/kernel/device/gpu/image.h b/intern/cycles/kernel/device/gpu/image.h index 9c92f6a693c..83e7aa869c1 100644 --- a/intern/cycles/kernel/device/gpu/image.h +++ b/intern/cycles/kernel/device/gpu/image.h @@ -186,7 +186,7 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, flo const int texture_type = info.data_type; if (texture_type == IMAGE_DATA_TYPE_FLOAT4 || texture_type == IMAGE_DATA_TYPE_BYTE4 || texture_type == IMAGE_DATA_TYPE_HALF4 || texture_type == IMAGE_DATA_TYPE_USHORT4) { - if (info.interpolation == INTERPOLATION_CUBIC) { + if (info.interpolation == INTERPOLATION_CUBIC || info.interpolation == INTERPOLATION_SMART) { return kernel_tex_image_interp_bicubic(info, x, y); } else { @@ -198,7 +198,7 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, flo else { float f; - if (info.interpolation == INTERPOLATION_CUBIC) { + if (info.interpolation == INTERPOLATION_CUBIC || info.interpolation == INTERPOLATION_SMART) { f = kernel_tex_image_interp_bicubic(info, x, y); } else { @@ -241,7 +241,7 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, #endif if (texture_type == IMAGE_DATA_TYPE_FLOAT4 || texture_type == IMAGE_DATA_TYPE_BYTE4 || texture_type == IMAGE_DATA_TYPE_HALF4 || texture_type == IMAGE_DATA_TYPE_USHORT4) { - if (interpolation == INTERPOLATION_CUBIC) { + if (interpolation == INTERPOLATION_CUBIC || interpolation == INTERPOLATION_SMART) { return kernel_tex_image_interp_tricubic(info, x, y, z); } else { @@ -252,7 +252,7 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, else { float f; - if (interpolation == INTERPOLATION_CUBIC) { + if (interpolation == INTERPOLATION_CUBIC || interpolation == INTERPOLATION_SMART) { f = kernel_tex_image_interp_tricubic(info, x, y, z); } else { -- cgit v1.2.3 From 3b28c785d4e438428af3aa513d56f5fd394498e4 Mon Sep 17 00:00:00 2001 From: Ethan-Hall Date: Wed, 9 Mar 2022 17:00:22 +0100 Subject: Cycles: show viewport hair settings when using Cycles Before this patch, users had to switch render engines just to change how the hair should be displayed in solid and material preview viewport shading modes. Differential Revision: https://developer.blender.org/D14290 --- intern/cycles/blender/addon/ui.py | 9 +++++++++ release/scripts/startup/bl_ui/properties_render.py | 22 ++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 10a72c598e2..2b74a1b7ccf 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -11,6 +11,7 @@ from bl_ui.utils import PresetPanel from bpy.types import Panel from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel +from bl_ui.properties_render import draw_hair_settings from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel class CyclesPresetPanel(PresetPanel, Panel): @@ -355,6 +356,13 @@ class CYCLES_RENDER_PT_hair(CyclesButtonsPanel, Panel): if ccscene.shape == 'RIBBONS': col.prop(ccscene, "subdivisions", text="Curve Subdivisions") +class CYCLES_RENDER_PT_hair_viewport_display(CyclesButtonsPanel, Panel): + bl_label = "Viewport Display" + bl_parent_id = "CYCLES_RENDER_PT_hair" + bl_options = {'DEFAULT_CLOSED'} + + def draw(self, context): + draw_hair_settings(self, context) class CYCLES_RENDER_PT_volumes(CyclesButtonsPanel, Panel): bl_label = "Volumes" @@ -2153,6 +2161,7 @@ classes = ( CYCLES_RENDER_PT_volumes, CYCLES_RENDER_PT_subdivision, CYCLES_RENDER_PT_hair, + CYCLES_RENDER_PT_hair_viewport_display, CYCLES_RENDER_PT_simplify, CYCLES_RENDER_PT_simplify_viewport, CYCLES_RENDER_PT_simplify_render, diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index b7522cbb04e..2a7af8357e7 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -484,6 +484,18 @@ class RENDER_PT_eevee_film(RenderButtonsPanel, Panel): sub.prop(props, "overscan_size", text="") +def draw_hair_settings(self, context): + layout = self.layout + scene = context.scene + rd = scene.render + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + + layout.prop(rd, "hair_type", text="Shape", expand=True) + layout.prop(rd, "hair_subdiv") + + class RENDER_PT_eevee_hair(RenderButtonsPanel, Panel): bl_label = "Hair" bl_options = {'DEFAULT_CLOSED'} @@ -494,14 +506,7 @@ class RENDER_PT_eevee_hair(RenderButtonsPanel, Panel): return (context.engine in cls.COMPAT_ENGINES) def draw(self, context): - layout = self.layout - scene = context.scene - rd = scene.render - - layout.use_property_split = True - - layout.prop(rd, "hair_type", expand=True) - layout.prop(rd, "hair_subdiv") + draw_hair_settings(self, context) class RENDER_PT_eevee_performance(RenderButtonsPanel, Panel): @@ -519,6 +524,7 @@ class RENDER_PT_eevee_performance(RenderButtonsPanel, Panel): rd = scene.render layout.use_property_split = True + layout.use_property_decorate = False # No animation. layout.prop(rd, "use_high_quality_normals") -- cgit v1.2.3 From 22807d2075b0bcc7b5ae655ba43b08a4e44b002b Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 11 Mar 2022 11:34:03 -0600 Subject: Curves: Move constructor/assignment Add the ability to move `CurvesGeometry` without copying its attributes and data. The benefit is more intuitive management of the data-block copying, and less overhead for copying in some cases. The "moved-from" source is left in an empty but valid state. A test file is added to test the move constructor. --- source/blender/blenkernel/BKE_curves.hh | 2 + source/blender/blenkernel/CMakeLists.txt | 1 + .../blender/blenkernel/intern/curves_geometry.cc | 40 +++++++++++++ .../blenkernel/intern/curves_geometry_test.cc | 66 ++++++++++++++++++++++ source/blender/makesdna/DNA_curves_types.h | 4 +- 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 source/blender/blenkernel/intern/curves_geometry_test.cc diff --git a/source/blender/blenkernel/BKE_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 6cab9c8ff90..875cf48ed43 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -62,7 +62,9 @@ class CurvesGeometry : public ::CurvesGeometry { */ CurvesGeometry(int point_size, int curve_size); CurvesGeometry(const CurvesGeometry &other); + CurvesGeometry(CurvesGeometry &&other); CurvesGeometry &operator=(const CurvesGeometry &other); + CurvesGeometry &operator=(CurvesGeometry &&other); ~CurvesGeometry(); static CurvesGeometry &wrap(::CurvesGeometry &dna_struct) diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 3828d442f58..bd632380ada 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -807,6 +807,7 @@ if(WITH_GTESTS) intern/asset_test.cc intern/bpath_test.cc intern/cryptomatte_test.cc + intern/curves_geometry_test.cc intern/fcurve_test.cc intern/idprop_serialize_test.cc intern/image_partial_update_test.cc diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index afa0e6e452d..dd91e788e5a 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -84,6 +84,42 @@ CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other) return *this; } +/* The source should be empty, but in a valid state so that using it further will work. */ +static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src) +{ + dst.point_size = src.point_size; + std::swap(dst.point_data, src.point_data); + CustomData_free(&src.point_data, src.point_size); + src.point_size = 0; + + dst.curve_size = src.curve_size; + std::swap(dst.curve_data, dst.curve_data); + CustomData_free(&src.curve_data, src.curve_size); + src.curve_size = 0; + + std::swap(dst.curve_offsets, src.curve_offsets); + MEM_SAFE_FREE(src.curve_offsets); + + std::swap(dst.runtime, src.runtime); + + src.update_customdata_pointers(); + dst.update_customdata_pointers(); +} + +CurvesGeometry::CurvesGeometry(CurvesGeometry &&other) + : CurvesGeometry(other.point_size, other.curve_size) +{ + move_curves_geometry(*this, other); +} + +CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other) +{ + if (this != &other) { + move_curves_geometry(*this, other); + } + return *this; +} + CurvesGeometry::~CurvesGeometry() { CustomData_free(&this->point_data, this->point_size); @@ -124,6 +160,8 @@ int CurvesGeometry::evaluated_points_size() const IndexRange CurvesGeometry::range_for_curve(const int index) const { + BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[index]; const int offset_next = this->curve_offsets[index + 1]; return {offset, offset_next - offset}; @@ -131,6 +169,8 @@ IndexRange CurvesGeometry::range_for_curve(const int index) const IndexRange CurvesGeometry::range_for_curves(const IndexRange curves) const { + BLI_assert(this->curve_size > 0); + BLI_assert(this->curve_offsets != nullptr); const int offset = this->curve_offsets[curves.start()]; const int offset_next = this->curve_offsets[curves.one_after_last()]; return {offset, offset_next - offset}; diff --git a/source/blender/blenkernel/intern/curves_geometry_test.cc b/source/blender/blenkernel/intern/curves_geometry_test.cc new file mode 100644 index 00000000000..3a43c0c8102 --- /dev/null +++ b/source/blender/blenkernel/intern/curves_geometry_test.cc @@ -0,0 +1,66 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BKE_curves.hh" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +static CurvesGeometry create_basic_curves(const int points_size, const int curves_size) +{ + CurvesGeometry curves(points_size, curves_size); + + const int curve_length = points_size / curves_size; + for (const int i : curves.curves_range()) { + curves.offsets()[i] = points_size * curve_length; + } + curves.offsets().last() = points_size; + + for (const int i : curves.points_range()) { + curves.positions()[i] = {float(i), float(i % curve_length), 0.0f}; + } + + return curves; +} + +TEST(curves_geometry, Empty) +{ + CurvesGeometry empty(0, 0); + empty.cyclic(); + float3 min; + float3 max; + EXPECT_FALSE(empty.bounds_min_max(min, max)); +} + +TEST(curves_geometry, Move) +{ + CurvesGeometry curves = create_basic_curves(100, 10); + + const int *offsets_data = curves.offsets().data(); + const float3 *positions_data = curves.positions().data(); + + CurvesGeometry other = std::move(curves); + + /* The old curves should be empty, and the offsets are expected to be null. */ + EXPECT_EQ(curves.points_size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(curves.curve_offsets, nullptr); /* NOLINT: bugprone-use-after-move */ + + /* Just a basic check that the new curves work okay. */ + float3 min; + float3 max; + EXPECT_TRUE(other.bounds_min_max(min, max)); + + curves = std::move(other); + + CurvesGeometry second_other(std::move(curves)); + + /* The data should not have been reallocated ever. */ + EXPECT_EQ(second_other.positions().data(), positions_data); + EXPECT_EQ(second_other.offsets().data(), offsets_data); +} + +} // namespace blender::bke::tests diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index 98d2aa4b295..d3e69315265 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -69,7 +69,8 @@ typedef struct CurvesGeometry { /** * The start index of each curve in the point data. The size of each curve can be calculated by * subtracting the offset from the next offset. That is valid even for the last curve because - * this array is allocated with a length one larger than the number of splines. + * this array is allocated with a length one larger than the number of splines. This is allowed + * to be null when there are no curves. * * \note This is *not* stored in #CustomData because its size is one larger than #curve_data. */ @@ -77,6 +78,7 @@ typedef struct CurvesGeometry { /** * All attributes stored on control points (#ATTR_DOMAIN_POINT). + * This might not contain a layer for positions if there are no points. */ CustomData point_data; -- cgit v1.2.3 From bd9f94e91720e6d2ce5308344e83c5a787351669 Mon Sep 17 00:00:00 2001 From: Harley Acheson Date: Fri, 11 Mar 2022 10:04:24 -0800 Subject: Fix T94692: Show Cached OneDrive Thumbnails When OneDrive files are offline, show preexisting thumbnails. See D13930 for details. Differential Revision: https://developer.blender.org/D13930 Reviewed by Julian Eisel --- source/blender/editors/space_file/filelist.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index d47e67b9d27..363e19a8905 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -339,7 +339,7 @@ typedef struct FileListEntryPreview { char path[FILE_MAX]; uint flags; int index; - + int attributes; /* from FileDirEntry. */ int icon_id; } FileListEntryPreview; @@ -1623,8 +1623,10 @@ static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdat IMB_thumb_path_lock(preview->path); /* Always generate biggest preview size for now, it's simpler and avoids having to re-generate - * in case user switch to a bigger preview size. */ - ImBuf *imbuf = IMB_thumb_manage(preview->path, THB_LARGE, source); + * in case user switch to a bigger preview size. Do not create preview when file is offline. */ + ImBuf *imbuf = (preview->attributes & FILE_ATTR_OFFLINE) ? + IMB_thumb_read(preview->path, THB_LARGE) : + IMB_thumb_manage(preview->path, THB_LARGE, source); IMB_thumb_path_unlock(preview->path); if (imbuf) { preview->icon_id = BKE_icon_imbuf_create(imbuf); @@ -1704,11 +1706,6 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE); - if (!entry->preview_icon_id && (entry->attributes & FILE_ATTR_OFFLINE)) { - entry->flags |= FILE_ENTRY_INVALID_PREVIEW; - return; - } - if (entry->preview_icon_id) { return; } @@ -1735,6 +1732,7 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); preview->index = index; preview->flags = entry->typeflag; + preview->attributes = entry->attributes; preview->icon_id = 0; if (preview_in_memory) { -- cgit v1.2.3 From db7c4d7a1cf3a4aab7d40df9de5f3f3d33e88cb6 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 11 Mar 2022 14:55:42 -0600 Subject: Cleanup: Use new enum for NURBS curve knots modes Move the definition of the enum to `Curves` DNA, since the values will be saved in files, and ongoing development needs to use this. --- source/blender/blenkernel/BKE_spline.hh | 7 ------ source/blender/blenkernel/intern/curve_eval.cc | 15 ++++++------ source/blender/blenkernel/intern/spline_nurbs.cc | 10 ++++---- source/blender/makesdna/DNA_curves_types.h | 8 +++++++ .../legacy/node_geo_legacy_curve_spline_type.cc | 4 ++-- .../geometry/nodes/node_geo_curve_spline_type.cc | 28 +++++++++++----------- 6 files changed, 35 insertions(+), 37 deletions(-) diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index ed9b743b524..1e3134020c6 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -449,13 +449,6 @@ class BezierSpline final : public Spline { */ class NURBSpline final : public Spline { public: - enum class KnotsMode { - Normal, - EndPoint, - Bezier, - EndPointBezier, - }; - /** Method used to recalculate the knots vector when points are added or removed. */ KnotsMode knots_mode; diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 59c2155255b..191a510947e 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -203,21 +203,21 @@ static Spline::NormalCalculationMode normal_mode_from_dna_curve(const int twist_ return Spline::NormalCalculationMode::Minimum; } -static NURBSpline::KnotsMode knots_mode_from_dna_nurb(const short flag) +static KnotsMode knots_mode_from_dna_nurb(const short flag) { switch (flag & (CU_NURB_ENDPOINT | CU_NURB_BEZIER)) { case CU_NURB_ENDPOINT: - return NURBSpline::KnotsMode::EndPoint; + return NURBS_KNOT_MODE_ENDPOINT; case CU_NURB_BEZIER: - return NURBSpline::KnotsMode::Bezier; + return NURBS_KNOT_MODE_BEZIER; case CU_NURB_ENDPOINT | CU_NURB_BEZIER: - return NURBSpline::KnotsMode::EndPointBezier; + return NURBS_KNOT_MODE_ENDPOINT_BEZIER; default: - return NURBSpline::KnotsMode::Normal; + return NURBS_KNOT_MODE_NORMAL; } BLI_assert_unreachable(); - return NURBSpline::KnotsMode::Normal; + return NURBS_KNOT_MODE_NORMAL; } static SplinePtr spline_from_dna_bezier(const Nurb &nurb) @@ -421,8 +421,7 @@ std::unique_ptr curves_to_curve_eval(const Curves &curves) nurb_spline->resize(point_range.size()); nurb_spline->weights().copy_from(nurbs_weights.slice(point_range)); nurb_spline->set_order(nurbs_orders[curve_index]); - nurb_spline->knots_mode = static_cast( - nurbs_knots_modes[curve_index]); + nurb_spline->knots_mode = static_cast(nurbs_knots_modes[curve_index]); spline = std::move(nurb_spline); break; diff --git a/source/blender/blenkernel/intern/spline_nurbs.cc b/source/blender/blenkernel/intern/spline_nurbs.cc index 2146a4309ab..7dc5ac3ea12 100644 --- a/source/blender/blenkernel/intern/spline_nurbs.cc +++ b/source/blender/blenkernel/intern/spline_nurbs.cc @@ -142,8 +142,8 @@ bool NURBSpline::check_valid_size_and_order() const return false; } - if (ELEM(this->knots_mode, KnotsMode::Bezier, KnotsMode::EndPointBezier)) { - if (this->knots_mode == KnotsMode::Bezier && this->size() <= order_) { + if (ELEM(this->knots_mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER)) { + if (this->knots_mode == NURBS_KNOT_MODE_BEZIER && this->size() <= order_) { return false; } return (!is_cyclic_ || this->size() % (order_ - 1) == 0); @@ -162,10 +162,8 @@ void NURBSpline::calculate_knots() const { const KnotsMode mode = this->knots_mode; const int order = order_; - const bool is_bezier = ELEM( - mode, NURBSpline::KnotsMode::Bezier, NURBSpline::KnotsMode::EndPointBezier); - const bool is_end_point = ELEM( - mode, NURBSpline::KnotsMode::EndPoint, NURBSpline::KnotsMode::EndPointBezier); + const bool is_bezier = ELEM(mode, NURBS_KNOT_MODE_BEZIER, NURBS_KNOT_MODE_ENDPOINT_BEZIER); + const bool is_end_point = ELEM(mode, NURBS_KNOT_MODE_ENDPOINT, NURBS_KNOT_MODE_ENDPOINT_BEZIER); /* Inner knots are always repeated once except on Bezier case. */ const int repeat_inner = is_bezier ? order - 1 : 1; /* How many times to repeat 0.0 at the beginning of knot. */ diff --git a/source/blender/makesdna/DNA_curves_types.h b/source/blender/makesdna/DNA_curves_types.h index d3e69315265..f1626781fc6 100644 --- a/source/blender/makesdna/DNA_curves_types.h +++ b/source/blender/makesdna/DNA_curves_types.h @@ -40,6 +40,14 @@ typedef enum HandleType { BEZIER_HANDLE_ALIGN = 3, } HandleType; +/** Method used to calculate a NURBS curve's knot vector. */ +typedef enum KnotsMode { + NURBS_KNOT_MODE_NORMAL = 0, + NURBS_KNOT_MODE_ENDPOINT = 1, + NURBS_KNOT_MODE_BEZIER = 2, + NURBS_KNOT_MODE_ENDPOINT_BEZIER = 3, +} KnotsMode; + /** * A reusable data structure for geometry consisting of many curves. All control point data is * stored contiguously for better efficiency. Data for each curve is stored as a slice of the diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc index 4e3b0839da7..002c42c4c82 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_legacy_curve_spline_type.cc @@ -100,7 +100,7 @@ static SplinePtr poly_to_nurbs(const Spline &input) output->set_resolution(12); output->set_order(4); Spline::copy_base_settings(input, *output); - output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->knots_mode = NURBS_KNOT_MODE_BEZIER; output->attributes = input.attributes; return output; } @@ -128,7 +128,7 @@ static SplinePtr bezier_to_nurbs(const Spline &input) output->set_resolution(12); output->set_order(4); output->set_cyclic(input.is_cyclic()); - output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->knots_mode = NURBS_KNOT_MODE_BEZIER; output->attributes.reallocate(output->size()); copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 1b4429cc527..4118448b237 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -58,22 +58,22 @@ static void scale_output_assign(const Span input, template static void nurbs_to_bezier_assign(const Span input, const MutableSpan r_output, - const NURBSpline::KnotsMode knotsMode) + const KnotsMode knotsMode) { const int input_size = input.size(); const int output_size = r_output.size(); switch (knotsMode) { - case NURBSpline::KnotsMode::Bezier: + case NURBS_KNOT_MODE_BEZIER: scale_input_assign(input, 3, 1, r_output); break; - case NURBSpline::KnotsMode::Normal: + case NURBS_KNOT_MODE_NORMAL: for (const int i : IndexRange(output_size)) { r_output[i] = input[(i + 1) % input_size]; } break; - case NURBSpline::KnotsMode::EndPointBezier: - case NURBSpline::KnotsMode::EndPoint: + case NURBS_KNOT_MODE_ENDPOINT_BEZIER: + case NURBS_KNOT_MODE_ENDPOINT: for (const int i : IndexRange(1, output_size - 2)) { r_output[i] = input[i + 1]; } @@ -108,11 +108,11 @@ static void copy_attributes(const Spline &input_spline, Spline &output_spline, C } static Vector create_nurbs_to_bezier_handles(const Span nurbs_positions, - const NURBSpline::KnotsMode knots_mode) + const KnotsMode knots_mode) { const int nurbs_positions_size = nurbs_positions.size(); Vector handle_positions; - if (knots_mode == NURBSpline::KnotsMode::Bezier) { + if (knots_mode == NURBS_KNOT_MODE_BEZIER) { for (const int i : IndexRange(nurbs_positions_size)) { if (i % 3 == 1) { continue; @@ -128,7 +128,7 @@ static Vector create_nurbs_to_bezier_handles(const Span nurbs_po } } else { - const bool is_periodic = knots_mode == NURBSpline::KnotsMode::Normal; + const bool is_periodic = knots_mode == NURBS_KNOT_MODE_NORMAL; if (is_periodic) { handle_positions.append(nurbs_positions[1] + ((nurbs_positions[0] - nurbs_positions[1]) / 3)); @@ -170,9 +170,9 @@ static Vector create_nurbs_to_bezier_handles(const Span nurbs_po static Array create_nurbs_to_bezier_positions(const Span nurbs_positions, const Span handle_positions, - const NURBSpline::KnotsMode knots_mode) + const KnotsMode knots_mode) { - if (knots_mode == NURBSpline::KnotsMode::Bezier) { + if (knots_mode == NURBS_KNOT_MODE_BEZIER) { /* Every third NURBS position (starting from index 1) should be converted to Bezier position */ const int scale = 3; const int offset = 1; @@ -212,7 +212,7 @@ static SplinePtr poly_to_nurbs(const Spline &input) output->set_resolution(12); output->set_order(4); Spline::copy_base_settings(input, *output); - output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->knots_mode = NURBS_KNOT_MODE_BEZIER; output->attributes = input.attributes; return output; } @@ -240,7 +240,7 @@ static SplinePtr bezier_to_nurbs(const Spline &input) output->set_resolution(12); output->set_order(4); output->set_cyclic(input.is_cyclic()); - output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->knots_mode = NURBS_KNOT_MODE_BEZIER; output->attributes.reallocate(output->size()); copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { @@ -273,13 +273,13 @@ static SplinePtr nurbs_to_bezier(const Spline &input) const NURBSpline &nurbs_spline = static_cast(input); Span nurbs_positions; Vector nurbs_positions_vector; - NURBSpline::KnotsMode knots_mode; + KnotsMode knots_mode; if (nurbs_spline.is_cyclic()) { nurbs_positions_vector = nurbs_spline.positions(); nurbs_positions_vector.append(nurbs_spline.positions()[0]); nurbs_positions_vector.append(nurbs_spline.positions()[1]); nurbs_positions = nurbs_positions_vector; - knots_mode = NURBSpline::KnotsMode::Normal; + knots_mode = NURBS_KNOT_MODE_NORMAL; } else { nurbs_positions = nurbs_spline.positions(); -- cgit v1.2.3 From f19582ebf489c360f0f5fbad3cd5590d1e7ba462 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 11 Mar 2022 14:57:50 -0600 Subject: UI: Use property split in curves surface panel --- release/scripts/startup/bl_ui/properties_data_curves.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release/scripts/startup/bl_ui/properties_data_curves.py b/release/scripts/startup/bl_ui/properties_data_curves.py index 1bb5fc9afbe..4eefd5a0e0c 100644 --- a/release/scripts/startup/bl_ui/properties_data_curves.py +++ b/release/scripts/startup/bl_ui/properties_data_curves.py @@ -43,6 +43,8 @@ class DATA_PT_curves_surface(DataButtonsPanel, Panel): layout = self.layout ob = context.object + layout.use_property_split = True + layout.prop(ob.data, "surface") -- cgit v1.2.3 From 39c070ed8f7fb57bf1297177a391b853f33ba59d Mon Sep 17 00:00:00 2001 From: Dilith Jayakody Date: Fri, 11 Mar 2022 18:37:55 +0530 Subject: Fix T96229: GN Fillet Node unexpected limit radius behavior for 3 point splines This commit fixes T96229. The maximum possible radius was being used for 3 point splines, regardless of the current radius. Reviewed By: HooglyBoogly Maniphest Tasks: T96229 Differential Revision: https://developer.blender.org/D14311 --- source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc index 0b01efc4e9e..81ca87eec25 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -283,7 +283,7 @@ static void limit_radii(FilletData &fd, const bool cyclic) /* Assign the max_radii to the fillet data's radii. */ for (const int i : IndexRange(size)) { - radii[i] = max_radii[i]; + radii[i] = std::min(radii[i], max_radii[i]); } } -- cgit v1.2.3 From 8617ab153e25b264651353d82ede6849725ab5f1 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sat, 12 Mar 2022 10:33:32 +0100 Subject: Fix T96352: Gpencil crash using Normalize Thickness with Curves The stroke curve data could be NULL. --- source/blender/editors/gpencil/gpencil_edit.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 52ce7a7b8e1..47c84d168e9 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -5430,9 +5430,10 @@ static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) if (ED_gpencil_stroke_can_use(C, gps) == false) { continue; } - - bool selected = (is_curve_edit) ? gps->editcurve->flag |= GP_CURVE_SELECT : - (gps->flag & GP_STROKE_SELECT); + bool is_curve_ready = (gps->editcurve != NULL); + bool selected = (is_curve_edit && is_curve_ready) ? + gps->editcurve->flag |= GP_CURVE_SELECT : + (gps->flag & GP_STROKE_SELECT); if (!selected) { continue; } @@ -5445,7 +5446,7 @@ static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) } /* Loop all Polyline points. */ - if (!is_curve_edit) { + if (!is_curve_edit || !is_curve_ready) { for (int i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; if (mode == GP_NORMALIZE_THICKNESS) { -- cgit v1.2.3 From a2bb3c4cadceed828011e3141a20cec45726292f Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Sat, 12 Mar 2022 10:48:42 +0100 Subject: GPencil: Fix unreported select error in Normalize operator The curve selection was wrongly checked. --- source/blender/editors/gpencil/gpencil_edit.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 47c84d168e9..8506e90191f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -5432,7 +5432,7 @@ static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op) } bool is_curve_ready = (gps->editcurve != NULL); bool selected = (is_curve_edit && is_curve_ready) ? - gps->editcurve->flag |= GP_CURVE_SELECT : + (gps->editcurve->flag & GP_CURVE_SELECT) : (gps->flag & GP_STROKE_SELECT); if (!selected) { continue; -- cgit v1.2.3 From f6b73966101db0fef10abd7b3a18e4a1ca120013 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 12 Mar 2022 21:12:24 +1100 Subject: Fix transform gizmo check for shift to constrain to a plane The eventstate was checked instead of the current event being handled, causing the check to be invalid in some cases. --- source/blender/editors/transform/transform_gizmo_3d.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index f07fadb7fc1..5381785091a 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1824,7 +1824,7 @@ static void WIDGETGROUP_gizmo_draw_prepare(const bContext *C, wmGizmoGroup *gzgr static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, wmGizmoGroup *gzgroup, wmGizmo *gz, - const wmEvent *UNUSED(event)) + const wmEvent *event) { GizmoGroup *ggd = gzgroup->customdata; @@ -1867,9 +1867,8 @@ static void WIDGETGROUP_gizmo_invoke_prepare(const bContext *C, } if (axis != -1) { - wmWindow *win = CTX_wm_window(C); /* Swap single axis for two-axis constraint. */ - bool flip = (win->eventstate->modifier & KM_SHIFT) != 0; + const bool flip = (event->modifier & KM_SHIFT) != 0; BLI_assert(axis_idx != -1); const short axis_type = gizmo_get_axis_type(axis_idx); if (axis_type != MAN_AXES_ROTATE) { -- cgit v1.2.3 From 91dbc28363e5450f241eb9696aaca29075c01d20 Mon Sep 17 00:00:00 2001 From: Aaron Carlisle Date: Sun, 13 Mar 2022 00:49:41 -0500 Subject: Cleanup: clang format --- intern/cycles/integrator/denoiser.cpp | 3 ++- source/blender/editors/interface/interface_draw.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp index 62a6fd3361b..23ab825a4d2 100644 --- a/intern/cycles/integrator/denoiser.cpp +++ b/intern/cycles/integrator/denoiser.cpp @@ -80,7 +80,8 @@ static bool is_single_supported_device(Device *device, DenoiserType type) if (!device->info.multi_devices.empty()) { /* Some configurations will use multi_devices, but keep the type of an individual device. - * This does simplify checks for homogeneous setups, but here we really need a single device. */ + * This does simplify checks for homogeneous setups, but here we really need a single device. + */ return false; } diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index cb59680c062..decd8c03d70 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -411,7 +411,7 @@ static void histogram_draw_one(float r, immVertex2f(pos_attr, x2, y + (data[i] * h)); } immEnd(); - + GPU_line_width(1.0f); } else { -- cgit v1.2.3