switched graphics library from arts to cairo+pango
[rrdtool.git] / src / rrd_gfx.c
1 /****************************************************************************
2  * RRDtool 1.2.23  Copyright by Tobi Oetiker, 1997-2007
3  ****************************************************************************
4  * rrd_gfx.c  graphics wrapper for rrdtool
5   **************************************************************************/
6
7 /* #define DEBUG */
8
9 /* stupid MSVC doesnt support variadic macros = no debug for now! */
10 #ifdef _MSC_VER
11 # define RRDPRINTF()
12 #else
13 # ifdef DEBUG
14 #  define RRDPRINTF(...)  fprintf(stderr, __VA_ARGS__);
15 # else
16 #  define RRDPRINTF(...)
17 # endif                         /* DEBUG */
18 #endif                          /* _MSC_VER */
19
20 #include "rrd_tool.h"
21 #include "rrd_gfx.h"
22
23
24 /* create a new line */
25 void gfx_line(
26     cairo_t * cr,
27     double X0,
28     double Y0,
29     double X1,
30     double Y1,
31     double width,
32     gfx_color_t color)
33 {
34     gfx_dashed_line(cr, X0, Y0, X1, Y1, width, color, 0, 0);
35 }
36
37 void gfx_dashed_line(
38     cairo_t * cr,
39     double X0,
40     double Y0,
41     double X1,
42     double Y1,
43     double width,
44     gfx_color_t color,
45     double dash_on,
46     double dash_off)
47 {
48     double    dashes[] = { dash_on, dash_off };
49     double    x = 0;
50     double    y = 0;
51
52     cairo_save(cr);
53     cairo_new_path(cr);
54     cairo_set_line_width(cr, width);
55     gfx_line_fit(cr, &x, &y);
56     gfx_line_fit(cr, &X0, &Y0);
57     cairo_move_to(cr, X0, Y0);
58     gfx_line_fit(cr, &X1, &Y1);
59     cairo_line_to(cr, X1, Y1);
60     if (dash_on > 0 || dash_off > 0)
61         cairo_set_dash(cr, dashes, 2, x);
62     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
63                           color.alpha);
64     cairo_stroke(cr);
65     cairo_restore(cr);
66 }
67
68 /* create a new area */
69 void gfx_new_area(
70     cairo_t * cr,
71     double X0,
72     double Y0,
73     double X1,
74     double Y1,
75     double X2,
76     double Y2,
77     gfx_color_t color)
78 {
79     cairo_new_path(cr);
80     gfx_area_fit(cr, &X0, &Y0);
81     cairo_move_to(cr, X0, Y0);
82     gfx_area_fit(cr, &X1, &Y1);
83     cairo_line_to(cr, X1, Y1);
84     gfx_area_fit(cr, &X2, &Y2);
85     cairo_line_to(cr, X2, Y2);
86     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
87                           color.alpha);
88 }
89
90 /* add a point to a line or to an area */
91 void gfx_add_point(
92     cairo_t * cr,
93     double x,
94     double y)
95 {
96     gfx_area_fit(cr, &x, &y);
97     cairo_line_to(cr, x, y);
98 }
99
100 void gfx_close_path(
101     cairo_t * cr)
102 {
103     cairo_close_path(cr);
104     cairo_fill(cr);
105 }
106
107 /* create a text node */
108 static PangoLayout *gfx_prep_text(
109     cairo_t * cr,
110     double x,
111     gfx_color_t color,
112     char *font,
113     double size,
114     double tabwidth,
115     const char *text)
116 {
117     PangoLayout *layout;
118     PangoFontDescription *font_desc;
119
120     /* for performance reasons we might
121        want todo that only once ... tabs will always
122        be the same */
123     long      i;
124     long      tab_count = strlen(text);
125     long      tab_shift = fmod(x, tabwidth);
126
127     PangoTabArray *tab_array;
128
129     tab_array = pango_tab_array_new(tab_count, (gboolean) (1));
130     for (i = 1; i <= tab_count; i++) {
131         pango_tab_array_set_tab(tab_array,
132                                 i, PANGO_TAB_LEFT, tabwidth * i - tab_shift);
133     }
134     cairo_new_path(cr);
135     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
136                           color.alpha);
137     layout = pango_cairo_create_layout(cr);
138
139     pango_layout_set_tabs(layout, tab_array);
140     font_desc = pango_font_description_from_string(font);
141     pango_font_description_set_size(font_desc, size * PANGO_SCALE);
142     pango_layout_set_font_description(layout, font_desc);
143     pango_layout_set_markup(layout, text, -1);
144     return layout;
145 }
146
147 /* Size Text Node */
148 double gfx_get_text_width(
149     cairo_t * cr,
150     double start,
151     char *font,
152     double size,
153     double tabwidth,
154     char *text)
155 {
156     PangoLayout *layout;
157     PangoRectangle log_rect;
158     gfx_color_t color = { 0, 0, 0, 0 };
159     char     *tab;
160
161     /* turn \\t into tab */
162     while (tab = strstr(text, "\\t")) {
163         memmove(tab + 1, tab + 2, strlen(tab + 2));
164         tab[0] = (char) 9;
165     }
166     layout = gfx_prep_text(cr, start, color, font, size, tabwidth, text);
167     pango_layout_get_pixel_extents(layout, NULL, &log_rect);
168     pango_tab_array_free(pango_layout_get_tabs(layout));
169     g_object_unref(layout);
170     return log_rect.width;
171 }
172
173 void gfx_text(
174     cairo_t * cr,
175     double x,
176     double y,
177     gfx_color_t color,
178     char *font,
179     double size,
180     double tabwidth,
181     double angle,
182     enum gfx_h_align_en h_align,
183     enum gfx_v_align_en v_align,
184     const char *text)
185 {
186     PangoLayout *layout;
187     PangoRectangle log_rect;
188     PangoRectangle ink_rect;
189     double    sx = 0;
190     double    sy = 0;
191
192     cairo_save(cr);
193     cairo_translate(cr, x, y);
194 /*    gfx_line(cr,-2,0,2,0,1,color);
195     gfx_line(cr,0,-2,0,2,1,color); */
196     layout = gfx_prep_text(cr, x, color, font, size, tabwidth, text);
197     pango_layout_get_pixel_extents(layout, &ink_rect, &log_rect);
198     cairo_rotate(cr, -angle * G_PI / 180.0);
199     sx = log_rect.x;
200     sy = ink_rect.y;
201     switch (h_align) {
202     case GFX_H_RIGHT:
203         sx -= log_rect.width;
204         break;
205     case GFX_H_CENTER:
206         sx -= log_rect.width / 2;
207         break;
208     case GFX_H_LEFT:
209         break;
210     case GFX_H_NULL:
211         break;
212     }
213     sy += log_rect.y;
214     switch (v_align) {
215     case GFX_V_TOP:
216         break;
217     case GFX_V_CENTER:
218         sy -= log_rect.height / 2;
219         break;
220     case GFX_V_BOTTOM:
221         sy -= log_rect.height;
222         break;
223     case GFX_V_NULL:
224         break;
225     }
226     pango_cairo_update_layout(cr, layout);
227     cairo_move_to(cr, sx, sy);
228     pango_cairo_show_layout(cr, layout);
229     pango_tab_array_free(pango_layout_get_tabs(layout));
230     g_object_unref(layout);
231     cairo_restore(cr);
232
233 }
234
235 /* convert color */
236 struct gfx_color_t gfx_hex_to_col(
237     long unsigned int color)
238 {
239     struct gfx_color_t gfx_color;
240
241     gfx_color.red = 1.0 / 255.0 * ((color & 0xff000000) >> (3 * 8));
242     gfx_color.green = 1.0 / 255.0 * ((color & 0x00ff0000) >> (2 * 8));
243     gfx_color.blue = 1.0 / 255.0 * ((color & 0x0000ff00) >> (1 * 8));
244     gfx_color.alpha = 1.0 / 255.0 * (color & 0x000000ff);
245     return gfx_color;
246 }
247
248 /* gridfit_lines */
249
250 void gfx_line_fit(
251     cairo_t * cr,
252     double *x,
253     double *y)
254 {
255     double    line_width;
256     double    line_height;
257
258     cairo_user_to_device(cr, x, y);
259     line_width = cairo_get_line_width(cr);
260     line_height = line_width;
261     cairo_user_to_device_distance(cr, &line_width, &line_height);
262     line_width = line_width / 2.0 - (long) (line_width / 2.0);
263     line_height = line_height / 2.0 - (long) (line_height / 2.0);
264     *x = (double) ((long) (*x + 0.5)) + line_width;
265     *y = (double) ((long) (*y + 0.5)) + line_height;
266     cairo_device_to_user(cr, x, y);
267 }
268
269 /* gridfit_areas */
270
271 void gfx_area_fit(
272     cairo_t * cr,
273     double *x,
274     double *y)
275 {
276     cairo_user_to_device(cr, x, y);
277     *x = (double) ((long) (*x + 0.5));
278     *y = (double) ((long) (*y + 0.5));
279     cairo_device_to_user(cr, x, y);
280 }