Brush dynamic ideas.
This discussion is connected to the gimp-developer-list.gnome.org mailing list which is provided by the GIMP developers and not related to gimpusers.com.
This is a read-only list on gimpusers.com so this discussion thread is read-only, too.
Brush dynamic ideas. | Brian Allen Vanderburg II | 25 Oct 10:02 |
Brush dynamic ideas. | Martin Nordholts | 25 Oct 10:16 |
Brush dynamic ideas. | Alexia Death | 25 Oct 10:16 |
Brush dynamic ideas. | Brian Allen Vanderburg II | 25 Oct 21:48 |
Brush dynamic ideas.
I've recently compiled GIMP 2.6 on Linux (Debian 4). It took some work compiling some base libraries but overall was pretty simple. I may start looking though the code some to get acquainted with it mainly for writing custom plugins to do some things I want.
Anyhow I though about some more ideas for a brush dynamics system, these are just some ideas I doubt I could do much with them myself though but I may try to familiarize myself with the code anyhow.
In the brush dynamics system there are input controllers and output attributes (just the words I'm using, may not be the official names). The current system is a little limited because for any given input the same adjustment value applies to any outputs. It's not possible for 'pressure' to affect 'opacity' by 0.5 and 'color' by just 0.2 for instance.
First, the brush dynamics could be a separate dialog, or at least a button for each tool supporting the dynamics could open up the dialog or make it active if it is already opened. Instead of having mappings directly in the dialog, a list would exist supporting add, edit, delete, move up, and move down (and maybe save and load)
Each item in this list corresponds to a single input-output pair mapping. It can not modify the values of the input controller, but it can modify the values of the brush attribute. It also contains an offset and multiplier such that 'pre_output = inputs[selected_input] * multiplier + offset'. It may also be desired to adjust the final attribute in different ways. Two ways would be addition to the current value or multiplication by the current value:
if modify_mode == ADDITION:
outputs[selected_output] += (inputs[selected_input] * multiplier +
offset)
elif modify_mode == MULTIPLICATION:
outputs[selected_output] *= (inputs[selected_input] * multiplier +
offset)
The available outputs would depend on the selected brush type. Common values include:
opacity
hardness
color
size
jitter (if part of brush dynamics, it could be controlled by any of the
inputs)
rate
frame (for animated brushes, could be used to select output image from
the brush)
rotation (the rotation of the brush, could be used for weird calligraphy
effects of oddly shaped brushes, etc)
It has also been mentioned that curves would be nicer. This could be done and would just replace the offset and multiplier:
if modify_mode == ADDITION:
outputs[selected_output] += curve[inputs[selected_input]]
elif modify_mode == MULTIPLICATION:
outputs[selected_output] *= curve[inputs[selected_input]]
Additional input types could also be provided. The only ones may be tilt (for tablets that support them), angle (the angle between the previous mouse position and the current mouse position)
The values of the inputs would have to be calculated before processing the dynamics list, then the list processing is fairly straight forward:
// assume inputs is array of available input control values and
// outputs is the array of final brush attributes before applying the brush
for(node = first ; node ; node = node->next)
{
adjustment = inputs[node->selected_input] * node->multiplier +
node->constant;
// For curves
// adjustment = get_curve_value(node->curve,
inputs[node->selected_input]);
value = outputs[node->selected_output]
switch(node->method)
{
default:
case ADDITION:
value += adjustment;
break;
case MULTIPLICATION:
value *= adjustment;
break;
}
outputs[node->selected_output] = value;
}
// outputs now has the final brush attributes to use
Anyway those are the ideas. I doubt any of those are compatible with the current code though.
Brian Vanderburg II
Brush dynamic ideas.
Brian Allen Vanderburg II wrote:
Anyhow I though about some more ideas for a brush dynamics system, these are just some ideas I doubt I could do much with them myself though but I may try to familiarize myself with the code anyhow.
Hi and thanks for the suggestions! They are however not very original and along the lines of what most other people propose as far as an improved brush dynamics system goes. What is needed is less proposals and more people actually writing some code ;)
It's great to hear you are curious about the code. The best place to hang around when getting acquainted to the code is #gimp @ irc.gnome.org as this is where the core developers also hang around. Feel free to drop by and ask any questions you have. You also probably will want to get in touch with Alexia_Death who has a few brush dynamics hacks up her sleeve.
BR, Martin
Brush dynamic ideas.
On Saturday 25 October 2008 11:02:57 Brian Allen Vanderburg II wrote:
Anyhow I though about some more ideas for a brush dynamics system, these are just some ideas I doubt I could do much with them myself though but I may try to familiarize myself with the code anyhow.
Most of what you have suggested has already been detailed. There is a concept of curves driven dynamics GUI. But there is nobody to realize it. Thats where it is stuck now. I am capable of making the functionality, but not the GUI.
Anyway those are the ideas. I doubt any of those are compatible with the current code though.
Compatible in what sense? The dynamics calculation system is very simple and rather extensible. when a brush needs a value ie "opacity" it queries a function for it. The function returns the mixed value based on the coord set passed an the state of dynamics options. Extending it is very simple. Making the GUI for it and linking it to code is not(at least for me). If you would be interested of working on it, it would be great.
-- Alexia
Brush dynamic ideas.
alexiadeath@gmail.com wrote:
On Saturday 25 October 2008 11:02:57 Brian Allen Vanderburg II wrote:
Anyhow I though about some more ideas for a brush dynamics system, these are just some ideas I doubt I could do much with them myself though but I may try to familiarize myself with the code anyhow.
Most of what you have suggested has already been detailed. There is a concept of curves driven dynamics GUI. But there is nobody to realize it. Thats where it is stuck now. I am capable of making the functionality, but not the GUI.
I'm more of a C++ person (esp when it comes to GUIs) or python and usually use wxPython/wxWidgets so I don't really know much about how GTK works that much.
Anyway those are the ideas. I doubt any of those are compatible with the current code though.
Compatible in what sense? The dynamics calculation system is very simple and rather extensible. when a brush needs a value ie "opacity" it queries a function for it. The function returns the mixed value based on the coord set passed an the state of dynamics options. Extending it is very simple. Making the GUI for it and linking it to code is not(at least for me). If you would be interested of working on it, it would be great.
I've only done a little so far on GIMP 2.6.0 code. I've made a small change that makes it where jitter is controlled also by brush dynamics so that when jitter is enabled and a dynamic is also enabled, it will be used. Tested with velocity, faster motion causes a smaller jitter.
Here is a diff showing whats done. I've no idea if the diff is the correct format as I'm used to doing 'svn diff' and not just 'diff -r oldpath newpath'.
I can't seem to figure what prescale does. No matter how I adjust it in the interface the result always seems the same. If velocity is set to color, slow motion yields the first color and faster motion yields the second color, whether the slider is all the way to the right or the left.
Brian Vanderburg II
Only in gimp-2.6.0/app/gui: gimpdbusservice-glue.h
diff -r gimp-2.6.0/app/paint/gimpbrushcore.c
gimp-2.6.0-new/app/paint/gimpbrushcore.c
641a642
> gdouble real_jitter;
645c646,650
< jitter_dist = g_rand_double_range (core->rand, 0,
core->jitter);
---
> real_jitter = core->jitter *
> gimp_paint_options_get_dynamic_jitter (paint_options,
>
&paint_core->cur_coords);
>
> jitter_dist = g_rand_double_range (core->rand, 0,
real_jitter);
diff -r gimp-2.6.0/app/paint/gimppaintoptions.c
gimp-2.6.0-new/app/paint/gimppaintoptions.c
48a49
> #define DEFAULT_PRESSURE_JITTER FALSE
56a58
> #define DEFAULT_VELOCITY_JITTER FALSE
64a67
> #define DEFAULT_RANDOM_JITTER FALSE
97a101
> PROP_PRESSURE_JITTER,
105a110
> PROP_VELOCITY_JITTER,
113a119
> PROP_RANDOM_JITTER,
218a225,228
> GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_PRESSURE_JITTER,
> "pressure-jitter", NULL,
> DEFAULT_PRESSURE_JITTER,
> GIMP_PARAM_STATIC_STRINGS);
247a258,261
> GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_VELOCITY_JITTER,
> "velocity-jitter", NULL,
> DEFAULT_VELOCITY_JITTER,
> GIMP_PARAM_STATIC_STRINGS);
276a291,294
> GIMP_CONFIG_INSTALL_PROP_BOOLEAN (object_class, PROP_RANDOM_JITTER,
> "random-jitter", NULL,
> DEFAULT_RANDOM_JITTER,
> GIMP_PARAM_STATIC_STRINGS);
457a476,479
> case PROP_PRESSURE_JITTER:
> pressure_options->jitter = g_value_get_boolean (value);
> break;
>
485a508,511
> case PROP_VELOCITY_JITTER:
> velocity_options->jitter = g_value_get_boolean (value);
> break;
>
513a540,543
> case PROP_RANDOM_JITTER:
> random_options->jitter = g_value_get_boolean (value);
> break;
>
647a678,681
> case PROP_PRESSURE_JITTER:
> g_value_set_boolean (value, pressure_options->jitter);
> break;
>
675a710,713
> case PROP_VELOCITY_JITTER:
> g_value_set_boolean (value, velocity_options->jitter);
> break;
>
703a742,745
> case PROP_RANDOM_JITTER:
> g_value_set_boolean (value, random_options->jitter);
> break;
>
1247a1290,1327
>
> gdouble
> gimp_paint_options_get_dynamic_jitter (GimpPaintOptions *paint_options,
> const GimpCoords *coords)
> {
> gdouble jitter = 1.0;
>
> g_return_val_if_fail (GIMP_IS_PAINT_OPTIONS (paint_options), 1.0);
> g_return_val_if_fail (coords != NULL, 1.0);
>
> if (paint_options->pressure_options->jitter ||
> paint_options->velocity_options->jitter ||
> paint_options->random_options->jitter)
> {
> gdouble pressure = -1.0;
> gdouble velocity = -1.0;
> gdouble random = -1.0;
>
> if (paint_options->pressure_options->jitter)
> pressure = GIMP_PAINT_PRESSURE_SCALE * coords->pressure;
>
> if (paint_options->velocity_options->jitter)
> velocity = GIMP_PAINT_VELOCITY_SCALE * (1 - coords->velocity);
>
> if (paint_options->random_options->jitter)
> random = g_random_double_range (0.0, 1.0);
>
> jitter = gimp_paint_options_get_dynamics_mix (pressure,
>
paint_options->pressure_options->prescale,
> velocity,
>
paint_options->velocity_options->prescale,
> random,
>
paint_options->random_options->prescale);
> }
>
> return jitter;
> }
>
diff -r gimp-2.6.0/app/paint/gimppaintoptions.h
gimp-2.6.0-new/app/paint/gimppaintoptions.h
45a46
> gboolean jitter;
156a158,160
> gdouble gimp_paint_options_get_dynamic_jitter (GimpPaintOptions
*paint_options,
> const GimpCoords
*coords);
>
diff -r gimp-2.6.0/app/tools/gimppaintoptions-gui.c
gimp-2.6.0-new/app/tools/gimppaintoptions-gui.c
55a56
> static gboolean tool_has_jitter_dynamics (GType tool_type);
177a179,184
> if (tool_has_jitter_dynamics (tool_type))
> {
> dynamics_labels[n_dynamics] = gtk_label_new(_("Jitter"));
> n_dynamics++;
> }
>
348a356,368
> static gboolean
> tool_has_jitter_dynamics (GType tool_type)
> {
> return (g_type_is_a (tool_type, GIMP_TYPE_PAINTBRUSH_TOOL) ||
> tool_type == GIMP_TYPE_CLONE_TOOL ||
> tool_type == GIMP_TYPE_HEAL_TOOL ||
> tool_type == GIMP_TYPE_PERSPECTIVE_CLONE_TOOL ||
> tool_type == GIMP_TYPE_CONVOLVE_TOOL ||
> tool_type == GIMP_TYPE_SMUDGE_TOOL ||
> tool_type == GIMP_TYPE_DODGE_BURN_TOOL ||
> tool_type == GIMP_TYPE_ERASER_TOOL);
> }
>
452a473,483
> if (tool_has_jitter_dynamics (tool_type))
> {
> button = dynamics_check_button_new (config, "pressure-jitter",
> table, column, row);
> g_signal_connect (button, "size_allocate",
> G_CALLBACK (dynamics_check_button_size_allocate),
> labels[column - 1]);
>
> column++;
> }
>
498a530,535
> if (tool_has_jitter_dynamics (tool_type))
> {
> dynamics_check_button_new (config, "velocity-jitter",
> table, column++, row);
> }
>
544a582,587
> if (tool_has_jitter_dynamics (tool_type))
> {
> dynamics_check_button_new (config, "random-jitter",
> table, column++, row);
> }
>
Only in gimp-2.6.0-new/app/tools: .gimppaintoptions-gui.c.swp
Only in gimp-2.6.0-new: build
Only in gimp-2.6.0/data/tips: gimp-tips.xml
Only in gimp-2.6.0/devel-docs/libgimp: xml
Only in gimp-2.6.0/devel-docs/libgimpbase: xml
Only in gimp-2.6.0/devel-docs/libgimpcolor: xml
Only in gimp-2.6.0/devel-docs/libgimpconfig: xml
Only in gimp-2.6.0/devel-docs/libgimpmath: xml
Only in gimp-2.6.0/devel-docs/libgimpmodule: xml
Only in gimp-2.6.0/devel-docs/libgimpthumb: xml
Only in gimp-2.6.0/devel-docs/libgimpwidgets: xml
Only in gimp-2.6.0/libgimpbase: gimpversion.h
Only in gimp-2.6.0/libgimpwidgets: gimp-wilber-pixbufs.h
Only in gimp-2.6.0/plug-ins/pagecurl: pagecurl-icons.h
Only in gimp-2.6.0/plug-ins/pygimp: gimpthumb.c
Only in gimp-2.6.0/plug-ins/pygimp: gimpui.c
Only in gimp-2.6.0-new: staging