Blender V5.0
paint_image_2d_curve_mask.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include "DNA_brush_types.h"
12
13#include "BKE_brush.hh"
14
15#include "BLI_math_vector.h"
16
17#include "paint_intern.hh"
18
20
26constexpr int CurveSamplesBaseLen = 1024;
34
35static int aa_samples_per_texel_axis(const Brush *brush, const float radius)
36{
37 int aa_samples = 1.0f / (radius * 0.20f);
39 aa_samples = clamp_i(
41 }
42 else {
43 aa_samples = 1;
44 }
45 return aa_samples;
46}
47
48/* create a mask with the falloff strength */
49static void update_curve_mask(CurveMaskCache *curve_mask_cache,
50 const Brush *brush,
51 const int diameter,
52 const float radius,
53 const float cursor_position[2])
54{
55 BLI_assert(curve_mask_cache->curve_mask != nullptr);
56 int offset = int(floorf(diameter / 2.0f));
57 float clamped_radius = max_ff(radius, 0.5f);
58
59 ushort *m = curve_mask_cache->curve_mask;
60
61 const int aa_samples = aa_samples_per_texel_axis(brush, radius);
62 const float aa_offset = 1.0f / (2.0f * float(aa_samples));
63 const float aa_step = 1.0f / float(aa_samples);
64
65 float bpos[2];
66 bpos[0] = cursor_position[0] - floorf(cursor_position[0]) + offset;
67 bpos[1] = cursor_position[1] - floorf(cursor_position[1]) + offset;
68
69 float weight_factor = 65535.0f / float(aa_samples * aa_samples);
70
71 if (aa_samples == 1) {
72 /* When AA is disabled, snap the cursor to either the corners or centers of the pixels,
73 * depending on if the diameter is even or odd, respectively.*/
74
75 if (int(clamped_radius * 2) % 2 == 0) {
76 bpos[0] = roundf(bpos[0]);
77 bpos[1] = roundf(bpos[1]);
78 }
79 else {
80 bpos[0] = floorf(bpos[0]) + 0.5f;
81 bpos[1] = floorf(bpos[1]) + 0.5f;
82 }
83 }
84
85 for (int y = 0; y < diameter; y++) {
86 for (int x = 0; x < diameter; x++, m++) {
87 float pixel_xy[2];
88 pixel_xy[0] = float(x) + aa_offset;
89 float total_weight = 0;
90
91 for (int i = 0; i < aa_samples; i++) {
92 pixel_xy[1] = float(y) + aa_offset;
93 for (int j = 0; j < aa_samples; j++) {
94 const float len = len_v2v2(pixel_xy, bpos);
95 const int sample_index = min_ii((len / clamped_radius) * CurveSamplesBaseLen,
96 CurveSamplesLen - 1);
97 const float sample_weight = curve_mask_cache->sampled_curve[sample_index];
98
99 total_weight += sample_weight;
100
101 pixel_xy[1] += aa_step;
102 }
103 pixel_xy[0] += aa_step;
104 }
105 *m = ushort(total_weight * weight_factor);
106 }
107 }
108}
109
110static bool is_sampled_curve_valid(const CurveMaskCache *curve_mask_cache, const Brush *brush)
111{
112 if (curve_mask_cache->sampled_curve == nullptr) {
113 return false;
114 }
115 return curve_mask_cache->last_curve_timestamp ==
117}
118
119static void sampled_curve_free(CurveMaskCache *curve_mask_cache)
120{
121 MEM_SAFE_FREE(curve_mask_cache->sampled_curve);
122 curve_mask_cache->last_curve_timestamp = 0;
123}
124
125static void update_sampled_curve(CurveMaskCache *curve_mask_cache, const Brush *brush)
126{
127 if (curve_mask_cache->sampled_curve == nullptr) {
128 curve_mask_cache->sampled_curve = MEM_malloc_arrayN<float>(CurveSamplesLen, __func__);
129 }
130
131 for (int i = 0; i < CurveSamplesLen; i++) {
132 const float len = i / float(CurveSamplesBaseLen);
133 const float sample_weight = BKE_brush_curve_strength_clamped(brush, len, 1.0f);
134 curve_mask_cache->sampled_curve[i] = sample_weight;
135 }
137}
138
139static size_t diameter_to_curve_mask_size(const int diameter)
140{
141 return diameter * diameter * sizeof(ushort);
142}
143
144static bool is_curve_mask_size_valid(const CurveMaskCache *curve_mask_cache, const int diameter)
145{
146 return curve_mask_cache->curve_mask_size == diameter_to_curve_mask_size(diameter);
147}
148
149static void curve_mask_free(CurveMaskCache *curve_mask_cache)
150{
151 curve_mask_cache->curve_mask_size = 0;
152 MEM_SAFE_FREE(curve_mask_cache->curve_mask);
153}
154
155static void curve_mask_allocate(CurveMaskCache *curve_mask_cache, const int diameter)
156{
157 const size_t curve_mask_size = diameter_to_curve_mask_size(diameter);
158 curve_mask_cache->curve_mask = static_cast<ushort *>(MEM_mallocN(curve_mask_size, __func__));
159 curve_mask_cache->curve_mask_size = curve_mask_size;
160}
161
162} // namespace blender::ed::sculpt_paint
163
164using namespace blender::ed::sculpt_paint;
165
167{
168 sampled_curve_free(curve_mask_cache);
169 curve_mask_free(curve_mask_cache);
170}
171
173 const Brush *brush,
174 const int diameter,
175 const float radius,
176 const float cursor_position[2])
177{
178 if (!is_sampled_curve_valid(curve_mask_cache, brush)) {
179 update_sampled_curve(curve_mask_cache, brush);
180 }
181
182 if (!is_curve_mask_size_valid(curve_mask_cache, diameter)) {
183 curve_mask_free(curve_mask_cache);
184 curve_mask_allocate(curve_mask_cache, diameter);
185 }
186 update_curve_mask(curve_mask_cache, brush, diameter, radius, cursor_position);
187}
float BKE_brush_curve_strength_clamped(const Brush *br, float p, float len)
Definition brush.cc:1637
#define BLI_assert(a)
Definition BLI_assert.h:46
MINLINE float max_ff(float a, float b)
MINLINE int min_ii(int a, int b)
MINLINE int clamp_i(int value, int min, int max)
#define M_SQRT2
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
unsigned short ushort
@ BRUSH_PAINT_ANTIALIASING
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
nullptr float
#define roundf(x)
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
constexpr int CurveSamplesBaseLen
Number of samples to use between 0..1.
static void sampled_curve_free(CurveMaskCache *curve_mask_cache)
static int aa_samples_per_texel_axis(const Brush *brush, const float radius)
static void update_curve_mask(CurveMaskCache *curve_mask_cache, const Brush *brush, const int diameter, const float radius, const float cursor_position[2])
static size_t diameter_to_curve_mask_size(const int diameter)
static void curve_mask_free(CurveMaskCache *curve_mask_cache)
static bool is_sampled_curve_valid(const CurveMaskCache *curve_mask_cache, const Brush *brush)
static void curve_mask_allocate(CurveMaskCache *curve_mask_cache, const int diameter)
static bool is_curve_mask_size_valid(const CurveMaskCache *curve_mask_cache, const int diameter)
static void update_sampled_curve(CurveMaskCache *curve_mask_cache, const Brush *brush)
constexpr int CurveSamplesLen
Number of samples to store in the cache.
void paint_curve_mask_cache_free_data(CurveMaskCache *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])
#define floorf
struct CurveMapping * curve_distance_falloff
int sampling_flag
Caching structure for curve mask.
ushort * curve_mask
Curve mask that can be passed as curve_mask parameter when.
float * sampled_curve
sampled version of the brush curve-mapping.
int last_curve_timestamp
Last CurveMapping.changed_timestamp being read.
size_t curve_mask_size
Size in bytes of the curve_mask field.
i
Definition text_draw.cc:230
uint len