diff --git a/source/blender/compositor/nodes/COM_KuwaharaNode.cc b/source/blender/compositor/nodes/COM_KuwaharaNode.cc index a266e7d55fb..725143795b3 100644 --- a/source/blender/compositor/nodes/COM_KuwaharaNode.cc +++ b/source/blender/compositor/nodes/COM_KuwaharaNode.cc @@ -25,6 +25,7 @@ void KuwaharaNode::convert_to_operations(NodeConverter &converter, switch (data->variation) { case CMP_NODE_KUWAHARA_CLASSIC: { KuwaharaClassicOperation *kuwahara_classic = new KuwaharaClassicOperation(); + kuwahara_classic->set_data(data); converter.add_operation(kuwahara_classic); converter.map_input_socket(get_input_socket(0), kuwahara_classic->get_input_socket(0)); converter.map_input_socket(get_input_socket(1), kuwahara_classic->get_input_socket(1)); diff --git a/source/blender/compositor/operations/COM_KuwaharaClassicOperation.cc b/source/blender/compositor/operations/COM_KuwaharaClassicOperation.cc index cc076307415..9c6bd1d2d4b 100644 --- a/source/blender/compositor/operations/COM_KuwaharaClassicOperation.cc +++ b/source/blender/compositor/operations/COM_KuwaharaClassicOperation.cc @@ -54,8 +54,10 @@ void KuwaharaClassicOperation::execute_pixel_sampled(float output[4], size_reader_->read_sampled(size, x, y, sampler); const int kernel_size = int(math::max(0.0f, size[0])); - /* Naive implementation is more accurate for small kernel sizes. */ - if (kernel_size >= 4) { + /* For high radii, we accelerate the filter using a summed area table, making the filter + * execute in constant time as opposed to having quadratic complexity. Except if high precision + * is enabled, since summed area tables are less precise. */ + if (!data_->high_precision && size[0] > 5.0f) { for (int q = 0; q < 4; q++) { /* A fancy expression to compute the sign of the quadrant q. */ int2 sign = int2((q % 2) * 2 - 1, ((q / 2) * 2 - 1)); @@ -172,10 +174,13 @@ void KuwaharaClassicOperation::update_memory_buffer_partial(MemoryBuffer *output float4 mean_of_squared_color[4] = {float4(0.0f), float4(0.0f), float4(0.0f), float4(0.0f)}; int quadrant_pixel_count[4] = {0, 0, 0, 0}; - const int kernel_size = int(math::max(0.0f, *size_image->get_elem(x, y))); + const float size = *size_image->get_elem(x, y); + const int kernel_size = int(math::max(0.0f, size)); - /* Naive implementation is more accurate for small kernel sizes. */ - if (kernel_size >= 4) { + /* For high radii, we accelerate the filter using a summed area table, making the filter + * execute in constant time as opposed to having quadratic complexity. Except if high precision + * is enabled, since summed area tables are less precise. */ + if (!data_->high_precision && size > 5.0f) { for (int q = 0; q < 4; q++) { /* A fancy expression to compute the sign of the quadrant q. */ int2 sign = int2((q % 2) * 2 - 1, ((q / 2) * 2 - 1)); diff --git a/source/blender/compositor/operations/COM_KuwaharaClassicOperation.h b/source/blender/compositor/operations/COM_KuwaharaClassicOperation.h index da260961378..2b049036b7a 100644 --- a/source/blender/compositor/operations/COM_KuwaharaClassicOperation.h +++ b/source/blender/compositor/operations/COM_KuwaharaClassicOperation.h @@ -9,6 +9,7 @@ namespace blender::compositor { class KuwaharaClassicOperation : public MultiThreadedOperation { + const NodeKuwaharaData *data_; SocketReader *image_reader_; SocketReader *size_reader_; SocketReader *sat_reader_; @@ -17,6 +18,11 @@ class KuwaharaClassicOperation : public MultiThreadedOperation { public: KuwaharaClassicOperation(); + void set_data(const NodeKuwaharaData *data) + { + data_ = data; + } + void init_execution() override; void deinit_execution() override; void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 2d1cb8d02d0..74d9636fd89 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -1067,6 +1067,8 @@ typedef struct NodeKuwaharaData { int uniformity; float sharpness; float eccentricity; + char high_precision; + char _pad[3]; } NodeKuwaharaData; typedef struct NodeAntiAliasingData { diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 2e082fd5edc..6b777951314 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -8582,6 +8582,14 @@ static void def_cmp_kuwahara(StructRNA *srna) RNA_def_property_ui_text(prop, "", "Variation of Kuwahara filter to use"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + prop = RNA_def_property(srna, "use_high_precision", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "high_precision", 1); + RNA_def_property_ui_text( + prop, + "High Precision", + "Uses a more precise but slower method. Use if the output contains undesirable noise"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + prop = RNA_def_property(srna, "uniformity", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, nullptr, "uniformity"); RNA_def_property_range(prop, 0.0, 50.0); diff --git a/source/blender/nodes/composite/nodes/node_composite_kuwahara.cc b/source/blender/nodes/composite/nodes/node_composite_kuwahara.cc index 200e544e092..48b34defeab 100644 --- a/source/blender/nodes/composite/nodes/node_composite_kuwahara.cc +++ b/source/blender/nodes/composite/nodes/node_composite_kuwahara.cc @@ -57,7 +57,10 @@ static void node_composit_buts_kuwahara(uiLayout *layout, bContext * /*C*/, Poin const int variation = RNA_enum_get(ptr, "variation"); - if (variation == CMP_NODE_KUWAHARA_ANISOTROPIC) { + if (variation == CMP_NODE_KUWAHARA_CLASSIC) { + uiItemR(col, ptr, "use_high_precision", UI_ITEM_NONE, nullptr, ICON_NONE); + } + else if (variation == CMP_NODE_KUWAHARA_ANISOTROPIC) { uiItemR(col, ptr, "uniformity", UI_ITEM_NONE, nullptr, ICON_NONE); uiItemR(col, ptr, "sharpness", UI_ITEM_NONE, nullptr, ICON_NONE); uiItemR(col, ptr, "eccentricity", UI_ITEM_NONE, nullptr, ICON_NONE); @@ -88,9 +91,12 @@ class ConvertKuwaharaOperation : public NodeOperation { void execute_classic() { /* For high radii, we accelerate the filter using a summed area table, making the filter - * execute in constant time as opposed to the trivial quadratic complexity. */ + * execute in constant time as opposed to having quadratic complexity. Except if high precision + * is enabled, since summed area tables are less precise. */ Result &size_input = get_input("Size"); - if (size_input.is_single_value() && size_input.get_float_value() > 5.0f) { + if (!node_storage(bnode()).high_precision && + (size_input.is_texture() || size_input.get_float_value() > 5.0f)) + { execute_classic_summed_area_table(); return; }