The Big Commit (tm): Remove Cairo.Color and most of Gdk.Color usage from libprolooks
[libprolooks.git] / src / Helpers.vala
blobf2185d35bbf37dcced70c4fac4c4ddf9c2d1fe29
1 /*
2 Copyright 2009 by Hans Baier
3 License: LGPLv2+
4 */
6 using Gtk;
7 using Gdk;
9 namespace Prolooks {
10 public enum ButtonState {
11 NORMAL,
12 PRESSED;
15 public enum ButtonType {
16 PRESS_BUTTON,
17 TOGGLE_BUTTON;
20 public static Gdk.RGBA rgba_new (double red, double green, double blue, double alpha = 1.0) {
21 Gdk.RGBA color = RGBA();
22 color.red = red;
23 color.green = green;
24 color.blue = blue;
25 color.alpha = alpha;
26 return color;
29 public static Gdk.RGBA rgba_from_string (string s) {
30 Gdk.RGBA color = RGBA();
31 color.parse(s);
32 color.alpha = 1.0;
33 return color;
36 public static Gdk.RGBA gdk_color_to_rgba (Gdk.Color color) {
37 Gdk.RGBA rgba = RGBA();
38 rgba.red = (double)color.red / (double)uint16.MAX;
39 rgba.green = (double)color.green / (double)uint16.MAX;
40 rgba.blue = (double)color.blue / (double)uint16.MAX;
41 return rgba;
44 public static void add_color_stop_to (Cairo.Pattern p, double offset, Gdk.RGBA color) {
45 p.add_color_stop_rgba (offset, color.red, color.green, color.blue, color.alpha);
48 public static void set_line_width_from_device (Cairo.Context cr) {
49 double ux=1, uy=1;
50 cr.device_to_user (ref ux, ref uy);
51 if (ux < uy) {
52 ux = uy;
54 cr.set_line_width (ux);
57 public static void set_source_color (Cairo.Context cr, Gdk.RGBA color, double alpha = 1.0) {
58 cr.set_source_rgba(color.red, color.green, color.blue, alpha);
61 public static void set_source_color_string (Cairo.Context cr, string color, double alpha = 1.0) {
62 Gdk.RGBA c = rgba_from_string(color);
63 c.alpha = alpha;
64 Gdk.cairo_set_source_rgba (cr, c);
67 public static void add_color_stop (Cairo.Pattern p, double offset, Gdk.RGBA color, double alpha = 1.0) {
68 p.add_color_stop_rgba (offset, color.red, color.green, color.blue, alpha);
71 public static void add_color_stop_str (Cairo.Pattern p, double offset, string color, double alpha = 1.0) {
72 add_color_stop (p, offset, rgba_from_string (color), alpha);
75 public static Cairo.Pattern create_gradient (double x1, double y1, double x2, double y2, Gdk.RGBA start, Gdk.RGBA stop, double alpha_start = 1.0, double alpha_stop = 1.0) {
76 Cairo.Pattern gradient = new Cairo.Pattern.linear(x1, y1, x2, y2);
77 add_color_stop(gradient, 0, start, alpha_start);
78 add_color_stop(gradient, 1, stop, alpha_stop);
79 return gradient;
82 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) {
83 return create_gradient (x1, y1, x2, y2, rgba_from_string (start), rgba_from_string (stop), alpha_start, alpha_stop);
86 public static void rounded_rect (Cairo.Context cr, double x, double y, double w, double h, double radius_x=5, double radius_y=5) {
87 // from mono moonlight aka mono silverlight
88 // test limits (without using multiplications)
89 // http://graphics.stanford.edu/courses/cs248-98-fall/Final/q1.html
90 double ARC_TO_BEZIER = 0.55228475;
92 if (radius_x > w - radius_x)
93 radius_x = w / 2;
94 if (radius_y > h - radius_y)
95 radius_y = h / 2;
97 // approximate (quite close) the arc using a bezier curve
98 var c1 = ARC_TO_BEZIER * radius_x;
99 var c2 = ARC_TO_BEZIER * radius_y;
101 cr.new_path();
102 cr.move_to ( x + radius_x, y);
103 cr.rel_line_to ( w - 2 * radius_x, 0.0);
104 cr.rel_curve_to ( c1, 0.0, radius_x, c2, radius_x, radius_y);
105 cr.rel_line_to ( 0, h - 2 * radius_y);
106 cr.rel_curve_to ( 0.0, c2, c1 - radius_x, radius_y, -radius_x, radius_y);
107 cr.rel_line_to ( -w + 2 * radius_x, 0);
108 cr.rel_curve_to ( -c1, 0, -radius_x, -c2, -radius_x, -radius_y);
109 cr.rel_line_to (0, -h + 2 * radius_y);
110 cr.rel_curve_to (0.0, -c2, radius_x - c1, -radius_y, radius_x, -radius_y);
111 cr.close_path ();
114 public static void background_gradient(Cairo.Context cr, double w, double h) {
115 // outside background
116 Gdk.RGBA background_gradient_start = RGBA();
117 Gdk.RGBA background_gradient_stop = RGBA();
118 background_gradient_start.parse("#bebdc2");
119 background_gradient_stop.parse("#b1b4b9");
121 cr.rectangle (0, 0, w, h);
122 Cairo.Pattern background_gradient = new Cairo.Pattern.linear(0, 0, 0, h);
123 add_color_stop(background_gradient, 0, background_gradient_start);
124 add_color_stop(background_gradient, 1, background_gradient_stop);
125 cr.set_source (background_gradient);
126 cr.fill ();
129 public static double modula(double number, double divisor) {
130 return (((int)number % (int)divisor) + (number - (int)number));
133 public class HSL {
134 public double hue {get; set;}
135 public double saturation {get; set;}
136 public double lightness {get; set;}
137 public double alpha {get; set;}
139 public HSL() {
140 alpha = 1.0;
143 public string to_string() {
144 return "HSL (%f, %f, %f)".printf (hue, saturation, lightness);
147 public Gdk.RGBA to_rgba() {
148 int i;
149 var hue_shift = new double[3];
150 var color_shift = new double[3];
151 double m1;
152 double m2;
153 double m3;
155 if (lightness <= 0.5)
156 m2 = lightness * (1 + saturation);
157 else
158 m2 = lightness + saturation - lightness * saturation;
160 m1 = 2 * lightness - m2;
162 hue_shift[0] = hue + 120;
163 hue_shift[1] = hue;
164 hue_shift[2] = hue - 120;
166 color_shift[0] = color_shift[1] = color_shift[2] = lightness;
168 i = (saturation == 0) ? 3 : 0;
170 for (; i < 3; i++)
172 m3 = hue_shift[i];
174 if (m3 > 360)
175 m3 = modula(m3, 360);
176 else if (m3 < 0)
177 m3 = 360 - modula(Math.fabs(m3), 360);
179 if (m3 < 60)
180 color_shift[i] = m1 + (m2 - m1) * m3 / 60.0;
181 else if (m3 < 180)
182 color_shift[i] = m2;
183 else if (m3 < 240)
184 color_shift[i] = m1 + (m2 - m1) * (240 - m3) / 60.0;
185 else
186 color_shift[i] = m1;
189 Gdk.RGBA color = { color_shift[0], color_shift[1], color_shift[2], alpha};
191 return color;
194 public void from_rgba(Gdk.RGBA color) {
195 double min, max, delta;
197 double red = color.red;
198 double green = color.green;
199 double blue = color.blue;
200 this.alpha = color.alpha;
202 if (red > green) {
203 if (red > blue)
204 max = red;
205 else
206 max = blue;
208 if (green < blue)
209 min = green;
210 else
211 min = blue;
212 } else {
213 if (green > blue)
214 max = green;
215 else
216 max = blue;
218 if (red < blue)
219 min = red;
220 else
221 min = blue;
224 lightness = (max + min) / 2.0;
226 if (Math.fabs(max - min) < 0.0001) {
227 hue = 0.0;
228 saturation = 0.0;
229 } else {
230 if (lightness <= 0.5)
231 saturation = (max - min) / (max + min);
232 else
233 saturation = (max - min) / (2.0 - max - min);
235 delta = max -min;
237 if (red == max)
238 hue = (green - blue) / delta;
239 else if (green == max)
240 hue = 2.0 + (blue - red) / delta;
241 else if (blue == max)
242 hue = 4.0 + (red - green) / delta;
244 hue *= 60.0;
245 if (hue < 0.0)
246 hue += 360.0;
251 public class HSV {
252 public double hue {get; set;}
253 public double saturation {get; set;}
254 public double value {get; set;}
255 public double alpha {get; set;}
257 public HSV() {
258 alpha = 1.0;
261 public string to_string() {
262 return "HSV (%f, %f, %f)".printf (hue, saturation, value);
265 public HSV.for_rgba (Gdk.RGBA color) {
266 from_rgba (color);
269 public Gdk.RGBA to_rgba() {
270 double r = 0.0, g = 0.0, b = 0.0;
271 double v = value;
272 int hi;
273 double f, p, q, t;
275 hi = (int) modula(Math.floor(hue / 60.0), 6);
276 f = hue / 60.0 - Math.floor(hue / 60.0);
277 p = value * (1.0 - saturation);
278 q = value * (1.0 - f * saturation);
279 t = value * (1.0 - (1.0 - f) * saturation);
281 switch (hi) {
282 case 0: r = value; g = t; b = p; break;
283 case 1: r = q; g = value; b = p; break;
284 case 2: r = p; g = value; b = t; break;
285 case 3: r = p; g = q; b = value; break;
286 case 4: r = t; g = p; b = value; break;
287 case 5: r = value; g = p; b = q; break;
288 default: break;
291 Gdk.RGBA color = {r, g, b, alpha };
293 return color;
296 public void from_rgba(Gdk.RGBA color) {
297 double min, max, delta;
299 double red = color.red;
300 double green = color.green;
301 double blue = color.blue;
302 this.alpha = color.alpha;
304 if (red > green) {
305 if (red > blue)
306 max = red;
307 else
308 max = blue;
310 if (green < blue)
311 min = green;
312 else
313 min = blue;
314 } else {
315 if (green > blue)
316 max = green;
317 else
318 max = blue;
320 if (red < blue)
321 min = red;
322 else
323 min = blue;
326 value = max;
328 if (Math.fabs(max - min) < 0.0001)
330 hue = 0.0;
331 saturation = 0.0;
333 else
335 if (max < 0.0001)
336 saturation = 0;
337 else
338 saturation = (max - min) / max;
340 delta = max - min;
342 if (red == max)
343 hue = (green - blue) / delta;
344 else if (green == max)
345 hue = 2.0 + (blue - red) / delta;
346 else if (blue == max)
347 hue = 4.0 + (red - green) / delta;
349 hue *= 60.0;
350 if (hue < 0.0)
351 hue += 360.0;
356 public static Gdk.RGBA rgba_shade(Gdk.RGBA orig, double shade_ratio) {
357 HSL HSL = new HSL ();
358 HSL.from_rgba (orig);
360 HSL.lightness = Math.fmin (HSL.lightness * shade_ratio, 1.0);
361 HSL.lightness = Math.fmax (HSL.lightness, 0.0);
363 HSL.saturation = Math.fmin (HSL.saturation * shade_ratio, 1.0);
364 HSL.saturation = Math.fmax (HSL.saturation, 0.0);
366 Gdk.RGBA result = HSL.to_rgba ();
367 return result;
370 public static Gdk.Pixbuf cairo_image_surface_to_pixbuf (Cairo.ImageSurface surface) {
371 if (surface.get_format() != Cairo.Format.ARGB32)
372 return null;
374 weak uchar[] knob_data = surface.get_data ();
376 for (int i = 0; i < surface.get_height() * surface.get_stride(); i += 4) {
377 uchar r = knob_data[i+0];
378 uchar g = knob_data[i+1];
379 uchar b = knob_data[i+2];
380 uchar a = knob_data[i+3];
382 //FIXME: Undo premultiplied alpha here
383 knob_data[i+0] = b;
384 knob_data[i+1] = g;
385 knob_data[i+2] = r;
386 knob_data[i+3] = a;
389 return new Gdk.Pixbuf.from_data (knob_data, Gdk.Colorspace.RGB, true, 8, surface.get_width(), surface.get_height(), surface.get_stride() , null);
392 } // namespace Prolooks