44 "The size of dilation/erosion in pixels. Positive values dilates and negative values "
46 .compositor_expects_single_value();
51 "The size of the falloff from the edges in pixels. If less than two pixels, the edges "
52 "will be anti-aliased")
53 .compositor_expects_single_value();
122 horizontal_pass_result.
release();
127 if (this->
context().use_gpu()) {
164 return horizontal_pass_result;
192 return horizontal_pass_result;
197 if (this->
context().use_gpu()) {
231 return "compositor_morphological_step_dilate";
233 return "compositor_morphological_step_erode";
260 const float limit = IsDilate ? std::numeric_limits<float>::lowest() :
261 std::numeric_limits<float>::max();
262 const auto morphology_operator = [](
const float a,
const float b) {
263 if constexpr (IsDilate) {
279 const int tiles_count = int(
math::ceil(
float(image_size.x) /
size));
283 Array<float> prefix_table(size);
284 Array<float> suffix_table(size);
285 for (const int64_t y : sub_y_range) {
286 for (const int64_t tile_index : IndexRange(tiles_count)) {
287 const int64_t tile_start = tile_index * size;
290 const int64_t tile_center = tile_start + size / 2;
292 float prefix_value = limit;
293 float suffix_value = limit;
297 for (const int64_t i : IndexRange(size)) {
298 const float right_value = input.load_pixel_fallback(int2(tile_center + i, y), limit);
299 prefix_value = morphology_operator(prefix_value, right_value);
300 prefix_table[i] = prefix_value;
304 const float left_value = input.load_pixel_fallback(int2(tile_center - i, y), limit);
305 suffix_value = morphology_operator(suffix_value, left_value);
306 suffix_table[size - 1 - i] = suffix_value;
309 const IndexRange tile_range = IndexRange(tile_start, size);
310 const IndexRange safe_tile_range = tile_range.intersect(IndexRange(image_size.x));
313 for (const int64_t x : safe_tile_range) {
316 const int64_t table_index = x - tile_start;
317 const float prefix_value = prefix_table[table_index];
318 const float suffix_value = suffix_table[table_index];
320 const float value = morphology_operator(prefix_value, suffix_value);
324 output.store_pixel(int2(y, x), value);
348 if (this->
context().use_gpu()) {
363 output.steal_data(output_mask);
369 GPUShader *shader =
context().get_shader(
"compositor_morphological_distance_threshold");
380 output.allocate_texture(domain);
381 output.bind_as_image(shader,
"output_img");
395 output.allocate_texture(domain);
397 const int2 image_size =
input.domain().size;
455 bool is_center_masked = input.load_pixel<float>(texel) > 0.5f;
459 int minimum_squared_distance = radius * radius * 2;
463 const int2 start = math::max(texel - radius, int2(0)) - texel;
464 const int2 end = math::min(texel + radius + 1, image_size) - texel;
468 for (int y = start.y; y < end.y; y++) {
469 const int yy = y * y;
470 for (int x = start.x; x < end.x; x++) {
471 bool is_sample_masked = input.load_pixel<float>(texel + int2(x, y)) > 0.5f;
472 if (is_center_masked != is_sample_masked) {
473 minimum_squared_distance = math::min(minimum_squared_distance, x * x + yy);
480 float signed_minimum_distance =
math::sqrt(
float(minimum_squared_distance)) *
481 (is_center_masked ? 1.0f : -1.0f);
487 output.store_pixel(texel, value);
507 node_storage(
bnode()).falloff);
517 if (
input.is_single_value()) {
551 return this->
get_input(
"Size").get_single_value_default(0);
556 return math::max(0.0f, this->
get_input(
"Falloff Size").get_single_value_default(0.0f));
579 ntype.
ui_name =
"Dilate/Erode";
583 ntype.
draw_buttons = file_ns::node_composit_buts_dilateerode;
584 ntype.
declare = file_ns::cmp_node_dilate_declare;
586 ntype.
initfunc = file_ns::node_composit_init_dilateerode;
#define NODE_STORAGE_FUNCS(StorageT)
#define NODE_CLASS_OP_FILTER
#define CMP_NODE_DILATEERODE
#define BLI_assert_unreachable()
@ CMP_NODE_DILATE_ERODE_STEP
@ CMP_NODE_DILATE_ERODE_DISTANCE_FEATHER
@ CMP_NODE_DILATE_ERODE_DISTANCE_THRESHOLD
@ CMP_NODE_DILATE_ERODE_DISTANCE
void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value)
void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float value)
void GPU_shader_bind(GPUShader *shader, const blender::gpu::shader::SpecializationConstants *constants_state=nullptr)
#define NOD_REGISTER_NODE(REGISTER_FUNC)
@ UI_ITEM_R_SPLIT_EMPTY_NAME
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Result create_result(ResultType type, ResultPrecision precision)
GPUShader * get_shader(const char *info_name, ResultPrecision precision)
const bNode & bnode() const
NodeOperation(Context &context, DNode node)
Result & get_result(StringRef identifier)
Context & context() const
Result & get_input(StringRef identifier) const
virtual Domain compute_domain()
void share_data(const Result &source)
void allocate_texture(Domain domain, bool from_pool=true)
void unbind_as_texture() const
void bind_as_texture(GPUShader *shader, const char *texture_name) const
void bind_as_image(GPUShader *shader, const char *image_name, bool read=false) const
void unbind_as_image() const
void execute_distance_threshold_gpu(Result &output)
void execute_distance_threshold_cpu(Result &output)
CMPNodeDilateErodeMethod get_method()
Result execute_step_horizontal_pass_gpu()
void execute_step_vertical_pass(Result &horizontal_pass_result)
Result execute_step_horizontal_pass_cpu()
const char * get_morphological_step_shader_name()
void execute_distance_threshold()
Result execute_step_horizontal_pass()
void execute_step_pass_cpu(const Result &input, Result &output)
void execute_distance_feather()
int get_structuring_element_size()
void execute_step_vertical_pass_gpu(Result &horizontal_pass_result)
int get_morphological_distance_threshold_radius()
NodeOperation(Context &context, DNode node)
void execute_step_vertical_pass_cpu(Result &horizontal_pass_result)
float distance(VecOp< float, D >, VecOp< float, D >) RET
void * MEM_callocN(size_t len, const char *str)
bNodeSocket * node_find_socket(bNode &node, eNodeSocketInOut in_out, StringRef identifier)
void node_register_type(bNodeType &ntype)
void node_set_socket_availability(bNodeTree &ntree, bNodeSocket &sock, bool is_available)
void node_type_storage(bNodeType &ntype, std::optional< StringRefNull > storagename, void(*freefunc)(bNode *node), void(*copyfunc)(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node))
void morphological_distance(Context &context, const Result &input, Result &output, const int distance)
void morphological_distance_feather(Context &context, const Result &input, Result &output, const int distance, const int falloff_type=PROP_SMOOTH)
void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size=int2(16))
void smaa(Context &context, const Result &input, Result &output, const float threshold=0.1f, const float local_contrast_adaptation_factor=2.0f, const int corner_rounding=25)
void parallel_for(const int2 range, const Function &function)
T clamp(const T &a, const T &min, const T &max)
T min(const T &a, const T &b)
T max(const T &a, const T &b)
static void cmp_node_dilate_declare(NodeDeclarationBuilder &b)
static void node_composit_init_dilateerode(bNodeTree *, bNode *node)
static void node_composit_buts_dilateerode(uiLayout *layout, bContext *, PointerRNA *ptr)
static void node_update(bNodeTree *ntree, bNode *node)
static NodeOperation * get_compositor_operation(Context &context, DNode node)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
VecBase< int32_t, 2 > int2
static void register_node_type_cmp_dilateerode()
void cmp_node_type_base(blender::bke::bNodeType *ntype, std::string idname, const std::optional< int16_t > legacy_type)
void node_free_standard_storage(bNode *node)
void node_copy_standard_storage(bNodeTree *, bNode *dest_node, const bNode *src_node)
int RNA_enum_get(PointerRNA *ptr, const char *name)
std::string ui_description
NodeGetCompositorOperationFunction get_compositor_operation
void(* initfunc)(bNodeTree *ntree, bNode *node)
const char * enum_name_legacy
void(* draw_buttons)(uiLayout *, bContext *C, PointerRNA *ptr)
NodeDeclareFunction declare
void(* updatefunc)(bNodeTree *ntree, bNode *node)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
static pxr::UsdShadeInput get_input(const pxr::UsdShadeShader &usd_shader, const pxr::TfToken &input_name)