fix off by 1 error
[rrdtool.git] / src / rrd_gfx.c
1 /****************************************************************************
2  * RRDtool 1.4.3  Copyright by Tobi Oetiker, 1997-2010
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 /* 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;
119     
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 }
143
144 void gfx_close_path(
145     image_desc_t *im)
146 {
147     cairo_t  *cr = im->cr;
148
149     cairo_close_path(cr);
150     cairo_fill(cr);
151 }
152
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;
165
166     static double last_tabwidth = -1;
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 (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);
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 */
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
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;
260
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);
299
300 }
301
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;
307
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 }
314
315 /* gridfit_lines */
316
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;
325
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 - ceil(line_width / 2.0);
333     line_height = line_height / 2.0 - ceil(line_height / 2.0);
334     *x = floor(*x - 0.5) - line_width;
335     *y = ceil(*y + 0.5) + line_height;
336     cairo_device_to_user(cr, x, y);
337 }
338
339 /* gridfit_areas */
340
341 void gfx_area_fit(
342     image_desc_t *im,
343     double *x,
344     double *y)
345 {
346     cairo_t  *cr = im->cr;
347
348     if (!im->gridfit)
349         return;
350     cairo_user_to_device(cr, x, y);
351     *x = floor(*x);
352     *y = ceil(*y);
353     cairo_device_to_user(cr, x, y);
354 }