icon pixel compatibility
[ardour.git] / libs / gtkmm2ext / ardour_icon.cc
1 /*
2     Copyright (C) 2009 Paul Davis
3     Copyright (C) 2015 Robin Gareus <robin@gareus.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18
19 */
20
21 #include <math.h> // M_PI
22 #include <assert.h>
23 #include <algorithm> // std:min
24 #include "gtkmm2ext/ardour_icon.h"
25
26 using namespace Gtkmm2ext::ArdourIcon;
27
28 /* general style info:
29  *
30  * - geometry: icons should be centered, spanning
31  *   wh = std::min (width * .5, height *.5) * .55;
32  *
33  * - all shapes should have a contrasting outline
34  *   (usually white foreground, black outline)
35  */
36
37 #define OUTLINEWIDTH 1.5 // px
38
39 #define VECTORICONSTROKEFILL(fillalpha)                    \
40         cairo_set_line_width (cr, OUTLINEWIDTH);           \
41         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);          \
42         cairo_stroke_preserve (cr);                        \
43         cairo_set_source_rgba (cr, 1, 1, 1, (fillalpha));  \
44         cairo_fill (cr);
45
46 #define VECTORICONSTROKEOUTLINE(LW, color)                 \
47         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);     \
48         cairo_set_line_width (cr, (LW) + OUTLINEWIDTH);    \
49         ardour_icon_set_source_inv_rgba (cr, color);       \
50         cairo_stroke_preserve (cr);                        \
51         ardour_icon_set_source_rgba (cr, color);           \
52         cairo_set_line_width (cr, (LW));                   \
53         cairo_stroke (cr);
54
55
56 /** convert 32bit 'RRGGBBAA' to cairo doubles
57  * from libs/canvas/utils.cc and  canvas/types.h: typedef uint32_t Color;
58  */
59 static void ardour_icon_set_source_rgba (cairo_t *cr, uint32_t color)
60 {
61         cairo_set_source_rgba (cr,
62                         ((color >> 24) & 0xff) / 255.0,
63                         ((color >> 16) & 0xff) / 255.0,
64                         ((color >>  8) & 0xff) / 255.0,
65                         ((color >>  0) & 0xff) / 255.0
66                         );
67 }
68
69 /** inverse color */
70 static void ardour_icon_set_source_inv_rgba (cairo_t *cr, uint32_t color)
71 {
72         cairo_set_source_rgba (cr,
73                         1.0 - ((color >> 24) & 0xff) / 255.0,
74                         1.0 - ((color >> 16) & 0xff) / 255.0,
75                         1.0 - ((color >>  8) & 0xff) / 255.0,
76                         ((color >>  0) & 0xff) / 255.0
77                         );
78 }
79
80 /*****************************************************************************
81  * Tool Icons.
82  * Foreground is always white, compatible with small un-blurred rendering.
83  */
84
85 /** internal edit icon */
86 static void icon_tool_content (cairo_t *cr, const int width, const int height) {
87 #define EM_POINT(X,Y) round (x + (X) * em) + .5, round (y + (Y) * em) + .5
88
89                 const double x  = width * .5;
90                 const double y  = height * .5;
91                 const double em = std::min (x, y) * .1; // 1px at 20x20
92
93                 // draw dot outlines (control-points)
94                 cairo_move_to (cr, EM_POINT(-6.0,  0.0));
95                 cairo_close_path (cr);
96                 cairo_move_to (cr, EM_POINT(-2.5,  4.0));
97                 cairo_close_path (cr);
98                 cairo_move_to (cr, EM_POINT( 5.0, -5.0));
99                 cairo_close_path (cr);
100
101                 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
102                 ardour_icon_set_source_inv_rgba (cr, 0xffffffff);
103                 cairo_set_line_width (cr, 3 * em + OUTLINEWIDTH);
104                 cairo_stroke (cr);
105
106                 // "midi note" lines
107                 cairo_move_to (cr, EM_POINT(-7.0, -5.0));
108                 cairo_line_to (cr, EM_POINT( 0.0, -5.0));
109
110                 cairo_move_to (cr, EM_POINT( 2.0,  4.0));
111                 cairo_line_to (cr, EM_POINT( 6.0,  4.0));
112
113                 // automation line (connect control-points)
114                 cairo_move_to (cr, EM_POINT(-6.0,  0.0));
115                 cairo_line_to (cr, EM_POINT(-2.5,  4.0));
116                 cairo_line_to (cr, EM_POINT( 5.0, -5.0));
117
118                 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
119                 VECTORICONSTROKEOUTLINE(1 * em, 0xffffffff);
120
121                 // remove automation line outline at control-points
122                 cairo_move_to (cr, EM_POINT(-6.0,  0.0));
123                 cairo_close_path (cr);
124                 cairo_move_to (cr, EM_POINT(-2.5,  4.0));
125                 cairo_close_path (cr);
126                 cairo_move_to (cr, EM_POINT( 5.0, -5.0));
127                 cairo_close_path (cr);
128
129                 ardour_icon_set_source_rgba (cr, 0xffffffff);
130                 cairo_set_line_width (cr, 3 * em);
131                 cairo_stroke (cr);
132 #undef EM_POINT
133 }
134
135 /** range tool |<->| */
136 static void icon_tool_range (cairo_t *cr, const int width, const int height)
137 {
138         const double x  = width * .5;
139         const double y  = height * .5;
140         const double wh = std::min (x, y) * .55;
141         const double lw = rint (wh / 6.0); // line width
142         const double ar = wh * .6; // arrow
143
144         const double bw = rint (wh);
145         const double y0 = ceil (y);
146         const double ym = rint (y0 - wh * .1) + .5; // arrow-horizontal; slightly to the top, on a px
147         const double x0 = rint(x - wh); // left arrow tip
148         const double x1 = rint(x + wh); // right arrow tip
149
150         // left and right box
151         cairo_rectangle (cr, x0 - lw, y0 - bw, 2 * lw,  2 * bw);
152         VECTORICONSTROKEFILL(1.0);
153         cairo_rectangle (cr, x1 - lw, y0 - bw, 2 * lw,  2 * bw);
154         VECTORICONSTROKEFILL(1.0);
155
156         // arrows
157         cairo_move_to (cr, x0 + ar, ym - ar);
158         cairo_line_to (cr, x0 + .5, ym);
159         cairo_line_to (cr, x0 + ar, ym + ar);
160
161         cairo_move_to (cr, x1 - ar, ym - ar);
162         cairo_line_to (cr, x1 - .5, ym);
163         cairo_line_to (cr, x1 - ar, ym + ar);
164
165         // line connecting the arrows
166         cairo_move_to (cr, x0, ym);
167         cairo_line_to (cr, x1, ym);
168         VECTORICONSTROKEOUTLINE(lw, 0xffffffff);
169
170         cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
171         cairo_rectangle (cr, x0 - lw, y0 - wh, 2 * lw,  2 * wh);
172         cairo_fill (cr);
173         cairo_rectangle (cr, x1 - lw, y0 - wh, 2 * lw,  2 * wh);
174         cairo_fill (cr);
175 }
176
177 /** Grab/Object tool - 6x8em "hand", with 'em' wide index finger. */
178 static void icon_tool_grab (cairo_t *cr, const int width, const int height)
179 {
180         const double x  = width * .5;
181         const double y  = height * .5;
182         const double em = std::min (x, y) * .15; // 1.5px at 20x20
183
184 #define EM_POINT(X,Y) x + (X) * em, y + (Y) * em
185
186         // wrist
187         cairo_move_to (cr, EM_POINT( 2.0,  4.0));
188         cairo_line_to (cr, EM_POINT(-1.5,  4.0));
189         cairo_line_to (cr, EM_POINT(-2.5,  2.0));
190         // thumb
191         cairo_line_to (cr, EM_POINT(-3.0,  1.0));
192
193         // index finger
194         cairo_line_to (cr, EM_POINT(-2.0,  0.0));
195         cairo_line_to (cr, EM_POINT(-2.1, -4.0));
196         cairo_line_to (cr, EM_POINT(-1.5, -4.5));
197         cairo_line_to (cr, EM_POINT(-1.1, -4.0));
198         cairo_line_to (cr, EM_POINT(-1.0,  0.1));
199
200         // middle finger knuckle
201         cairo_line_to (cr, EM_POINT(-0.6,  0.3));
202         cairo_line_to (cr, EM_POINT(-0.3,  0.0));
203         cairo_line_to (cr, EM_POINT(-0.2, -0.2));
204         cairo_line_to (cr, EM_POINT( 0.1, -0.3));
205         cairo_line_to (cr, EM_POINT( 0.4, -0.2));
206         cairo_line_to (cr, EM_POINT( 0.5,  0.1));
207
208         // ring finger knuckle
209         cairo_line_to (cr, EM_POINT( 0.8,  0.4));
210         cairo_line_to (cr, EM_POINT( 1.1,  0.2));
211         cairo_line_to (cr, EM_POINT( 1.2,  0.0));
212         cairo_line_to (cr, EM_POINT( 1.5, -0.1));
213         cairo_line_to (cr, EM_POINT( 1.8,  0.0));
214         cairo_line_to (cr, EM_POINT( 1.9,  0.4));
215
216         // pinky
217         cairo_line_to (cr, EM_POINT( 2.0,  0.6));
218         cairo_line_to (cr, EM_POINT( 2.4,  0.4));
219         cairo_line_to (cr, EM_POINT( 2.8,  0.5));
220         cairo_line_to (cr, EM_POINT( 3.0,  1.0));
221
222         // wrist
223         cairo_line_to (cr, EM_POINT( 3.0,  1.5));
224         cairo_line_to (cr, EM_POINT( 2.0,  4.0));
225
226         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
227         cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
228         VECTORICONSTROKEFILL(1.0);
229 #undef EM_POINT
230 }
231
232 /** cut icon - scissors */
233 static void icon_tool_cut (cairo_t *cr, const int width, const int height)
234 {
235         const double x  = width * .5;
236         const double y  = height * .5;
237         const double em = std::min (x, y) * .1; // 1px at 20x20
238
239 #define EM_POINT(X,Y) x + (X) * em, y + (Y) * em
240
241         cairo_save (cr);
242         cairo_translate (cr, EM_POINT(4, -3));
243         cairo_scale (cr, 1.6, 1.0); // ellipse
244         cairo_arc (cr, 0., 0., 1.5 * em, 0., 2 * M_PI);
245         cairo_restore (cr);
246
247         cairo_move_to (cr, EM_POINT(-6.0,  2.5));
248         cairo_line_to (cr, EM_POINT( 5.5, -2.0));
249
250         cairo_move_to (cr, EM_POINT(-6.0, -2.5));
251         cairo_line_to (cr, EM_POINT( 5.5,  2.0));
252
253         cairo_save (cr);
254         cairo_translate (cr, EM_POINT(4,  3));
255         cairo_scale (cr, 1.6, 1.0); // ellipse
256         cairo_arc (cr, 0., 0., 1.5 * em, 0., 2 * M_PI);
257         cairo_restore (cr);
258
259         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
260         cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
261
262         VECTORICONSTROKEOUTLINE (1.5 * em, 0xffffffff);
263 #undef EM_POINT
264 }
265
266 /** time stretch icon */
267 static void icon_tool_stretch (cairo_t *cr, const int width, const int height)
268 {
269         const double x  = width * .5;
270         const double y  = height * .5;
271         const double wh = std::min (x, y) * .55;
272
273         const double y0 = ceil (y);
274         const double bw = rint (wh);
275         const double lw = rint (wh / 3.0) / 2.0;
276         const double x0 = rint (x + lw) + .5;
277
278         // box indication region
279         cairo_rectangle (cr, x0 - lw - bw - .5, y0 - bw, lw + bw, 2 * bw);
280         VECTORICONSTROKEFILL (0.75);
281
282         cairo_set_line_width (cr, 1.0);
283
284         // inside/left arrow
285         cairo_move_to (cr, x0,          y);
286         cairo_line_to (cr, x0 - lw * 2, y);
287         cairo_line_to (cr, x0 - lw * 2, y - lw * 3.5);
288         cairo_line_to (cr, x0 - lw * 6, y);
289         cairo_line_to (cr, x0 - lw * 2, y + lw * 3.5);
290         cairo_line_to (cr, x0 - lw * 2, y);
291
292         cairo_set_source_rgba (cr, 0, 0, 0, .5);
293         cairo_stroke_preserve (cr);
294         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
295         cairo_fill (cr);
296
297         // outside/right arrow
298         cairo_move_to (cr, x0,          y);
299         cairo_line_to (cr, x0 + lw * 2, y);
300         cairo_line_to (cr, x0 + lw * 2, y - lw * 4);
301         cairo_line_to (cr, x0 + lw * 6, y);
302         cairo_line_to (cr, x0 + lw * 2, y + lw * 4);
303         cairo_line_to (cr, x0 + lw * 2, y);
304
305         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
306         cairo_stroke_preserve (cr);
307         cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
308         cairo_fill (cr);
309 }
310
311 /** audition - small speaker with sound-waves*/
312 static void icon_tool_audition (cairo_t *cr, const int width, const int height)
313 {
314         const double x  = width * .5;
315         const double y  = height * .5;
316         const double em = std::min (x, y) * .1; // 1px at 20x20
317
318 #define EM_POINT(X,Y) x + (X) * em, y + (Y) * em
319
320         cairo_move_to (cr, EM_POINT(-7.0, -2.0));
321         cairo_line_to (cr, EM_POINT(-7.0,  2.0));
322         cairo_line_to (cr, EM_POINT(-6.0,  3.0));
323         cairo_line_to (cr, EM_POINT(-3.0,  3.0));
324         cairo_line_to (cr, EM_POINT( 2.0,  6.0));
325         cairo_line_to (cr, EM_POINT( 2.0, -6.0));
326         cairo_line_to (cr, EM_POINT(-3.0, -3.0));
327         cairo_line_to (cr, EM_POINT(-6.0, -3.0));
328         cairo_close_path (cr);
329
330         cairo_pattern_t *speaker;
331         speaker = cairo_pattern_create_linear (EM_POINT(0, -3.0), EM_POINT(0, 3.0));
332         cairo_pattern_add_color_stop_rgba (speaker, 0.0,  0.8, 0.8, 0.8, 1.0);
333         cairo_pattern_add_color_stop_rgba (speaker, 0.25, 1.0, 1.0, 1.0, 1.0);
334         cairo_pattern_add_color_stop_rgba (speaker, 1.0,  0.6, 0.6, 0.6, 1.0);
335
336         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
337         cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
338         cairo_set_line_width (cr, 1.5);
339         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
340         cairo_stroke_preserve (cr);
341         cairo_set_source (cr, speaker);
342         cairo_fill (cr);
343         cairo_pattern_destroy (speaker);
344
345         // TODO use a slight curve
346         cairo_move_to (cr, EM_POINT(-3.0, -3.0));
347         cairo_line_to (cr, EM_POINT(-3.5,  0.0));
348         cairo_line_to (cr, EM_POINT(-3.0,  3.0));
349         cairo_set_source_rgba (cr, 0, 0, 0, 0.7);
350         cairo_set_line_width (cr, 1.0);
351         cairo_stroke (cr);
352
353
354         cairo_save (cr);
355         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
356         cairo_set_source_rgba (cr, 1, 1, 1, 1);
357
358         cairo_translate (cr, EM_POINT (4.0, 0));
359         cairo_scale (cr, 0.8, 1.25); // ellipse
360
361         cairo_arc (cr, 0, 0, 4 * em, -.5 * M_PI, .5 * M_PI);
362         cairo_set_line_width (cr, .8 * em);
363         cairo_stroke (cr);
364
365         cairo_arc (cr, 0, 0, 2 * em, -.5 * M_PI, .5 * M_PI);
366         cairo_set_line_width (cr, .5 * em);
367         cairo_stroke (cr);
368         cairo_restore (cr);
369 #undef EM_POINT
370 }
371
372 /** pen top-left to bottom right */
373 static void icon_tool_draw (cairo_t *cr, const int width, const int height)
374 {
375         const double x  = width * .5;
376         const double y  = height * .5;
377         const double em = std::min (x, y) * .1; // 1px at 20x20
378
379 #define EM_POINT(X,Y) x + (X) * em, y + (Y) * em
380
381         // pen [6,-5] to [-3, 3]
382         // y = -8 * x / 9 + 1/3
383
384         // top-right end
385         cairo_move_to (cr, EM_POINT( 5.0, -6.11));
386         cairo_line_to (cr, EM_POINT( 6.4, -5.35)); // todo round properly.
387         cairo_line_to (cr, EM_POINT( 7.0, -3.88));
388
389         // bottom-left w/tip
390         cairo_line_to (cr, EM_POINT(-2.0,  4.11));
391         cairo_line_to (cr, EM_POINT(-6.0,  5.66)); // pen tip
392         cairo_line_to (cr, EM_POINT(-4.0,  1.88));
393         cairo_close_path (cr);
394
395         cairo_pattern_t *pen;
396         pen = cairo_pattern_create_linear (EM_POINT(-3.0, -6.0), EM_POINT(6.0, 4.0));
397         cairo_pattern_add_color_stop_rgba (pen, 0.4, 0.6, 0.6, 0.6, 1.0);
398         cairo_pattern_add_color_stop_rgba (pen, 0.5, 1.0, 1.0, 1.0, 1.0);
399         cairo_pattern_add_color_stop_rgba (pen, 0.6, 0.1, 0.1, 0.1, 1.0);
400
401         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
402         cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
403         cairo_set_line_width (cr, em + .5);
404         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
405         cairo_stroke_preserve (cr);
406         cairo_set_source (cr, pen);
407         cairo_fill (cr);
408
409         // separate the tip
410         cairo_move_to (cr, EM_POINT(-2.0,  4.11));
411         cairo_line_to (cr, EM_POINT(-3.0,  2.8)); // slight curve [-3,3]
412         cairo_line_to (cr, EM_POINT(-4.0,  2.0));
413         cairo_set_line_width (cr, OUTLINEWIDTH);
414         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
415         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
416         cairo_stroke (cr);
417
418         // pen tip
419         cairo_move_to (cr, EM_POINT(-5.0, 3.9));
420         cairo_line_to (cr, EM_POINT(-6.0, 5.66));
421         cairo_line_to (cr, EM_POINT(-4.1, 4.9));
422         cairo_close_path (cr);
423         cairo_set_source_rgba (cr, 0, 0, 0, 0.7);
424         cairo_set_line_width (cr, em);
425         cairo_stroke_preserve (cr);
426         cairo_fill (cr);
427
428         cairo_pattern_destroy (pen);
429 #undef EM_POINT
430 }
431
432 /** Toolbar icon - Time Axis View reduce height */
433 static void icon_tav_shrink (cairo_t *cr, const int width, const int height)
434 {
435         const double x = width * .5;
436         const double y = height * .5;
437         const double wh = std::min (x, y) * .66;
438         const double ar = std::min (x, y) * .15;
439         const double tri = .7 * (wh - ar);
440
441         cairo_rectangle (cr, x - wh, y - ar, 2 * wh, 2 * ar);
442         VECTORICONSTROKEFILL(.75);
443
444         cairo_set_line_width (cr, 1.0);
445
446         cairo_move_to (cr, x,       y - ar - 0.5);
447         cairo_line_to (cr, x - tri, y - wh + 0.5);
448         cairo_line_to (cr, x + tri, y - wh + 0.5);
449         cairo_close_path (cr);
450
451         cairo_set_source_rgba (cr, 1, 1, 1, .75);
452         cairo_stroke_preserve (cr);
453         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
454         cairo_fill (cr);
455
456         cairo_move_to (cr, x,       y + ar + 0.5);
457         cairo_line_to (cr, x - tri, y + wh - 0.5);
458         cairo_line_to (cr, x + tri, y + wh - 0.5);
459         cairo_close_path (cr);
460
461         cairo_set_source_rgba (cr, 1, 1, 1, .75);
462         cairo_stroke_preserve (cr);
463         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
464         cairo_fill (cr);
465 }
466
467 /** Toolbar icon - Time Axis View increase height */
468 static void icon_tav_expand (cairo_t *cr, const int width, const int height)
469 {
470         const double x = width * .5;
471         const double y = height * .5;
472         const double wh = std::min (x, y) * .66;
473         const double ar = std::min (x, y) * .15;
474         const double tri = .7 * (wh - ar);
475
476         cairo_rectangle (cr, x - wh, y - wh, 2 * wh, 2 * wh);
477         VECTORICONSTROKEFILL(.75);
478
479         cairo_set_line_width (cr, 1.0);
480
481         cairo_move_to (cr, x,       y - wh + 0.5);
482         cairo_line_to (cr, x - tri, y - ar - 0.5);
483         cairo_line_to (cr, x + tri, y - ar - 0.5);
484         cairo_close_path (cr);
485
486         cairo_set_source_rgba (cr, 1, 1, 1, .5);
487         cairo_stroke_preserve (cr);
488         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
489         cairo_fill (cr);
490
491         cairo_move_to (cr, x      , y + wh - 0.5);
492         cairo_line_to (cr, x - tri, y + ar + 0.5);
493         cairo_line_to (cr, x + tri, y + ar + 0.5);
494         cairo_close_path (cr);
495
496         cairo_set_source_rgba (cr, 1, 1, 1, .5);
497         cairo_stroke_preserve (cr);
498         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
499         cairo_fill (cr);
500 }
501
502
503 /*****************************************************************************
504  * Record enable (transport & track header).
505  *
506  * hardcoded "red" #f46f6f
507  */
508
509 /** standard rec-enable circle */
510 static void icon_rec_enable (cairo_t *cr, const int width, const int height, const Gtkmm2ext::ActiveState state)
511 {
512         const double x = width * .5;
513         const double y = height * .5;
514         const double r = std::min (x, y) * .55;
515         cairo_arc (cr, x, y, r, 0, 2 * M_PI);
516         if (state == Gtkmm2ext::ExplicitActive)
517                 cairo_set_source_rgba (cr, 0.95, 0.10, 0.10, 1.0);
518         else
519                 cairo_set_source_rgba (cr, 0.95, 0.44, 0.44, 1.0); // #f46f6f
520         cairo_fill_preserve (cr);
521         cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.8); // outline
522         cairo_set_line_width (cr, 1);
523         cairo_stroke (cr);
524 }
525
526 /** tape-mode, "reel" */
527 static void icon_rec_tape (cairo_t *cr, const int width, const int height, const Gtkmm2ext::ActiveState state)
528 {
529         const double x = width * .5;
530         const double y = height * .5;
531         const double r = std::min (x, y) * .6;
532         const double slit = .11 * M_PI;
533         cairo_translate (cr, x, y);
534
535         cairo_arc (cr, 0, 0, r, 0, 2 * M_PI);
536         if (state == Gtkmm2ext::ExplicitActive) {
537                 cairo_set_source_rgba (cr, .95, .1, .1, 1.);
538         } else {
539                 cairo_set_source_rgba (cr, .95, .44, .44, 1.); // #f46f6f
540         }
541         cairo_fill_preserve (cr);
542         cairo_set_source_rgba (cr, .0, .0, .0, .5);
543         cairo_set_line_width (cr, 1);
544         cairo_stroke (cr);
545
546         cairo_save (cr);
547         cairo_set_source_rgba (cr, .15, .07, .07, 1.0);
548
549         cairo_rotate (cr, -.5 * M_PI);
550         cairo_move_to (cr, 0, 0);
551         cairo_arc (cr, 0, 0, r *.85, -slit, slit);
552         cairo_line_to (cr, 0, 0);
553         cairo_close_path (cr);
554
555         cairo_fill (cr);
556         cairo_rotate (cr, 2. * M_PI / 3.);
557
558         cairo_move_to (cr, 0, 0);
559         cairo_arc (cr, 0, 0, r *.85, -slit, slit);
560         cairo_line_to (cr, 0, 0);
561         cairo_close_path (cr);
562         cairo_fill (cr);
563
564         cairo_rotate (cr, 2. * M_PI / 3.);
565         cairo_move_to (cr, 0, 0);
566         cairo_arc (cr, 0, 0, r *.85, -slit, slit);
567         cairo_line_to (cr, 0, 0);
568         cairo_close_path (cr);
569         cairo_fill (cr);
570
571         cairo_restore (cr);
572
573         cairo_arc (cr, 0, 0, r * .3, 0, 2 * M_PI);
574         if (state == Gtkmm2ext::ExplicitActive)
575                 cairo_set_source_rgba (cr, .95, .1, .1, 1.);
576         else
577                 cairo_set_source_rgba (cr, .95, .44, .44, 1.); // #f46f6f
578         cairo_fill (cr);
579         cairo_set_source_rgba (cr, .0, .0, .0, 1.0);
580         cairo_arc (cr, 0, 0, r *.15, 0, 2 * M_PI); // hole in the middle
581         cairo_fill (cr);
582 }
583
584
585 /*****************************************************************************
586  * Transport buttons, foreground is always white
587  */
588
589 /** stop square box */
590 static void icon_transport_stop (cairo_t *cr, const int width, const int height)
591 {
592         const int wh = std::min (width, height);
593         cairo_rectangle (cr,
594                         (width - wh) * .5 + wh * .25,
595                         (height - wh) * .5 + wh * .25,
596                         wh * .5, wh * .5);
597         VECTORICONSTROKEFILL(0.9); // small 'shine'
598 }
599
600 /** play triangle */
601 static void icon_transport_play (cairo_t *cr, const int width, const int height)
602 {
603         const int wh = std::min (width, height) * .5;
604         const double y = height * .5;
605         const double x = width - wh;
606
607         const double tri = ceil (.577 * wh); // 1/sqrt(3)
608
609         cairo_move_to (cr,  x + wh * .5, y);
610         cairo_line_to (cr,  x - wh * .5, y - tri);
611         cairo_line_to (cr,  x - wh * .5, y + tri);
612         cairo_close_path (cr);
613
614         VECTORICONSTROKEFILL(0.9);
615 }
616
617 /** Midi Panic "!" */
618 static void icon_transport_panic (cairo_t *cr, const int width, const int height)
619 {
620         const int wh = std::min (width, height) * .1;
621         const double xc = width * .5;
622         const double yh = height;
623         cairo_rectangle (cr,
624                          xc - wh, yh *.19,
625                          wh * 2,  yh *.41);
626         VECTORICONSTROKEFILL(0.9);
627
628         cairo_arc (cr, xc, yh *.75, wh, 0, 2 * M_PI);
629         VECTORICONSTROKEFILL(0.9);
630 }
631
632 /** various combinations of lines and triangles "|>|", ">|" "|>" */
633 static void icon_transport_ck (cairo_t *cr,
634                 const enum Gtkmm2ext::ArdourIcon::Icon icon,
635                 const int width, const int height)
636 {
637         // small play triangle
638         int wh = std::min (width, height);
639         const double y = height * .5;
640         const double x = width - wh * .5;
641         wh *= .18;
642         const double tri = ceil (.577 * wh * 2); // 1/sqrt(3)
643
644         const float ln = std::min (width, height) * .07;
645
646         if (icon == TransportStart || icon == TransportRange) {
647                 cairo_rectangle (cr,
648                                 x - wh - ln, y  - tri * 1.7,
649                                 ln * 2,  tri * 3.4);
650
651                 VECTORICONSTROKEFILL(1.0);
652         }
653
654         if (icon == TransportEnd || icon == TransportRange) {
655                 cairo_rectangle (cr,
656                                 x + wh - ln, y  - tri * 1.7,
657                                 ln * 2,  tri * 3.4);
658
659                 VECTORICONSTROKEFILL(1.0);
660         }
661
662         if (icon == TransportStart) {
663                 cairo_move_to (cr,  x - wh, y);
664                 cairo_line_to (cr,  x + wh, y - tri);
665                 cairo_line_to (cr,  x + wh, y + tri);
666         } else {
667                 cairo_move_to (cr,  x + wh, y);
668                 cairo_line_to (cr,  x - wh, y - tri);
669                 cairo_line_to (cr,  x - wh, y + tri);
670         }
671
672         cairo_close_path (cr);
673         VECTORICONSTROKEFILL(1.0);
674 }
675
676 /** loop spiral */
677 static void icon_transport_loop (cairo_t *cr, const int width, const int height)
678 {
679         const double x = width * .5;
680         const double y = height * .5;
681         const double r = std::min (x, y);
682
683         cairo_arc          (cr, x, y, r * .62, 0, 2 * M_PI);
684         cairo_arc_negative (cr, x, y, r * .35, 2 * M_PI, 0);
685
686         VECTORICONSTROKEFILL(1.0);
687
688 #define ARCARROW(rad, ang) \
689         x + (rad) * sin ((ang) * 2.0 * M_PI), y + (rad) * cos ((ang) * 2.0 * M_PI)
690
691         cairo_move_to (cr, ARCARROW(r * .35, .72));
692         cairo_line_to (cr, ARCARROW(r * .15, .72));
693         cairo_line_to (cr, ARCARROW(r * .56, .60));
694         cairo_line_to (cr, ARCARROW(r * .75, .72));
695         cairo_line_to (cr, ARCARROW(r * .62, .72));
696
697         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
698         cairo_stroke_preserve (cr);
699         cairo_close_path (cr);
700         cairo_set_source_rgba (cr, 1, 1, 1, 1.0);
701         cairo_fill (cr);
702 #undef ARCARROW
703 }
704
705 /** de-construct thorwil's metronom */
706 static void icon_transport_metronom (cairo_t *cr, const int width, const int height)
707 {
708         const double x  = width * .5;
709         const double y  = height * .5;
710         const double wh = std::min (x, y);
711         const double h  = wh * .85;
712         const double w  = wh * .55;
713         const double lw = w  * .34;
714
715         cairo_rectangle (cr,
716                         x - w * .7, y + h * .25,
717                         w * 1.4, lw);
718
719         VECTORICONSTROKEFILL(1.0);
720
721         cairo_move_to (cr,  x - w,       y + h);
722         cairo_line_to (cr,  x + w,       y + h);
723         cairo_line_to (cr,  x + w * .35, y - h);
724         cairo_line_to (cr,  x - w * .35, y - h);
725         cairo_line_to (cr,  x - w,       y + h);
726
727         cairo_move_to (cr,  x - w + lw,       y + h -lw);
728         cairo_line_to (cr,  x - w * .35 + lw, y - h + lw);
729         cairo_line_to (cr,  x + w * .35 - lw, y - h + lw);
730         cairo_line_to (cr,  x + w - lw,       y + h -lw);
731         cairo_line_to (cr,  x - w + lw,       y + h -lw);
732
733         VECTORICONSTROKEFILL(1.0);
734
735         // Pendulum
736         // ddx = .70 w      = .75 * .5 wh              = .375 wh
737         // ddy = .75 h - lw = .75 * .8 wh - wh .5 * .2 = .5 wh
738         // ang = (ddx/ddy):
739         // -> angle = atan (ang) = atan (375 / .5) ~= 36deg
740         const double dx = lw * .2;  // 1 - cos(tan^-1(ang))
741         const double dy = lw * .4;  // 1 - sin(tan^-1(ang))
742         cairo_move_to (cr,  x - w * .3     , y + h * .25 + lw * .5);
743         cairo_line_to (cr,  x - w + dx     , y - h + lw + dy);
744         cairo_line_to (cr,  x - w + lw     , y - h + lw);
745         cairo_line_to (cr,  x - w * .3 + lw, y + h * .25 + lw * .5);
746         cairo_close_path (cr);
747
748         VECTORICONSTROKEFILL(1.0);
749
750         cairo_rectangle (cr,
751                         x - w * .7, y + h * .25,
752                         w * 1.4, lw);
753         cairo_fill (cr);
754 }
755
756
757 /*****************************************************************************
758  * Zoom: In "+", Out "-" and Full "[]"
759  */
760 static void icon_zoom (cairo_t *cr, const enum Gtkmm2ext::ArdourIcon::Icon icon, const int width, const int height, const uint32_t fg_color)
761 {
762         const double x = width * .5;
763         const double y = height * .5;
764         const double r = std::min (x, y) * .7;
765         const double wh = std::min (x, y) * .45;
766
767         // draw handle first
768 #define LINE45DEG(rad) \
769         x + r * (rad) * .707, y + r * (rad) * .707 // sin(45deg) = cos(45deg) = .707
770         cairo_move_to (cr, LINE45DEG(.9));
771         cairo_line_to (cr, LINE45DEG(1.3));
772         cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
773         cairo_set_line_width (cr, 3.0);
774         cairo_set_source_rgba (cr, 0, 0, 0, 1.0);
775         cairo_stroke (cr);
776 #undef LINE45DEG
777
778         // lens
779         ardour_icon_set_source_rgba (cr, fg_color);
780         cairo_arc (cr, x, y, r, 0, 2 * M_PI);
781         cairo_fill_preserve (cr);
782
783         // add a lens gradient
784         cairo_pattern_t *lens;
785         lens = cairo_pattern_create_radial (x - r, y - r, r * .5, x - r, y - r, r * 2);
786         cairo_pattern_add_color_stop_rgba (lens, 0, 1, 1, 1, .4);
787         cairo_pattern_add_color_stop_rgba (lens, 1, 0, 0, 0, .4);
788         cairo_set_source (cr, lens);
789         cairo_fill_preserve (cr);
790         cairo_pattern_destroy (lens);
791
792         // outline
793         cairo_set_line_width (cr, 1.5);
794         //ardour_icon_set_source_inv_rgba (cr, fg_color); // alpha
795         cairo_set_source_rgba (cr, .0, .0, .0, .8);
796         cairo_stroke (cr);
797
798         // add "+", "-" or "[]"
799         cairo_set_line_cap (cr, CAIRO_LINE_CAP_BUTT);
800         cairo_set_line_width (cr, 1.5);
801         ardour_icon_set_source_inv_rgba (cr, fg_color);
802
803         if (icon == ZoomIn || icon == ZoomOut) {
804                 cairo_move_to (cr, x - wh, y);
805                 cairo_line_to (cr, x + wh, y);
806                 cairo_stroke (cr);
807         }
808         if (icon == ZoomIn) {
809                 cairo_move_to (cr, x, y - wh);
810                 cairo_line_to (cr, x, y + wh);
811                 cairo_stroke (cr);
812         }
813         if (icon == ZoomFull) {
814                 const double br0 = std::min (x, y) * .1;
815                 const double br1 = std::min (x, y) * .3;
816                 const double bry = std::min (x, y) * .3;
817                 cairo_move_to (cr, x - br0, y - bry);
818                 cairo_line_to (cr, x - br1, y - bry);
819                 cairo_line_to (cr, x - br1, y + bry);
820                 cairo_line_to (cr, x - br0, y + bry);
821                 cairo_stroke (cr);
822
823                 cairo_move_to (cr, x + br0, y - bry);
824                 cairo_line_to (cr, x + br1, y - bry);
825                 cairo_line_to (cr, x + br1, y + bry);
826                 cairo_line_to (cr, x + br0, y + bry);
827                 cairo_stroke (cr);
828         }
829 }
830
831
832 /*****************************************************************************
833  * Misc buttons
834  */
835
836 /** "close" - "X" , no outline */
837 static void icon_close_cross (cairo_t *cr, const int width, const int height, const uint32_t fg_color)
838 {
839         const double x = width * .5;
840         const double y = height * .5;
841         const double o = .5 + std::min (x, y) * .4;
842         ardour_icon_set_source_rgba (cr, fg_color);
843         cairo_set_line_width (cr, 1);
844         cairo_move_to (cr, x-o, y-o);
845         cairo_line_to (cr, x+o, y+o);
846         cairo_move_to (cr, x+o, y-o);
847         cairo_line_to (cr, x-o, y+o);
848         cairo_stroke (cr);
849 }
850
851 /** "<" */
852 static void icon_nudge_left (cairo_t *cr, const int width, const int height, const uint32_t fg_color)
853 {
854         const double x = width * .5;
855         const double y = height * .5;
856         const double wh = std::min (x, y);
857
858         const double tri_x = .3 * wh;
859         const double tri_y = .6 * wh;
860
861         cairo_move_to (cr, x + tri_x, y - tri_y);
862         cairo_line_to (cr, x - tri_x, y);
863         cairo_line_to (cr, x + tri_x, y + tri_y);
864         VECTORICONSTROKEOUTLINE(1.5, fg_color);
865 }
866
867 /** ">" */
868 static void icon_nudge_right (cairo_t *cr, const int width, const int height, const uint32_t fg_color)
869 {
870
871         const double x = width * .5;
872         const double y = height * .5;
873         const double wh = std::min (x, y);
874
875         const double tri_x = .3 * wh;
876         const double tri_y = .6 * wh;
877
878         cairo_move_to (cr, x - tri_x, y - tri_y);
879         cairo_line_to (cr, x + tri_x, y);
880         cairo_line_to (cr, x - tri_x, y + tri_y);
881         VECTORICONSTROKEOUTLINE(1.5, fg_color);
882
883 }
884
885 /** mixer strip narrow/wide */
886 static void icon_strip_width (cairo_t *cr, const int width, const int height, const uint32_t fg_color)
887 {
888         const double x0 = width   * .2;
889         const double x1 = width   * .8;
890
891         const double y0 = height  * .25;
892         const double y1= height   * .75;
893
894         const double ym= height   * .5;
895
896         // arrow
897         const double xa0= height  * .39;
898         const double xa1= height  * .61;
899         const double ya0= height  * .35;
900         const double ya1= height  * .65;
901
902         ardour_icon_set_source_rgba (cr, fg_color);
903         cairo_set_line_width (cr, 1);
904
905         // left + right
906         cairo_move_to (cr, x0, y0);
907         cairo_line_to (cr, x0, y1);
908         cairo_move_to (cr, x1, y0);
909         cairo_line_to (cr, x1, y1);
910
911         // horiz center line
912         cairo_move_to (cr, x0, ym);
913         cairo_line_to (cr, x1, ym);
914
915         // arrow left
916         cairo_move_to (cr,  x0, ym);
917         cairo_line_to (cr, xa0, ya0);
918         cairo_move_to (cr,  x0, ym);
919         cairo_line_to (cr, xa0, ya1);
920
921         // arrow right
922         cairo_move_to (cr,  x1,  ym);
923         cairo_line_to (cr, xa1, ya0);
924         cairo_move_to (cr,  x1,  ym);
925         cairo_line_to (cr, xa1, ya1);
926         cairo_stroke (cr);
927 }
928
929 /** 5-pin DIN MIDI socket */
930 static void icon_din_midi (cairo_t *cr, const int width, const int height, const uint32_t fg_color)
931 {
932         const double x = width * .5;
933         const double y = height * .5;
934         const double r = std::min (x, y) * .75;
935         ardour_icon_set_source_rgba (cr, fg_color);
936         cairo_set_line_width (cr, 1);
937         cairo_arc (cr, x, y, r, .57 * M_PI, 2.43 * M_PI);
938         cairo_stroke (cr);
939
940         // pins equally spaced 45deg
941         cairo_arc (cr, x, y * 0.5, r * .15, 0, 2 * M_PI);
942         cairo_fill (cr);
943         cairo_arc (cr, x * 0.5, y, r * .15, 0, 2 * M_PI);
944         cairo_fill (cr);
945         cairo_arc (cr, x * 1.5, y, r * .15, 0, 2 * M_PI);
946         cairo_fill (cr);
947         //  .5 + .5 * .5 * sin(45deg),  1.5 - .5 * .5 * cos(45deg)
948         cairo_arc (cr, x * 0.677, y * .677, r * .15, 0, 2 * M_PI);
949         cairo_fill (cr);
950         cairo_arc (cr, x * 1.323, y * .677, r * .15, 0, 2 * M_PI);
951         cairo_fill (cr);
952
953         // bottom notch
954         cairo_arc (cr, x, y+r, r * .26, 1.05 * M_PI, 1.95 * M_PI);
955         cairo_stroke (cr);
956 }
957
958
959 /*****************************************************************************/
960
961 bool
962 Gtkmm2ext::ArdourIcon::render (cairo_t *cr,
963                                const enum Gtkmm2ext::ArdourIcon::Icon icon,
964                                const int width, const int height,
965                                const Gtkmm2ext::ActiveState state,
966                                const uint32_t fg_color)
967 {
968         bool rv = true;
969         cairo_save (cr);
970
971         assert (width > 5 && height > 5);
972
973         switch (icon) {
974                 case TransportStop:
975                         icon_transport_stop (cr, width, height);
976                         break;
977                 case TransportPlay:
978                         icon_transport_play (cr, width, height);
979                         break;
980                 case TransportLoop:
981                         icon_transport_loop (cr, width, height);
982                         break;
983                 case TransportMetronom:
984                         icon_transport_metronom (cr, width, height);
985                         break;
986                 case TransportPanic:
987                         icon_transport_panic (cr, width, height);
988                         break;
989                 case TransportStart: // no break
990                 case TransportEnd:   // no break
991                 case TransportRange:
992                         icon_transport_ck (cr, icon, width, height);
993                         break;
994                 case RecTapeMode:
995                         icon_rec_tape (cr, width, height, state);
996                         break;
997                 case RecButton:
998                         icon_rec_enable (cr, width, height, state);
999                         break;
1000                 case CloseCross:
1001                         icon_close_cross (cr, width, height, fg_color);
1002                         break;
1003                 case StripWidth:
1004                         icon_strip_width (cr, width, height, fg_color);
1005                         break;
1006                 case DinMidi:
1007                         icon_din_midi (cr, width, height, fg_color);
1008                         break;
1009                 case NudgeLeft:
1010                         icon_nudge_left (cr, width, height, fg_color);
1011                         break;
1012                 case NudgeRight:
1013                         icon_nudge_right (cr, width, height, fg_color);
1014                         break;
1015                 case ZoomIn:  // no break
1016                 case ZoomOut: // no break
1017                 case ZoomFull:
1018                         icon_zoom (cr, icon, width, height, fg_color);
1019                         break;
1020                 case TimeAxisShrink:
1021                         icon_tav_shrink (cr, width, height);
1022                         break;
1023                 case TimeAxisExpand:
1024                         icon_tav_expand (cr, width, height);
1025                         break;
1026                 case ToolRange:
1027                         icon_tool_range (cr, width, height);
1028                         break;
1029                 case ToolGrab:
1030                         icon_tool_grab (cr, width, height);
1031                         break;
1032                 case ToolCut:
1033                         icon_tool_cut (cr, width, height);
1034                         break;
1035                 case ToolStretch:
1036                         icon_tool_stretch (cr, width, height);
1037                         break;
1038                 case ToolAudition:
1039                         icon_tool_audition (cr, width, height);
1040                         break;
1041                 case ToolDraw:
1042                         icon_tool_draw (cr, width, height);
1043                         break;
1044                 case ToolContent:
1045                         icon_tool_content (cr, width, height);
1046                         break;
1047                 default:
1048                         rv = false;
1049                         break;
1050         }
1051         cairo_restore (cr);
1052         return rv;
1053 }
1054
1055 #undef VECTORICONSTROKEFILL
1056 #undef VECTORICONSTROKEOUTLINE