self.set_color_rgb([int(s)/255.0 for s in rest.split()])
elif version <= 1 and command == 'change_radius':
if rest != '0.0': error = 'change_radius is not supported any more'
+ elif version <= 2 and command == 'adapt_color_from_image':
+ if rest != '0.0': error = 'adapt_color_from_image is obsolete, ignored; use smudge and smudge_length instead'
elif version <= 1 and command == 'painting_time':
pass
else:
# mypaint brush file
-color 0 0 0
+# you can edit this file and then select the brush in mypaint (again) to reload
+version 2
opaque 0.4
-opaque_multiply 0.0 | pressure 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
+opaque_multiply 0.0 | pressure (0.000000 0.000000), (1.000000 1.000000)
+opaque_linearize 0.0
radius_logarithmic 2.0
-hardness 0.6 | pressure 1.0 -0.4 0.0 0.0 0.0 0.0 0.0 0.0
+hardness 0.6 | pressure (0.000000 0.000000), (1.000000 -0.400000)
dabs_per_basic_radius 5.0
dabs_per_actual_radius 5.0
dabs_per_second 0.0
radius_by_random 0.0
speed1_slowness 0.04
speed2_slowness 0.8
-offset_by_random 0.8 | pressure 1.0 -0.4 0.0 0.0 0.0 0.0 0.0 0.0 | speed 1.0 0.4 0.0 0.0 0.0 0.0 0.0 0.0
+speed1_gamma -6.69
+speed2_gamma 4.0
+offset_by_random 0.7 | pressure (0.000000 0.000000), (1.000000 -0.400000) | speed1 (0.000000 0.000000), (1.000000 0.200000)
offset_by_speed 0.0
offset_by_speed_slowness 1.0
slow_tracking 0.0
slow_tracking_per_dab 0.0
-color_value 0.0
-color_saturation 0.0
-color_hue 0.0
-adapt_color_from_image 1.0
-change_radius 0.0
+tracking_noise 0.0
+color_h 0.0
+color_s 0.0
+color_v 0.0
+change_color_h 0.0
+change_color_l 0.0
+change_color_hsl_s 0.0
+change_color_v 0.0
+change_color_hsv_s 0.0
+smudge 1.0
+smudge_length 0.0
stroke_treshold 0.0
-opaque_linearize 0.0
+stroke_duration_logarithmic 4.0
+stroke_holdtime 0.0
+custom_input 0.0
+custom_input_slowness 0.0
+++ /dev/null
-# mypaint brush file
-color 214 79 36
-opaque 1.0
-opaque_multiply 0.0 | pressure 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
-radius_logarithmic 2.0
-hardness 1.0
-dabs_per_basic_radius 0.0
-dabs_per_actual_radius 2.0
-dabs_per_second 0.0
-radius_by_random 0.0
-speed1_slowness 0.04
-speed2_slowness 1.0
-offset_by_random 0.0
-offset_by_speed 0.0
-offset_by_speed_slowness 1.0
-slow_tracking 0.0
-slow_tracking_per_dab 0.0
-color_value 0.0
-color_saturation 0.0
-color_hue 0.62
-adapt_color_from_image 0.46
-change_radius 0.0
-opaque_linearize 0.0
# this file saves brushorder
# the first one (upper left) will be selected at startup
-s007
s005
o043
charcoal
ink
pencil
-s006
smudge
blur
+bulk
+s011
+s000
+s006
s008
o011
o033
s016
s017
s018
-s000
s015
-s011
+s007
s023
-bulk
s014
s003
+o007
+old_b004
bi003
s012
bi002
o000
o022
o014
-s027
o018
o945
-o007
o398
-old_b004
o512
o501
o665
o021
o036
o028
-o037
handschrift
o017
old_b024
# mypaint brush file
# you can edit this file and then select the brush in mypaint (again) to reload
version 2
-opaque 1.0 | speed1 (0.000000 0.000000), (1.000000 -0.200000)
-opaque_multiply 0.0 | pressure (0.000000 0.000000), (1.000000 1.000000)
+opaque 0.64 | speed1 (0.000000 0.000000), (1.000000 -0.200000)
+opaque_multiply 0.0 | pressure (0.000000 0.000000), (0.077381 0.197917), (0.229167 0.385417), (0.589286 0.510417), (1.000000 1.000000)
opaque_linearize 0.0
-radius_logarithmic 1.6
-hardness 0.2 | pressure (0.000000 0.000000), (1.000000 0.400000)
+radius_logarithmic 1.9 | pressure (0.000000 0.400000), (1.000000 -0.600000)
+hardness 0.32 | pressure (0.000000 0.000000), (1.000000 0.400000)
dabs_per_basic_radius 0.0
-dabs_per_actual_radius 2.0
+dabs_per_actual_radius 2.66
dabs_per_second 0.0
radius_by_random 0.0
speed1_slowness 0.04
color_s 0.0
color_v 0.0
change_color_h 0.0
-change_color_s 0.0
+change_color_l 0.0
+change_color_hsl_s 0.0
change_color_v 0.0
-adapt_color_from_image 0.0 | pressure (0.000000 -0.000000), (0.033951 0.303333), (0.169753 0.110000), (0.311728 0.036667), (0.478395 0.013333), (1.000000 -0.000000)
+change_color_hsv_s 0.0
+smudge 0.02 | pressure (0.000000 1.000000), (0.166667 1.000000), (0.345238 0.843750), (0.619048 0.364583), (1.000000 -0.093750)
+smudge_length 0.28
stroke_treshold 0.0
stroke_duration_logarithmic 4.0
stroke_holdtime 0.0
# mypaint brush file
-color 0 0 0
-opaque 1.0 | speed 1.0 -0.2 0.0 0.0 0.0 0.0 0.0 0.0
-opaque_multiply 0.0 | pressure 1.0 1.0 0.0 0.0 0.0 0.0 0.0 0.0
+# you can edit this file and then select the brush in mypaint (again) to reload
+version 2
+opaque 1.0 | speed1 (0.000000 0.000000), (1.000000 -0.200000)
+opaque_multiply 0.0 | pressure (0.000000 0.000000), (1.000000 1.000000)
+opaque_linearize 0.0
radius_logarithmic 1.6
-hardness 0.2 | pressure 1.0 0.4 0.0 0.0 0.0 0.0 0.0 0.0
+hardness 0.2 | pressure (0.000000 0.000000), (1.000000 0.400000)
dabs_per_basic_radius 0.0
dabs_per_actual_radius 2.0
dabs_per_second 0.0
radius_by_random 0.0
speed1_slowness 0.04
speed2_slowness 0.8
+speed1_gamma 4.0
+speed2_gamma 4.0
offset_by_random 0.0
offset_by_speed 0.0
offset_by_speed_slowness 1.0
slow_tracking 0.0
slow_tracking_per_dab 0.0
-color_value 0.0
-color_saturation 0.0
-color_hue 0.0
-adapt_color_from_image 0.65
-change_radius 0.0
+tracking_noise 0.0
+color_h 0.0
+color_s 0.0
+color_v 0.0
+change_color_h 0.0
+change_color_l 0.0
+change_color_hsl_s 0.0
+change_color_v 0.0
+change_color_hsv_s 0.0
+smudge 1.0
+smudge_length 0.35
stroke_treshold 0.0
-opaque_linearize 0.0
+stroke_duration_logarithmic 4.0
+stroke_holdtime 0.0
+custom_input 0.0
+custom_input_slowness 0.0
['opaque_linearize', 'opaque linearize', True, 0.0, 0.9, 2.0, "Correct the nonlinearity introduced by blending multiple dabs on top of each other. This correction should get you a linear (\"natural\") pressure response when pressure is mapped to opaque_multiply, as it is usually done. 0.9 is good for standard strokes, set it smaller if your brush scatters a lot, or higher if you use dabs_per_second.\n0.0 the opaque value above is for the individual dabs\n1.0 the opaque value above is for the final brush stroke, assuming each pixel gets (dabs_per_radius*2) brushdabs on average during a stroke"],
['radius_logarithmic', 'radius', False, -2.0, 2.0, 5.0, "basic brush radius (logarithmic)\n 0.7 means 2 pixels\n 3.0 means 20 pixels"],
['hardness', 'hardness', False, 0.0, 0.8, 1.0, "hard brush-circle borders (setting to zero will draw nothing)"],
- ['dabs_per_basic_radius', 'dabs per basic radius', True, 0.0, 0.0, 5.0, "how many dabs to draw while the pointer moves a distance of one brush radius (more precise: the base value of the radius)"],
- ['dabs_per_actual_radius', 'dabs per actual radius', True, 0.0, 2.0, 5.0, "same as above, but the radius actually drawn is used, which can change dynamically"],
+ ['dabs_per_basic_radius', 'dabs per basic radius', True, 0.0, 0.0, 6.0, "how many dabs to draw while the pointer moves a distance of one brush radius (more precise: the base value of the radius)"],
+ ['dabs_per_actual_radius', 'dabs per actual radius', True, 0.0, 2.0, 6.0, "same as above, but the radius actually drawn is used, which can change dynamically"],
['dabs_per_second', 'dabs per second', True, 0.0, 0.0, 80.0, "dabs to draw each second, no matter how far the pointer moves"],
['radius_by_random', 'radius by random', False, 0.0, 0.0, 1.5, "Alter the radius randomly each dab. You can also do this with the by_random input on the radius setting. If you do it here, there are two differences:\n1) the opaque value will be corrected such that a big-radius dabs is more transparent\n2) it will not change the actual radius seen by dabs_per_actual_radius"],
['speed1_slowness', 'speed1 slowness', False, 0.0, 0.04, 0.2, "how slow the input speed1 is following the real speed\n0.0 change immediatly as your speed changes (not recommended, but try it)"],
['color_h', 'color hue', True, 0.0, 0.0, 1.0, "color hue"],
['color_s', 'color saturation', True, -0.5, 0.0, 1.5, "color saturation"],
['color_v', 'color value', True, -0.5, 0.0, 1.5, "color value (brightness, intensity)"],
- ['change_color_h', 'change color hue', False, -2.0, 0.0, 2.0, "change color hue\n-1.0 clockwise color hue shift\n 0.0 disable\n 1.0 counterclockwise hue shift"],
- ['change_color_s', 'change color saturation', False, -2.0, 0.0, 2.0, "change the color saturation\n-1.0 more grayish\n 0.0 disable\n 1.0 more saturated"],
- ['change_color_v', 'change color value', False, -2.0, 0.0, 2.0, "change the color value (brightness, intensity) from the choosen color\n-1.0 darker\n 0.0 disable\n 1.0 brigher"],
- ['adapt_color_from_image', 'adapt color from image', False, 0.0, 0.0, 1.0, "slowly change the color to the one you're painting on (some kind of smudge tool)\nNote that this happens /before/ the hue/saturation/brighness adjustment above: you can get very different effects (eg brighten image) by combining with them."],
+ ['change_color_h', 'change color hue', False, -2.0, 0.0, 2.0, "Change color hue.\n-1.0 clockwise color hue shift\n 0.0 disable\n 1.0 counterclockwise hue shift"],
+ ['change_color_l', 'change color lightness (HSL)', False, -2.0, 0.0, 2.0, "Change the color lightness (luminance) using the HSL color model.\n-1.0 blacker\n 0.0 disable\n 1.0 whiter"],
+ ['change_color_hsl_s', 'change color satur. (HSL)', False, -2.0, 0.0, 2.0, "Change the color saturation using the HSL color model.\n-1.0 more grayish\n 0.0 disable\n 1.0 more saturated"],
+ ['change_color_v', 'change color value (HSV)', False, -2.0, 0.0, 2.0, "Change the color value (brightness, intensity) using the HSV color model. HSV changes are applied before HSL.\n-1.0 darker\n 0.0 disable\n 1.0 brigher"],
+ ['change_color_hsv_s', 'change color satur. (HSV)', False, -2.0, 0.0, 2.0, "Change the color saturation using the HSV color model. HSV changes are applied before HSL.\n-1.0 more grayish\n 0.0 disable\n 1.0 more saturated"],
+ ['smudge', 'smudge', False, 0.0, 0.0, 1.0, "Paint with the smudge color instead of the brush color. The smudge color is slowly changed to the color you are painting on.\n 0.0 do not use the smudge color\n 0.5 mix the smudge color with the brush color\n 1.0 use only the smudge color"],
+ ['smudge_length', 'smudge length', False, 0.0, 0.5, 1.0, "This controls how fast the smudge color becomes the color you are painting on.\n0.0 immediately change the smudge color\n1.0 never change the smudge color (FIXME: initially black!)"],
['stroke_treshold', 'stroke treshold', True, 0.0, 0.0, 0.5, "How much pressure is needed to start a stroke. This affects the stroke input only. Mypaint does not need a minimal pressure to start drawing."],
['stroke_duration_logarithmic', 'stroke duration', False, -1.0, 4.0, 7.0, "How far you have to move until the stroke input reaches 1.0. This value is logarithmic (negative values will not inverse the process)."],
settings_migrate = {
- # old cname new cname scale function
- 'color_hue' : ('change_color_h', lambda y: y*64.0/360.0),
- 'color_saturation' : ('change_color_s', lambda y: y*128.0/256.0),
- 'color_value' : ('change_color_v', lambda y: y*128.0/256.0),
- 'speed_slowness' : ('speed1_slowness', None),
+ # old cname new cname scale function
+ 'color_hue' : ('change_color_h', lambda y: y*64.0/360.0),
+ 'color_saturation' : ('change_color_hsv_s', lambda y: y*128.0/256.0),
+ 'color_value' : ('change_color_v', lambda y: y*128.0/256.0),
+ 'speed_slowness' : ('speed1_slowness', None),
+ 'change_color_s' : ('change_color_hsv_s', None),
}
# the states are not (yet?) exposed to the user
dist # "distance" moved since last dab, a new dab is drawn at 1.0
actual_radius # used by count_dabs_to, thus a state!
-smudge_r, smudge_g, smudge_b # for adapt_color_from_image
+smudge_r, smudge_g, smudge_b
actual_x, actual_y # for slow position
norm_dx_slow, norm_dy_slow # note: now this is dx/dt * (1/radius)
('Undo', None, 'Undo', '<control>Z', None, self.undo_cb),
('Redo', None, 'Redo', '<control>Y', None, self.redo_cb),
('ModifyLastStroke', None, 'Modify Last Stroke', 'm', None, self.modify_last_stroke_cb),
- ('ModifyEnd', None, 'Stop Modifying', '<control>m', None, self.modify_end_cb),
+ ('ModifyEnd', None, 'Stop Modifying', 'n', None, self.modify_end_cb),
('LowerLastStroke', None, 'Lower Last Stroke (Slow!)', 'Page_Down', None, self.lower_or_raise_last_stroke_cb),
('RaiseLastStroke', None, 'Raise Last Stroke', 'Page_Up', None, self.lower_or_raise_last_stroke_cb),
('Darker', None, 'Darker', None, None, self.darker_cb),
('Bigger', None, 'Bigger', 'f', None, self.brush_bigger_cb),
('Smaller', None, 'Smaller', 'd', None, self.brush_smaller_cb),
- ('MoreOpaque', None, 'More Opaque', None, None, self.more_opaque_cb),
- ('LessOpaque', None, 'Less Opaque', None, None, self.less_opaque_cb),
+ ('MoreOpaque', None, 'More Opaque', 's', None, self.more_opaque_cb),
+ ('LessOpaque', None, 'Less Opaque', 'a', None, self.less_opaque_cb),
('PickColor', None, 'Pick Color', 'r', None, self.pick_color_cb),
('ChangeColor', None, 'Change Color', 'v', None, self.change_color_cb),
opaque = settings[BRUSH_OPAQUE] * settings[BRUSH_OPAQUE_MULTIPLY];
if (opaque >= 1.0) opaque = 1.0;
- //if (opaque <= 0.0) opaque = 0.0;
- if (opaque <= 0.0) return 0;
+ if (opaque <= 0.0) opaque = 0.0;
+ //if (opaque == 0.0) return 0; <-- bad idea: need to update smudge state.
if (settings[BRUSH_OPAQUE_LINEARIZE]) {
// OPTIMIZE: no need to recalculate this for each dab
float alpha, beta, alpha_dab, beta_dab;
// color part
float color_h, color_s, color_v;
- if (!settings[BRUSH_ADAPT_COLOR_FROM_IMAGE]) {
+ if (settings[BRUSH_SMUDGE] <= 0.0) {
// normal case (do not smudge)
- // but to allow a smooth transition to smudging, ... FIXME: do what?
color_h = b->settings[BRUSH_COLOR_H]->base_value;
color_s = b->settings[BRUSH_COLOR_S]->base_value;
color_v = b->settings[BRUSH_COLOR_V]->base_value;
- } else {
- // ignore the brush color, only use the smudge state
+ } else if (settings[BRUSH_SMUDGE] >= 1.0) {
+ // smudge only (ignore the original color)
color_h = b->states[STATE_SMUDGE_R];
color_s = b->states[STATE_SMUDGE_G];
color_v = b->states[STATE_SMUDGE_B];
rgb_to_hsv_float (&color_h, &color_s, &color_v);
+ } else {
+ // mix (in RGB) the smudge color with the brush color
+ color_h = b->settings[BRUSH_COLOR_H]->base_value;
+ color_s = b->settings[BRUSH_COLOR_S]->base_value;
+ color_v = b->settings[BRUSH_COLOR_V]->base_value;
+ // XXX clamp??!? before this call?
+ hsv_to_rgb_float (&color_h, &color_s, &color_v);
+ float fac = settings[BRUSH_SMUDGE];
+ color_h = (1-fac)*color_h + fac*b->states[STATE_SMUDGE_R];
+ color_s = (1-fac)*color_s + fac*b->states[STATE_SMUDGE_G];
+ color_v = (1-fac)*color_v + fac*b->states[STATE_SMUDGE_B];
+ rgb_to_hsv_float (&color_h, &color_s, &color_v);
+ }
- { // update the smudge state
- int px, py;
- guchar *rgb;
- float v = settings[BRUSH_ADAPT_COLOR_FROM_IMAGE];
- px = ROUND(x); px = CLAMP(px, 0, s->w-1);
- py = ROUND(y); py = CLAMP(py, 0, s->h-1);
- rgb = PixelXY(s, px, py);
- b->states[STATE_SMUDGE_R] = (1-v)*b->states[STATE_SMUDGE_R] + v*rgb[0]/255.0;
- b->states[STATE_SMUDGE_G] = (1-v)*b->states[STATE_SMUDGE_G] + v*rgb[1]/255.0;
- b->states[STATE_SMUDGE_B] = (1-v)*b->states[STATE_SMUDGE_B] + v*rgb[2]/255.0;
- }
+ // update the smudge state
+ if (settings[BRUSH_SMUDGE_LENGTH] < 1.0) {
+ float fac = settings[BRUSH_SMUDGE_LENGTH];
+ if (fac < 0.0) fac = 0;
+ int px, py;
+ guchar *rgb;
+ px = ROUND(x); px = CLAMP(px, 0, s->w-1);
+ py = ROUND(y); py = CLAMP(py, 0, s->h-1);
+ rgb = PixelXY(s, px, py);
+ b->states[STATE_SMUDGE_R] = fac*b->states[STATE_SMUDGE_R] + (1-fac)*rgb[0]/255.0;
+ b->states[STATE_SMUDGE_G] = fac*b->states[STATE_SMUDGE_G] + (1-fac)*rgb[1]/255.0;
+ b->states[STATE_SMUDGE_B] = fac*b->states[STATE_SMUDGE_B] + (1-fac)*rgb[2]/255.0;
}
+ // HSV color change
color_h += settings[BRUSH_CHANGE_COLOR_H];
- color_s += settings[BRUSH_CHANGE_COLOR_S];
+ color_s += settings[BRUSH_CHANGE_COLOR_HSV_S];
color_v += settings[BRUSH_CHANGE_COLOR_V];
-
+
+
+ // HSL color change
+ if (settings[BRUSH_CHANGE_COLOR_L] || settings[BRUSH_CHANGE_COLOR_HSL_S]) {
+ float h, s, l;
+ // (calculating way too much here, can be optimized if neccessary)
+ hsv_to_rgb_float (&color_h, &color_s, &color_v);
+ rgb_to_hsl_float (&color_h, &color_s, &color_v);
+ color_v += settings[BRUSH_CHANGE_COLOR_L];
+ color_s += settings[BRUSH_CHANGE_COLOR_HSL_S];
+ hsl_to_rgb_float (&color_h, &color_s, &color_v);
+ rgb_to_hsv_float (&color_h, &color_s, &color_v);
+ }
+
{ // final calculations
gint c[3];
* it under the terms of the GNU General Public License.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY. See the COPYING file for more details.
+ *
+ * This file contains some modified code from the GIMP:
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ * Adapted 2007 by Martin Renold to fit into MyPaint.
*/
#include "helpers.h"
}
-// inputs and outputs are all [0,1]
+// (from gimp_rgb_to_hsv)
void
rgb_to_hsv_float (float *r_, float *g_, float *b_)
{
g = *g_;
b = *b_;
+ r = CLAMP(r, 0.0, 1.0);
+ g = CLAMP(g, 0.0, 1.0);
+ b = CLAMP(b, 0.0, 1.0);
+
max = MAX3(r, g, b);
min = MIN3(r, g, b);
*b_ = v;
}
+// (from gimp_hsv_to_rgb)
+void
+hsv_to_rgb_float (float *h_, float *s_, float *v_)
+{
+ gint i;
+ gdouble f, w, q, t;
+ float h, s, v;
+ float r, g, b;
+
+ h = *h_;
+ s = *s_;
+ v = *v_;
+
+ h = h - floor(h);
+ s = CLAMP(s, 0.0, 1.0);
+ v = CLAMP(v, 0.0, 1.0);
+
+ gdouble hue;
+
+ if (s == 0.0)
+ {
+ r = v;
+ g = v;
+ b = v;
+ }
+ else
+ {
+ hue = h;
+
+ if (hue == 1.0)
+ hue = 0.0;
+
+ hue *= 6.0;
+
+ i = (gint) hue;
+ f = hue - i;
+ w = v * (1.0 - s);
+ q = v * (1.0 - (s * f));
+ t = v * (1.0 - (s * (1.0 - f)));
+
+ switch (i)
+ {
+ case 0:
+ r = v;
+ g = t;
+ b = w;
+ break;
+ case 1:
+ r = q;
+ g = v;
+ b = w;
+ break;
+ case 2:
+ r = w;
+ g = v;
+ b = t;
+ break;
+ case 3:
+ r = w;
+ g = q;
+ b = v;
+ break;
+ case 4:
+ r = t;
+ g = w;
+ b = v;
+ break;
+ case 5:
+ r = v;
+ g = w;
+ b = q;
+ break;
+ }
+ }
+
+ *h_ = r;
+ *s_ = g;
+ *v_ = b;
+}
+
+// gimp_rgb_to_hsl:
+void
+rgb_to_hsl_float (float *r_, float *g_, float *b_)
+{
+ gdouble max, min, delta;
+
+ float h, s, l;
+ float r, g, b;
+
+ r = *r_;
+ g = *g_;
+ b = *b_;
+
+ r = CLAMP(r, 0.0, 1.0);
+ g = CLAMP(g, 0.0, 1.0);
+ b = CLAMP(b, 0.0, 1.0);
+
+ max = MAX3(r, g, b);
+ min = MIN3(r, g, b);
+
+ l = (max + min) / 2.0;
+
+ if (max == min)
+ {
+ s = 0.0;
+ h = 0.0; //GIMP_HSL_UNDEFINED;
+ }
+ else
+ {
+ if (l <= 0.5)
+ s = (max - min) / (max + min);
+ else
+ s = (max - min) / (2.0 - max - min);
+
+ delta = max - min;
+
+ if (delta == 0.0)
+ delta = 1.0;
+
+ if (r == max)
+ {
+ h = (g - b) / delta;
+ }
+ else if (g == max)
+ {
+ h = 2.0 + (b - r) / delta;
+ }
+ else if (b == max)
+ {
+ h = 4.0 + (r - g) / delta;
+ }
+
+ h /= 6.0;
+
+ if (h < 0.0)
+ h += 1.0;
+ }
+
+ *r_ = h;
+ *g_ = s;
+ *b_ = l;
+}
+
+static double
+hsl_value (gdouble n1,
+ gdouble n2,
+ gdouble hue)
+{
+ gdouble val;
+
+ if (hue > 6.0)
+ hue -= 6.0;
+ else if (hue < 0.0)
+ hue += 6.0;
+
+ if (hue < 1.0)
+ val = n1 + (n2 - n1) * hue;
+ else if (hue < 3.0)
+ val = n2;
+ else if (hue < 4.0)
+ val = n1 + (n2 - n1) * (4.0 - hue);
+ else
+ val = n1;
+
+ return val;
+}
+
+
+/**
+ * gimp_hsl_to_rgb:
+ * @hsl: A color value in the HSL colorspace
+ * @rgb: The value converted to a value in the RGB colorspace
+ *
+ * Convert a HSL color value to an RGB color value.
+ **/
+void
+hsl_to_rgb_float (float *h_, float *s_, float *l_)
+{
+ float h, s, l;
+ float r, g, b;
+
+ h = *h_;
+ s = *s_;
+ l = *l_;
+
+ h = h - floor(h);
+ s = CLAMP(s, 0.0, 1.0);
+ l = CLAMP(l, 0.0, 1.0);
+
+ if (s == 0)
+ {
+ /* achromatic case */
+ r = l;
+ g = l;
+ b = l;
+ }
+ else
+ {
+ gdouble m1, m2;
+
+ if (l <= 0.5)
+ m2 = l * (1.0 + s);
+ else
+ m2 = l + s - l * s;
+
+ m1 = 2.0 * l - m2;
+
+ r = hsl_value (m1, m2, h * 6.0 + 2.0);
+ g = hsl_value (m1, m2, h * 6.0);
+ b = hsl_value (m1, m2, h * 6.0 - 2.0);
+ }
+
+ *h_ = r;
+ *s_ = g;
+ *l_ = b;
+}
// tested, copied from my mass project
void ExpandRectToIncludePoint(Rect * r, int x, int y)
void rgb_to_hsv_int (gint *red /*h*/, gint *green /*s*/, gint *blue /*v*/);
void hsv_to_rgb_int (gint *hue /*r*/, gint *saturation /*g*/, gint *value /*b*/);
void rgb_to_hsv_float (float *r_ /*h*/, float *g_ /*s*/, float *b_ /*v*/);
+void hsv_to_rgb_float (float *h_, float *s_, float *v_);
+void hsl_to_rgb_float (float *h_, float *s_, float *l_);
+void rgb_to_hsl_float (float *r_, float *g_, float *b_);
typedef struct { int x, y, w, h; } Rect;