Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc')
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc188
1 files changed, 188 insertions, 0 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc
new file mode 100644
index 00000000000..8d57a3d9152
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/paint_image_2d_curve_mask.cc
@@ -0,0 +1,188 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup ed
+ */
+
+#include "BLI_math.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_brush_types.h"
+
+#include "BKE_brush.h"
+
+#include "paint_intern.h"
+
+namespace blender::ed::sculpt_paint {
+
+constexpr int AntiAliasingSamplesPerTexelAxisMin = 3;
+constexpr int AntiAliasingSamplesPerTexelAxisMax = 16;
+/**
+ * \brief Number of samples to use between 0..1.
+ */
+constexpr int CurveSamplesBaseLen = 1024;
+/**
+ * \brief Number of samples to store in the cache.
+ *
+ * M_SQRT2 is used as brushes are circles and the curve_mask is square.
+ * + 1 to fix floating rounding issues.
+ */
+constexpr int CurveSamplesLen = M_SQRT2 * CurveSamplesBaseLen + 1;
+
+static int aa_samples_per_texel_axis(const Brush *brush, const float radius)
+{
+ int aa_samples = 1.0f / (radius * 0.20f);
+ if (brush->sampling_flag & BRUSH_PAINT_ANTIALIASING) {
+ aa_samples = clamp_i(
+ aa_samples, AntiAliasingSamplesPerTexelAxisMin, AntiAliasingSamplesPerTexelAxisMax);
+ }
+ else {
+ aa_samples = 1;
+ }
+ return aa_samples;
+}
+
+/* create a mask with the falloff strength */
+static void update_curve_mask(CurveMaskCache *curve_mask_cache,
+ const Brush *brush,
+ const int diameter,
+ const float radius,
+ const float cursor_position[2])
+{
+ BLI_assert(curve_mask_cache->curve_mask != nullptr);
+ int offset = (int)floorf(diameter / 2.0f);
+
+ unsigned short *m = curve_mask_cache->curve_mask;
+
+ const int aa_samples = aa_samples_per_texel_axis(brush, radius);
+ const float aa_offset = 1.0f / (2.0f * (float)aa_samples);
+ const float aa_step = 1.0f / (float)aa_samples;
+
+ float bpos[2];
+ bpos[0] = cursor_position[0] - floorf(cursor_position[0]) + offset;
+ bpos[1] = cursor_position[1] - floorf(cursor_position[1]) + offset;
+
+ float weight_factor = 65535.0f / (float)(aa_samples * aa_samples);
+
+ for (int y = 0; y < diameter; y++) {
+ for (int x = 0; x < diameter; x++, m++) {
+ float pixel_xy[2];
+ pixel_xy[0] = static_cast<float>(x) + aa_offset;
+ float total_weight = 0;
+
+ for (int i = 0; i < aa_samples; i++) {
+ pixel_xy[1] = static_cast<float>(y) + aa_offset;
+ for (int j = 0; j < aa_samples; j++) {
+ const float len = len_v2v2(pixel_xy, bpos);
+ const int sample_index = min_ii((len / radius) * CurveSamplesBaseLen,
+ CurveSamplesLen - 1);
+ const float sample_weight = curve_mask_cache->sampled_curve[sample_index];
+
+ total_weight += sample_weight;
+
+ pixel_xy[1] += aa_step;
+ }
+ pixel_xy[0] += aa_step;
+ }
+ *m = (unsigned short)(total_weight * weight_factor);
+ }
+ }
+}
+
+static bool is_sampled_curve_valid(const CurveMaskCache *curve_mask_cache, const Brush *brush)
+{
+ if (curve_mask_cache->sampled_curve == nullptr) {
+ return false;
+ }
+ return curve_mask_cache->last_curve_timestamp == brush->curve->changed_timestamp;
+}
+
+static void sampled_curve_free(CurveMaskCache *curve_mask_cache)
+{
+ MEM_SAFE_FREE(curve_mask_cache->sampled_curve);
+ curve_mask_cache->last_curve_timestamp = 0;
+}
+
+static void update_sampled_curve(CurveMaskCache *curve_mask_cache, const Brush *brush)
+{
+ if (curve_mask_cache->sampled_curve == nullptr) {
+ curve_mask_cache->sampled_curve = static_cast<float *>(
+ MEM_mallocN(CurveSamplesLen * sizeof(float), __func__));
+ }
+
+ for (int i = 0; i < CurveSamplesLen; i++) {
+ const float len = i / float(CurveSamplesBaseLen);
+ const float sample_weight = BKE_brush_curve_strength_clamped(brush, len, 1.0f);
+ curve_mask_cache->sampled_curve[i] = sample_weight;
+ }
+ curve_mask_cache->last_curve_timestamp = brush->curve->changed_timestamp;
+}
+
+static size_t diameter_to_curve_mask_size(const int diameter)
+{
+ return diameter * diameter * sizeof(ushort);
+}
+
+static bool is_curve_mask_size_valid(const CurveMaskCache *curve_mask_cache, const int diameter)
+{
+ return curve_mask_cache->curve_mask_size == diameter_to_curve_mask_size(diameter);
+}
+
+static void curve_mask_free(CurveMaskCache *curve_mask_cache)
+{
+ curve_mask_cache->curve_mask_size = 0;
+ MEM_SAFE_FREE(curve_mask_cache->curve_mask);
+}
+
+static void curve_mask_allocate(CurveMaskCache *curve_mask_cache, const int diameter)
+{
+ const size_t curve_mask_size = diameter_to_curve_mask_size(diameter);
+ curve_mask_cache->curve_mask = static_cast<unsigned short *>(
+ MEM_mallocN(curve_mask_size, __func__));
+ curve_mask_cache->curve_mask_size = curve_mask_size;
+}
+
+} // namespace blender::ed::sculpt_paint
+
+using namespace blender::ed::sculpt_paint;
+
+void paint_curve_mask_cache_free_data(CurveMaskCache *curve_mask_cache)
+{
+ sampled_curve_free(curve_mask_cache);
+ curve_mask_free(curve_mask_cache);
+}
+
+void paint_curve_mask_cache_update(CurveMaskCache *curve_mask_cache,
+ const Brush *brush,
+ const int diameter,
+ const float radius,
+ const float cursor_position[2])
+{
+ if (!is_sampled_curve_valid(curve_mask_cache, brush)) {
+ update_sampled_curve(curve_mask_cache, brush);
+ }
+
+ if (!is_curve_mask_size_valid(curve_mask_cache, diameter)) {
+ curve_mask_free(curve_mask_cache);
+ curve_mask_allocate(curve_mask_cache, diameter);
+ }
+ update_curve_mask(curve_mask_cache, brush, diameter, radius, cursor_position);
+}