1 /****************************************************************************
2 * RRDtool 1.4.3 Copyright by Tobi Oetiker, 1997-2010
3 ****************************************************************************
4 * rrd_gfx.c graphics wrapper for rrdtool
5 **************************************************************************/
7 /* #define DEBUG */
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 */
20 #include "rrd_tool.h"
21 #include "rrd_graph.h"
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 }
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[2];
50 double x = 0;
51 double y = 0;
53 dashes[0] = dash_on;
54 dashes[1] = dash_off;
56 cairo_save(cr);
57 cairo_new_path(cr);
58 cairo_set_line_width(cr, width);
59 gfx_line_fit(im, &x, &y);
60 gfx_line_fit(im, &X0, &Y0);
61 cairo_move_to(cr, X0, Y0);
62 gfx_line_fit(im, &X1, &Y1);
63 cairo_line_to(cr, X1, Y1);
64 if (dash_on > 0 || dash_off > 0)
65 cairo_set_dash(cr, dashes, 2, x);
66 cairo_set_source_rgba(cr, color.red, color.green, color.blue,
67 color.alpha);
68 cairo_stroke(cr);
69 cairo_restore(cr);
70 }
72 /* create a new area */
73 void gfx_new_area(
74 image_desc_t *im,
75 double X0,
76 double Y0,
77 double X1,
78 double Y1,
79 double X2,
80 double Y2,
81 gfx_color_t color)
82 {
83 cairo_t *cr = im->cr;
85 cairo_new_path(cr);
86 gfx_area_fit(im, &X0, &Y0);
87 cairo_move_to(cr, X0, Y0);
88 gfx_area_fit(im, &X1, &Y1);
89 cairo_line_to(cr, X1, Y1);
90 gfx_area_fit(im, &X2, &Y2);
91 cairo_line_to(cr, X2, Y2);
92 cairo_set_source_rgba(cr, color.red, color.green, color.blue,
93 color.alpha);
94 }
96 /* add a point to a line or to an area */
97 void gfx_add_point(
98 image_desc_t *im,
99 double x,
100 double y)
101 {
102 cairo_t *cr = im->cr;
104 gfx_area_fit(im, &x, &y);
105 cairo_line_to(cr, x, y);
106 }
108 /* add a point to a line or to an area */
109 void gfx_add_rect_fadey(
110 image_desc_t *im,
111 double x1,double y1,
112 double x2,double y2,
113 double py,
114 gfx_color_t color1,
115 gfx_color_t color2,
116 double height)
117 {
118 cairo_t *cr = im->cr;
120 cairo_new_path(cr);
121 gfx_area_fit(im, &x1, &y1);
122 gfx_area_fit(im, &x2, &y2);
123 cairo_line_to(cr, x1, y1);
124 cairo_line_to(cr, x1, y2);
125 cairo_line_to(cr, x2, y2);
126 cairo_line_to(cr, x2, y1);
127 cairo_close_path(cr);
128 cairo_pattern_t* p;
129 if (height < 0) {
130 p = cairo_pattern_create_linear(x1,y1,x2,y1+height);
131 } else if (height > 0) {
132 p = cairo_pattern_create_linear(x1,(y2+py)/2+height,x2,(y2+py)/2);
133 } else {
134 p = cairo_pattern_create_linear(x1,y1,x2,(y2+py)/2);
135 }
136 //cairo_pattern_t* p = cairo_pattern_create_linear(x1,py+50,x2,py);
137 cairo_pattern_add_color_stop_rgba(p, 1, color1.red,color1.green,color1.blue,color1.alpha);
138 cairo_pattern_add_color_stop_rgba(p, 0, color2.red,color2.green,color2.blue,color2.alpha);
139 cairo_set_source(cr, p);
140 cairo_pattern_destroy(p);
141 cairo_fill(cr);
142 }
144 void gfx_close_path(
145 image_desc_t *im)
146 {
147 cairo_t *cr = im->cr;
149 cairo_close_path(cr);
150 cairo_fill(cr);
151 }
153 /* create a text node */
154 static PangoLayout *gfx_prep_text(
155 image_desc_t *im,
156 double x,
157 gfx_color_t color,
158 PangoFontDescription *font_desc,
159 double tabwidth,
160 const char *text)
161 {
162 PangoLayout *layout = im->layout;
163 const PangoFontDescription *pfd;
164 cairo_t *cr = im->cr;
166 static double last_tabwidth = -1;
170 /* for performance reasons we might
171 want todo that only once ... tabs will always
172 be the same */
173 long i;
174 long tab_count = strlen(text);
175 long tab_shift = fmod(x, tabwidth);
176 int border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
178 gchar *utf8_text;
180 if (last_tabwidth < 0 || last_tabwidth != tabwidth){
181 PangoTabArray *tab_array;
182 // fprintf(stderr,"t");
183 last_tabwidth = tabwidth;
184 tab_array = pango_tab_array_new(tab_count, (gboolean) (1));
185 for (i = 1; i <= tab_count; i++) {
186 pango_tab_array_set_tab(tab_array,
187 i, PANGO_TAB_LEFT,
188 tabwidth * i - tab_shift + border);
189 }
190 pango_layout_set_tabs(layout, tab_array);
191 pango_tab_array_free(tab_array);
192 }
193 pfd = pango_layout_get_font_description(layout);
195 if (!pfd || !pango_font_description_equal (pfd,font_desc)){
196 pango_layout_set_font_description(layout, font_desc);
197 }
198 // fprintf(stderr,"%s\n",pango_font_description_to_string(pango_layout_get_font_description(layout)));
200 cairo_new_path(cr);
201 cairo_set_source_rgba(cr, color.red, color.green, color.blue,
202 color.alpha);
203 /* layout = pango_cairo_create_layout(cr); */
205 // pango_cairo_context_set_font_options(pango_context, im->font_options);
206 // pango_cairo_context_set_resolution(pango_context, 100);
208 /* pango_cairo_update_context(cr, pango_context); */
211 /* pango expects the string to be utf-8 encoded */
212 utf8_text = g_locale_to_utf8((const gchar *) text, -1, NULL, NULL, NULL);
214 /* In case of an error, i.e. utf8_text == NULL (locale settings messed
215 * up?), we fall back to a possible "invalid UTF-8 string" warning instead
216 * of provoking a failed assertion in libpango. */
217 if (im->with_markup)
218 pango_layout_set_markup(layout, utf8_text ? utf8_text : text, -1);
219 else
220 pango_layout_set_text(layout, utf8_text ? utf8_text : text, -1);
222 g_free(utf8_text);
223 return layout;
224 }
226 /* Size Text Node */
227 double gfx_get_text_width(
228 image_desc_t *im,
229 double start,
230 PangoFontDescription *font_desc,
231 double tabwidth,
232 char *text)
233 {
234 PangoLayout *layout;
235 PangoRectangle log_rect;
236 gfx_color_t color = { 0, 0, 0, 0 };
237 layout = gfx_prep_text(im, start, color, font_desc, tabwidth, text);
238 pango_layout_get_pixel_extents(layout, NULL, &log_rect);
239 /* g_object_unref(layout); */
240 return log_rect.width;
241 }
243 void gfx_text(
244 image_desc_t *im,
245 double x,
246 double y,
247 gfx_color_t color,
248 PangoFontDescription *font_desc,
249 double tabwidth,
250 double angle,
251 enum gfx_h_align_en h_align,
252 enum gfx_v_align_en v_align,
253 const char *text)
254 {
255 PangoLayout *layout;
256 PangoRectangle log_rect;
257 cairo_t *cr = im->cr;
258 double sx = 0;
259 double sy = 0;
261 cairo_save(cr);
262 cairo_translate(cr, x, y);
263 /* gfx_line(cr,-2,0,2,0,1,color);
264 gfx_line(cr,0,-2,0,2,1,color); */
265 layout = gfx_prep_text(im, x, color, font_desc, tabwidth, text);
266 pango_layout_get_pixel_extents(layout, NULL, &log_rect);
267 cairo_rotate(cr, -angle * G_PI / 180.0);
268 sx = log_rect.x;
269 switch (h_align) {
270 case GFX_H_RIGHT:
271 sx -= log_rect.width;
272 break;
273 case GFX_H_CENTER:
274 sx -= log_rect.width / 2;
275 break;
276 case GFX_H_LEFT:
277 break;
278 case GFX_H_NULL:
279 break;
280 }
281 sy = log_rect.y;
282 switch (v_align) {
283 case GFX_V_TOP:
284 break;
285 case GFX_V_CENTER:
286 sy -= log_rect.height / 2;
287 break;
288 case GFX_V_BOTTOM:
289 sy -= log_rect.height;
290 break;
291 case GFX_V_NULL:
292 break;
293 }
294 pango_cairo_update_layout(cr, layout);
295 cairo_move_to(cr, sx, sy);
296 pango_cairo_show_layout(cr, layout);
297 /* g_object_unref(layout); */
298 cairo_restore(cr);
300 }
302 /* convert color */
303 struct gfx_color_t gfx_hex_to_col(
304 long unsigned int color)
305 {
306 struct gfx_color_t gfx_color;
308 gfx_color.red = 1.0 / 255.0 * ((color & 0xff000000) >> (3 * 8));
309 gfx_color.green = 1.0 / 255.0 * ((color & 0x00ff0000) >> (2 * 8));
310 gfx_color.blue = 1.0 / 255.0 * ((color & 0x0000ff00) >> (1 * 8));
311 gfx_color.alpha = 1.0 / 255.0 * (color & 0x000000ff);
312 return gfx_color;
313 }
315 /* gridfit_lines */
317 void gfx_line_fit(
318 image_desc_t *im,
319 double *x,
320 double *y)
321 {
322 cairo_t *cr = im->cr;
323 double line_width;
324 double line_height;
326 if (!im->gridfit)
327 return;
328 cairo_user_to_device(cr, x, y);
329 line_width = cairo_get_line_width(cr);
330 line_height = line_width;
331 cairo_user_to_device_distance(cr, &line_width, &line_height);
332 line_width = line_width / 2.0 - (long) (line_width / 2.0);
333 line_height = line_height / 2.0 - (long) (line_height / 2.0);
334 *x = (double) ((long) (*x + 0.5)) - line_width;
335 *y = (double) ((long) (*y + 0.5)) + line_height;
336 cairo_device_to_user(cr, x, y);
337 }
339 /* gridfit_areas */
341 void gfx_area_fit(
342 image_desc_t *im,
343 double *x,
344 double *y)
345 {
346 cairo_t *cr = im->cr;
348 if (!im->gridfit)
349 return;
350 cairo_user_to_device(cr, x, y);
351 *x = (double) ((long) (*x + 0.5));
352 *y = (double) ((long) (*y + 0.5));
353 cairo_device_to_user(cr, x, y);
354 }