diff --git a/source/blender/compositor/COM_context.hh b/source/blender/compositor/COM_context.hh index e193ece6190..f93b60d0a8f 100644 --- a/source/blender/compositor/COM_context.hh +++ b/source/blender/compositor/COM_context.hh @@ -66,7 +66,7 @@ class Context { virtual Bounds get_compositing_region() const = 0; /* Get the result where the result of the compositor should be written. */ - virtual Result get_output() = 0; + virtual Result get_output(Domain domain) = 0; /* Get the result where the result of the compositor viewer should be written, given the domain * of the result to be viewed, its precision, and whether the output is a non-color data image @@ -102,6 +102,12 @@ class Context { * or support for viewers. */ virtual bool treat_viewer_as_compositor_output() const; + /* True if the compositor input/output should use output region/bounds setup in the context. */ + virtual bool use_context_bounds_for_input_output() const + { + return true; + } + /* Populates the given meta data from the render stamp information of the given render pass. */ virtual void populate_meta_data_for_pass(const Scene *scene, int view_layer_id, diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc index 7fc38094854..c5964976c6d 100644 --- a/source/blender/draw/engines/compositor/compositor_engine.cc +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -110,7 +110,7 @@ class Context : public compositor::Context { .value_or(Bounds(int2(0))); } - compositor::Result get_output() override + compositor::Result get_output(compositor::Domain /*domain*/) override { compositor::Result result = this->create_result(compositor::ResultType::Color, compositor::ResultPrecision::Half); diff --git a/source/blender/nodes/composite/nodes/node_composite_group_input.cc b/source/blender/nodes/composite/nodes/node_composite_group_input.cc index 660691f21bd..8df3746c74c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_group_input.cc +++ b/source/blender/nodes/composite/nodes/node_composite_group_input.cc @@ -67,6 +67,7 @@ class GroupInputOperation : public NodeOperation { else { this->execute_pass_cpu(pass, result); } + result.set_transformation(pass.domain().transformation); } void execute_pass_gpu(const Result &pass, Result &result) @@ -125,9 +126,12 @@ class GroupInputOperation : public NodeOperation { * compositing region into an appropriately sized result. */ const int2 lower_bound = this->context().get_compositing_region().min; - result.allocate_texture(Domain(this->context().get_compositing_region_size())); + const int2 size = this->context().use_context_bounds_for_input_output() ? + this->context().get_compositing_region_size() : + pass.domain().size; + result.allocate_texture(size); - parallel_for(result.domain().size, [&](const int2 texel) { + parallel_for(size, [&](const int2 texel) { result.store_pixel_generic_type(texel, pass.load_pixel_generic_type(texel + lower_bound)); }); } diff --git a/source/blender/nodes/composite/nodes/node_composite_group_output.cc b/source/blender/nodes/composite/nodes/node_composite_group_output.cc index e1b4004968a..29a42915747 100644 --- a/source/blender/nodes/composite/nodes/node_composite_group_output.cc +++ b/source/blender/nodes/composite/nodes/node_composite_group_output.cc @@ -68,7 +68,7 @@ class GroupOutputOperation : public NodeOperation { float4 color = image.get_single_value(); const Domain domain = this->compute_domain(); - Result output = this->context().get_output(); + Result output = this->context().get_output(domain); if (this->context().use_gpu()) { GPU_texture_clear(output, GPU_DATA_FLOAT, color); } @@ -90,7 +90,7 @@ class GroupOutputOperation : public NodeOperation { void execute_copy_gpu(const Result &image) { const Domain domain = this->compute_domain(); - Result output = this->context().get_output(); + Result output = this->context().get_output(domain); gpu::Shader *shader = this->context().get_shader("compositor_write_output", output.precision()); @@ -114,9 +114,11 @@ class GroupOutputOperation : public NodeOperation { void execute_copy_cpu(const Result &image) { const Domain domain = this->compute_domain(); - Result output = this->context().get_output(); + Result output = this->context().get_output(domain); - const Bounds bounds = this->context().get_compositing_region(); + const Bounds bounds = this->context().use_context_bounds_for_input_output() ? + this->context().get_compositing_region() : + Bounds(int2(0, 0), domain.size); parallel_for(domain.size, [&](const int2 texel) { const int2 output_texel = texel + bounds.min; if (output_texel.x > bounds.max.x || output_texel.y > bounds.max.y) { @@ -130,7 +132,10 @@ class GroupOutputOperation : public NodeOperation { * applied. */ Domain compute_domain() override { - return Domain(this->context().get_compositing_region_size()); + if (this->context().use_context_bounds_for_input_output()) { + return Domain(this->context().get_compositing_region_size()); + } + return NodeOperation::compute_domain(); } }; diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index c222eed4d1d..1f02ba0e900 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -144,7 +144,9 @@ class ViewerOperation : public NodeOperation { { /* Viewers are treated as composite outputs that should be in the bounds of the compositing * region. */ - if (this->context().treat_viewer_as_compositor_output()) { + if (this->context().treat_viewer_as_compositor_output() && + this->context().use_context_bounds_for_input_output()) + { return this->context().get_compositing_region(); } @@ -156,7 +158,9 @@ class ViewerOperation : public NodeOperation { { /* Viewers are treated as composite outputs that should be in the domain of the compositing * region. */ - if (context().treat_viewer_as_compositor_output()) { + if (this->context().treat_viewer_as_compositor_output() && + this->context().use_context_bounds_for_input_output()) + { return Domain(context().get_compositing_region_size()); } diff --git a/source/blender/render/intern/compositor.cc b/source/blender/render/intern/compositor.cc index 6ebf20d1145..7d3aa9cb164 100644 --- a/source/blender/render/intern/compositor.cc +++ b/source/blender/render/intern/compositor.cc @@ -170,7 +170,7 @@ class Context : public compositor::Context { return Bounds(int2(0), this->get_render_size()); } - compositor::Result get_output() override + compositor::Result get_output(compositor::Domain /*domain*/) override { const int2 render_size = get_render_size(); if (output_result_.is_allocated()) { diff --git a/source/blender/sequencer/SEQ_modifier.hh b/source/blender/sequencer/SEQ_modifier.hh index a4b884e930a..030b627d258 100644 --- a/source/blender/sequencer/SEQ_modifier.hh +++ b/source/blender/sequencer/SEQ_modifier.hh @@ -55,10 +55,10 @@ struct StripModifierTypeInfo { /* copy data from one modifier to another */ void (*copy_data)(StripModifierData *smd, StripModifierData *target); - /* Apply modifier on an image buffer. - * quad contains four corners of the (pre-transform) strip rectangle in pixel space. */ + /* Apply modifier on an image buffer. */ void (*apply)(const RenderData *render_data, - const StripScreenQuad &quad, + const Strip *strip, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask); @@ -82,11 +82,6 @@ void modifier_clear(Strip *strip); void modifier_free(StripModifierData *smd); void modifier_unique_name(Strip *strip, StripModifierData *smd); StripModifierData *modifier_find_by_name(Strip *strip, const char *name); -void modifier_apply_stack(const RenderData *context, - SeqRenderState *state, - const Strip *strip, - ImBuf *ibuf, - int timeline_frame); StripModifierData *modifier_copy(Strip &strip_dst, StripModifierData *mod_src); void modifier_list_copy(Strip *strip_new, Strip *strip); int sequence_supports_modifiers(Strip *strip); diff --git a/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc b/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc index a0a108f78dd..a5c0c77fc71 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_brightness_contrast.cc @@ -29,27 +29,32 @@ struct BrightContrastApplyOp { float mul; float add; - template - void apply(ImageT *image, const MaskT *mask, IndexRange size) + template + void apply(ImageT *image, MaskSampler &mask, int image_x, IndexRange y_range) { - for ([[maybe_unused]] int64_t i : size) { - /* NOTE: arguably incorrect usage of "raw" values, should be un-premultiplied. - * Not changing behavior for now, but would be good to fix someday. */ - float4 input = load_pixel_raw(image); + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + /* NOTE: arguably incorrect usage of "raw" values, should be un-premultiplied. + * Not changing behavior for now, but would be good to fix someday. */ + float4 input = load_pixel_raw(image); - float4 result; - result = input * this->mul + this->add; - result.w = input.w; + float4 result; + result = input * this->mul + this->add; + result.w = input.w; - apply_and_advance_mask(input, result, mask); - store_pixel_raw(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_raw(result, image); + image += 4; + } } } }; static void brightcontrast_apply(const RenderData * /*render_data*/, - const StripScreenQuad & /*quad*/, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) @@ -76,7 +81,7 @@ static void brightcontrast_apply(const RenderData * /*render_data*/, op.add = op.mul * brightness + delta; } - apply_modifier_op(op, ibuf, mask); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); } static void brightcontrast_panel_draw(const bContext *C, Panel *panel) diff --git a/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc b/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc index 998d784204f..bbaffdc958a 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_color_balance.cc @@ -85,69 +85,79 @@ struct ColorBalanceApplyOp { float lut[3][CB_TABLE_SIZE]; /* Apply on a byte image via a table lookup. */ - template void apply(uchar *image, const MaskT *mask, IndexRange size) + template + void apply(uchar *image, MaskSampler &mask, int image_x, IndexRange y_range) { - for ([[maybe_unused]] int64_t i : size) { - float4 input = load_pixel_premul(image); + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = load_pixel_premul(image); - float4 result; - int p0 = int(input.x * (CB_TABLE_SIZE - 1.0f) + 0.5f); - int p1 = int(input.y * (CB_TABLE_SIZE - 1.0f) + 0.5f); - int p2 = int(input.z * (CB_TABLE_SIZE - 1.0f) + 0.5f); - result.x = this->lut[0][p0]; - result.y = this->lut[1][p1]; - result.z = this->lut[2][p2]; - result.w = input.w; + float4 result; + int p0 = int(input.x * (CB_TABLE_SIZE - 1.0f) + 0.5f); + int p1 = int(input.y * (CB_TABLE_SIZE - 1.0f) + 0.5f); + int p2 = int(input.z * (CB_TABLE_SIZE - 1.0f) + 0.5f); + result.x = this->lut[0][p0]; + result.y = this->lut[1][p1]; + result.z = this->lut[2][p2]; + result.w = input.w; - apply_and_advance_mask(input, result, mask); - store_pixel_premul(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_premul(result, image); + image += 4; + } } } /* Apply on a float image by doing full math. */ - template void apply(float *image, const MaskT *mask, IndexRange size) + template + void apply(float *image, MaskSampler &mask, int image_x, IndexRange y_range) { - if (this->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { - /* Lift/Gamma/Gain */ - for ([[maybe_unused]] int64_t i : size) { - float4 input = load_pixel_premul(image); + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + if (this->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + /* Lift/Gamma/Gain */ + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = load_pixel_premul(image); - float4 result; - result.x = color_balance_lgg( - input.x, this->lift.x, this->gain.x, this->gamma.x, this->multiplier); - result.y = color_balance_lgg( - input.y, this->lift.y, this->gain.y, this->gamma.y, this->multiplier); - result.z = color_balance_lgg( - input.z, this->lift.z, this->gain.z, this->gamma.z, this->multiplier); - result.w = input.w; + float4 result; + result.x = color_balance_lgg( + input.x, this->lift.x, this->gain.x, this->gamma.x, this->multiplier); + result.y = color_balance_lgg( + input.y, this->lift.y, this->gain.y, this->gamma.y, this->multiplier); + result.z = color_balance_lgg( + input.z, this->lift.z, this->gain.z, this->gamma.z, this->multiplier); + result.w = input.w; - apply_and_advance_mask(input, result, mask); - store_pixel_premul(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_premul(result, image); + image += 4; + } } - } - else if (this->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER) { - /* Slope/Offset/Power */ - for ([[maybe_unused]] int64_t i : size) { - float4 input = load_pixel_premul(image); + else if (this->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER) { + /* Slope/Offset/Power */ + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = load_pixel_premul(image); - float4 result; - result.x = color_balance_sop( - input.x, this->slope.x, this->offset.x, this->power.x, this->multiplier); - result.y = color_balance_sop( - input.y, this->slope.y, this->offset.y, this->power.y, this->multiplier); - result.z = color_balance_sop( - input.z, this->slope.z, this->offset.z, this->power.z, this->multiplier); - result.w = input.w; + float4 result; + result.x = color_balance_sop( + input.x, this->slope.x, this->offset.x, this->power.x, this->multiplier); + result.y = color_balance_sop( + input.y, this->slope.y, this->offset.y, this->power.y, this->multiplier); + result.z = color_balance_sop( + input.z, this->slope.z, this->offset.z, this->power.z, this->multiplier); + result.w = input.w; - apply_and_advance_mask(input, result, mask); - store_pixel_premul(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_premul(result, image); + image += 4; + } + } + else { + BLI_assert_unreachable(); } - } - else { - BLI_assert_unreachable(); } } @@ -245,7 +255,8 @@ static void colorBalance_init_data(StripModifierData *smd) } static void colorBalance_apply(const RenderData * /*render_data*/, - const StripScreenQuad & /*quad*/, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) @@ -254,7 +265,7 @@ static void colorBalance_apply(const RenderData * /*render_data*/, ColorBalanceApplyOp op; op.init(*cbmd, ibuf->byte_buffer.data != nullptr); - apply_modifier_op(op, ibuf, mask); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); } static void colorBalance_panel_draw(const bContext *C, Panel *panel) diff --git a/source/blender/sequencer/intern/modifiers/MOD_compositor.cc b/source/blender/sequencer/intern/modifiers/MOD_compositor.cc index 2a04a20ed77..1cca678ed6c 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_compositor.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_compositor.cc @@ -24,6 +24,7 @@ #include "SEQ_modifier.hh" #include "SEQ_modifiertypes.hh" #include "SEQ_render.hh" +#include "SEQ_transform.hh" #include "UI_interface.hh" #include "UI_interface_layout.hh" @@ -42,18 +43,26 @@ class CompositorContext : public compositor::Context { ImBuf *image_buffer_; ImBuf *mask_buffer_; + float3x3 xform_; public: CompositorContext(const RenderData &render_data, const SequencerCompositorModifierData *modifier_data, ImBuf *image_buffer, - ImBuf *mask_buffer) + ImBuf *mask_buffer, + const Strip *strip) : compositor::Context(), render_data_(render_data), modifier_data_(modifier_data), image_buffer_(image_buffer), - mask_buffer_(mask_buffer) + mask_buffer_(mask_buffer), + xform_(float3x3::identity()) { + if (mask_buffer) { + /* Note: do not use passed transform matrix since compositor coordinate + * space is not from the image corner, but rather centered on the image. */ + xform_ = math::invert(image_transform_matrix_get(render_data.scene, strip)); + } } const Scene &get_scene() const override @@ -80,24 +89,45 @@ class CompositorContext : public compositor::Context { return true; } + bool use_context_bounds_for_input_output() const override + { + return false; + } + Bounds get_compositing_region() const override { return Bounds(int2(0), int2(image_buffer_->x, image_buffer_->y)); } - compositor::Result get_output() override + compositor::Result get_output(compositor::Domain domain) override { compositor::Result result = this->create_result(compositor::ResultType::Color); + if (domain.size.x != image_buffer_->x || domain.size.y != image_buffer_->y) { + /* Output size is different (e.g. image is blurred with expanded bounds); + * need to allocate appropriately sized buffer. */ + IMB_free_all_data(image_buffer_); + image_buffer_->x = domain.size.x; + image_buffer_->y = domain.size.y; + IMB_alloc_float_pixels(image_buffer_, 4, false); + } result.wrap_external(image_buffer_->float_buffer.data, int2(image_buffer_->x, image_buffer_->y)); return result; } - compositor::Result get_viewer_output(compositor::Domain /*domain*/, + compositor::Result get_viewer_output(compositor::Domain domain, bool /*is_data*/, compositor::ResultPrecision /*precision*/) override { compositor::Result result = this->create_result(compositor::ResultType::Color); + if (domain.size.x != image_buffer_->x || domain.size.y != image_buffer_->y) { + /* Output size is different (e.g. image is blurred with expanded bounds); + * need to allocate appropriately sized buffer. */ + IMB_free_all_data(image_buffer_); + image_buffer_->x = domain.size.x; + image_buffer_->y = domain.size.y; + IMB_alloc_float_pixels(image_buffer_, 4, false); + } result.wrap_external(image_buffer_->float_buffer.data, int2(image_buffer_->x, image_buffer_->y)); return result; @@ -114,6 +144,7 @@ class CompositorContext : public compositor::Context { else if (name == "Mask" && mask_buffer_) { result.wrap_external(mask_buffer_->float_buffer.data, int2(mask_buffer_->x, mask_buffer_->y)); + result.set_transformation(xform_); } return result; @@ -169,7 +200,8 @@ static bool ensure_linear_float_buffer(ImBuf *ibuf) } static void compositor_modifier_apply(const RenderData *render_data, - const StripScreenQuad & /*quad*/, + const Strip *strip, + const float /*transform*/[3][3], StripModifierData *strip_modifier_data, ImBuf *image_buffer, ImBuf *mask) @@ -189,7 +221,7 @@ static void compositor_modifier_apply(const RenderData *render_data, const bool was_float_linear = ensure_linear_float_buffer(image_buffer); const bool was_byte = image_buffer->float_buffer.data == nullptr; - CompositorContext context(*render_data, modifier_data, image_buffer, linear_mask); + CompositorContext context(*render_data, modifier_data, image_buffer, linear_mask, strip); compositor::Evaluator evaluator(context); evaluator.evaluate(); diff --git a/source/blender/sequencer/intern/modifiers/MOD_curves.cc b/source/blender/sequencer/intern/modifiers/MOD_curves.cc index 4b7b5d835af..f2d9c942c23 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_curves.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_curves.cc @@ -48,25 +48,30 @@ static void curves_copy_data(StripModifierData *target, StripModifierData *smd) struct CurvesApplyOp { const CurveMapping *curve_mapping; - template - void apply(ImageT *image, const MaskT *mask, IndexRange size) + template + void apply(ImageT *image, MaskSampler &mask, int image_x, IndexRange y_range) { - for ([[maybe_unused]] int64_t i : size) { - float4 input = load_pixel_premul(image); + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = load_pixel_premul(image); - float4 result; - BKE_curvemapping_evaluate_premulRGBF(this->curve_mapping, result, input); - result.w = input.w; + float4 result; + BKE_curvemapping_evaluate_premulRGBF(this->curve_mapping, result, input); + result.w = input.w; - apply_and_advance_mask(input, result, mask); - store_pixel_premul(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_premul(result, image); + image += 4; + } } } }; static void curves_apply(const RenderData * /*render_data*/, - const StripScreenQuad & /*quad*/, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) @@ -83,7 +88,7 @@ static void curves_apply(const RenderData * /*render_data*/, CurvesApplyOp op; op.curve_mapping = &cmd->curve_mapping; - apply_modifier_op(op, ibuf, mask); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); BKE_curvemapping_premultiply(&cmd->curve_mapping, true); } diff --git a/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc b/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc index 88687d52621..4d4a506f8c1 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_hue_correct.cc @@ -61,47 +61,52 @@ static void hue_correct_copy_data(StripModifierData *target, StripModifierData * struct HueCorrectApplyOp { const CurveMapping *curve_mapping; - template - void apply(ImageT *image, const MaskT *mask, IndexRange size) + template + void apply(ImageT *image, MaskSampler &mask, int image_x, IndexRange y_range) { - for ([[maybe_unused]] int64_t i : size) { - /* NOTE: arguably incorrect usage of "raw" values, should be un-premultiplied. - * Not changing behavior for now, but would be good to fix someday. */ - float4 input = load_pixel_raw(image); - float4 result; - result.w = input.w; + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + /* NOTE: arguably incorrect usage of "raw" values, should be un-premultiplied. + * Not changing behavior for now, but would be good to fix someday. */ + float4 input = load_pixel_raw(image); + float4 result; + result.w = input.w; - float3 hsv; - rgb_to_hsv(input.x, input.y, input.z, &hsv.x, &hsv.y, &hsv.z); + float3 hsv; + rgb_to_hsv(input.x, input.y, input.z, &hsv.x, &hsv.y, &hsv.z); - /* adjust hue, scaling returned default 0.5 up to 1 */ - float f; - f = BKE_curvemapping_evaluateF(this->curve_mapping, 0, hsv.x); - hsv.x += f - 0.5f; + /* adjust hue, scaling returned default 0.5 up to 1 */ + float f; + f = BKE_curvemapping_evaluateF(this->curve_mapping, 0, hsv.x); + hsv.x += f - 0.5f; - /* adjust saturation, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->curve_mapping, 1, hsv.x); - hsv.y *= (f * 2.0f); + /* adjust saturation, scaling returned default 0.5 up to 1 */ + f = BKE_curvemapping_evaluateF(this->curve_mapping, 1, hsv.x); + hsv.y *= (f * 2.0f); - /* adjust value, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->curve_mapping, 2, hsv.x); - hsv.z *= (f * 2.0f); + /* adjust value, scaling returned default 0.5 up to 1 */ + f = BKE_curvemapping_evaluateF(this->curve_mapping, 2, hsv.x); + hsv.z *= (f * 2.0f); - hsv.x = hsv.x - floorf(hsv.x); /* mod 1.0 */ - hsv.y = math::clamp(hsv.y, 0.0f, 1.0f); + hsv.x = hsv.x - floorf(hsv.x); /* mod 1.0 */ + hsv.y = math::clamp(hsv.y, 0.0f, 1.0f); - /* convert back to rgb */ - hsv_to_rgb(hsv.x, hsv.y, hsv.z, &result.x, &result.y, &result.z); + /* convert back to rgb */ + hsv_to_rgb(hsv.x, hsv.y, hsv.z, &result.x, &result.y, &result.z); - apply_and_advance_mask(input, result, mask); - store_pixel_raw(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_raw(result, image); + image += 4; + } } } }; static void hue_correct_apply(const RenderData * /*render_data*/, - const StripScreenQuad & /*quad*/, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) @@ -112,7 +117,7 @@ static void hue_correct_apply(const RenderData * /*render_data*/, HueCorrectApplyOp op; op.curve_mapping = &hcmd->curve_mapping; - apply_modifier_op(op, ibuf, mask); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); } static void hue_correct_panel_draw(const bContext *C, Panel *panel) diff --git a/source/blender/sequencer/intern/modifiers/MOD_mask.cc b/source/blender/sequencer/intern/modifiers/MOD_mask.cc index 2474fe626f0..0f94c4abb95 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_mask.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_mask.cc @@ -7,6 +7,7 @@ */ #include "BLI_math_base.h" +#include "BLI_math_matrix.hh" #include "BLT_translation.hh" @@ -14,6 +15,8 @@ #include "DNA_sequence_types.h" #include "SEQ_modifier.hh" +#include "SEQ_render.hh" +#include "SEQ_transform.hh" #include "UI_interface.hh" #include "UI_interface_layout.hh" @@ -22,49 +25,37 @@ namespace blender::seq { -static float load_mask_min(const uchar *&mask) -{ - float m = float(min_iii(mask[0], mask[1], mask[2])) * (1.0f / 255.0f); - mask += 4; - return m; -} -static float load_mask_min(const float *&mask) -{ - float m = min_fff(mask[0], mask[1], mask[2]); - mask += 4; - return m; -} -static float load_mask_min(const void *& /*mask*/) -{ - return 1.0f; -} - struct MaskApplyOp { - template - void apply(ImageT *image, const MaskT *mask, IndexRange size) + template + void apply(ImageT *image, MaskSampler &mask, int image_x, IndexRange y_range) { - for ([[maybe_unused]] int64_t i : size) { - float m = load_mask_min(mask); + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float m = mask.load_mask_min(); - if constexpr (std::is_same_v) { - /* Byte buffer is straight, so only affect on alpha itself, this is - * the only way to alpha-over byte strip after applying mask modifier. */ - image[3] = uchar(image[3] * m); + if constexpr (std::is_same_v) { + /* Byte buffer is straight, so only affect on alpha itself, this is + * the only way to alpha-over byte strip after applying mask modifier. */ + image[3] = uchar(image[3] * m); + } + else if constexpr (std::is_same_v) { + /* Float buffers are premultiplied, so need to premul color as well to make it + * easy to alpha-over masked strip. */ + float4 pix(image); + pix *= m; + *reinterpret_cast(image) = pix; + } + image += 4; } - else if constexpr (std::is_same_v) { - /* Float buffers are premultiplied, so need to premul color as well to make it - * easy to alpha-over masked strip. */ - float4 pix(image); - pix *= m; - *reinterpret_cast(image) = pix; - } - image += 4; } } }; -static void maskmodifier_apply(const RenderData * /*render_data*/, - const StripScreenQuad & /*quad*/, +static void maskmodifier_apply(const RenderData * /* render_data */, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData * /*smd*/, ImBuf *ibuf, ImBuf *mask) @@ -75,7 +66,7 @@ static void maskmodifier_apply(const RenderData * /*render_data*/, } MaskApplyOp op; - apply_modifier_op(op, ibuf, mask); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); /* Image has gained transparency. */ ibuf->planes = R_IMF_PLANES_RGBA; diff --git a/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc b/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc index f4660f9d5eb..878df639f7e 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_tonemap.cc @@ -7,7 +7,6 @@ */ #include "BLI_array.hh" -#include "BLI_math_geom.h" #include "BLT_translation.hh" @@ -76,14 +75,6 @@ static void pixels_to_scene_linear_byte(const ColorSpace *colorspace, (float *)dst, int(count), 1, 4, colorspace, false); } -static void scene_linear_to_image_chunk_float(ImBuf *ibuf, IndexRange range) -{ - const ColorSpace *colorspace = ibuf->float_buffer.colorspace; - float4 *fptr = reinterpret_cast(ibuf->float_buffer.data); - IMB_colormanagement_scene_linear_to_colorspace( - (float *)(fptr + range.first()), int(range.size()), 1, 4, colorspace); -} - static void scene_linear_to_image_chunk_byte(float4 *src, ImBuf *ibuf, IndexRange range) { const ColorSpace *colorspace = ibuf->byte_buffer.colorspace; @@ -97,104 +88,6 @@ static void scene_linear_to_image_chunk_byte(float4 *src, ImBuf *ibuf, IndexRang } } -static void tonemap_simple(float4 *scene_linear, - ImBuf *mask, - IndexRange range, - const AvgLogLum &avg) -{ - const float4 *mask_float = mask != nullptr ? (const float4 *)mask->float_buffer.data : nullptr; - const uchar4 *mask_byte = mask != nullptr ? (const uchar4 *)mask->byte_buffer.data : nullptr; - - int64_t index = 0; - for (const int64_t pixel_index : range) { - float4 input = scene_linear[index]; - - /* Apply correction. */ - float3 pixel = input.xyz() * avg.al; - float3 d = pixel + avg.tmmd->offset; - pixel.x /= (d.x == 0.0f) ? 1.0f : d.x; - pixel.y /= (d.y == 0.0f) ? 1.0f : d.y; - pixel.z /= (d.z == 0.0f) ? 1.0f : d.z; - const float igm = avg.igm; - if (igm != 0.0f) { - pixel.x = powf(math::max(pixel.x, 0.0f), igm); - pixel.y = powf(math::max(pixel.y, 0.0f), igm); - pixel.z = powf(math::max(pixel.z, 0.0f), igm); - } - - /* Apply mask. */ - if (mask != nullptr) { - float3 msk(1.0f); - if (mask_byte != nullptr) { - rgb_uchar_to_float(msk, mask_byte[pixel_index]); - } - else if (mask_float != nullptr) { - msk = mask_float[pixel_index].xyz(); - } - pixel = math::interpolate(input.xyz(), pixel, msk); - } - - scene_linear[index] = float4(pixel.x, pixel.y, pixel.z, input.w); - index++; - } -} - -static void tonemap_rd_photoreceptor(float4 *scene_linear, - ImBuf *mask, - IndexRange range, - const AvgLogLum &avg) -{ - const float4 *mask_float = mask != nullptr ? (const float4 *)mask->float_buffer.data : nullptr; - const uchar4 *mask_byte = mask != nullptr ? (const uchar4 *)mask->byte_buffer.data : nullptr; - - const float f = expf(-avg.tmmd->intensity); - const float m = (avg.tmmd->contrast > 0.0f) ? avg.tmmd->contrast : - (0.3f + 0.7f * powf(avg.auto_key, 1.4f)); - const float ic = 1.0f - avg.tmmd->correction, ia = 1.0f - avg.tmmd->adaptation; - - int64_t index = 0; - for (const int64_t pixel_index : range) { - float4 input = scene_linear[index]; - - /* Apply correction. */ - float3 pixel = input.xyz(); - const float L = IMB_colormanagement_get_luminance(pixel); - float I_l = pixel.x + ic * (L - pixel.x); - float I_g = avg.cav.x + ic * (avg.lav - avg.cav.x); - float I_a = I_l + ia * (I_g - I_l); - pixel.x /= std::max(pixel.x + powf(f * I_a, m), 1.0e-30f); - I_l = pixel.y + ic * (L - pixel.y); - I_g = avg.cav.y + ic * (avg.lav - avg.cav.y); - I_a = I_l + ia * (I_g - I_l); - pixel.y /= std::max(pixel.y + powf(f * I_a, m), 1.0e-30f); - I_l = pixel.z + ic * (L - pixel.z); - I_g = avg.cav.z + ic * (avg.lav - avg.cav.z); - I_a = I_l + ia * (I_g - I_l); - pixel.z /= std::max(pixel.z + powf(f * I_a, m), 1.0e-30f); - - /* Apply mask. */ - if (mask != nullptr) { - float3 msk(1.0f); - if (mask_byte != nullptr) { - rgb_uchar_to_float(msk, mask_byte[pixel_index]); - } - else if (mask_float != nullptr) { - msk = mask_float[pixel_index].xyz(); - } - pixel = math::interpolate(input.xyz(), pixel, msk); - } - - scene_linear[index] = float4(pixel.x, pixel.y, pixel.z, input.w); - index++; - } -} - -static bool is_point_inside_quad(const StripScreenQuad &quad, int x, int y) -{ - float2 pt(x + 0.5f, y + 0.5f); - return isect_point_quad_v2(pt, quad.v0, quad.v1, quad.v2, quad.v3); -} - struct AreaLuminance { int64_t pixel_count = 0; double sum = 0.0f; @@ -204,41 +97,150 @@ struct AreaLuminance { float max = -FLT_MAX; }; -static void tonemap_calc_chunk_luminance(const StripScreenQuad &quad, - const bool all_pixels_inside_quad, - const int width, - const IndexRange y_range, - const float4 *scene_linear, - AreaLuminance &r_lum) +static void scene_linear_to_image_chunk_float(ImBuf *ibuf, IndexRange range) { - for (const int y : y_range) { - for (int x = 0; x < width; x++) { - if (all_pixels_inside_quad || is_point_inside_quad(quad, x, y)) { - float4 pixel = *scene_linear; - r_lum.pixel_count++; - float L = IMB_colormanagement_get_luminance(pixel); - r_lum.sum += L; - r_lum.color_sum.x += pixel.x; - r_lum.color_sum.y += pixel.y; - r_lum.color_sum.z += pixel.z; - r_lum.log_sum += logf(math::max(L, 0.0f) + 1e-5f); - r_lum.max = math::max(r_lum.max, L); - r_lum.min = math::min(r_lum.min, L); + const ColorSpace *colorspace = ibuf->float_buffer.colorspace; + float4 *fptr = reinterpret_cast(ibuf->float_buffer.data); + IMB_colormanagement_scene_linear_to_colorspace( + (float *)(fptr + range.first()), int(range.size()), 1, 4, colorspace); +} + +template +static void tonemap_simple( + float4 *scene_linear, MaskSampler &mask, int image_x, IndexRange y_range, const AvgLogLum &avg) +{ + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = *scene_linear; + + /* Apply correction. */ + float3 pixel = input.xyz() * avg.al; + float3 d = pixel + avg.tmmd->offset; + pixel.x /= (d.x == 0.0f) ? 1.0f : d.x; + pixel.y /= (d.y == 0.0f) ? 1.0f : d.y; + pixel.z /= (d.z == 0.0f) ? 1.0f : d.z; + const float igm = avg.igm; + if (igm != 0.0f) { + pixel.x = powf(math::max(pixel.x, 0.0f), igm); + pixel.y = powf(math::max(pixel.y, 0.0f), igm); + pixel.z = powf(math::max(pixel.z, 0.0f), igm); } + + /* Apply mask. */ + float4 result(pixel.x, pixel.y, pixel.z, input.w); + mask.apply_mask(input, result); + *scene_linear = result; scene_linear++; } } } -static AreaLuminance tonemap_calc_input_luminance(const StripScreenQuad &quad, const ImBuf *ibuf) +template +static void tonemap_rd_photoreceptor( + float4 *scene_linear, MaskSampler &mask, int image_x, IndexRange y_range, const AvgLogLum &avg) { - /* Pixels outside the pre-transform strip area are ignored for luminance calculations. - * If strip area covers whole image, we can trivially accept all pixels. */ - const bool all_pixels_inside_quad = is_point_inside_quad(quad, 0, 0) && - is_point_inside_quad(quad, ibuf->x - 1, 0) && - is_point_inside_quad(quad, 0, ibuf->y - 1) && - is_point_inside_quad(quad, ibuf->x - 1, ibuf->y - 1); + const float f = expf(-avg.tmmd->intensity); + const float m = (avg.tmmd->contrast > 0.0f) ? avg.tmmd->contrast : + (0.3f + 0.7f * powf(avg.auto_key, 1.4f)); + const float ic = 1.0f - avg.tmmd->correction, ia = 1.0f - avg.tmmd->adaptation; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = *scene_linear; + + /* Apply correction. */ + float3 pixel = input.xyz(); + const float L = IMB_colormanagement_get_luminance(pixel); + float I_l = pixel.x + ic * (L - pixel.x); + float I_g = avg.cav.x + ic * (avg.lav - avg.cav.x); + float I_a = I_l + ia * (I_g - I_l); + pixel.x /= std::max(pixel.x + powf(f * I_a, m), 1.0e-30f); + I_l = pixel.y + ic * (L - pixel.y); + I_g = avg.cav.y + ic * (avg.lav - avg.cav.y); + I_a = I_l + ia * (I_g - I_l); + pixel.y /= std::max(pixel.y + powf(f * I_a, m), 1.0e-30f); + I_l = pixel.z + ic * (L - pixel.z); + I_g = avg.cav.z + ic * (avg.lav - avg.cav.z); + I_a = I_l + ia * (I_g - I_l); + pixel.z /= std::max(pixel.z + powf(f * I_a, m), 1.0e-30f); + + /* Apply mask. */ + float4 result(pixel.x, pixel.y, pixel.z, input.w); + mask.apply_mask(input, result); + *scene_linear = result; + scene_linear++; + } + } +} + +struct TonemapApplyOp { + AreaLuminance lum; + AvgLogLum data; + eModTonemapType type; + ImBuf *ibuf; + + template + void apply(ImageT *image, MaskSampler &mask, int image_x, IndexRange y_range) + { + const IndexRange pixel_range(y_range.first() * image_x, y_range.size() * image_x); + if constexpr (std::is_same_v) { + /* Float pixels: no need for temporary storage. Luminance calculation already converted + * data to scene linear. */ + float4 *pixels = (float4 *)(image + y_range.first() * image_x * 4); + if (this->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) { + tonemap_rd_photoreceptor(pixels, mask, image_x, y_range, data); + } + else { + BLI_assert(this->type == SEQ_TONEMAP_RH_SIMPLE); + tonemap_simple(pixels, mask, image_x, y_range, data); + } + scene_linear_to_image_chunk_float(this->ibuf, pixel_range); + } + else { + /* Byte pixels: temporary storage for scene linear pixel values. */ + Array scene_linear(pixel_range.size()); + pixels_to_scene_linear_byte(ibuf->byte_buffer.colorspace, + ibuf->byte_buffer.data + pixel_range.first() * 4, + scene_linear.data(), + pixel_range.size()); + if (this->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) { + tonemap_rd_photoreceptor(scene_linear.data(), mask, image_x, y_range, data); + } + else { + BLI_assert(this->type == SEQ_TONEMAP_RH_SIMPLE); + tonemap_simple(scene_linear.data(), mask, image_x, y_range, data); + } + scene_linear_to_image_chunk_byte(scene_linear.data(), this->ibuf, pixel_range); + } + } +}; + +static void tonemap_calc_chunk_luminance(const int width, + const IndexRange y_range, + const float4 *scene_linear, + AreaLuminance &r_lum) +{ + for ([[maybe_unused]] const int y : y_range) { + for (int x = 0; x < width; x++) { + float4 pixel = *scene_linear; + r_lum.pixel_count++; + float L = IMB_colormanagement_get_luminance(pixel); + r_lum.sum += L; + r_lum.color_sum.x += pixel.x; + r_lum.color_sum.y += pixel.y; + r_lum.color_sum.z += pixel.z; + r_lum.log_sum += logf(math::max(L, 0.0f) + 1e-5f); + r_lum.max = math::max(r_lum.max, L); + r_lum.min = math::min(r_lum.min, L); + scene_linear++; + } + } +} + +static AreaLuminance tonemap_calc_input_luminance(const ImBuf *ibuf) +{ AreaLuminance lum; lum = threading::parallel_reduce( IndexRange(ibuf->y), @@ -254,15 +256,14 @@ static AreaLuminance tonemap_calc_input_luminance(const StripScreenQuad &quad, c float4 *fptr = reinterpret_cast(ibuf->float_buffer.data); fptr += y_range.first() * ibuf->x; pixels_to_scene_linear_float(ibuf->float_buffer.colorspace, fptr, chunk_size); - tonemap_calc_chunk_luminance(quad, all_pixels_inside_quad, ibuf->x, y_range, fptr, lum); + tonemap_calc_chunk_luminance(ibuf->x, y_range, fptr, lum); } else { const uchar *bptr = ibuf->byte_buffer.data + y_range.first() * ibuf->x * 4; Array scene_linear(chunk_size); pixels_to_scene_linear_byte( ibuf->byte_buffer.colorspace, bptr, scene_linear.data(), chunk_size); - tonemap_calc_chunk_luminance( - quad, all_pixels_inside_quad, ibuf->x, y_range, scene_linear.data(), lum); + tonemap_calc_chunk_luminance(ibuf->x, y_range, scene_linear.data(), lum); } return lum; }, @@ -281,64 +282,36 @@ static AreaLuminance tonemap_calc_input_luminance(const StripScreenQuad &quad, c } static void tonemapmodifier_apply(const RenderData * /*render_data*/, - const StripScreenQuad &quad, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) { const SequencerTonemapModifierData *tmmd = (const SequencerTonemapModifierData *)smd; - AreaLuminance lum = tonemap_calc_input_luminance(quad, ibuf); - if (lum.pixel_count == 0) { + TonemapApplyOp op; + op.type = eModTonemapType(tmmd->type); + op.ibuf = ibuf; + op.lum = tonemap_calc_input_luminance(ibuf); + if (op.lum.pixel_count == 0) { return; /* Strip is zero size or off-screen. */ } - AvgLogLum data; - data.tmmd = tmmd; - data.lav = lum.sum / lum.pixel_count; - data.cav.x = lum.color_sum.x / lum.pixel_count; - data.cav.y = lum.color_sum.y / lum.pixel_count; - data.cav.z = lum.color_sum.z / lum.pixel_count; - float maxl = log(double(lum.max) + 1e-5f); - float minl = log(double(lum.min) + 1e-5f); - float avl = lum.log_sum / lum.pixel_count; - data.auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f; + op.data.tmmd = tmmd; + op.data.lav = op.lum.sum / op.lum.pixel_count; + op.data.cav.x = op.lum.color_sum.x / op.lum.pixel_count; + op.data.cav.y = op.lum.color_sum.y / op.lum.pixel_count; + op.data.cav.z = op.lum.color_sum.z / op.lum.pixel_count; + float maxl = log(double(op.lum.max) + 1e-5f); + float minl = log(double(op.lum.min) + 1e-5f); + float avl = op.lum.log_sum / op.lum.pixel_count; + op.data.auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f; float al = exp(double(avl)); - data.al = (al == 0.0f) ? 0.0f : (tmmd->key / al); - data.igm = (tmmd->gamma == 0.0f) ? 1.0f : (1.0f / tmmd->gamma); + op.data.al = (al == 0.0f) ? 0.0f : (tmmd->key / al); + op.data.igm = (tmmd->gamma == 0.0f) ? 1.0f : (1.0f / tmmd->gamma); - threading::parallel_for( - IndexRange(int64_t(ibuf->x) * ibuf->y), 64 * 1024, [&](IndexRange range) { - if (ibuf->float_buffer.data != nullptr) { - /* Float pixels: no need for temporary storage. Luminance calculation already converted - * data to scene linear. */ - float4 *pixels = (float4 *)(ibuf->float_buffer.data) + range.first(); - if (tmmd->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) { - tonemap_rd_photoreceptor(pixels, mask, range, data); - } - else { - BLI_assert(tmmd->type == SEQ_TONEMAP_RH_SIMPLE); - tonemap_simple(pixels, mask, range, data); - } - scene_linear_to_image_chunk_float(ibuf, range); - } - else { - /* Byte pixels: temporary storage for scene linear pixel values. */ - Array scene_linear(range.size()); - pixels_to_scene_linear_byte(ibuf->byte_buffer.colorspace, - ibuf->byte_buffer.data + range.first() * 4, - scene_linear.data(), - range.size()); - if (tmmd->type == SEQ_TONEMAP_RD_PHOTORECEPTOR) { - tonemap_rd_photoreceptor(scene_linear.data(), mask, range, data); - } - else { - BLI_assert(tmmd->type == SEQ_TONEMAP_RH_SIMPLE); - tonemap_simple(scene_linear.data(), mask, range, data); - } - scene_linear_to_image_chunk_byte(scene_linear.data(), ibuf, range); - } - }); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); } static void tonemapmodifier_panel_draw(const bContext *C, Panel *panel) diff --git a/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc b/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc index e28e1d577c5..398f5bca1a0 100644 --- a/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc +++ b/source/blender/sequencer/intern/modifiers/MOD_white_balance.cc @@ -30,36 +30,41 @@ static void whiteBalance_init_data(StripModifierData *smd) struct WhiteBalanceApplyOp { float multiplier[3]; - template - void apply(ImageT *image, const MaskT *mask, IndexRange size) + template + void apply(ImageT *image, MaskSampler &mask, int image_x, IndexRange y_range) { - for ([[maybe_unused]] int64_t i : size) { - float4 input = load_pixel_premul(image); + image += y_range.first() * image_x * 4; + for (int64_t y : y_range) { + mask.begin_row(y); + for ([[maybe_unused]] int64_t x : IndexRange(image_x)) { + float4 input = load_pixel_premul(image); - float4 result; - result.w = input.w; + float4 result; + result.w = input.w; #if 0 - mul_v3_v3(result, multiplier); + mul_v3_v3(result, multiplier); #else - /* similar to division without the clipping */ - for (int i = 0; i < 3; i++) { - /* Prevent pow argument from being negative. This whole math - * breaks down overall with any HDR colors; would be good to - * revisit and do something more proper. */ - float f = max_ff(1.0f - input[i], 0.0f); - result[i] = 1.0f - powf(f, this->multiplier[i]); - } + /* similar to division without the clipping */ + for (int i = 0; i < 3; i++) { + /* Prevent pow argument from being negative. This whole math + * breaks down overall with any HDR colors; would be good to + * revisit and do something more proper. */ + float f = max_ff(1.0f - input[i], 0.0f); + result[i] = 1.0f - powf(f, this->multiplier[i]); + } #endif - apply_and_advance_mask(input, result, mask); - store_pixel_premul(result, image); - image += 4; + mask.apply_mask(input, result); + store_pixel_premul(result, image); + image += 4; + } } } }; static void whiteBalance_apply(const RenderData * /*render_data*/, - const StripScreenQuad & /*quad*/, + const Strip * /*strip*/, + const float transform[3][3], StripModifierData *smd, ImBuf *ibuf, ImBuf *mask) @@ -70,7 +75,7 @@ static void whiteBalance_apply(const RenderData * /*render_data*/, op.multiplier[0] = (data->white_value[0] != 0.0f) ? 1.0f / data->white_value[0] : FLT_MAX; op.multiplier[1] = (data->white_value[1] != 0.0f) ? 1.0f / data->white_value[1] : FLT_MAX; op.multiplier[2] = (data->white_value[2] != 0.0f) ? 1.0f / data->white_value[2] : FLT_MAX; - apply_modifier_op(op, ibuf, mask); + apply_modifier_op(op, ibuf, mask, float3x3(transform)); } static void whiteBalance_panel_draw(const bContext *C, Panel *panel) diff --git a/source/blender/sequencer/intern/modifiers/modifier.cc b/source/blender/sequencer/intern/modifiers/modifier.cc index 36d72f67779..26d6a73227c 100644 --- a/source/blender/sequencer/intern/modifiers/modifier.cc +++ b/source/blender/sequencer/intern/modifiers/modifier.cc @@ -35,6 +35,7 @@ #include "SEQ_select.hh" #include "SEQ_sequencer.hh" #include "SEQ_time.hh" +#include "SEQ_transform.hh" #include "SEQ_utils.hh" #include "UI_interface.hh" @@ -292,30 +293,6 @@ void store_pixel_raw(float4 pix, float *ptr) *reinterpret_cast(ptr) = pix; } -/* Byte mask */ -void apply_and_advance_mask(float4 input, float4 &result, const uchar *&mask) -{ - float3 m; - rgb_uchar_to_float(m, mask); - result.x = math::interpolate(input.x, result.x, m.x); - result.y = math::interpolate(input.y, result.y, m.y); - result.z = math::interpolate(input.z, result.z, m.z); - mask += 4; -} - -/* Float mask */ -void apply_and_advance_mask(float4 input, float4 &result, const float *&mask) -{ - float3 m(mask); - result.x = math::interpolate(input.x, result.x, m.x); - result.y = math::interpolate(input.y, result.y, m.y); - result.z = math::interpolate(input.z, result.z, m.z); - mask += 4; -} - -/* No mask */ -void apply_and_advance_mask(float4 /*input*/, float4 & /*result*/, const void *& /*mask*/) {} - /** * \a timeline_frame is offset by \a fra_offset only in case we are using a real mask. */ @@ -339,27 +316,17 @@ static ImBuf *modifier_render_mask_input(const RenderData *context, * fine, but if it is a byte image then we also just take that without * extra memory allocations or conversions. All modifiers are expected * to handle mask being either type. */ - mask_input = seq_render_mask(context, mask_id, timeline_frame - fra_offset, false); + mask_input = seq_render_mask(context->depsgraph, + context->rectx, + context->recty, + mask_id, + timeline_frame - fra_offset, + false); } return mask_input; } -static ImBuf *modifier_mask_get(StripModifierData *smd, - const RenderData *context, - SeqRenderState *state, - int timeline_frame, - int fra_offset) -{ - return modifier_render_mask_input(context, - state, - smd->mask_input_type, - smd->mask_strip, - smd->mask_id, - timeline_frame, - fra_offset); -} - /* -------------------------------------------------------------------- */ /** \name Public Modifier Functions * \{ */ @@ -499,11 +466,10 @@ static bool skip_modifier(Scene *scene, const StripModifierData *smd, int timeli void modifier_apply_stack(const RenderData *context, SeqRenderState *state, const Strip *strip, + const float3x3 &transform, ImBuf *ibuf, int timeline_frame) { - const StripScreenQuad quad = get_strip_screen_quad(context, strip); - if (strip->modifiers.first && (strip->flag & SEQ_USE_LINEAR_MODIFIERS)) { render_imbuf_from_sequencer_space(context->scene, ibuf); } @@ -530,8 +496,14 @@ void modifier_apply_stack(const RenderData *context, frame_offset = smd->mask_id ? ((Mask *)smd->mask_id)->sfra : 0; } - ImBuf *mask = modifier_mask_get(smd, context, state, timeline_frame, frame_offset); - smti->apply(context, quad, smd, ibuf, mask); + ImBuf *mask = modifier_render_mask_input(context, + state, + smd->mask_input_type, + smd->mask_strip, + smd->mask_id, + timeline_frame, + frame_offset); + smti->apply(context, strip, transform.ptr(), smd, ibuf, mask); if (mask) { IMB_freeImBuf(mask); } diff --git a/source/blender/sequencer/intern/modifiers/modifier.hh b/source/blender/sequencer/intern/modifiers/modifier.hh index c32bba636b2..66b8d277ac3 100644 --- a/source/blender/sequencer/intern/modifiers/modifier.hh +++ b/source/blender/sequencer/intern/modifiers/modifier.hh @@ -8,6 +8,9 @@ * \ingroup sequencer */ +#include "BLI_math_color.h" +#include "BLI_math_interp.hh" +#include "BLI_math_matrix.hh" #include "BLI_math_vector.hh" #include "BLI_task.hh" @@ -15,6 +18,7 @@ struct bContext; struct ARegionType; +struct ImBuf; struct Strip; struct uiLayout; struct Panel; @@ -23,6 +27,19 @@ struct PointerRNA; namespace blender::seq { +struct RenderData; +struct SeqRenderState; + +/* `transform` is transformation from strip image local pixel coordinates + * to the full render area pixel coordinates. This is used to sample + * modifier masks (since masks are in full render area space). */ +void modifier_apply_stack(const RenderData *context, + SeqRenderState *state, + const Strip *strip, + const float3x3 &transform, + ImBuf *ibuf, + int timeline_frame); + bool modifier_persistent_uids_are_valid(const Strip &strip); void draw_mask_input_type_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr); @@ -43,21 +60,186 @@ float4 load_pixel_raw(const uchar *ptr); float4 load_pixel_raw(const float *ptr); void store_pixel_raw(const float4 pix, uchar *ptr); void store_pixel_raw(const float4 pix, float *ptr); -void apply_and_advance_mask(const float4 input, float4 &result, const uchar *&mask); -void apply_and_advance_mask(const float4 input, float4 &result, const float *&mask); -void apply_and_advance_mask(const float4 input, float4 &result, const void *&mask); + +/* Mask sampler for #apply_modifier_op: no mask is present. */ +struct MaskSamplerNone { + void begin_row(int64_t /*y*/) {} + void apply_mask(const float4 /*input*/, float4 & /*result*/) {} + float load_mask_min() + { + return 0.0f; + } +}; + +/* Mask sampler for #apply_modifier_op: floating point mask, + * same size as input, no transform. */ +struct MaskSamplerDirectFloat { + MaskSamplerDirectFloat(const ImBuf *mask) : mask(mask) + { + BLI_assert(mask && mask->float_buffer.data); + } + void begin_row(int64_t y) + { + BLI_assert(y >= 0 && y < mask->y); + ptr = mask->float_buffer.data + y * mask->x * 4; + } + void apply_mask(const float4 input, float4 &result) + { + float3 m(this->ptr); + result.x = math::interpolate(input.x, result.x, m.x); + result.y = math::interpolate(input.y, result.y, m.y); + result.z = math::interpolate(input.z, result.z, m.z); + this->ptr += 4; + } + float load_mask_min() + { + float r = min_fff(this->ptr[0], this->ptr[1], this->ptr[2]); + this->ptr += 4; + return r; + } + const float *ptr = nullptr; + const ImBuf *mask; +}; + +/* Mask sampler for #apply_modifier_op: byte mask, + * same size as input, no transform. */ +struct MaskSamplerDirectByte { + MaskSamplerDirectByte(const ImBuf *mask) : mask(mask) + { + BLI_assert(mask && mask->byte_buffer.data); + } + void begin_row(int64_t y) + { + BLI_assert(y >= 0 && y < mask->y); + ptr = mask->byte_buffer.data + y * mask->x * 4; + } + void apply_mask(const float4 input, float4 &result) + { + float3 m; + rgb_uchar_to_float(m, this->ptr); + result.x = math::interpolate(input.x, result.x, m.x); + result.y = math::interpolate(input.y, result.y, m.y); + result.z = math::interpolate(input.z, result.z, m.z); + this->ptr += 4; + } + float load_mask_min() + { + float r = float(min_iii(this->ptr[0], this->ptr[1], this->ptr[2])) * (1.0f / 255.0f); + this->ptr += 4; + return r; + } + const uchar *ptr = nullptr; + const ImBuf *mask; +}; + +/* Mask sampler for #apply_modifier_op: floating point mask, + * sample mask with a transform. */ +struct MaskSamplerTransformedFloat { + MaskSamplerTransformedFloat(const ImBuf *mask, const float3x3 &transform) + : mask(mask), transform(transform) + { + BLI_assert(mask && mask->float_buffer.data); + start_uv = transform.location().xy(); + add_x = transform.x_axis().xy(); + add_y = transform.y_axis().xy(); + } + void begin_row(int64_t y) + { + this->cur_y = y; + this->cur_x = 0; + /* Sample at pixel centers. */ + this->cur_uv_row = this->start_uv + (y + 0.5f) * this->add_y + 0.5f * this->add_x; + } + void apply_mask(const float4 input, float4 &result) + { + float2 uv = this->cur_uv_row + this->cur_x * this->add_x - 0.5f; + float4 m; + math::interpolate_bilinear_border_fl( + this->mask->float_buffer.data, m, this->mask->x, this->mask->y, 4, uv.x, uv.y); + result.x = math::interpolate(input.x, result.x, m.x); + result.y = math::interpolate(input.y, result.y, m.y); + result.z = math::interpolate(input.z, result.z, m.z); + this->cur_x++; + } + float load_mask_min() + { + float2 uv = this->cur_uv_row + this->cur_x * this->add_x - 0.5f; + float4 m; + math::interpolate_bilinear_border_fl( + this->mask->float_buffer.data, m, this->mask->x, this->mask->y, 4, uv.x, uv.y); + float r = min_fff(m.x, m.y, m.z); + this->cur_x++; + return r; + } + int64_t cur_x = 0, cur_y = 0; + const ImBuf *mask; + const float3x3 transform; + float2 start_uv, add_x, add_y; + float2 cur_uv_row; +}; + +/* Mask sampler for #apply_modifier_op: byte mask, + * sample mask with a transform. */ +struct MaskSamplerTransformedByte { + MaskSamplerTransformedByte(const ImBuf *mask, const float3x3 &transform) + : mask(mask), transform(transform) + { + BLI_assert(mask && mask->byte_buffer.data); + start_uv = transform.location().xy(); + add_x = transform.x_axis().xy(); + add_y = transform.y_axis().xy(); + } + void begin_row(int64_t y) + { + this->cur_y = y; + this->cur_x = 0; + /* Sample at pixel centers. */ + this->cur_uv_row = this->start_uv + (y + 0.5f) * this->add_y + 0.5f * this->add_x; + } + void apply_mask(const float4 input, float4 &result) + { + float2 uv = this->cur_uv_row + this->cur_x * this->add_x - 0.5f; + uchar4 mb = math::interpolate_bilinear_border_byte( + this->mask->byte_buffer.data, this->mask->x, this->mask->y, uv.x, uv.y); + float3 m; + rgb_uchar_to_float(m, mb); + result.x = math::interpolate(input.x, result.x, m.x); + result.y = math::interpolate(input.y, result.y, m.y); + result.z = math::interpolate(input.z, result.z, m.z); + this->cur_x++; + } + float load_mask_min() + { + float2 uv = this->cur_uv_row + this->cur_x * this->add_x - 0.5f; + uchar4 m = math::interpolate_bilinear_border_byte( + this->mask->byte_buffer.data, this->mask->x, this->mask->y, uv.x, uv.y); + float r = float(min_iii(m.x, m.y, m.z)) * (1.0f / 255.0f); + this->cur_x++; + return r; + } + int64_t cur_x = 0, cur_y = 0; + const ImBuf *mask; + const float3x3 transform; + float2 start_uv, add_x, add_y; + float2 cur_uv_row; +}; /* Given `T` that implements an `apply` function: * - * template - * void apply(ImageT* image, const MaskT* mask, IndexRange size); + * template + * void apply(ImageT* image, MaskSampler &mask, int image_x, IndexRange y_range); * * this function calls the apply() function in parallel * chunks of the image to process, and with needed - * uchar, float or void types (void is used for mask, when there is - * no masking). Both input and mask images are expected to have + * uchar or float ImageT types, and with appropriate MaskSampler + * instantiated, depending on whether the mask exists, data type + * of the mask, and whether it needs a transformation or can be + * sampled directly. + * + * Both input and mask images are expected to have * 4 (RGBA) color channels. Input is modified. */ -template void apply_modifier_op(T &op, ImBuf *ibuf, const ImBuf *mask) +template +void apply_modifier_op(T &op, ImBuf *ibuf, const ImBuf *mask, const float3x3 &mask_transform) { if (ibuf == nullptr) { return; @@ -66,37 +248,67 @@ template void apply_modifier_op(T &op, ImBuf *ibuf, const ImBuf *mas "Sequencer only supports 4 channel images"); BLI_assert_msg(mask == nullptr || mask->channels == 0 || mask->channels == 4, "Sequencer only supports 4 channel images"); - - threading::parallel_for(IndexRange(size_t(ibuf->x) * ibuf->y), 32 * 1024, [&](IndexRange range) { + const bool direct_mask_sampling = mask == nullptr || (mask->x == ibuf->x && mask->y == ibuf->y && + math::is_identity(mask_transform)); + const int image_x = ibuf->x; + threading::parallel_for(IndexRange(ibuf->y), 16, [&](IndexRange y_range) { uchar *image_byte = ibuf->byte_buffer.data; float *image_float = ibuf->float_buffer.data; const uchar *mask_byte = mask ? mask->byte_buffer.data : nullptr; const float *mask_float = mask ? mask->float_buffer.data : nullptr; - const void *mask_none = nullptr; - int64_t offset = range.first() * 4; /* Instantiate the needed processing function based on image/mask * data types. */ if (image_byte) { if (mask_byte) { - op.apply(image_byte + offset, mask_byte + offset, range); + if (direct_mask_sampling) { + MaskSamplerDirectByte sampler(mask); + op.apply(image_byte, sampler, image_x, y_range); + } + else { + MaskSamplerTransformedByte sampler(mask, mask_transform); + op.apply(image_byte, sampler, image_x, y_range); + } } else if (mask_float) { - op.apply(image_byte + offset, mask_float + offset, range); + if (direct_mask_sampling) { + MaskSamplerDirectFloat sampler(mask); + op.apply(image_byte, sampler, image_x, y_range); + } + else { + MaskSamplerTransformedFloat sampler(mask, mask_transform); + op.apply(image_byte, sampler, image_x, y_range); + } } else { - op.apply(image_byte + offset, mask_none, range); + MaskSamplerNone sampler; + op.apply(image_byte, sampler, image_x, y_range); } } else if (image_float) { if (mask_byte) { - op.apply(image_float + offset, mask_byte + offset, range); + if (direct_mask_sampling) { + MaskSamplerDirectByte sampler(mask); + op.apply(image_float, sampler, image_x, y_range); + } + else { + MaskSamplerTransformedByte sampler(mask, mask_transform); + op.apply(image_float, sampler, image_x, y_range); + } } else if (mask_float) { - op.apply(image_float + offset, mask_float + offset, range); + if (direct_mask_sampling) { + MaskSamplerDirectFloat sampler(mask); + op.apply(image_float, sampler, image_x, y_range); + } + else { + MaskSamplerTransformedFloat sampler(mask, mask_transform); + op.apply(image_float, sampler, image_x, y_range); + } } else { - op.apply(image_float + offset, mask_none, range); + MaskSamplerNone sampler; + op.apply(image_float, sampler, image_x, y_range); } } }); diff --git a/source/blender/sequencer/intern/render.cc b/source/blender/sequencer/intern/render.cc index 454f8dfe798..563c19f45e6 100644 --- a/source/blender/sequencer/intern/render.cc +++ b/source/blender/sequencer/intern/render.cc @@ -71,6 +71,7 @@ #include "cache/intra_frame_cache.hh" #include "cache/source_image_cache.hh" #include "effects/effects.hh" +#include "modifiers/modifier.hh" #include "multiview.hh" #include "prefetch.hh" #include "proxy.hh" @@ -440,19 +441,21 @@ static bool seq_need_scale_to_render_size(const Strip *strip, bool is_proxy_imag return true; } -static float3x3 sequencer_image_crop_transform_matrix(const Scene *scene, - const Strip *strip, - const ImBuf *in, - const ImBuf *out, - const float image_scale_factor, - const float preview_scale_factor) +static float3x3 calc_strip_transform_matrix(const Scene *scene, + const Strip *strip, + const int in_x, + const int in_y, + const int out_x, + const int out_y, + const float image_scale_factor, + const float preview_scale_factor) { const StripTransform *transform = strip->data->transform; /* This value is intentionally kept as integer. Otherwise images with odd dimensions would * be translated to center of canvas by non-integer value, which would cause it to be * interpolated. Interpolation with 0 user defined translation is unwanted behavior. */ - const int3 image_center_offs((out->x - in->x) / 2, (out->y - in->y) / 2, 0); + const int3 image_center_offs((out_x - in_x) / 2, (out_y - in_y) / 2, 0); const float2 translation(transform->xofs * preview_scale_factor, transform->yofs * preview_scale_factor); @@ -461,12 +464,12 @@ static float3x3 sequencer_image_crop_transform_matrix(const Scene *scene, transform->scale_y * image_scale_factor); const float2 origin = image_transform_origin_get(scene, strip); - const float2 pivot(in->x * origin[0], in->y * origin[1]); + const float2 pivot(in_x * origin[0], in_y * origin[1]); const float3x3 matrix = math::from_loc_rot_scale( translation + float2(image_center_offs), rotation, scale); const float3x3 mat_pivot = math::from_origin_transform(matrix, pivot); - return math::invert(mat_pivot); + return mat_pivot; } static void sequencer_image_crop_init(const Strip *strip, @@ -531,17 +534,14 @@ static eIMBInterpolationFilterMode get_auto_filter(const StripTransform *transfo return IMB_FILTER_BILINEAR; } -static void sequencer_preprocess_transform_crop( - ImBuf *in, ImBuf *out, const RenderData *context, Strip *strip, const bool is_proxy_image) +static void sequencer_preprocess_transform_crop(ImBuf *in, + ImBuf *out, + const RenderData *context, + Strip *strip, + const float3x3 &matrix, + const bool do_scale_to_render_size, + const float preview_scale_factor) { - const Scene *scene = context->scene; - const float preview_scale_factor = get_render_scale_factor(*context); - const bool do_scale_to_render_size = seq_need_scale_to_render_size(strip, is_proxy_image); - const float image_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f; - - float3x3 matrix = sequencer_image_crop_transform_matrix( - scene, strip, in, out, image_scale_factor, preview_scale_factor); - /* Proxy image is smaller, so crop values must be corrected by proxy scale factor. * Proxy scale factor always matches preview_scale_factor. */ rctf source_crop; @@ -628,56 +628,26 @@ static ImBuf *input_preprocess(const RenderData *context, const bool is_proxy_image) { Scene *scene = context->scene; - ImBuf *preprocessed_ibuf = nullptr; /* Deinterlace. */ if ((strip->flag & SEQ_FILTERY) && !ELEM(strip->type, STRIP_TYPE_MOVIE, STRIP_TYPE_MOVIECLIP)) { - /* Change original image pointer to avoid another duplication in SEQ_USE_TRANSFORM. */ - preprocessed_ibuf = IMB_makeSingleUser(ibuf); - ibuf = preprocessed_ibuf; - - IMB_filtery(preprocessed_ibuf); - } - - if (sequencer_use_crop(strip) || sequencer_use_transform(strip) || context->rectx != ibuf->x || - context->recty != ibuf->y) - { - const int x = context->rectx; - const int y = context->recty; - preprocessed_ibuf = IMB_allocImBuf( - x, y, 32, ibuf->float_buffer.data ? IB_float_data : IB_byte_data); - - sequencer_preprocess_transform_crop(ibuf, preprocessed_ibuf, context, strip, is_proxy_image); - - seq_imbuf_assign_spaces(scene, preprocessed_ibuf); - IMB_metadata_copy(preprocessed_ibuf, ibuf); - IMB_freeImBuf(ibuf); - } - - /* Duplicate ibuf if we still have original. */ - if (preprocessed_ibuf == nullptr) { - preprocessed_ibuf = IMB_makeSingleUser(ibuf); - } - - if (strip->flag & SEQ_FLIPX) { - IMB_flipx(preprocessed_ibuf); - } - - if (strip->flag & SEQ_FLIPY) { - IMB_flipy(preprocessed_ibuf); + ibuf = IMB_makeSingleUser(ibuf); + IMB_filtery(ibuf); } if (strip->sat != 1.0f) { - IMB_saturation(preprocessed_ibuf, strip->sat); + ibuf = IMB_makeSingleUser(ibuf); + IMB_saturation(ibuf, strip->sat); } if (strip->flag & SEQ_MAKE_FLOAT) { - if (!preprocessed_ibuf->float_buffer.data) { - seq_imbuf_to_sequencer_space(scene, preprocessed_ibuf, true); + if (!ibuf->float_buffer.data) { + ibuf = IMB_makeSingleUser(ibuf); + seq_imbuf_to_sequencer_space(scene, ibuf, true); } - if (preprocessed_ibuf->byte_buffer.data) { - IMB_free_byte_pixels(preprocessed_ibuf); + if (ibuf->byte_buffer.data) { + IMB_free_byte_pixels(ibuf); } } @@ -687,15 +657,71 @@ static ImBuf *input_preprocess(const RenderData *context, } if (mul != 1.0f) { + ibuf = IMB_makeSingleUser(ibuf); const bool multiply_alpha = (strip->flag & SEQ_MULTIPLY_ALPHA); - multiply_ibuf(preprocessed_ibuf, mul, multiply_alpha); + multiply_ibuf(ibuf, mul, multiply_alpha); } + const float preview_scale_factor = get_render_scale_factor(*context); + const bool do_scale_to_render_size = seq_need_scale_to_render_size(strip, is_proxy_image); + const float image_scale_factor = do_scale_to_render_size ? preview_scale_factor : 1.0f; + if (strip->modifiers.first) { - modifier_apply_stack(context, state, strip, preprocessed_ibuf, timeline_frame); + ibuf = IMB_makeSingleUser(ibuf); + float3x3 matrix = calc_strip_transform_matrix(scene, + strip, + ibuf->x, + ibuf->y, + context->rectx, + context->recty, + image_scale_factor, + preview_scale_factor); + modifier_apply_stack(context, state, strip, matrix, ibuf, timeline_frame); } - return preprocessed_ibuf; + if (sequencer_use_crop(strip) || sequencer_use_transform(strip) || context->rectx != ibuf->x || + context->recty != ibuf->y) + { + const int x = context->rectx; + const int y = context->recty; + ImBuf *transformed_ibuf = IMB_allocImBuf( + x, y, 32, ibuf->float_buffer.data ? IB_float_data : IB_byte_data); + + /* Note: calculate matrix again; modifiers can actually change the image size. */ + float3x3 matrix = calc_strip_transform_matrix(scene, + strip, + ibuf->x, + ibuf->y, + context->rectx, + context->recty, + image_scale_factor, + preview_scale_factor); + matrix = math::invert(matrix); + sequencer_preprocess_transform_crop(ibuf, + transformed_ibuf, + context, + strip, + matrix, + do_scale_to_render_size, + preview_scale_factor); + + seq_imbuf_assign_spaces(scene, transformed_ibuf); + IMB_metadata_copy(transformed_ibuf, ibuf); + IMB_freeImBuf(ibuf); + ibuf = transformed_ibuf; + } + + if (strip->flag & SEQ_FLIPX) { + ibuf = IMB_makeSingleUser(ibuf); + IMB_flipx(ibuf); + } + + if (strip->flag & SEQ_FLIPY) { + ibuf = IMB_makeSingleUser(ibuf); + IMB_flipy(ibuf); + } + + return ibuf; } static ImBuf *seq_render_preprocess_ibuf(const RenderData *context, @@ -1261,7 +1287,12 @@ static ImBuf *seq_render_movieclip_strip(const RenderData *context, return ibuf; } -ImBuf *seq_render_mask(const RenderData *context, Mask *mask, float frame_index, bool make_float) +ImBuf *seq_render_mask(Depsgraph *depsgraph, + int width, + int height, + const Mask *mask, + float frame_index, + bool make_float) { /* TODO: add option to rasterize to alpha imbuf? */ ImBuf *ibuf = nullptr; @@ -1284,19 +1315,18 @@ ImBuf *seq_render_mask(const RenderData *context, Mask *mask, float frame_index, /* anim-data */ adt = BKE_animdata_from_id(&mask->id); const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( - context->depsgraph, mask->sfra + frame_index); + depsgraph, mask->sfra + frame_index); BKE_animsys_evaluate_animdata(&mask_temp->id, adt, &anim_eval_context, ADT_RECALC_ANIM, false); - maskbuf = MEM_malloc_arrayN(size_t(context->rectx) * size_t(context->recty), __func__); + maskbuf = MEM_malloc_arrayN(size_t(width) * size_t(height), __func__); mr_handle = BKE_maskrasterize_handle_new(); - BKE_maskrasterize_handle_init( - mr_handle, mask_temp, context->rectx, context->recty, true, true, true); + BKE_maskrasterize_handle_init(mr_handle, mask_temp, width, height, true, true, true); BKE_id_free(nullptr, &mask_temp->id); - BKE_maskrasterize_buffer(mr_handle, context->rectx, context->recty, maskbuf); + BKE_maskrasterize_buffer(mr_handle, width, height, maskbuf); BKE_maskrasterize_handle_free(mr_handle); @@ -1305,12 +1335,11 @@ ImBuf *seq_render_mask(const RenderData *context, Mask *mask, float frame_index, const float *fp_src; float *fp_dst; - ibuf = IMB_allocImBuf( - context->rectx, context->recty, 32, IB_float_data | IB_uninitialized_pixels); + ibuf = IMB_allocImBuf(width, height, 32, IB_float_data | IB_uninitialized_pixels); fp_src = maskbuf; fp_dst = ibuf->float_buffer.data; - i = context->rectx * context->recty; + i = width * height; while (--i) { fp_dst[0] = fp_dst[1] = fp_dst[2] = *fp_src; fp_dst[3] = 1.0f; @@ -1324,12 +1353,11 @@ ImBuf *seq_render_mask(const RenderData *context, Mask *mask, float frame_index, const float *fp_src; uchar *ub_dst; - ibuf = IMB_allocImBuf( - context->rectx, context->recty, 32, IB_byte_data | IB_uninitialized_pixels); + ibuf = IMB_allocImBuf(width, height, 32, IB_byte_data | IB_uninitialized_pixels); fp_src = maskbuf; ub_dst = ibuf->byte_buffer.data; - i = context->rectx * context->recty; + i = width * height; while (--i) { ub_dst[0] = ub_dst[1] = ub_dst[2] = uchar(*fp_src * 255.0f); /* already clamped */ ub_dst[3] = 255; @@ -1348,7 +1376,8 @@ static ImBuf *seq_render_mask_strip(const RenderData *context, Strip *strip, flo { bool make_float = (strip->flag & SEQ_MAKE_FLOAT) != 0; - return seq_render_mask(context, strip->mask, frame_index, make_float); + return seq_render_mask( + context->depsgraph, context->rectx, context->recty, strip->mask, frame_index, make_float); } static ImBuf *seq_render_scene_strip_ex(const RenderData *context, diff --git a/source/blender/sequencer/intern/render.hh b/source/blender/sequencer/intern/render.hh index 144fd51ebc6..0b3486283ad 100644 --- a/source/blender/sequencer/intern/render.hh +++ b/source/blender/sequencer/intern/render.hh @@ -12,6 +12,7 @@ #include "BLI_set.hh" #include "BLI_vector.hh" +struct Depsgraph; struct ImBuf; struct LinkNode; struct ListBase; @@ -55,7 +56,12 @@ ImBuf *seq_render_strip(const RenderData *context, /* Renders Mask into an image suitable for sequencer: * RGB channels contain mask intensity; alpha channel is opaque. */ -ImBuf *seq_render_mask(const RenderData *context, Mask *mask, float frame_index, bool make_float); +ImBuf *seq_render_mask(Depsgraph *depsgraph, + int width, + int height, + const Mask *mask, + float frame_index, + bool make_float); void seq_imbuf_assign_spaces(const Scene *scene, ImBuf *ibuf); StripScreenQuad get_strip_screen_quad(const RenderData *context, const Strip *strip); diff --git a/tests/files/sequence_editing/effects/mod_comp_blur_expand.blend b/tests/files/sequence_editing/effects/mod_comp_blur_expand.blend new file mode 100644 index 00000000000..7ba24eed799 --- /dev/null +++ b/tests/files/sequence_editing/effects/mod_comp_blur_expand.blend @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1808a063f48e5aad3dc88dba4f2b7838426cad577f1c6053dbfee76ba0567a0c +size 84689 diff --git a/tests/files/sequence_editing/effects/reference/adjustment_layer_stack.png b/tests/files/sequence_editing/effects/reference/adjustment_layer_stack.png index ccaeb2a7c13..2b999103621 100644 --- a/tests/files/sequence_editing/effects/reference/adjustment_layer_stack.png +++ b/tests/files/sequence_editing/effects/reference/adjustment_layer_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f065a5161a2fab6f9085177fad4ff191f0f512f533d590225d60c154968c8242 -size 35622 +oid sha256:f352d108d2f26a528057460929b88dc08f38d589e6780cc4ea49a24a4dd87251 +size 35569 diff --git a/tests/files/sequence_editing/effects/reference/mod_bright_contrast.png b/tests/files/sequence_editing/effects/reference/mod_bright_contrast.png index 57bd8cee83b..5c5dfbd243a 100644 --- a/tests/files/sequence_editing/effects/reference/mod_bright_contrast.png +++ b/tests/files/sequence_editing/effects/reference/mod_bright_contrast.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e661dba85d768bda493724a3f5780d8c2ea7dc3381049d3267412f4416566e7e -size 72138 +oid sha256:735e3cb07369f3562fce65929a711a35f5e724e52ccbf409e59e4b48a26d4f64 +size 71547 diff --git a/tests/files/sequence_editing/effects/reference/mod_colorbalance.png b/tests/files/sequence_editing/effects/reference/mod_colorbalance.png index 69aac9f814b..afe3bc8ae8d 100644 --- a/tests/files/sequence_editing/effects/reference/mod_colorbalance.png +++ b/tests/files/sequence_editing/effects/reference/mod_colorbalance.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d4411c767210040941e09910ae220a1aecccb1b07271771ea73c823324d3947 -size 101220 +oid sha256:eaf52c0bf20112c81b8db3b267f66592baa65eef4441493be8759bee70fde6bf +size 101098 diff --git a/tests/files/sequence_editing/effects/reference/mod_comp.png b/tests/files/sequence_editing/effects/reference/mod_comp.png index 24824d89be8..3741a872caf 100644 --- a/tests/files/sequence_editing/effects/reference/mod_comp.png +++ b/tests/files/sequence_editing/effects/reference/mod_comp.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7b778d45609b65b11c4b92d1fafa6e167f303645391bd18b0a008d4243e95c8c -size 92110 +oid sha256:70a5f834ecc412e0effcf278bbc289713be8fb731c14f5eff3d54e50dac120e2 +size 101041 diff --git a/tests/files/sequence_editing/effects/reference/mod_comp_blur_expand.png b/tests/files/sequence_editing/effects/reference/mod_comp_blur_expand.png new file mode 100644 index 00000000000..13a679f5dc8 --- /dev/null +++ b/tests/files/sequence_editing/effects/reference/mod_comp_blur_expand.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:166101e96648151dced51a184192aeaf2606cf180f8a9577feffa63c3d17ad56 +size 83863 diff --git a/tests/files/sequence_editing/effects/reference/mod_curves.png b/tests/files/sequence_editing/effects/reference/mod_curves.png index 20fa7a48656..37ab1cc1108 100644 --- a/tests/files/sequence_editing/effects/reference/mod_curves.png +++ b/tests/files/sequence_editing/effects/reference/mod_curves.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13399f05acc8569f553e7fb88793c5070745987a5bbb1381e9e48b8a3abded27 -size 82200 +oid sha256:6558e390a7e5b9e3350817a6d476de22e3a439fe89941f9edde1b34955961d99 +size 82263 diff --git a/tests/files/sequence_editing/effects/reference/mod_huecorrect.png b/tests/files/sequence_editing/effects/reference/mod_huecorrect.png index 90c9881154f..b853fffa61f 100644 --- a/tests/files/sequence_editing/effects/reference/mod_huecorrect.png +++ b/tests/files/sequence_editing/effects/reference/mod_huecorrect.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8313b13866f4ede35ac87860e2bdc00c1b46d621457de98514672bc19ec28d84 -size 76894 +oid sha256:4b6c7e5507aa4f712f776748976f82b8f260aa32eea46159a0d057ce0ae71bcf +size 78087 diff --git a/tests/files/sequence_editing/effects/reference/mod_mask.png b/tests/files/sequence_editing/effects/reference/mod_mask.png index 45ee664a9f9..bb6fd020747 100644 --- a/tests/files/sequence_editing/effects/reference/mod_mask.png +++ b/tests/files/sequence_editing/effects/reference/mod_mask.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1f216542e32174f7bcfc48c1d3ab1a56f9005490fa55408f314070fb512ff993 -size 60077 +oid sha256:ac388f3e9848be25b6a7855e92aa39e12614a25a1dde742cc8c0bea23d9285c6 +size 60240 diff --git a/tests/files/sequence_editing/effects/reference/mod_mask_id.png b/tests/files/sequence_editing/effects/reference/mod_mask_id.png index a0d47218168..221aa55f9b1 100644 --- a/tests/files/sequence_editing/effects/reference/mod_mask_id.png +++ b/tests/files/sequence_editing/effects/reference/mod_mask_id.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:52907512c7ca43ccfa74281ad5a12718e34f0e96b09d5c9b2ad9552b97270639 -size 38805 +oid sha256:4e2cb03d2c99ff42c1138c0e96c04e5a8a214a57164138f495dd76c69c560ecd +size 40337 diff --git a/tests/files/sequence_editing/effects/reference/mod_mask_id_rot.png b/tests/files/sequence_editing/effects/reference/mod_mask_id_rot.png index fca10f0bf02..334da895954 100644 --- a/tests/files/sequence_editing/effects/reference/mod_mask_id_rot.png +++ b/tests/files/sequence_editing/effects/reference/mod_mask_id_rot.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:765e6be0f6e0797ffe3c25838af9a11c4d3dc57d20540f112ebff6920f76e312 -size 50634 +oid sha256:371bd44dd4e0d589133d1e384aaafda7b95571a459c24dace70248c88551b929 +size 51698 diff --git a/tests/files/sequence_editing/effects/reference/mod_tonemap.png b/tests/files/sequence_editing/effects/reference/mod_tonemap.png index d96d4b74f0a..60434b8c3d8 100644 --- a/tests/files/sequence_editing/effects/reference/mod_tonemap.png +++ b/tests/files/sequence_editing/effects/reference/mod_tonemap.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07efcbb21962e1e310be6fdb4f532b9f44ad46ab68ef4a527fde2237b9a28bb4 -size 212475 +oid sha256:313f5ac13389c0d4ef95fd66248bfb4660edf24f52f165dbe390464bd120e261 +size 212504 diff --git a/tests/files/sequence_editing/effects/reference/mod_whitebalance.png b/tests/files/sequence_editing/effects/reference/mod_whitebalance.png index 4a810f21672..e666115d15a 100644 --- a/tests/files/sequence_editing/effects/reference/mod_whitebalance.png +++ b/tests/files/sequence_editing/effects/reference/mod_whitebalance.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5473594c0e1a8b8360655eb1abedfcb871a0815470b22356804ecb7bea5e300f -size 76897 +oid sha256:d18134270c655456d42652cc72c6949f021b00396c3d7beea72abcbc1d0c39c1 +size 80096 diff --git a/tests/files/sequence_editing/effects/reference/text_outline_stack.png b/tests/files/sequence_editing/effects/reference/text_outline_stack.png index ab7f1831062..a2b45bb7da0 100644 --- a/tests/files/sequence_editing/effects/reference/text_outline_stack.png +++ b/tests/files/sequence_editing/effects/reference/text_outline_stack.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:71d00c0a6615c857cb641512d00300e293f292b5b4f3d37ba8af5ce99fc2abd6 -size 13266 +oid sha256:020d44b58a0bc7b1f64d9b70dec1ea8b7f17e26fc16ba1482889b42debb65db5 +size 13210