prepare for the release of rrdtool-1.3rc6
[rrdtool.git] / src / rrd_gfx.c
1 /****************************************************************************
2  * RRDtool 1.3rc6  Copyright by Tobi Oetiker, 1997-2008
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_graph.h"
22
23
24 /* create a new line */
25 void gfx_line(
26     image_desc_t *im,
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(im, X0, Y0, X1, Y1, width, color, 0, 0);
35 }
36
37 void gfx_dashed_line(
38     image_desc_t *im,
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     cairo_t  *cr = im->cr;
49     double    dashes[] = { dash_on, dash_off };
50     double    x = 0;
51     double    y = 0;
52
53     cairo_save(cr);
54     cairo_new_path(cr);
55     cairo_set_line_width(cr, width);
56     gfx_line_fit(im, &x, &y);
57     gfx_line_fit(im, &X0, &Y0);
58     cairo_move_to(cr, X0, Y0);
59     gfx_line_fit(im, &X1, &Y1);
60     cairo_line_to(cr, X1, Y1);
61     if (dash_on > 0 || dash_off > 0)
62         cairo_set_dash(cr, dashes, 2, x);
63     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
64                           color.alpha);
65     cairo_stroke(cr);
66     cairo_restore(cr);
67 }
68
69 /* create a new area */
70 void gfx_new_area(
71     image_desc_t *im,
72     double X0,
73     double Y0,
74     double X1,
75     double Y1,
76     double X2,
77     double Y2,
78     gfx_color_t color)
79 {
80     cairo_t  *cr = im->cr;
81
82     cairo_new_path(cr);
83     gfx_area_fit(im, &X0, &Y0);
84     cairo_move_to(cr, X0, Y0);
85     gfx_area_fit(im, &X1, &Y1);
86     cairo_line_to(cr, X1, Y1);
87     gfx_area_fit(im, &X2, &Y2);
88     cairo_line_to(cr, X2, Y2);
89     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
90                           color.alpha);
91 }
92
93 /* add a point to a line or to an area */
94 void gfx_add_point(
95     image_desc_t *im,
96     double x,
97     double y)
98 {
99     cairo_t  *cr = im->cr;
100
101     gfx_area_fit(im, &x, &y);
102     cairo_line_to(cr, x, y);
103 }
104
105 void gfx_close_path(
106     image_desc_t *im)
107 {
108     cairo_t  *cr = im->cr;
109
110     cairo_close_path(cr);
111     cairo_fill(cr);
112 }
113
114 /* create a text node */
115 static PangoLayout *gfx_prep_text(
116     image_desc_t *im,
117     double x,
118     gfx_color_t color,
119     char *font,
120     double size,
121     double tabwidth,
122     const char *text)
123 {
124     PangoLayout *layout;
125     PangoFontDescription *font_desc;
126     cairo_t  *cr = im->cr;
127
128     /* for performance reasons we might
129        want todo that only once ... tabs will always
130        be the same */
131     long      i;
132     long      tab_count = strlen(text);
133     long      tab_shift = fmod(x, tabwidth);
134     int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
135
136     PangoTabArray *tab_array;
137     PangoContext *pango_context;
138
139     tab_array = pango_tab_array_new(tab_count, (gboolean) (1));
140     for (i = 1; i <= tab_count; i++) {
141         pango_tab_array_set_tab(tab_array,
142                                 i, PANGO_TAB_LEFT, tabwidth * i - tab_shift+border);
143     }
144     cairo_new_path(cr);
145     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
146                           color.alpha);
147     layout = pango_cairo_create_layout(cr);
148     pango_context = pango_layout_get_context(layout);
149     pango_cairo_context_set_font_options(pango_context, im->font_options);
150     pango_cairo_context_set_resolution(pango_context, 100);
151
152 /*     pango_cairo_update_context(cr, pango_context); */
153
154     pango_layout_set_tabs(layout, tab_array);
155     font_desc = pango_font_description_from_string(font);
156     pango_font_description_set_size(font_desc, size * PANGO_SCALE);
157     pango_layout_set_font_description(layout, font_desc);
158     pango_layout_set_markup(layout, text, -1);
159     return layout;
160 }
161
162 /* Size Text Node */
163 double gfx_get_text_width(
164     image_desc_t *im,
165     double start,
166     char *font,
167     double size,
168     double tabwidth,
169     char *text)
170 {
171     PangoLayout *layout;
172     PangoRectangle log_rect;
173     gfx_color_t color = { 0, 0, 0, 0 };
174     layout = gfx_prep_text(im, start, color, font, size, tabwidth, text);
175     pango_layout_get_pixel_extents(layout, NULL, &log_rect);
176     pango_tab_array_free(pango_layout_get_tabs(layout));
177     g_object_unref(layout);
178     return log_rect.width;
179 }
180
181 void gfx_text(
182     image_desc_t *im,
183     double x,
184     double y,
185     gfx_color_t color,
186     char *font,
187     double size,
188     double tabwidth,
189     double angle,
190     enum gfx_h_align_en h_align,
191     enum gfx_v_align_en v_align,
192     const char *text)
193 {
194     PangoLayout *layout;
195     PangoRectangle log_rect;
196     PangoRectangle ink_rect;
197     cairo_t  *cr = im->cr;
198     double    sx = 0;
199     double    sy = 0;
200
201     cairo_save(cr);
202     cairo_translate(cr, x, y);
203 /*    gfx_line(cr,-2,0,2,0,1,color);
204     gfx_line(cr,0,-2,0,2,1,color); */
205     layout = gfx_prep_text(im, x, color, font, size, tabwidth, text);
206     pango_layout_get_pixel_extents(layout, &ink_rect, &log_rect);
207     cairo_rotate(cr, -angle * G_PI / 180.0);
208     sx = log_rect.x;
209     switch (h_align) {
210     case GFX_H_RIGHT:
211         sx -= log_rect.width;
212         break;
213     case GFX_H_CENTER:
214         sx -= log_rect.width / 2;
215         break;
216     case GFX_H_LEFT:
217         break;
218     case GFX_H_NULL:
219         break;
220     }
221     sy = log_rect.y;
222     switch (v_align) {
223     case GFX_V_TOP:
224         break;
225     case GFX_V_CENTER:
226         sy -= log_rect.height / 2;
227         break;
228     case GFX_V_BOTTOM:
229         sy -= log_rect.height;
230         break;
231     case GFX_V_NULL:
232         break;
233     }
234     pango_cairo_update_layout(cr, layout);
235     cairo_move_to(cr, sx, sy);
236     pango_cairo_show_layout(cr, layout);
237     pango_tab_array_free(pango_layout_get_tabs(layout));
238     g_object_unref(layout);
239     cairo_restore(cr);
240
241 }
242
243 /* convert color */
244 struct gfx_color_t gfx_hex_to_col(
245     long unsigned int color)
246 {
247     struct gfx_color_t gfx_color;
248
249     gfx_color.red = 1.0 / 255.0 * ((color & 0xff000000) >> (3 * 8));
250     gfx_color.green = 1.0 / 255.0 * ((color & 0x00ff0000) >> (2 * 8));
251     gfx_color.blue = 1.0 / 255.0 * ((color & 0x0000ff00) >> (1 * 8));
252     gfx_color.alpha = 1.0 / 255.0 * (color & 0x000000ff);
253     return gfx_color;
254 }
255
256 /* gridfit_lines */
257
258 void gfx_line_fit(
259     image_desc_t *im,
260     double *x,
261     double *y)
262 {
263     cairo_t  *cr = im->cr;
264     double    line_width;
265     double    line_height;
266
267     if (!im->gridfit)
268         return;
269     cairo_user_to_device(cr, x, y);
270     line_width = cairo_get_line_width(cr);
271     line_height = line_width;
272     cairo_user_to_device_distance(cr, &line_width, &line_height);
273     line_width = line_width / 2.0 - (long) (line_width / 2.0);
274     line_height = line_height / 2.0 - (long) (line_height / 2.0);
275     *x = (double) ((long) (*x + 0.5)) - line_width;
276     *y = (double) ((long) (*y + 0.5)) + line_height;
277     cairo_device_to_user(cr, x, y);
278 }
279
280 /* gridfit_areas */
281
282 void gfx_area_fit(
283     image_desc_t *im,
284     double *x,
285     double *y)
286 {
287     cairo_t  *cr = im->cr;
288
289     if (!im->gridfit)
290         return;
291     cairo_user_to_device(cr, x, y);
292     *x = (double) ((long) (*x + 0.5));
293     *y = (double) ((long) (*y + 0.5));
294     cairo_device_to_user(cr, x, y);
295 }