1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software  Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edsculpt
22  */
23 
24 #include "MEM_guardedalloc.h"
25 
26 #include "BLI_blenlib.h"
27 #include "BLI_hash.h"
28 #include "BLI_math.h"
29 #include "BLI_math_color_blend.h"
30 #include "BLI_task.h"
31 
32 #include "DNA_mesh_types.h"
33 #include "DNA_meshdata_types.h"
34 
35 #include "BKE_brush.h"
36 #include "BKE_colortools.h"
37 #include "BKE_context.h"
38 #include "BKE_mesh.h"
39 #include "BKE_mesh_mapping.h"
40 #include "BKE_object.h"
41 #include "BKE_paint.h"
42 #include "BKE_pbvh.h"
43 #include "BKE_scene.h"
44 
45 #include "IMB_colormanagement.h"
46 
47 #include "DEG_depsgraph.h"
48 
49 #include "WM_api.h"
50 #include "WM_message.h"
51 #include "WM_toolsystem.h"
52 #include "WM_types.h"
53 
54 #include "ED_object.h"
55 #include "ED_screen.h"
56 #include "ED_sculpt.h"
57 #include "paint_intern.h"
58 #include "sculpt_intern.h"
59 
60 #include "RNA_access.h"
61 #include "RNA_define.h"
62 
63 #include "UI_interface.h"
64 
65 #include "bmesh.h"
66 
67 #include <math.h>
68 #include <stdlib.h>
69 
70 typedef enum eSculptColorFilterTypes {
71   COLOR_FILTER_FILL,
72   COLOR_FILTER_HUE,
73   COLOR_FILTER_SATURATION,
74   COLOR_FILTER_VALUE,
75   COLOR_FILTER_BRIGHTNESS,
76   COLOR_FILTER_CONTRAST,
77   COLOR_FILTER_RED,
78   COLOR_FILTER_GREEN,
79   COLOR_FILTER_BLUE,
80   COLOR_FILTER_SMOOTH,
81 } eSculptColorFilterTypes;
82 
83 static EnumPropertyItem prop_color_filter_types[] = {
84     {COLOR_FILTER_FILL, "FILL", 0, "Fill", "Fill with a specific color"},
85     {COLOR_FILTER_HUE, "HUE", 0, "Hue", "Change hue"},
86     {COLOR_FILTER_SATURATION, "SATURATION", 0, "Saturation", "Change saturation"},
87     {COLOR_FILTER_VALUE, "VALUE", 0, "Value", "Change value"},
88 
89     {COLOR_FILTER_BRIGHTNESS, "BRIGTHNESS", 0, "Brightness", "Change brightness"},
90     {COLOR_FILTER_CONTRAST, "CONTRAST", 0, "Contrast", "Change contrast"},
91 
92     {COLOR_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth colors"},
93 
94     {COLOR_FILTER_RED, "RED", 0, "Red", "Change red channel"},
95     {COLOR_FILTER_GREEN, "GREEN", 0, "Green", "Change green channel"},
96     {COLOR_FILTER_BLUE, "BLUE", 0, "Blue", "Change blue channel"},
97     {0, NULL, 0, NULL, NULL},
98 };
99 
color_filter_task_cb(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict UNUSED (tls))100 static void color_filter_task_cb(void *__restrict userdata,
101                                  const int n,
102                                  const TaskParallelTLS *__restrict UNUSED(tls))
103 {
104   SculptThreadedTaskData *data = userdata;
105   SculptSession *ss = data->ob->sculpt;
106 
107   const int mode = data->filter_type;
108 
109   SculptOrigVertData orig_data;
110   SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
111 
112   PBVHVertexIter vd;
113   BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
114   {
115     SCULPT_orig_vert_data_update(&orig_data, &vd);
116     float orig_color[3], final_color[4], hsv_color[3];
117     int hue;
118     float brightness, contrast, gain, delta, offset;
119     float fade = vd.mask ? *vd.mask : 0.0f;
120     fade = 1.0f - fade;
121     fade *= data->filter_strength;
122     fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index);
123     if (fade == 0.0f) {
124       continue;
125     }
126 
127     copy_v3_v3(orig_color, orig_data.col);
128 
129     switch (mode) {
130       case COLOR_FILTER_FILL: {
131         float fill_color_rgba[4];
132         copy_v3_v3(fill_color_rgba, data->filter_fill_color);
133         fill_color_rgba[3] = 1.0f;
134         fade = clamp_f(fade, 0.0f, 1.0f);
135         mul_v4_fl(fill_color_rgba, fade);
136         blend_color_mix_float(final_color, orig_data.col, fill_color_rgba);
137         break;
138       }
139       case COLOR_FILTER_HUE:
140         rgb_to_hsv_v(orig_color, hsv_color);
141         hue = hsv_color[0] + fade;
142         hsv_color[0] = fabs((hsv_color[0] + fade) - hue);
143         hsv_to_rgb_v(hsv_color, final_color);
144         break;
145       case COLOR_FILTER_SATURATION:
146         rgb_to_hsv_v(orig_color, hsv_color);
147         hsv_color[1] = clamp_f(hsv_color[1] + fade, 0.0f, 1.0f);
148         hsv_to_rgb_v(hsv_color, final_color);
149         break;
150       case COLOR_FILTER_VALUE:
151         rgb_to_hsv_v(orig_color, hsv_color);
152         hsv_color[2] = clamp_f(hsv_color[2] + fade, 0.0f, 1.0f);
153         hsv_to_rgb_v(hsv_color, final_color);
154         break;
155       case COLOR_FILTER_RED:
156         orig_color[0] = clamp_f(orig_color[0] + fade, 0.0f, 1.0f);
157         copy_v3_v3(final_color, orig_color);
158         break;
159       case COLOR_FILTER_GREEN:
160         orig_color[1] = clamp_f(orig_color[1] + fade, 0.0f, 1.0f);
161         copy_v3_v3(final_color, orig_color);
162         break;
163       case COLOR_FILTER_BLUE:
164         orig_color[2] = clamp_f(orig_color[2] + fade, 0.0f, 1.0f);
165         copy_v3_v3(final_color, orig_color);
166         break;
167       case COLOR_FILTER_BRIGHTNESS:
168         fade = clamp_f(fade, -1.0f, 1.0f);
169         brightness = fade;
170         contrast = 0;
171         delta = contrast / 2.0f;
172         gain = 1.0f - delta * 2.0f;
173         delta *= -1;
174         offset = gain * (brightness + delta);
175         for (int i = 0; i < 3; i++) {
176           final_color[i] = clamp_f(gain * orig_color[i] + offset, 0.0f, 1.0f);
177         }
178         break;
179       case COLOR_FILTER_CONTRAST:
180         fade = clamp_f(fade, -1.0f, 1.0f);
181         brightness = 0;
182         contrast = fade;
183         delta = contrast / 2.0f;
184         gain = 1.0f - delta * 2.0f;
185         if (contrast > 0) {
186           gain = 1.0f / ((gain != 0.0f) ? gain : FLT_EPSILON);
187           offset = gain * (brightness - delta);
188         }
189         else {
190           delta *= -1;
191           offset = gain * (brightness + delta);
192         }
193         for (int i = 0; i < 3; i++) {
194           final_color[i] = clamp_f(gain * orig_color[i] + offset, 0.0f, 1.0f);
195         }
196         break;
197       case COLOR_FILTER_SMOOTH: {
198         fade = clamp_f(fade, -1.0f, 1.0f);
199         float smooth_color[4];
200         SCULPT_neighbor_color_average(ss, smooth_color, vd.index);
201         blend_color_interpolate_float(final_color, vd.col, smooth_color, fade);
202         break;
203       }
204     }
205 
206     copy_v3_v3(vd.col, final_color);
207 
208     if (vd.mvert) {
209       vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
210     }
211   }
212   BKE_pbvh_vertex_iter_end;
213   BKE_pbvh_node_mark_update_color(data->nodes[n]);
214 }
215 
sculpt_color_filter_modal(bContext * C,wmOperator * op,const wmEvent * event)216 static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent *event)
217 {
218   Object *ob = CTX_data_active_object(C);
219   SculptSession *ss = ob->sculpt;
220   Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
221   const int mode = RNA_enum_get(op->ptr, "type");
222   float filter_strength = RNA_float_get(op->ptr, "strength");
223 
224   if (event->type == LEFTMOUSE && event->val == KM_RELEASE) {
225     SCULPT_undo_push_end();
226     SCULPT_filter_cache_free(ss);
227     SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR);
228     return OPERATOR_FINISHED;
229   }
230 
231   if (event->type != MOUSEMOVE) {
232     return OPERATOR_RUNNING_MODAL;
233   }
234 
235   const float len = event->prevclickx - event->x;
236   filter_strength = filter_strength * -len * 0.001f;
237 
238   float fill_color[3];
239   RNA_float_get_array(op->ptr, "fill_color", fill_color);
240   IMB_colormanagement_srgb_to_scene_linear_v3(fill_color);
241 
242   SculptThreadedTaskData data = {
243       .sd = sd,
244       .ob = ob,
245       .nodes = ss->filter_cache->nodes,
246       .filter_type = mode,
247       .filter_strength = filter_strength,
248       .filter_fill_color = fill_color,
249   };
250 
251   TaskParallelSettings settings;
252   BLI_parallel_range_settings_defaults(&settings);
253 
254   BKE_pbvh_parallel_range_settings(&settings, true, ss->filter_cache->totnode);
255   BLI_task_parallel_range(0, ss->filter_cache->totnode, &data, color_filter_task_cb, &settings);
256 
257   SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR);
258 
259   return OPERATOR_RUNNING_MODAL;
260 }
261 
sculpt_color_filter_invoke(bContext * C,wmOperator * op,const wmEvent * event)262 static int sculpt_color_filter_invoke(bContext *C, wmOperator *op, const wmEvent *event)
263 {
264   Object *ob = CTX_data_active_object(C);
265   Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
266   SculptSession *ss = ob->sculpt;
267   int mode = RNA_enum_get(op->ptr, "type");
268   PBVH *pbvh = ob->sculpt->pbvh;
269 
270   const bool use_automasking = SCULPT_is_automasking_enabled(sd, ss, NULL);
271   if (use_automasking) {
272     /* Update the active face set manually as the paint cursor is not enabled when using the Mesh
273      * Filter Tool. */
274     float mouse[2];
275     SculptCursorGeometryInfo sgi;
276     mouse[0] = event->mval[0];
277     mouse[1] = event->mval[1];
278     SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
279   }
280 
281   /* Disable for multires and dyntopo for now */
282   if (!ss->pbvh) {
283     return OPERATOR_CANCELLED;
284   }
285   if (BKE_pbvh_type(pbvh) != PBVH_FACES) {
286     return OPERATOR_CANCELLED;
287   }
288 
289   if (!ss->vcol) {
290     return OPERATOR_CANCELLED;
291   }
292 
293   SCULPT_undo_push_begin("color filter");
294 
295   BKE_sculpt_color_layer_create_if_needed(ob);
296 
297   /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of
298    * earlier steps modifying the data. */
299   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
300   const bool needs_topology_info = mode == COLOR_FILTER_SMOOTH || use_automasking;
301   BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, true);
302 
303   if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) {
304     return OPERATOR_CANCELLED;
305   }
306 
307   SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COLOR);
308   FilterCache *filter_cache = ss->filter_cache;
309   filter_cache->active_face_set = SCULPT_FACE_SET_NONE;
310   filter_cache->automasking = SCULPT_automasking_cache_init(sd, NULL, ob);
311 
312   WM_event_add_modal_handler(C, op);
313   return OPERATOR_RUNNING_MODAL;
314 }
315 
SCULPT_OT_color_filter(struct wmOperatorType * ot)316 void SCULPT_OT_color_filter(struct wmOperatorType *ot)
317 {
318   /* identifiers */
319   ot->name = "Filter Color";
320   ot->idname = "SCULPT_OT_color_filter";
321   ot->description = "Applies a filter to modify the current sculpt vertex colors";
322 
323   /* api callbacks */
324   ot->invoke = sculpt_color_filter_invoke;
325   ot->modal = sculpt_color_filter_modal;
326   ot->poll = SCULPT_vertex_colors_poll;
327 
328   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
329 
330   /* rna */
331   RNA_def_enum(ot->srna, "type", prop_color_filter_types, COLOR_FILTER_HUE, "Filter type", "");
332   RNA_def_float(
333       ot->srna, "strength", 1.0f, -10.0f, 10.0f, "Strength", "Filter Strength", -10.0f, 10.0f);
334 
335   PropertyRNA *prop = RNA_def_float_color(
336       ot->srna, "fill_color", 3, NULL, 0.0f, FLT_MAX, "Fill Color", "", 0.0f, 1.0f);
337   RNA_def_property_subtype(prop, PROP_COLOR_GAMMA);
338 }
339