Blender V4.3
grease_pencil_tint.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "BKE_attribute.hh"
6#include "BKE_brush.hh"
7#include "BKE_colortools.hh"
8#include "BKE_context.hh"
9#include "BKE_curves.hh"
10#include "BKE_grease_pencil.hh"
11#include "BKE_material.h"
12#include "BKE_paint.hh"
13
14#include "BLI_bounds.hh"
16#include "BLI_math_color.h"
17#include "BLI_math_geom.h"
18
20
21#include "ED_curves.hh"
22#include "ED_grease_pencil.hh"
23#include "ED_view3d.hh"
24
25#include "WM_api.hh"
26#include "WM_types.hh"
27
29
31
32using ed::greasepencil::MutableDrawingInfo;
33
35 public:
36 void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
37 void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
38 void on_stroke_done(const bContext &C) override;
39
40 private:
41 float radius_;
42 float strength_;
43 bool active_layer_only_;
44 ColorGeometry4f color_;
46 Array<Array<float2>> screen_positions_per_drawing_;
47
48 void execute_tint(const bContext &C, const InputSample &extension_sample);
49};
50
51void TintOperation::on_stroke_begin(const bContext &C, const InputSample & /*start_sample*/)
52{
53 using namespace blender::bke::greasepencil;
54 Scene *scene = CTX_data_scene(&C);
56 Brush *brush = BKE_paint_brush(paint);
57
60
61 if (brush->gpencil_settings == nullptr) {
63 }
64 BLI_assert(brush->gpencil_settings != nullptr);
65
67
68 radius_ = brush->size;
69 strength_ = brush->alpha;
70 active_layer_only_ = ((brush->gpencil_settings->flag & GP_BRUSH_ACTIVE_LAYER_ONLY) != 0);
71
72 float4 color_linear;
73 color_linear[3] = 1.0f;
74 srgb_to_linearrgb_v3_v3(color_linear, BKE_brush_color_get(scene, paint, brush));
75
76 color_ = ColorGeometry4f(color_linear);
77
79 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(obact->data);
80
81 if (active_layer_only_) {
82 /* Tint only on the drawings of the active layer. */
83 const Layer *active_layer = grease_pencil.get_active_layer();
84 if (!active_layer) {
85 return;
86 }
88 *scene, grease_pencil, *active_layer);
89 }
90 else {
91 /* Tint on all editable drawings. */
92 drawings_ = ed::greasepencil::retrieve_editable_drawings(*scene, grease_pencil);
93 }
94
95 if (drawings_.is_empty()) {
96 return;
97 }
98
99 ARegion *region = CTX_wm_region(&C);
101 Object *ob_eval = DEG_get_evaluated_object(depsgraph, obact);
102
103 screen_positions_per_drawing_.reinitialize(drawings_.size());
104
105 threading::parallel_for_each(drawings_, [&](const MutableDrawingInfo &drawing_info) {
106 const int drawing_index = (&drawing_info - drawings_.data());
107
108 bke::CurvesGeometry &strokes = drawing_info.drawing.strokes_for_write();
109 const Layer &layer = grease_pencil.layer(drawing_info.layer_index);
110
111 screen_positions_per_drawing_[drawing_index].reinitialize(strokes.points_num());
112
115 ob_eval, *obact, drawing_info.layer_index, drawing_info.frame_number);
116
117 for (const int point : strokes.points_range()) {
119 region,
120 math::transform_point(layer.to_world_space(*ob_eval), deformation.positions[point]),
121 screen_positions_per_drawing_[drawing_index][point],
123 }
124 });
125}
126
127void TintOperation::execute_tint(const bContext &C, const InputSample &extension_sample)
128{
129 if (drawings_.is_empty()) {
130 return;
131 }
132
133 using namespace blender::bke::greasepencil;
134 Scene *scene = CTX_data_scene(&C);
136
138 Brush *brush = BKE_paint_brush(paint);
139
140 /* Get the brush's data. */
141 const float2 mouse_position = extension_sample.mouse_position;
142 float radius = radius_;
143 float strength = strength_;
144 if (BKE_brush_use_size_pressure(brush)) {
146 brush->gpencil_settings->curve_sensitivity, 0, extension_sample.pressure);
147 }
148 if (BKE_brush_use_alpha_pressure(brush)) {
149 strength *= BKE_curvemapping_evaluateF(
150 brush->gpencil_settings->curve_strength, 0, extension_sample.pressure);
151 }
152 /* Attenuate factor to get a smoother tinting. */
153 float fill_strength = strength / 100.0f;
154
155 strength = math::clamp(strength, 0.0f, 1.0f);
156 fill_strength = math::clamp(fill_strength, 0.0f, 1.0f);
157
158 const bool tint_strokes = ELEM(
160 const bool tint_fills = ELEM(
162
163 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(obact->data);
164
165 std::atomic<bool> changed = false;
166 const auto execute_tint_on_drawing = [&](Drawing &drawing, const int drawing_index) {
167 bke::CurvesGeometry &strokes = drawing.strokes_for_write();
168
171 OffsetIndices<int> points_by_curve = strokes.points_by_curve();
172
173 const Span<float2> screen_space_positions =
174 screen_positions_per_drawing_[drawing_index].as_span();
175
176 auto point_inside_stroke = [&](const Span<float2> points, const float2 mouse) {
177 std::optional<Bounds<float2>> bbox = bounds::min_max(points);
178 if (!bbox.has_value()) {
179 return false;
180 }
181 Bounds<float2> &box = bbox.value();
182 if (mouse.x < box.min.x || mouse.x > box.max.x || mouse.y < box.min.y || mouse.y > box.max.y)
183 {
184 return false;
185 }
186 return isect_point_poly_v2(
187 mouse, reinterpret_cast<const float(*)[2]>(points.data()), points.size());
188 };
189
190 threading::parallel_for(strokes.curves_range(), 128, [&](const IndexRange range) {
191 for (const int curve : range) {
192 bool stroke_touched = false;
193 for (const int curve_point : points_by_curve[curve].index_range()) {
194 if (tint_strokes) {
195 const int point = curve_point + points_by_curve[curve].first();
196 const float distance = math::distance(screen_space_positions[point], mouse_position);
197 const float influence = strength * BKE_brush_curve_strength(brush, distance, radius);
198 if (influence > 0.0f) {
199 stroke_touched = true;
200 /* Manually do an alpha-over mix, not using `ColorGeometry4f::premultiply_alpha`
201 * since the vertex color in GPv3 is stored as straight alpha (which is technically
202 * `ColorPaint4f`). */
203 float4 premultiplied;
204 straight_to_premul_v4_v4(premultiplied, vertex_colors[point]);
205 float4 rgba = float4(
206 math::interpolate(float3(premultiplied), float3(color_), influence),
207 vertex_colors[point][3]);
208 rgba[3] = rgba[3] * (1.0f - influence) + influence;
209 premul_to_straight_v4_v4(vertex_colors[point], rgba);
210 }
211 }
212 }
213 if (tint_fills && !fill_colors.is_empty()) {
214 /* Will tint fill color when either the brush being inside the fill region or touching
215 * the stroke. */
216 const bool fill_effective = stroke_touched ||
217 point_inside_stroke(screen_space_positions.slice(
218 points_by_curve[curve].first(),
219 points_by_curve[curve].size()),
220 mouse_position);
221 if (fill_effective) {
222 float4 premultiplied;
223 straight_to_premul_v4_v4(premultiplied, fill_colors[curve]);
224 float4 rgba = float4(
225 math::interpolate(float3(premultiplied), float3(color_), fill_strength),
226 fill_colors[curve][3]);
227 rgba[3] = rgba[3] * (1.0f - fill_strength) + fill_strength;
228 premul_to_straight_v4_v4(fill_colors[curve], rgba);
229 stroke_touched = true;
230 }
231 }
232 if (stroke_touched) {
233 changed.store(true, std::memory_order_relaxed);
234 }
235 }
236 });
237 };
238
239 threading::parallel_for_each(drawings_, [&](const MutableDrawingInfo &info) {
240 const int drawing_index = (&info - drawings_.data());
241 execute_tint_on_drawing(info.drawing, drawing_index);
242 });
243
244 if (changed) {
245 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
246 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
247 }
248}
249
250void TintOperation::on_stroke_extended(const bContext &C, const InputSample &extension_sample)
251{
252 execute_tint(C, extension_sample);
253}
254
256
257std::unique_ptr<GreasePencilStrokeOperation> new_tint_operation()
258{
259 return std::make_unique<TintOperation>();
260}
261
262} // namespace blender::ed::sculpt_paint::greasepencil
bool BKE_brush_use_alpha_pressure(const Brush *brush)
Definition brush.cc:1096
bool BKE_brush_use_size_pressure(const Brush *brush)
Definition brush.cc:1091
void BKE_brush_init_gpencil_settings(Brush *brush)
Definition brush.cc:563
const float * BKE_brush_color_get(const Scene *scene, const Paint *paint, const Brush *brush)
Definition brush.cc:1029
float BKE_curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
void BKE_curvemapping_init(CurveMapping *cumap)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
Low-level operations for curves.
Low-level operations for grease pencil.
General operations, lookup, etc. for materials.
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:477
Brush * BKE_paint_brush(Paint *paint)
Definition paint.cc:649
#define BLI_assert(a)
Definition BLI_assert.h:50
void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
bool isect_point_poly_v2(const float pt[2], const float verts[][2], unsigned int nr)
#define ELEM(...)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ GP_BRUSH_ACTIVE_LAYER_ONLY
@ GPPAINT_MODE_STROKE
@ GPPAINT_MODE_FILL
@ GPPAINT_MODE_BOTH
@ V3D_PROJ_TEST_NOP
Definition ED_view3d.hh:275
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
BPy_StructRNA * depsgraph
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
IndexRange points_range() const
bke::CurvesGeometry & strokes_for_write()
MutableSpan< ColorGeometry4f > fill_colors_for_write()
MutableSpan< ColorGeometry4f > vertex_colors_for_write()
float4x4 to_world_space(const Object &object) const
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval, const Object &ob_orig, int layer_index, int frame)
std::optional< Bounds< T > > min_max(const std::optional< Bounds< T > > &a, const T &b)
Definition BLI_bounds.hh:46
Vector< MutableDrawingInfo > retrieve_editable_drawings_from_layer(const Scene &scene, GreasePencil &grease_pencil, const blender::bke::greasepencil::Layer &layer)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
std::unique_ptr< GreasePencilStrokeOperation > new_tint_operation()
T clamp(const T &a, const T &min, const T &max)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for_each(Range &&range, const Function &function)
Definition BLI_task.hh:58
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:95
VecBase< float, 4 > float4
VecBase< float, 2 > float2
ColorSceneLinear4f< eAlpha::Premultiplied > ColorGeometry4f
Definition BLI_color.hh:337
struct CurveMapping * curve_sensitivity
struct CurveMapping * curve_strength
float alpha
struct CurveMapping * curve
struct BrushGpencilSettings * gpencil_settings
struct ToolSettings * toolsettings
void WM_event_add_notifier(const bContext *C, uint type, void *reference)