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

paint_image_2d_curve_mask.cc « sculpt_paint « editors « blender « source - git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: f5657b004e205d167762b6de1974db306c2f9fb1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/* SPDX-License-Identifier: GPL-2.0-or-later
 * Copyright 2001-2002 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);
  int clamped_radius = max_ff(radius, 1.0);

  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 / clamped_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);
}