Blender V5.0
MOD_compositor.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2025 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_math_base.h"
10#include "BLI_rect.h"
11
12#include "BLT_translation.hh"
13
14#include "COM_context.hh"
15#include "COM_domain.hh"
16#include "COM_evaluator.hh"
17
18#include "DNA_sequence_types.h"
19
21
23
24#include "SEQ_modifier.hh"
25#include "SEQ_modifiertypes.hh"
26#include "SEQ_render.hh"
27#include "SEQ_transform.hh"
28
29#include "UI_interface.hh"
31
32#include "RNA_access.hh"
33
34#include "modifier.hh"
35#include "render.hh"
36
37namespace blender::seq {
38
40 private:
41 const RenderData &render_data_;
42 const SequencerCompositorModifierData *modifier_data_;
43
44 ImBuf *image_buffer_;
45 ImBuf *mask_buffer_;
46 float3x3 xform_;
47 float2 result_translation_ = float2(0, 0);
48
49 public:
50 CompositorContext(const RenderData &render_data,
51 const SequencerCompositorModifierData *modifier_data,
52 ImBuf *image_buffer,
53 ImBuf *mask_buffer,
54 const Strip &strip)
55 : compositor::Context(),
56 render_data_(render_data),
57 modifier_data_(modifier_data),
58 image_buffer_(image_buffer),
59 mask_buffer_(mask_buffer),
60 xform_(float3x3::identity())
61 {
62 if (mask_buffer) {
63 /* Note: do not use passed transform matrix since compositor coordinate
64 * space is not from the image corner, but rather centered on the image. */
65 xform_ = math::invert(image_transform_matrix_get(render_data.scene, &strip));
66 }
67 }
68
70 {
71 return result_translation_;
72 }
73
74 const Scene &get_scene() const override
75 {
76 return *render_data_.scene;
77 }
78
79 const bNodeTree &get_node_tree() const override
80 {
81 return *DEG_get_evaluated<bNodeTree>(render_data_.depsgraph, modifier_data_->node_group);
82 }
83
92
94 {
95 return true;
96 }
97
99 {
100 return false;
101 }
102
104 {
105 return Bounds<int2>(int2(0), int2(image_buffer_->x, image_buffer_->y));
106 }
107
109 {
110 result_translation_ = domain.transformation.location();
112 if (domain.size.x != image_buffer_->x || domain.size.y != image_buffer_->y) {
113 /* Output size is different (e.g. image is blurred with expanded bounds);
114 * need to allocate appropriately sized buffer. */
115 IMB_free_all_data(image_buffer_);
116 image_buffer_->x = domain.size.x;
117 image_buffer_->y = domain.size.y;
118 IMB_alloc_float_pixels(image_buffer_, 4, false);
119 }
120 result.wrap_external(image_buffer_->float_buffer.data,
121 int2(image_buffer_->x, image_buffer_->y));
122 return result;
123 }
124
126 bool /*is_data*/,
127 compositor::ResultPrecision /*precision*/) override
128 {
129 /* Within compositor modifier, output and viewer output function the same. */
130 return get_output(domain);
131 }
132
134 {
136
137 if (name == "Image") {
138 result.wrap_external(image_buffer_->float_buffer.data,
139 int2(image_buffer_->x, image_buffer_->y));
140 }
141 else if (name == "Mask" && mask_buffer_) {
142 result.wrap_external(mask_buffer_->float_buffer.data,
143 int2(mask_buffer_->x, mask_buffer_->y));
144 result.set_transformation(xform_);
145 }
146
147 return result;
148 }
149
150 bool use_gpu() const override
151 {
152 return false;
153 }
154};
155
156static void compositor_modifier_init_data(StripModifierData *strip_modifier_data)
157{
158 SequencerCompositorModifierData *modifier_data =
159 reinterpret_cast<SequencerCompositorModifierData *>(strip_modifier_data);
160 modifier_data->node_group = nullptr;
161}
162
163static bool is_linear_float_buffer(ImBuf *image_buffer)
164{
165 return image_buffer->float_buffer.data &&
167}
168
170{
171 if (!ibuf) {
172 return false;
173 }
174
175 /* Already have scene linear float pixels, nothing to do. */
176 if (is_linear_float_buffer(ibuf)) {
177 return true;
178 }
179
180 if (ibuf->float_buffer.data == nullptr) {
182 }
183 else {
184 const char *from_colorspace = IMB_colormanagement_get_float_colorspace(ibuf);
185 const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(
188 ibuf->x,
189 ibuf->y,
190 ibuf->channels,
191 from_colorspace,
192 to_colorspace,
193 true);
195 }
196 return false;
197}
198
200 StripModifierData *strip_modifier_data,
201 ImBuf *mask)
202{
203 const SequencerCompositorModifierData *modifier_data =
204 reinterpret_cast<SequencerCompositorModifierData *>(strip_modifier_data);
205 if (!modifier_data->node_group) {
206 return;
207 }
208
209 ImBuf *linear_mask = mask;
211 linear_mask = IMB_dupImBuf(mask);
212 ensure_linear_float_buffer(linear_mask);
213 }
214
215 const bool was_float_linear = ensure_linear_float_buffer(context.image);
216 const bool was_byte = context.image->float_buffer.data == nullptr;
217
218 CompositorContext com_context(
219 context.render_data, modifier_data, context.image, linear_mask, context.strip);
220 compositor::Evaluator evaluator(com_context);
221 evaluator.evaluate();
222
223 context.result_translation += com_context.get_result_translation();
224
225 if (mask != linear_mask) {
226 IMB_freeImBuf(linear_mask);
227 }
228
229 if (was_float_linear) {
230 return;
231 }
232
233 if (was_byte) {
234 IMB_byte_from_float(context.image);
235 IMB_free_float_pixels(context.image);
236 }
237 else {
238 seq_imbuf_to_sequencer_space(context.render_data.scene, context.image, true);
239 }
240}
241
243{
244 uiLayout *layout = panel->layout;
246
247 layout->use_property_split_set(true);
248
249 uiTemplateID(layout,
250 C,
251 ptr,
252 "node_group",
253 "NODE_OT_new_compositor_sequencer_node_group",
254 nullptr,
255 nullptr);
256
257 if (uiLayout *mask_input_layout = layout->panel_prop(
258 C, ptr, "open_mask_input_panel", IFACE_("Mask Input")))
259 {
260 draw_mask_input_type_settings(C, mask_input_layout, ptr);
261 }
262}
263
269
271 /*idname*/ "Compositor",
272 /*name*/ CTX_N_(BLT_I18NCONTEXT_ID_SEQUENCE, "Compositor"),
273 /*struct_name*/ "SequencerCompositorModifierData",
274 /*struct_size*/ sizeof(SequencerCompositorModifierData),
275 /*init_data*/ compositor_modifier_init_data,
276 /*free_data*/ nullptr,
277 /*copy_data*/ nullptr,
279 /*panel_register*/ compositor_modifier_register,
280};
281
282}; // namespace blender::seq
#define CTX_N_(context, msgid)
#define BLT_I18NCONTEXT_ID_SEQUENCE
#define IFACE_(msgid)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ eSeqModifierType_Compositor
void IMB_colormanagement_assign_float_colorspace(ImBuf *ibuf, const char *name)
bool IMB_colormanagement_space_is_scene_linear(const ColorSpace *colorspace)
void IMB_colormanagement_transform_float(float *buffer, int width, int height, int channels, const char *from_colorspace, const char *to_colorspace, bool predivide)
@ COLOR_ROLE_SCENE_LINEAR
const char * IMB_colormanagement_role_colorspace_name_get(int role)
const char * IMB_colormanagement_get_float_colorspace(const ImBuf *ibuf)
ImBuf * IMB_dupImBuf(const ImBuf *ibuf1)
void IMB_byte_from_float(ImBuf *ibuf)
void IMB_free_all_data(ImBuf *ibuf)
void IMB_freeImBuf(ImBuf *ibuf)
void IMB_free_float_pixels(ImBuf *ibuf)
bool IMB_alloc_float_pixels(ImBuf *ibuf, const unsigned int channels, bool initialize_pixels=true)
void IMB_float_from_byte(ImBuf *ibuf)
#define C
Definition RandGen.cpp:29
void uiTemplateID(uiLayout *layout, const bContext *C, PointerRNA *ptr, blender::StringRefNull propname, const char *newop, const char *openop, const char *unlinkop, int filter=UI_TEMPLATE_ID_FILTER_ALL, bool live_icon=false, std::optional< blender::StringRef > text=std::nullopt)
PointerRNA * UI_panel_custom_data_get(const Panel *panel)
Result create_result(ResultType type, ResultPrecision precision)
const bNodeTree & get_node_tree() const override
CompositorContext(const RenderData &render_data, const SequencerCompositorModifierData *modifier_data, ImBuf *image_buffer, ImBuf *mask_buffer, const Strip &strip)
bool treat_viewer_as_compositor_output() const override
bool use_context_bounds_for_input_output() const override
compositor::Result get_input(StringRef name) override
Bounds< int2 > get_compositing_region() const override
compositor::Result get_output(compositor::Domain domain) override
compositor::Result get_viewer_output(compositor::Domain domain, bool, compositor::ResultPrecision) override
const Scene & get_scene() const override
compositor::OutputTypes needed_outputs() const override
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
CartesianBasis invert(const CartesianBasis &basis)
void seq_imbuf_to_sequencer_space(const Scene *scene, ImBuf *ibuf, bool make_float)
Definition render.cc:115
float3x3 image_transform_matrix_get(const Scene *scene, const Strip *strip)
void draw_mask_input_type_settings(const bContext *C, uiLayout *layout, PointerRNA *ptr)
StripModifierTypeInfo seqModifierType_Compositor
static bool is_linear_float_buffer(ImBuf *image_buffer)
static void compositor_modifier_panel_draw(const bContext *C, Panel *panel)
static void compositor_modifier_register(ARegionType *region_type)
static bool ensure_linear_float_buffer(ImBuf *ibuf)
static void compositor_modifier_init_data(StripModifierData *strip_modifier_data)
static void compositor_modifier_apply(ModifierApplyContext &context, StripModifierData *strip_modifier_data, ImBuf *mask)
PanelType * modifier_panel_register(ARegionType *region_type, const eStripModifierType type, PanelDrawFn draw)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
const char * name
const ColorSpace * colorspace
ImBufFloatBuffer float_buffer
struct uiLayout * layout
PanelLayout panel_prop(const bContext *C, PointerRNA *open_prop_owner, blender::StringRefNull open_prop_name)
void use_property_split_set(bool value)
PointerRNA * ptr
Definition wm_files.cc:4238