1 /****************************************************************************
2  * RRDtool 1.7.2 Copyright by Tobi Oetiker
3  ****************************************************************************
4  * rrd_gfx.c  graphics wrapper for rrdtool
5   **************************************************************************/
6 
7 /* #define DEBUG */
8 
9 /* stupid MSVC doesn't 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 #include "unused.h"
24 
25 
26 /* create a new line */
gfx_line(image_desc_t * im,double X0,double Y0,double X1,double Y1,double width,gfx_color_t color)27 void gfx_line(
28     image_desc_t *im,
29     double X0,
30     double Y0,
31     double X1,
32     double Y1,
33     double width,
34     gfx_color_t color)
35 {
36     gfx_dashed_line(im, X0, Y0, X1, Y1, width, color, 0, 0);
37 }
38 
gfx_dashed_line(image_desc_t * im,double X0,double Y0,double X1,double Y1,double width,gfx_color_t color,double dash_on,double dash_off)39 void gfx_dashed_line(
40     image_desc_t *im,
41     double X0,
42     double Y0,
43     double X1,
44     double Y1,
45     double width,
46     gfx_color_t color,
47     double dash_on,
48     double dash_off)
49 {
50     cairo_t  *cr = im->cr;
51     double    dashes[2];
52     double    x = 0;
53     double    y = 0;
54 
55     dashes[0] = dash_on;
56     dashes[1] = dash_off;
57 
58     cairo_save(cr);
59     cairo_new_path(cr);
60     cairo_set_line_width(cr, width);
61     gfx_line_fit(im, &x, &y);
62     gfx_line_fit(im, &X0, &Y0);
63     cairo_move_to(cr, X0, Y0);
64     gfx_line_fit(im, &X1, &Y1);
65     cairo_line_to(cr, X1, Y1);
66     if (dash_on > 0 || dash_off > 0)
67         cairo_set_dash(cr, dashes, 2, x);
68     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
69                           color.alpha);
70     cairo_stroke(cr);
71     cairo_restore(cr);
72 }
73 
74 /* create a new area */
gfx_new_area(image_desc_t * im,double X0,double Y0,double X1,double Y1,double X2,double Y2,gfx_color_t color)75 void gfx_new_area(
76     image_desc_t *im,
77     double X0,
78     double Y0,
79     double X1,
80     double Y1,
81     double X2,
82     double Y2,
83     gfx_color_t color)
84 {
85     cairo_t  *cr = im->cr;
86 
87     cairo_new_path(cr);
88     gfx_area_fit(im, &X0, &Y0);
89     cairo_move_to(cr, X0, Y0);
90     gfx_area_fit(im, &X1, &Y1);
91     cairo_line_to(cr, X1, Y1);
92     gfx_area_fit(im, &X2, &Y2);
93     cairo_line_to(cr, X2, Y2);
94     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
95                           color.alpha);
96 }
97 
98 /* add a point to a line or to an area */
gfx_add_point(image_desc_t * im,double x,double y)99 void gfx_add_point(
100     image_desc_t *im,
101     double x,
102     double y)
103 {
104     cairo_t  *cr = im->cr;
105 
106     gfx_area_fit(im, &x, &y);
107     cairo_line_to(cr, x, y);
108 }
109 
110 /* add a point to a line or to an area */
gfx_add_rect_fadey(image_desc_t * im,double x1,double y1,double x2,double y2,double py,gfx_color_t color1,gfx_color_t color2,double height)111 void gfx_add_rect_fadey(
112     image_desc_t *im,
113     double x1,double y1,
114 	double x2,double y2,
115 	double py,
116     gfx_color_t color1,
117 	gfx_color_t color2,
118 	double height)
119 {
120     cairo_t  *cr = im->cr;
121 
122 	cairo_new_path(cr);
123     gfx_area_fit(im, &x1, &y1);
124     gfx_area_fit(im, &x2, &y2);
125     cairo_line_to(cr, x1, y1);
126     cairo_line_to(cr, x1, y2);
127 	cairo_line_to(cr, x2, y2);
128 	cairo_line_to(cr, x2, y1);
129 	cairo_close_path(cr);
130 	cairo_pattern_t* p;
131 	if (height < 0) {
132 		p = cairo_pattern_create_linear(x1,y1,x2,y1+height);
133 	} else if (height > 0) {
134 		p = cairo_pattern_create_linear(x1,(y2+py)/2+height,x2,(y2+py)/2);
135 	} else {
136 		p = cairo_pattern_create_linear(x1,y1,x2,(y2+py)/2);
137 	}
138 	//cairo_pattern_t* p = cairo_pattern_create_linear(x1,py+50,x2,py);
139 	cairo_pattern_add_color_stop_rgba(p, 1, color1.red,color1.green,color1.blue,color1.alpha);
140 	cairo_pattern_add_color_stop_rgba(p, 0, color2.red,color2.green,color2.blue,color2.alpha);
141     cairo_set_source(cr, p);
142 	cairo_pattern_destroy(p);
143 	cairo_fill(cr);
144 }
145 
gfx_close_path(image_desc_t * im)146 void gfx_close_path(
147     image_desc_t *im)
148 {
149     cairo_t  *cr = im->cr;
150 
151     cairo_close_path(cr);
152     cairo_fill(cr);
153 }
154 
155 /* create a text node */
gfx_prep_text(image_desc_t * im,double x,gfx_color_t color,PangoFontDescription * font_desc,double tabwidth,const char * text)156 static PangoLayout *gfx_prep_text(
157     image_desc_t *im,
158     double x,
159     gfx_color_t color,
160     PangoFontDescription *font_desc,
161     double tabwidth,
162     const char *text)
163 {
164     PangoLayout  *layout = im->layout;
165     const PangoFontDescription *pfd;
166     cairo_t  *cr = im->cr;
167 
168 
169 
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;
177 
178     gchar    *utf8_text;
179 
180     if (im->last_tabwidth < 0 || im->last_tabwidth != tabwidth){
181         PangoTabArray *tab_array;
182         // fprintf(stderr,"t");
183         im->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);
194 
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)));
199 
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); */
204 
205 //    pango_cairo_context_set_font_options(pango_context, im->font_options);
206 //    pango_cairo_context_set_resolution(pango_context, 100);
207 
208 /*     pango_cairo_update_context(cr, pango_context); */
209 
210 
211     /* pango expects the string to be utf-8 encoded */
212     utf8_text = g_locale_to_utf8((const gchar *) text, -1, NULL, NULL, NULL);
213 
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);
221 
222     g_free(utf8_text);
223     return layout;
224 }
225 
226 /* Size Text Node */
gfx_get_text_width(image_desc_t * im,double start,PangoFontDescription * font_desc,double tabwidth,char * text)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 }
242 
gfx_get_text_height(image_desc_t * im,double UNUSED (start),PangoFontDescription * font_desc,double tabwidth,char * text)243 double gfx_get_text_height(
244     image_desc_t *im,
245     double UNUSED(start),
246     PangoFontDescription *font_desc,
247     double tabwidth,
248     char *text)
249 {
250     PangoLayout *layout;
251     PangoRectangle log_rect;
252     gfx_color_t color = { 0, 0, 0, 0 };
253     layout = gfx_prep_text(im, 0.0, color, font_desc, tabwidth, text);
254     pango_layout_get_pixel_extents(layout, NULL, &log_rect);
255 /*    g_object_unref(layout); */
256     return log_rect.height;
257 }
258 
gfx_text(image_desc_t * im,double x,double y,gfx_color_t color,PangoFontDescription * font_desc,double tabwidth,double angle,enum gfx_h_align_en h_align,enum gfx_v_align_en v_align,const char * text)259 void gfx_text(
260     image_desc_t *im,
261     double x,
262     double y,
263     gfx_color_t color,
264     PangoFontDescription *font_desc,
265     double tabwidth,
266     double angle,
267     enum gfx_h_align_en h_align,
268     enum gfx_v_align_en v_align,
269     const char *text)
270 {
271     PangoLayout *layout;
272     PangoRectangle log_rect;
273     cairo_t  *cr = im->cr;
274     double    sx = 0;
275     double    sy = 0;
276 
277     cairo_save(cr);
278     cairo_translate(cr, x, y);
279 /*    gfx_line(cr,-2,0,2,0,1,color);
280     gfx_line(cr,0,-2,0,2,1,color); */
281     layout = gfx_prep_text(im, x, color, font_desc, tabwidth, text);
282     pango_layout_get_pixel_extents(layout, NULL, &log_rect);
283     cairo_rotate(cr, -angle * G_PI / 180.0);
284     sx = log_rect.x;
285     switch (h_align) {
286     case GFX_H_RIGHT:
287         sx -= log_rect.width;
288         break;
289     case GFX_H_CENTER:
290         sx -= log_rect.width / 2;
291         break;
292     case GFX_H_LEFT:
293         break;
294     case GFX_H_NULL:
295         break;
296     }
297     sy = log_rect.y;
298     switch (v_align) {
299     case GFX_V_TOP:
300         break;
301     case GFX_V_CENTER:
302         sy -= log_rect.height / 2;
303         break;
304     case GFX_V_BOTTOM:
305         sy -= log_rect.height;
306         break;
307     case GFX_V_NULL:
308         break;
309     }
310     pango_cairo_update_layout(cr, layout);
311     cairo_move_to(cr, sx, sy);
312     pango_cairo_show_layout(cr, layout);
313 /*    g_object_unref(layout); */
314     cairo_restore(cr);
315 
316 }
317 
318 /* convert color */
gfx_hex_to_col(long unsigned int color)319 struct gfx_color_t gfx_hex_to_col(
320     long unsigned int color)
321 {
322     struct gfx_color_t gfx_color;
323 
324     gfx_color.red = 1.0 / 255.0 * ((color & 0xff000000) >> (3 * 8));
325     gfx_color.green = 1.0 / 255.0 * ((color & 0x00ff0000) >> (2 * 8));
326     gfx_color.blue = 1.0 / 255.0 * ((color & 0x0000ff00) >> (1 * 8));
327     gfx_color.alpha = 1.0 / 255.0 * (color & 0x000000ff);
328     return gfx_color;
329 }
330 
331 /* gridfit_lines */
332 
gfx_line_fit(image_desc_t * im,double * x,double * y)333 void gfx_line_fit(
334     image_desc_t *im,
335     double *x,
336     double *y)
337 {
338     cairo_t  *cr = im->cr;
339     double    line_width;
340     double    line_height;
341 
342     if (!im->gridfit)
343         return;
344     cairo_user_to_device(cr, x, y);
345     line_width = cairo_get_line_width(cr);
346     line_height = line_width;
347     cairo_user_to_device_distance(cr, &line_width, &line_height);
348     line_width = line_width / 2.0 - ceil(line_width / 2.0);
349     line_height = line_height / 2.0 - ceil(line_height / 2.0);
350     *x = floor(*x - 0.5) - line_width;
351     *y = ceil(*y + 0.5) + line_height;
352     cairo_device_to_user(cr, x, y);
353 }
354 
355 /* gridfit_areas */
356 
gfx_area_fit(image_desc_t * im,double * x,double * y)357 void gfx_area_fit(
358     image_desc_t *im,
359     double *x,
360     double *y)
361 {
362     cairo_t  *cr = im->cr;
363 
364     if (!im->gridfit)
365         return;
366     cairo_user_to_device(cr, x, y);
367     *x = floor(*x);
368     *y = ceil(*y);
369     cairo_device_to_user(cr, x, y);
370 }
371