ThorwilKnobImageSource: make lamp color themeable
[libprolooks.git] / prolooks / Helpers.vala
blob0b595c8a71443543a7488e35f2c843b8fcdff86e
1 /*
2 Copyright 2009 by Hans Baier
3 License: LGPLv2+
4 */
6 using Gtk;
7 using Gdk;
9 namespace Cairo {
11 public class Color {
12 public double red { get; set; }
13 public double green { get; set; }
14 public double blue { get; set; }
15 public double alpha { get; set; }
17 public Color (double red = 0.0, double green = 0.0, double blue = 0.0, double alpha = 1.0) {
18 this.red = red; this.green = green; this.blue = blue; this.alpha = alpha;
21 public Cairo.Color copy () {
22 return new Cairo.Color (red, green, blue, alpha);
25 public Color.from_string (string webcolor) {
26 set_from_string(webcolor);
29 public Cairo.Color shade (double shade_factor) {
30 Prolooks.HSL hsl = new Prolooks.HSL ();
31 hsl.from_cairo_color (this);
33 hsl.lightness = Math.fmin (hsl.lightness * shade_factor, 1.0);
34 hsl.lightness = Math.fmax (hsl.lightness, 0.0);
36 hsl.saturation = Math.fmin (hsl.saturation * shade_factor, 1.0);
37 hsl.saturation = Math.fmax (hsl.saturation, 0.0);
39 return hsl.to_cairo_color ();
42 public void set_to (Cairo.Color a_color) {
43 red = a_color.red;
44 green = a_color.green;
45 blue = a_color.blue;
46 alpha = a_color.alpha;
49 public void set_as_source_in (Cairo.Context cr) {
50 cr.set_source_rgba (red, green, blue, alpha);
53 public void add_color_stop_to (Cairo.Pattern p, double offset) {
54 p.add_color_stop_rgba (offset, red, green, blue, alpha);
57 public void set_from_string (string webcolor) {
58 set_to(Prolooks.gdk_color_to_cairo(Prolooks.color_from_string(webcolor)));
63 namespace Prolooks {
65 public enum ButtonState {
66 NORMAL,
67 PRESSED;
70 public enum ButtonType {
71 PRESS_BUTTON,
72 TOGGLE_BUTTON;
75 public static void set_line_width_from_device (Cairo.Context cr) {
76 double ux=1, uy=1;
77 cr.device_to_user (ref ux, ref uy);
78 if (ux < uy) {
79 ux = uy;
81 cr.set_line_width (ux);
84 public static Gdk.Color color_from_string (string webcolor) {
85 Gdk.Color color;
86 Gdk.Color.parse(webcolor, out color);
87 return color;
90 public static string color_to_string (Gdk.Color color) {
91 var scale = uint16.MAX / uint8.MAX;
92 return "#%02x%02x%02x".printf (color.red / scale, color.green / scale, color.blue / scale);
95 public static Cairo.Color cairo_color_from_string (string webcolor) {
96 return gdk_color_to_cairo(color_from_string(webcolor));
99 public static void set_source_color (Cairo.Context cr, Gdk.Color color, double alpha = 1.0) {
100 cr.set_source_rgba((double)color.red / (double)uint16.MAX, (double)color.green / (double)uint16.MAX, (double)color.blue / (double)uint16.MAX, alpha);
103 public static void gdk_color_to_cairo_color (Gdk.Color color, ref double red, ref double green, ref double blue) {
104 red = (double)color.red / (double)uint16.MAX;
105 green = (double)color.green / (double)uint16.MAX;
106 blue = (double)color.blue / (double)uint16.MAX;
109 public static Cairo.Color gdk_color_to_cairo (Gdk.Color color) {
110 double r = 0, g = 0, b = 0;
111 gdk_color_to_cairo_color (color, ref r, ref g, ref b);
112 return new Cairo.Color (r, g, b);
115 public static Gdk.Color cairo_color_to_gdk (Cairo.Color cairo_color) {
116 Gdk.Color color = Gdk.Color();
118 color.red = (uint16) (cairo_color.red * (double)uint16.MAX);
119 color.green = (uint16) (cairo_color.green * (double)uint16.MAX);
120 color.blue = (uint16) (cairo_color.blue * (double)uint16.MAX);
122 return color;
125 public static void set_source_color_string (Cairo.Context cr, string color, double alpha = 1.0) {
126 set_source_color (cr, color_from_string (color), alpha);
129 public static void add_color_stop (Cairo.Pattern p, double offset, Gdk.Color color, double alpha = 1.0) {
130 p.add_color_stop_rgba (offset, (double)color.red / (double)uint16.MAX, (double)color.green / (double)uint16.MAX, (double)color.blue / (double)uint16.MAX, alpha);
133 public static void add_color_stop_str (Cairo.Pattern p, double offset, string color, double alpha = 1.0) {
134 add_color_stop (p, offset, color_from_string (color), alpha);
137 public static Cairo.Pattern create_gradient (double x1, double y1, double x2, double y2, Gdk.Color start, Gdk.Color stop, double alpha_start = 1.0, double alpha_stop = 1.0) {
138 Cairo.Pattern gradient = new Cairo.Pattern.linear(x1, y1, x2, y2);
139 add_color_stop(gradient, 0, start, alpha_start);
140 add_color_stop(gradient, 1, stop, alpha_stop);
141 return gradient;
144 public static Cairo.Pattern create_gradient_str (double x1, double y1, double x2, double y2, string start, string stop, double alpha_start = 1.0, double alpha_stop = 1.0) {
145 return create_gradient (x1, y1, x2, y2, color_from_string (start), color_from_string (stop), alpha_start, alpha_stop);
148 public static void rounded_rect (Cairo.Context cr, double x, double y, double w, double h, double radius_x=5, double radius_y=5) {
149 // from mono moonlight aka mono silverlight
150 // test limits (without using multiplications)
151 // http://graphics.stanford.edu/courses/cs248-98-fall/Final/q1.html
152 double ARC_TO_BEZIER = 0.55228475;
154 if (radius_x > w - radius_x)
155 radius_x = w / 2;
156 if (radius_y > h - radius_y)
157 radius_y = h / 2;
159 // approximate (quite close) the arc using a bezier curve
160 var c1 = ARC_TO_BEZIER * radius_x;
161 var c2 = ARC_TO_BEZIER * radius_y;
163 cr.new_path();
164 cr.move_to ( x + radius_x, y);
165 cr.rel_line_to ( w - 2 * radius_x, 0.0);
166 cr.rel_curve_to ( c1, 0.0, radius_x, c2, radius_x, radius_y);
167 cr.rel_line_to ( 0, h - 2 * radius_y);
168 cr.rel_curve_to ( 0.0, c2, c1 - radius_x, radius_y, -radius_x, radius_y);
169 cr.rel_line_to ( -w + 2 * radius_x, 0);
170 cr.rel_curve_to ( -c1, 0, -radius_x, -c2, -radius_x, -radius_y);
171 cr.rel_line_to (0, -h + 2 * radius_y);
172 cr.rel_curve_to (0.0, -c2, radius_x - c1, -radius_y, radius_x, -radius_y);
173 cr.close_path ();
176 public static void background_gradient(Cairo.Context cr, double w, double h) {
177 // outside background
178 Color background_gradient_start;
179 Color background_gradient_stop;
180 Color.parse("#bebdc2", out background_gradient_start);
181 Color.parse("#b1b4b9", out background_gradient_stop);
183 cr.rectangle (0, 0, w, h);
184 Cairo.Pattern background_gradient = new Cairo.Pattern.linear(0, 0, 0, h);
185 add_color_stop(background_gradient, 0, background_gradient_start);
186 add_color_stop(background_gradient, 1, background_gradient_stop);
187 cr.set_source (background_gradient);
188 cr.fill ();
191 public static double modula(double number, double divisor) {
192 return (((int)number % (int)divisor) + (number - (int)number));
195 public class HSL {
196 public double hue {get; set;}
197 public double saturation {get; set;}
198 public double lightness {get; set;}
200 public string to_string() {
201 return "HSL (%f, %f, %f)".printf (hue, saturation, lightness);
204 public Cairo.Color to_cairo_color() {
205 int i;
206 var hue_shift = new double[3];
207 var color_shift = new double[3];
208 double m1;
209 double m2;
210 double m3;
212 if (lightness <= 0.5)
213 m2 = lightness * (1 + saturation);
214 else
215 m2 = lightness + saturation - lightness * saturation;
217 m1 = 2 * lightness - m2;
219 hue_shift[0] = hue + 120;
220 hue_shift[1] = hue;
221 hue_shift[2] = hue - 120;
223 color_shift[0] = color_shift[1] = color_shift[2] = lightness;
225 i = (saturation == 0) ? 3 : 0;
227 for (; i < 3; i++)
229 m3 = hue_shift[i];
231 if (m3 > 360)
232 m3 = modula(m3, 360);
233 else if (m3 < 0)
234 m3 = 360 - modula(Math.fabs(m3), 360);
236 if (m3 < 60)
237 color_shift[i] = m1 + (m2 - m1) * m3 / 60.0;
238 else if (m3 < 180)
239 color_shift[i] = m2;
240 else if (m3 < 240)
241 color_shift[i] = m1 + (m2 - m1) * (240 - m3) / 60.0;
242 else
243 color_shift[i] = m1;
246 Cairo.Color color = new Cairo.Color(color_shift[0], color_shift[1], color_shift[2]);
248 return color;
251 public Gdk.Color to_gdk_color() {
252 return cairo_color_to_gdk(to_cairo_color());
255 public void from_gdk_color(Gdk.Color color) {
256 from_cairo_color(gdk_color_to_cairo(color));
259 public void from_cairo_color(Cairo.Color color) {
260 double min, max, delta;
262 double red = color.red;
263 double green = color.green;
264 double blue = color.blue;
266 if (red > green) {
267 if (red > blue)
268 max = red;
269 else
270 max = blue;
272 if (green < blue)
273 min = green;
274 else
275 min = blue;
276 } else {
277 if (green > blue)
278 max = green;
279 else
280 max = blue;
282 if (red < blue)
283 min = red;
284 else
285 min = blue;
288 lightness = (max + min) / 2.0;
290 if (Math.fabs(max - min) < 0.0001) {
291 hue = 0.0;
292 saturation = 0.0;
293 } else {
294 if (lightness <= 0.5)
295 saturation = (max - min) / (max + min);
296 else
297 saturation = (max - min) / (2.0 - max - min);
299 delta = max -min;
301 if (red == max)
302 hue = (green - blue) / delta;
303 else if (green == max)
304 hue = 2.0 + (blue - red) / delta;
305 else if (blue == max)
306 hue = 4.0 + (red - green) / delta;
308 hue *= 60.0;
309 if (hue < 0.0)
310 hue += 360.0;
315 public class HSV {
316 public double hue {get; set;}
317 public double saturation {get; set;}
318 public double value {get; set;}
320 public string to_string() {
321 return "HSV (%f, %f, %f)".printf (hue, saturation, value);
324 public HSV.for_gdk_color (Gdk.Color color) {
325 from_gdk_color (color);
328 public HSV.for_cairo_color (Cairo.Color color) {
329 from_cairo_color (color);
332 public Cairo.Color to_cairo_color() {
333 double r = 0.0, g = 0.0, b = 0.0;
334 double v = value;
335 int hi;
336 double f, p, q, t;
338 hi = (int) modula(Math.floor(hue / 60.0), 6);
339 f = hue / 60.0 - Math.floor(hue / 60.0);
340 p = value * (1.0 - saturation);
341 q = value * (1.0 - f * saturation);
342 t = value * (1.0 - (1.0 - f) * saturation);
344 switch (hi) {
345 case 0: r = value; g = t; b = p; break;
346 case 1: r = q; g = value; b = p; break;
347 case 2: r = p; g = value; b = t; break;
348 case 3: r = p; g = q; b = value; break;
349 case 4: r = t; g = p; b = value; break;
350 case 5: r = value; g = p; b = q; break;
351 default: break;
354 Cairo.Color color = new Cairo.Color(r, g, b);
356 return color;
359 public Gdk.Color to_gdk_color() {
360 return cairo_color_to_gdk(to_cairo_color());
363 public void from_gdk_color(Gdk.Color color) {
364 from_cairo_color(gdk_color_to_cairo(color));
367 public void from_cairo_color(Cairo.Color color) {
368 double min, max, delta;
370 double red = color.red;
371 double green = color.green;
372 double blue = color.blue;
374 if (red > green) {
375 if (red > blue)
376 max = red;
377 else
378 max = blue;
380 if (green < blue)
381 min = green;
382 else
383 min = blue;
384 } else {
385 if (green > blue)
386 max = green;
387 else
388 max = blue;
390 if (red < blue)
391 min = red;
392 else
393 min = blue;
396 value = max;
398 if (Math.fabs(max - min) < 0.0001)
400 hue = 0.0;
401 saturation = 0.0;
403 else
405 if (max < 0.0001)
406 saturation = 0;
407 else
408 saturation = (max - min) / max;
410 delta = max - min;
412 if (red == max)
413 hue = (green - blue) / delta;
414 else if (green == max)
415 hue = 2.0 + (blue - red) / delta;
416 else if (blue == max)
417 hue = 4.0 + (red - green) / delta;
419 hue *= 60.0;
420 if (hue < 0.0)
421 hue += 360.0;
426 public static Gdk.Color shade_color(Gdk.Color orig, double shade_ratio) {
427 HSL HSL = new HSL ();
428 HSL.from_gdk_color (orig);
430 HSL.lightness = Math.fmin (HSL.lightness * shade_ratio, 1.0);
431 HSL.lightness = Math.fmax (HSL.lightness, 0.0);
433 HSL.saturation = Math.fmin (HSL.saturation * shade_ratio, 1.0);
434 HSL.saturation = Math.fmax (HSL.saturation, 0.0);
436 Gdk.Color result = HSL.to_gdk_color ();
438 return result;
441 public static Gdk.Pixbuf cairo_image_surface_to_pixbuf (Cairo.ImageSurface surface) {
442 if (surface.get_format() != Cairo.Format.ARGB32)
443 return null;
445 weak uchar[] knob_data = surface.get_data ();
447 for (int i = 0; i < surface.get_height() * surface.get_stride(); i += 4) {
448 uchar r = knob_data[i+0];
449 uchar g = knob_data[i+1];
450 uchar b = knob_data[i+2];
451 uchar a = knob_data[i+3];
453 //FIXME: Undo premultiplied alpha here
454 knob_data[i+0] = b;
455 knob_data[i+1] = g;
456 knob_data[i+2] = r;
457 knob_data[i+3] = a;
460 return new Gdk.Pixbuf.from_data (knob_data, Gdk.Colorspace.RGB, true, 8, surface.get_width(), surface.get_height(), surface.get_stride() , null);
463 } // namespace Prolooks