trying to improve the pango performance I added facilities to cache layout,context...
[rrdtool.git] / src / rrd_gfx.c
1 /****************************************************************************
2  * RRDtool 1.3.1  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[2];
50     double    x = 0;
51     double    y = 0;
52
53     dashes[0] = dash_on;
54     dashes[1] = dash_off;
55
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 }
71
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;
84
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 }
95
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;
103
104     gfx_area_fit(im, &x, &y);
105     cairo_line_to(cr, x, y);
106 }
107
108 void gfx_close_path(
109     image_desc_t *im)
110 {
111     cairo_t  *cr = im->cr;
112
113     cairo_close_path(cr);
114     cairo_fill(cr);
115 }
116
117 /* create a text node */
118 static PangoLayout *gfx_prep_text(
119     image_desc_t *im,
120     double x,
121     gfx_color_t color,
122     char *font,
123     double size,
124     double tabwidth,
125     const char *text)
126 {
127     static PangoLayout  *layout = NULL;
128     static PangoContext *pango_context = NULL;
129     static PangoFontMap *pango_fontmap = NULL;
130     static char* last_font = NULL;
131     static double last_size = -1;
132     static double last_tabwidth = -1;
133
134     cairo_t  *cr = im->cr;
135
136     /* for performance reasons we might
137        want todo that only once ... tabs will always
138        be the same */
139     long      i;
140     long      tab_count = strlen(text);
141     long      tab_shift = fmod(x, tabwidth);
142     int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
143     
144     gchar    *utf8_text;
145
146     /* initialize pango only once ... */
147     if (!pango_fontmap){
148         pango_fontmap = pango_cairo_font_map_get_default ();
149     }
150     if (!pango_context){
151         // fprintf(stderr,"c");
152         pango_context = pango_cairo_font_map_create_context ((PangoCairoFontMap *) (pango_fontmap));
153         pango_cairo_context_set_resolution(pango_context, 100);
154     }
155     if (!layout){
156         // fprintf(stderr,"l");
157         layout =  pango_layout_new (pango_context);
158     }
159
160     pango_cairo_context_set_font_options(pango_context, im->font_options);
161
162     pango_cairo_update_context (cr, pango_context);
163
164         
165     if (last_tabwidth < 0 || last_tabwidth != tabwidth){
166         PangoTabArray *tab_array;
167         // fprintf(stderr,"t");
168         last_tabwidth = tabwidth;
169         tab_array = pango_tab_array_new(tab_count, (gboolean) (1));
170         for (i = 1; i <= tab_count; i++) {
171              pango_tab_array_set_tab(tab_array,
172                                      i, PANGO_TAB_LEFT,
173                                      tabwidth * i - tab_shift + border);
174         }
175         pango_layout_set_tabs(layout, tab_array);
176         pango_tab_array_free(tab_array);
177     }
178
179     if (last_font == NULL || strcmp(font,last_font) != 0){
180         PangoFontDescription *font_desc;
181         // fprintf(stderr,"f:%s",font);
182         if (last_font)
183            free(last_font);
184         last_font = strdup(font);       
185         font_desc = pango_font_description_from_string(font);
186         pango_layout_set_font_description(layout, font_desc);
187         pango_font_description_free(font_desc);
188    }
189
190    if (last_size < 0 || last_size != size ){
191         PangoFontDescription *font_desc;
192         font_desc =  pango_layout_get_font_description (layout);
193         pango_font_description_set_size(font_desc, size * PANGO_SCALE);
194         pango_layout_set_font_description(layout, font_desc);
195     }          
196
197     cairo_new_path(cr);
198     cairo_set_source_rgba(cr, color.red, color.green, color.blue,
199                           color.alpha);
200 /*     layout = pango_cairo_create_layout(cr); */
201
202 //    pango_cairo_context_set_font_options(pango_context, im->font_options);
203 //    pango_cairo_context_set_resolution(pango_context, 100);
204
205 /*     pango_cairo_update_context(cr, pango_context); */
206
207
208     /* pango expects the string to be utf-8 encoded */
209     utf8_text = g_locale_to_utf8((const gchar *) text, -1, NULL, NULL, NULL);
210
211     /* In case of an error, i.e. utf8_text == NULL (locale settings messed
212      * up?), we fall back to a possible "invalid UTF-8 string" warning instead
213      * of provoking a failed assertion in libpango. */
214     if (im->with_markup)
215         pango_layout_set_markup(layout, utf8_text ? utf8_text : text, -1);
216     else
217         pango_layout_set_text(layout, utf8_text ? utf8_text : text, -1);
218
219     g_free(utf8_text);
220     return layout;
221 }
222
223 /* Size Text Node */
224 double gfx_get_text_width(
225     image_desc_t *im,
226     double start,
227     char *font,
228     double size,
229     double tabwidth,
230     char *text)
231 {
232     PangoLayout *layout;
233     PangoRectangle log_rect;
234     gfx_color_t color = { 0, 0, 0, 0 };
235     layout = gfx_prep_text(im, start, color, font, size, tabwidth, text);
236     pango_layout_get_pixel_extents(layout, NULL, &log_rect);
237 /*    g_object_unref(layout); */
238     return log_rect.width;
239 }
240
241 void gfx_text(
242     image_desc_t *im,
243     double x,
244     double y,
245     gfx_color_t color,
246     char *font,
247     double size,
248     double tabwidth,
249     double angle,
250     enum gfx_h_align_en h_align,
251     enum gfx_v_align_en v_align,
252     const char *text)
253 {
254     PangoLayout *layout;
255     PangoRectangle log_rect;
256     cairo_t  *cr = im->cr;
257     double    sx = 0;
258     double    sy = 0;
259
260     cairo_save(cr);
261     cairo_translate(cr, x, y);
262 /*    gfx_line(cr,-2,0,2,0,1,color);
263     gfx_line(cr,0,-2,0,2,1,color); */
264     layout = gfx_prep_text(im, x, color, font, size, tabwidth, text);
265     pango_layout_get_pixel_extents(layout, NULL, &log_rect);
266     cairo_rotate(cr, -angle * G_PI / 180.0);
267     sx = log_rect.x;
268     switch (h_align) {
269     case GFX_H_RIGHT:
270         sx -= log_rect.width;
271         break;
272     case GFX_H_CENTER:
273         sx -= log_rect.width / 2;
274         break;
275     case GFX_H_LEFT:
276         break;
277     case GFX_H_NULL:
278         break;
279     }
280     sy = log_rect.y;
281     switch (v_align) {
282     case GFX_V_TOP:
283         break;
284     case GFX_V_CENTER:
285         sy -= log_rect.height / 2;
286         break;
287     case GFX_V_BOTTOM:
288         sy -= log_rect.height;
289         break;
290     case GFX_V_NULL:
291         break;
292     }
293     pango_cairo_update_layout(cr, layout);
294     cairo_move_to(cr, sx, sy);
295     pango_cairo_show_layout(cr, layout);
296 /*    g_object_unref(layout); */
297     cairo_restore(cr);
298
299 }
300
301 /* convert color */
302 struct gfx_color_t gfx_hex_to_col(
303     long unsigned int color)
304 {
305     struct gfx_color_t gfx_color;
306
307     gfx_color.red = 1.0 / 255.0 * ((color & 0xff000000) >> (3 * 8));
308     gfx_color.green = 1.0 / 255.0 * ((color & 0x00ff0000) >> (2 * 8));
309     gfx_color.blue = 1.0 / 255.0 * ((color & 0x0000ff00) >> (1 * 8));
310     gfx_color.alpha = 1.0 / 255.0 * (color & 0x000000ff);
311     return gfx_color;
312 }
313
314 /* gridfit_lines */
315
316 void gfx_line_fit(
317     image_desc_t *im,
318     double *x,
319     double *y)
320 {
321     cairo_t  *cr = im->cr;
322     double    line_width;
323     double    line_height;
324
325     if (!im->gridfit)
326         return;
327     cairo_user_to_device(cr, x, y);
328     line_width = cairo_get_line_width(cr);
329     line_height = line_width;
330     cairo_user_to_device_distance(cr, &line_width, &line_height);
331     line_width = line_width / 2.0 - (long) (line_width / 2.0);
332     line_height = line_height / 2.0 - (long) (line_height / 2.0);
333     *x = (double) ((long) (*x + 0.5)) - line_width;
334     *y = (double) ((long) (*y + 0.5)) + line_height;
335     cairo_device_to_user(cr, x, y);
336 }
337
338 /* gridfit_areas */
339
340 void gfx_area_fit(
341     image_desc_t *im,
342     double *x,
343     double *y)
344 {
345     cairo_t  *cr = im->cr;
346
347     if (!im->gridfit)
348         return;
349     cairo_user_to_device(cr, x, y);
350     *x = (double) ((long) (*x + 0.5));
351     *y = (double) ((long) (*y + 0.5));
352     cairo_device_to_user(cr, x, y);
353 }