X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Frrd_graph.c;h=f04a2b3017ae855061c3e1e9b07726cc247c0ae8;hb=40cc8b00296079815f08c2ff55f134efae83ea69;hp=f33cfc3dd8b981f1f820cb3494f6dc237a8c7bb4;hpb=657d850f957a2dd703e3aab2d7cde4b0f9711c15;p=rrdtool.git diff --git a/src/rrd_graph.c b/src/rrd_graph.c index f33cfc3..f04a2b3 100644 --- a/src/rrd_graph.c +++ b/src/rrd_graph.c @@ -1,5 +1,5 @@ /**************************************************************************** - * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007 + * RRDtool 1.2.99907080300 Copyright by Tobi Oetiker, 1997-2007 **************************************************************************** * rrd__graph.c produce graphs from data in rrdfiles ****************************************************************************/ @@ -140,16 +140,17 @@ ylab_t ylab[] = { gfx_color_t graph_col[] = /* default colors */ -{ 0xFFFFFFFF, /* canvas */ - 0xF0F0F0FF, /* background */ - 0xD0D0D0FF, /* shade A */ - 0xA0A0A0FF, /* shade B */ - 0x90909080, /* grid */ - 0xE0505080, /* major grid */ - 0x000000FF, /* font */ - 0x802020FF, /* arrow */ - 0x202020FF, /* axis */ - 0x000000FF /* frame */ +{ + {1.00, 1.00, 1.00, 1.00}, /* canvas */ + {0.95, 0.95, 0.95, 1.00}, /* background */ + {0.81, 0.81, 0.81, 1.00}, /* shade A */ + {0.62, 0.62, 0.62, 1.00}, /* shade B */ + {0.56, 0.56, 0.56, 0.75}, /* grid */ + {0.87, 0.31, 0.31, 0.60}, /* major grid */ + {0.00, 0.00, 0.00, 1.00}, /* font */ + {0.50, 0.12, 0.12, 1.00}, /* arrow */ + {0.12, 0.12, 0.12, 1.00}, /* axis */ + {0.00, 0.00, 0.00, 1.00} /* frame */ }; @@ -225,81 +226,79 @@ enum gf_en gf_conv( char *string) { - conv_if(PRINT, GF_PRINT) - conv_if(GPRINT, GF_GPRINT) - conv_if(COMMENT, GF_COMMENT) - conv_if(HRULE, GF_HRULE) - conv_if(VRULE, GF_VRULE) - conv_if(LINE, GF_LINE) - conv_if(AREA, GF_AREA) - conv_if(STACK, GF_STACK) - conv_if(TICK, GF_TICK) - conv_if(DEF, GF_DEF) - conv_if(CDEF, GF_CDEF) - conv_if(VDEF, GF_VDEF) -#ifdef WITH_PIECHART - conv_if(PART, GF_PART) -#endif - conv_if(XPORT, GF_XPORT) - conv_if(SHIFT, GF_SHIFT) - - return (-1); + conv_if(PRINT, GF_PRINT); + conv_if(GPRINT, GF_GPRINT); + conv_if(COMMENT, GF_COMMENT); + conv_if(HRULE, GF_HRULE); + conv_if(VRULE, GF_VRULE); + conv_if(LINE, GF_LINE); + conv_if(AREA, GF_AREA); + conv_if(STACK, GF_STACK); + conv_if(TICK, GF_TICK); + conv_if(TEXTALIGN, GF_TEXTALIGN); + conv_if(DEF, GF_DEF); + conv_if(CDEF, GF_CDEF); + conv_if(VDEF, GF_VDEF); + conv_if(XPORT, GF_XPORT); + conv_if(SHIFT, GF_SHIFT); + + return (-1); } enum gfx_if_en if_conv( char *string) { - conv_if(PNG, IF_PNG) - conv_if(SVG, IF_SVG) - conv_if(EPS, IF_EPS) - conv_if(PDF, IF_PDF) + conv_if(PNG, IF_PNG); + conv_if(SVG, IF_SVG); + conv_if(EPS, IF_EPS); + conv_if(PDF, IF_PDF); - return (-1); + return (-1); } enum tmt_en tmt_conv( char *string) { - conv_if(SECOND, TMT_SECOND) - conv_if(MINUTE, TMT_MINUTE) - conv_if(HOUR, TMT_HOUR) - conv_if(DAY, TMT_DAY) - conv_if(WEEK, TMT_WEEK) - conv_if(MONTH, TMT_MONTH) - conv_if(YEAR, TMT_YEAR) - return (-1); + conv_if(SECOND, TMT_SECOND); + conv_if(MINUTE, TMT_MINUTE); + conv_if(HOUR, TMT_HOUR); + conv_if(DAY, TMT_DAY); + conv_if(WEEK, TMT_WEEK); + conv_if(MONTH, TMT_MONTH); + conv_if(YEAR, TMT_YEAR); + return (-1); } enum grc_en grc_conv( char *string) { - conv_if(BACK, GRC_BACK) - conv_if(CANVAS, GRC_CANVAS) - conv_if(SHADEA, GRC_SHADEA) - conv_if(SHADEB, GRC_SHADEB) - conv_if(GRID, GRC_GRID) - conv_if(MGRID, GRC_MGRID) - conv_if(FONT, GRC_FONT) - conv_if(ARROW, GRC_ARROW) - conv_if(AXIS, GRC_AXIS) - conv_if(FRAME, GRC_FRAME) + conv_if(BACK, GRC_BACK); + conv_if(CANVAS, GRC_CANVAS); + conv_if(SHADEA, GRC_SHADEA); + conv_if(SHADEB, GRC_SHADEB); + conv_if(GRID, GRC_GRID); + conv_if(MGRID, GRC_MGRID); + conv_if(FONT, GRC_FONT); + conv_if(ARROW, GRC_ARROW); + conv_if(AXIS, GRC_AXIS); + conv_if(FRAME, GRC_FRAME); - return -1; + return -1; } enum text_prop_en text_prop_conv( char *string) { - conv_if(DEFAULT, TEXT_PROP_DEFAULT) - conv_if(TITLE, TEXT_PROP_TITLE) - conv_if(AXIS, TEXT_PROP_AXIS) - conv_if(UNIT, TEXT_PROP_UNIT) - conv_if(LEGEND, TEXT_PROP_LEGEND) - return -1; + conv_if(DEFAULT, TEXT_PROP_DEFAULT); + conv_if(TITLE, TEXT_PROP_TITLE); + conv_if(AXIS, TEXT_PROP_AXIS); + conv_if(UNIT, TEXT_PROP_UNIT); + conv_if(LEGEND, TEXT_PROP_LEGEND); + return -1; } @@ -309,6 +308,7 @@ int im_free( image_desc_t *im) { unsigned long i, ii; + cairo_status_t status = 0; if (im == NULL) return 0; @@ -326,7 +326,19 @@ int im_free( free(im->gdes[i].rpnp); } free(im->gdes); - gfx_destroy(im->canvas); + if (im->font_options) + cairo_font_options_destroy(im->font_options); + + if (im->cr) { + status = cairo_status(im->cr); + cairo_destroy(im->cr); + } + if (im->surface) + cairo_surface_destroy(im->surface); + if (status) + fprintf(stderr, "OOPS: Cairo has issuesm it can't even die: %s\n", + cairo_status_to_string(status)); + return 0; } @@ -520,6 +532,7 @@ void expand_range( #endif } + void apply_gridfit( image_desc_t *im) { @@ -704,6 +717,7 @@ void reduce_data( else { switch (cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVSEASONAL: case CF_DEVPREDICT: case CF_SEASONAL: @@ -729,6 +743,7 @@ void reduce_data( } else { switch (cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVSEASONAL: case CF_DEVPREDICT: case CF_SEASONAL: @@ -1425,6 +1440,7 @@ int print_calc( switch (im->gdes[i].cf) { case CF_HWPREDICT: + case CF_MHWPREDICT: case CF_DEVPREDICT: case CF_DEVSEASONAL: case CF_SEASONAL: @@ -1530,6 +1546,7 @@ int print_calc( graphelement = 1; break; case GF_COMMENT: + case GF_TEXTALIGN: case GF_DEF: case GF_CDEF: case GF_VDEF: @@ -1552,7 +1569,8 @@ int print_calc( /* place legends with color spots */ int leg_place( - image_desc_t *im) + image_desc_t *im, + int *gY) { /* graph labels */ int interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0; @@ -1565,6 +1583,7 @@ int leg_place( int glue = 0; int i, ii, mark = 0; char prt_fctn; /*special printfunctions */ + char default_txtalign = TXA_JUSTIFIED; /*default line orientation */ int *legspace; if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) { @@ -1573,10 +1592,18 @@ int leg_place( return -1; } + if (im->extra_flags & FULL_SIZE_MODE) + leg_y = leg_y_prev = + leg_y - (int) (im->text_prop[TEXT_PROP_LEGEND].size * 1.8); + for (i = 0; i < im->gdes_c; i++) { fill_last = fill; - /* hid legends for rules which are not displayed */ + /* hide legends for rules which are not displayed */ + + if (im->gdes[i].gf == GF_TEXTALIGN) { + default_txtalign = im->gdes[i].txtalign; + } if (!(im->extra_flags & FORCE_RULES_LEGEND)) { if (im->gdes[i].gf == GF_HRULE && @@ -1615,30 +1642,32 @@ int leg_place( return -1; } - - /* remove exess space */ + /* \n -> \l */ if (prt_fctn == 'n') { prt_fctn = 'l'; } + /* remove exess space from the end of the legend for \g */ while (prt_fctn == 'g' && leg_cc > 0 && im->gdes[i].legend[leg_cc - 1] == ' ') { leg_cc--; im->gdes[i].legend[leg_cc] = '\0'; } + if (leg_cc != 0) { + + /* no interleg space if string ends in \g */ legspace[i] = (prt_fctn == 'g' ? 0 : interleg); if (fill > 0) { - /* no interleg space if string ends in \g */ fill += legspace[i]; } - fill += gfx_get_text_width(im->canvas, fill + border, + fill += gfx_get_text_width(im, fill + border, im->text_prop[TEXT_PROP_LEGEND]. font, im->text_prop[TEXT_PROP_LEGEND]. size, im->tabwidth, - im->gdes[i].legend, 0); + im->gdes[i].legend); leg_c++; } else { legspace[i] = 0; @@ -1647,10 +1676,25 @@ int leg_place( if (prt_fctn == 'g') { prt_fctn = '\0'; } - if (prt_fctn == '\0') { - if (i == im->gdes_c - 1) - prt_fctn = 'l'; + if (prt_fctn == '\0') { + if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) { + /* just one legend item is left right or center */ + switch (default_txtalign) { + case TXA_RIGHT: + prt_fctn = 'r'; + break; + case TXA_CENTER: + prt_fctn = 'c'; + break; + case TXA_JUSTIFIED: + prt_fctn = 'j'; + break; + default: + prt_fctn = 'l'; + break; + } + } /* is it time to place the legends ? */ if (fill > im->ximg - 2 * border) { if (leg_c > 1) { @@ -1658,11 +1702,10 @@ int leg_place( i--; fill = fill_last; leg_c--; - prt_fctn = 'j'; - } else { - prt_fctn = 'l'; } - + } + if (leg_c == 1 && prt_fctn == 'j') { + prt_fctn = 'l'; } } @@ -1685,30 +1728,45 @@ int leg_place( im->gdes[ii].leg_x = leg_x; im->gdes[ii].leg_y = leg_y; leg_x += - gfx_get_text_width(im->canvas, leg_x, + gfx_get_text_width(im, leg_x, im->text_prop[TEXT_PROP_LEGEND]. font, im->text_prop[TEXT_PROP_LEGEND]. size, im->tabwidth, - im->gdes[ii].legend, 0) + im->gdes[ii].legend) + legspace[ii] + glue; } leg_y_prev = leg_y; - /* only add y space if there was text on the line */ - if (leg_x > border || prt_fctn == 's') - leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; - if (prt_fctn == 's') - leg_y -= im->text_prop[TEXT_PROP_LEGEND].size; + if (im->extra_flags & FULL_SIZE_MODE) { + /* only add y space if there was text on the line */ + if (leg_x > border || prt_fctn == 's') + leg_y -= im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + if (prt_fctn == 's') + leg_y += im->text_prop[TEXT_PROP_LEGEND].size; + } else { + if (leg_x > border || prt_fctn == 's') + leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + if (prt_fctn == 's') + leg_y -= im->text_prop[TEXT_PROP_LEGEND].size; + } fill = 0; leg_c = 0; mark = ii; } } - im->yimg = leg_y_prev; - /* if we did place some legends we have to add vertical space */ - if (leg_y != im->yimg) { - im->yimg += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + + if (im->extra_flags & FULL_SIZE_MODE) { + if (leg_y != leg_y_prev) { + *gY = leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + im->yorigin = + leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8; + } + } else { + im->yimg = leg_y_prev; + /* if we did place some legends we have to add vertical space */ + if (leg_y != im->yimg) + im->yimg += im->text_prop[TEXT_PROP_LEGEND].size * 1.8; } free(legspace); } @@ -1880,25 +1938,37 @@ int draw_horizontal_grid( } nlabels++; - gfx_new_text(im->canvas, - X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, - graph_label); - gfx_new_dashed_line(im->canvas, - X0 - 2, Y0, - X1 + 2, Y0, - MGRIDWIDTH, im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); + gfx_text(im, + X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font, + im->text_prop[TEXT_PROP_AXIS].size, + im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, + graph_label); + gfx_line(im, + X0 - 2, Y0, + X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, + X1, Y0, + X1 + 2, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, + X0 - 2, Y0, + X1 + 2, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID], + im->grid_dash_on, im->grid_dash_off); } else if (!(im->extra_flags & NOMINOR)) { - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, + X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, + X1, Y0, + X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, + X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } @@ -1927,6 +1997,10 @@ double frexp10( return mnt; } +/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */ +/* yes we are loosing precision by doing tos with floats instead of doubles + but it seems more stable this way. */ + static int AlmostEqual2sComplement( float A, float B, @@ -2036,11 +2110,18 @@ int horizontal_log_grid( break; /* major grid line */ - gfx_new_dashed_line(im->canvas, - X0 - 2, Y0, - X1 + 2, Y0, - MGRIDWIDTH, im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); + + gfx_line(im, + X0 - 2, Y0, X0, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); + gfx_line(im, + X1, Y0, X1 + 2, Y0, MGRIDWIDTH, im->graph_col[GRC_MGRID]); + + + gfx_dashed_line(im, + X0 - 2, Y0, + X1 + 2, Y0, + MGRIDWIDTH, im->graph_col[GRC_MGRID], + im->grid_dash_on, im->grid_dash_off); /* label */ if (im->extra_flags & FORCE_UNITS_SI) { @@ -2064,13 +2145,12 @@ int horizontal_log_grid( sprintf(graph_label, "%3.0f %c", pvalue, symbol); } else sprintf(graph_label, "%3.0e", value); - gfx_new_text(im->canvas, - X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, - graph_label); + gfx_text(im, + X0 - im->text_prop[TEXT_PROP_AXIS].size, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font, + im->text_prop[TEXT_PROP_AXIS].size, + im->tabwidth, 0.0, GFX_H_RIGHT, GFX_V_CENTER, graph_label); /* minor grid */ if (mid < 4 && exfrac == 1) { @@ -2099,11 +2179,17 @@ int horizontal_log_grid( break; /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, + X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, + X1, Y0, + X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, + X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } else if (exfrac > 1) { for (i = val_exp - exfrac / 3 * 2; i < val_exp; i += exfrac / 3) { @@ -2116,11 +2202,17 @@ int horizontal_log_grid( break; /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, + X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, + X1, Y0, + X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, + X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } @@ -2158,11 +2250,15 @@ int horizontal_log_grid( break; /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, + X1, Y0, X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, + X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } /* fancy minor gridlines */ @@ -2177,11 +2273,15 @@ int horizontal_log_grid( break; /* draw lines */ - gfx_new_dashed_line(im->canvas, - X0 - 1, Y0, - X1 + 1, Y0, - GRIDWIDTH, im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, + X0 - 2, Y0, X0, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_line(im, + X1, Y0, X1 + 2, Y0, GRIDWIDTH, im->graph_col[GRC_GRID]); + gfx_dashed_line(im, + X0 - 1, Y0, + X1 + 1, Y0, + GRIDWIDTH, im->graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } @@ -2252,9 +2352,13 @@ void vertical_grid( if (ti == timajor) continue; /* skip as falls on major grid line */ X0 = xtr(im, ti); - gfx_new_dashed_line(im->canvas, X0, Y0 + 1, X0, Y1 - 1, GRIDWIDTH, - im->graph_col[GRC_GRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, X0, Y1 - 2, X0, Y1, GRIDWIDTH, + im->graph_col[GRC_GRID]); + gfx_line(im, X0, Y0, X0, Y0 + 2, GRIDWIDTH, + im->graph_col[GRC_GRID]); + gfx_dashed_line(im, X0, Y0 + 1, X0, Y1 - 1, GRIDWIDTH, + im->graph_col[GRC_GRID], + im->grid_dash_on, im->grid_dash_off); } } @@ -2270,9 +2374,13 @@ void vertical_grid( if (ti < im->start || ti > im->end) continue; X0 = xtr(im, ti); - gfx_new_dashed_line(im->canvas, X0, Y0 + 3, X0, Y1 - 2, MGRIDWIDTH, - im->graph_col[GRC_MGRID], - im->grid_dash_on, im->grid_dash_off); + gfx_line(im, X0, Y1 - 2, X0, Y1, MGRIDWIDTH, + im->graph_col[GRC_MGRID]); + gfx_line(im, X0, Y0, X0, Y0 + 3, MGRIDWIDTH, + im->graph_col[GRC_MGRID]); + gfx_dashed_line(im, X0, Y0 + 3, X0, Y1 - 2, MGRIDWIDTH, + im->graph_col[GRC_MGRID], + im->grid_dash_on, im->grid_dash_off); } /* paint the labels below the graph */ @@ -2293,13 +2401,13 @@ void vertical_grid( #else # error "your libc has no strftime I guess we'll abort the exercise here." #endif - gfx_new_text(im->canvas, - xtr(im, tilab), - Y0 + im->text_prop[TEXT_PROP_AXIS].size * 1.4 + 5, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 0.0, - GFX_H_CENTER, GFX_V_BOTTOM, graph_label); + gfx_text(im, + xtr(im, tilab), + Y0 + 3, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font, + im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, 0.0, + GFX_H_CENTER, GFX_V_TOP, graph_label); } @@ -2310,29 +2418,33 @@ void axis_paint( image_desc_t *im) { /* draw x and y axis */ - /* gfx_new_line ( im->canvas, im->xorigin+im->xsize,im->yorigin, + /* gfx_line ( im->canvas, im->xorigin+im->xsize,im->yorigin, im->xorigin+im->xsize,im->yorigin-im->ysize, GRIDWIDTH, im->graph_col[GRC_AXIS]); - gfx_new_line ( im->canvas, im->xorigin,im->yorigin-im->ysize, + gfx_line ( im->canvas, im->xorigin,im->yorigin-im->ysize, im->xorigin+im->xsize,im->yorigin-im->ysize, GRIDWIDTH, im->graph_col[GRC_AXIS]); */ - gfx_new_line(im->canvas, im->xorigin - 4, im->yorigin, - im->xorigin + im->xsize + 4, im->yorigin, - MGRIDWIDTH, im->graph_col[GRC_AXIS]); + gfx_line(im, im->xorigin - 4, im->yorigin, + im->xorigin + im->xsize + 4, im->yorigin, + MGRIDWIDTH, im->graph_col[GRC_AXIS]); - gfx_new_line(im->canvas, im->xorigin, im->yorigin + 4, - im->xorigin, im->yorigin - im->ysize - 4, - MGRIDWIDTH, im->graph_col[GRC_AXIS]); + gfx_line(im, im->xorigin, im->yorigin + 4, + im->xorigin, im->yorigin - im->ysize - 4, + MGRIDWIDTH, im->graph_col[GRC_AXIS]); /* arrow for X and Y axis direction */ - gfx_new_area(im->canvas, im->xorigin + im->xsize + 2, im->yorigin - 2, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin + 0.5, /* LINEOFFSET */ + + gfx_new_area(im, im->xorigin + im->xsize + 2, im->yorigin - 3, im->xorigin + im->xsize + 2, im->yorigin + 3, im->xorigin + im->xsize + 7, im->yorigin, /* horyzontal */ im->graph_col[GRC_ARROW]); + gfx_close_path(im); - gfx_new_area(im->canvas, im->xorigin - 2, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin + 0.5, im->yorigin - im->ysize - 7, /* LINEOFFSET */ + gfx_new_area(im, im->xorigin - 3, im->yorigin - im->ysize - 2, im->xorigin + 3, im->yorigin - im->ysize - 2, im->xorigin, im->yorigin - im->ysize - 7, /* vertical */ im->graph_col[GRC_ARROW]); + gfx_close_path(im); + } @@ -2342,23 +2454,23 @@ void grid_paint( long i; int res = 0; double X0, Y0; /* points for filled graph and more */ - gfx_node_t *node; + struct gfx_color_t water_color; /* draw 3d border */ - node = gfx_new_area(im->canvas, 0, im->yimg, - 2, im->yimg - 2, 2, 2, im->graph_col[GRC_SHADEA]); - gfx_add_point(node, im->ximg - 2, 2); - gfx_add_point(node, im->ximg, 0); - gfx_add_point(node, 0, 0); -/* gfx_add_point( node , 0,im->yimg ); */ - - node = gfx_new_area(im->canvas, 2, im->yimg - 2, - im->ximg - 2, im->yimg - 2, - im->ximg - 2, 2, im->graph_col[GRC_SHADEB]); - gfx_add_point(node, im->ximg, 0); - gfx_add_point(node, im->ximg, im->yimg); - gfx_add_point(node, 0, im->yimg); -/* gfx_add_point( node , 0,im->yimg ); */ + gfx_new_area(im, 0, im->yimg, + 2, im->yimg - 2, 2, 2, im->graph_col[GRC_SHADEA]); + gfx_add_point(im, im->ximg - 2, 2); + gfx_add_point(im, im->ximg, 0); + gfx_add_point(im, 0, 0); + gfx_close_path(im); + + gfx_new_area(im, 2, im->yimg - 2, + im->ximg - 2, im->yimg - 2, + im->ximg - 2, 2, im->graph_col[GRC_SHADEB]); + gfx_add_point(im, im->ximg, 0); + gfx_add_point(im, im->ximg, im->yimg); + gfx_add_point(im, 0, im->yimg); + gfx_close_path(im); if (im->draw_x_grid == 1) @@ -2375,47 +2487,48 @@ void grid_paint( if (!res) { char *nodata = "No Data found"; - gfx_new_text(im->canvas, im->ximg / 2, - (2 * im->yorigin - im->ysize) / 2, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_AXIS].font, - im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, - 0.0, GFX_H_CENTER, GFX_V_CENTER, nodata); + gfx_text(im, im->ximg / 2, + (2 * im->yorigin - im->ysize) / 2, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_AXIS].font, + im->text_prop[TEXT_PROP_AXIS].size, im->tabwidth, + 0.0, GFX_H_CENTER, GFX_V_CENTER, nodata); } } /* yaxis unit description */ - gfx_new_text(im->canvas, - 10, (im->yorigin - im->ysize / 2), - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_UNIT].font, - im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth, - RRDGRAPH_YLEGEND_ANGLE, - GFX_H_LEFT, GFX_V_CENTER, im->ylegend); + gfx_text(im, + 10, (im->yorigin - im->ysize / 2), + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_UNIT].font, + im->text_prop[TEXT_PROP_UNIT].size, im->tabwidth, + RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend); /* graph title */ - gfx_new_text(im->canvas, - im->ximg / 2, im->text_prop[TEXT_PROP_TITLE].size * 1.3 + 4, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_TITLE].font, - im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0, - GFX_H_CENTER, GFX_V_CENTER, im->title); + gfx_text(im, + im->ximg / 2, 6, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_TITLE].font, + im->text_prop[TEXT_PROP_TITLE].size, im->tabwidth, 0.0, + GFX_H_CENTER, GFX_V_TOP, im->title); /* rrdtool 'logo' */ - gfx_new_text(im->canvas, - im->ximg - 7, 7, - (im->graph_col[GRC_FONT] & 0xffffff00) | 0x00000044, - im->text_prop[TEXT_PROP_AXIS].font, - 5.5, im->tabwidth, 270, - GFX_H_RIGHT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); + water_color = im->graph_col[GRC_FONT]; + water_color.alpha = 0.3; + gfx_text(im, + im->ximg - 4, 5, + water_color, + im->text_prop[TEXT_PROP_AXIS].font, + 5.5, im->tabwidth, -90, + GFX_H_LEFT, GFX_V_TOP, "RRDTOOL / TOBI OETIKER"); /* graph watermark */ if (im->watermark[0] != '\0') { - gfx_new_text(im->canvas, - im->ximg / 2, im->yimg - 6, - (im->graph_col[GRC_FONT] & 0xffffff00) | 0x00000044, - im->text_prop[TEXT_PROP_AXIS].font, - 5.5, im->tabwidth, 0, - GFX_H_CENTER, GFX_V_BOTTOM, im->watermark); + gfx_text(im, + im->ximg / 2, im->yimg - 6, + water_color, + im->text_prop[TEXT_PROP_AXIS].font, + 5.5, im->tabwidth, 0, + GFX_H_CENTER, GFX_V_BOTTOM, im->watermark); } /* graph labels */ @@ -2427,42 +2540,62 @@ void grid_paint( /* im->gdes[i].leg_y is the bottom of the legend */ X0 = im->gdes[i].leg_x; Y0 = im->gdes[i].leg_y; - gfx_new_text(im->canvas, X0, Y0, - im->graph_col[GRC_FONT], - im->text_prop[TEXT_PROP_LEGEND].font, - im->text_prop[TEXT_PROP_LEGEND].size, - im->tabwidth, 0.0, GFX_H_LEFT, GFX_V_BOTTOM, - im->gdes[i].legend); + gfx_text(im, X0, Y0, + im->graph_col[GRC_FONT], + im->text_prop[TEXT_PROP_LEGEND].font, + im->text_prop[TEXT_PROP_LEGEND].size, + im->tabwidth, 0.0, GFX_H_LEFT, GFX_V_BOTTOM, + im->gdes[i].legend); /* The legend for GRAPH items starts with "M " to have enough space for the box */ if (im->gdes[i].gf != GF_PRINT && im->gdes[i].gf != GF_GPRINT && im->gdes[i].gf != GF_COMMENT) { - int boxH, boxV; + double boxH, boxV; + double X1, Y1; - boxH = gfx_get_text_width(im->canvas, 0, + + boxH = gfx_get_text_width(im, 0, im->text_prop[TEXT_PROP_LEGEND]. font, im->text_prop[TEXT_PROP_LEGEND]. - size, im->tabwidth, "o", 0) * 1.2; - boxV = boxH * 1.1; + size, im->tabwidth, "o") * 1.2; + boxV = boxH; + + /* shift the box up a bit */ + Y0 -= boxV * 0.4; /* make sure transparent colors show up the same way as in the graph */ - node = gfx_new_area(im->canvas, - X0, Y0 - boxV, - X0, Y0, - X0 + boxH, Y0, im->graph_col[GRC_BACK]); - gfx_add_point(node, X0 + boxH, Y0 - boxV); - - node = gfx_new_area(im->canvas, - X0, Y0 - boxV, - X0, Y0, X0 + boxH, Y0, im->gdes[i].col); - gfx_add_point(node, X0 + boxH, Y0 - boxV); - node = gfx_new_line(im->canvas, - X0, Y0 - boxV, - X0, Y0, 1.0, im->graph_col[GRC_FRAME]); - gfx_add_point(node, X0 + boxH, Y0); - gfx_add_point(node, X0 + boxH, Y0 - boxV); - gfx_close_path(node); + + gfx_new_area(im, + X0, Y0 - boxV, + X0, Y0, X0 + boxH, Y0, im->graph_col[GRC_BACK]); + gfx_add_point(im, X0 + boxH, Y0 - boxV); + gfx_close_path(im); + + gfx_new_area(im, + X0, Y0 - boxV, + X0, Y0, X0 + boxH, Y0, im->gdes[i].col); + gfx_add_point(im, X0 + boxH, Y0 - boxV); + gfx_close_path(im); + + cairo_save(im->cr); + cairo_new_path(im->cr); + cairo_set_line_width(im->cr, 1.0); + X1 = X0 + boxH; + Y1 = Y0 - boxV; + gfx_line_fit(im, &X0, &Y0); + gfx_line_fit(im, &X1, &Y1); + cairo_move_to(im->cr, X0, Y0); + cairo_line_to(im->cr, X1, Y0); + cairo_line_to(im->cr, X1, Y1); + cairo_line_to(im->cr, X0, Y1); + cairo_close_path(im->cr); + cairo_set_source_rgba(im->cr, im->graph_col[GRC_FRAME].red, + im->graph_col[GRC_FRAME].green, + im->graph_col[GRC_FRAME].blue, + im->graph_col[GRC_FRAME].alpha); + cairo_stroke(im->cr); + cairo_restore(im->cr); } } } @@ -2490,7 +2623,7 @@ int lazy_check( return 0; if ((fd = fopen(im->graphfile, "rb")) == NULL) return 0; /* the file does not exist */ - switch (im->canvas->imgformat) { + switch (im->imgformat) { case IF_PNG: size = PngSize(fd, &(im->ximg), &(im->yimg)); break; @@ -2501,67 +2634,10 @@ int lazy_check( return size; } -#ifdef WITH_PIECHART -void pie_part( - image_desc_t *im, - gfx_color_t color, - double PieCenterX, - double PieCenterY, - double Radius, - double startangle, - double endangle) -{ - gfx_node_t *node; - double angle; - double step = M_PI / 50; /* Number of iterations for the circle; - ** 10 is definitely too low, more than - ** 50 seems to be overkill - */ - - /* Strange but true: we have to work clockwise or else - ** anti aliasing nor transparency don't work. - ** - ** This test is here to make sure we do it right, also - ** this makes the for...next loop more easy to implement. - ** The return will occur if the user enters a negative number - ** (which shouldn't be done according to the specs) or if the - ** programmers do something wrong (which, as we all know, never - ** happens anyway :) - */ - if (endangle < startangle) - return; - - /* Hidden feature: Radius decreases each full circle */ - angle = startangle; - while (angle >= 2 * M_PI) { - angle -= 2 * M_PI; - Radius *= 0.8; - } - - node = gfx_new_area(im->canvas, - PieCenterX + sin(startangle) * Radius, - PieCenterY - cos(startangle) * Radius, - PieCenterX, - PieCenterY, - PieCenterX + sin(endangle) * Radius, - PieCenterY - cos(endangle) * Radius, color); - for (angle = endangle; angle - startangle >= step; angle -= step) { - gfx_add_point(node, - PieCenterX + sin(angle) * Radius, - PieCenterY - cos(angle) * Radius); - } -} - -#endif int graph_size_location( image_desc_t *im, - int elements -#ifdef WITH_PIECHART - , - int piechart -#endif - ) + int elements) { /* The actual size of the image to draw is determined from ** several sources. The size given on the command line is @@ -2569,31 +2645,8 @@ int graph_size_location( ** and other things outside the graph area */ - /* +-+-------------------------------------------+ - ** |l|.................title.....................| - ** |e+--+-------------------------------+--------+ - ** |b| b| | | - ** |a| a| | pie | - ** |l| l| main graph area | chart | - ** |.| .| | area | - ** |t| y| | | - ** |r+--+-------------------------------+--------+ - ** |e| | x-axis labels | | - ** |v+--+-------------------------------+--------+ - ** | |..............legends......................| - ** +-+-------------------------------------------+ - ** | watermark | - ** +---------------------------------------------+ - */ int Xvertical = 0, Ytitle = 0, Xylabel = 0, Xmain = 0, Ymain = 0, -#ifdef WITH_PIECHART - Xpie = 0, Ypie = 0, -#endif - Yxlabel = 0, -#if 0 - Xlegend = 0, Ylegend = 0, -#endif - Xspacing = 15, Yspacing = 15, Ywatermark = 4; + Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4; if (im->extra_flags & ONLY_GRAPH) { im->xorigin = 0; @@ -2604,153 +2657,255 @@ int graph_size_location( return 0; } + /** +---+--------------------------------------------+ + ** | y |...............graph title..................| + ** | +---+-------------------------------+--------+ + ** | a | y | | | + ** | x | | | | + ** | i | a | | pie | + ** | s | x | main graph area | chart | + ** | | i | | area | + ** | t | s | | | + ** | i | | | | + ** | t | l | | | + ** | l | b +-------------------------------+--------+ + ** | e | l | x axis labels | | + ** +---+---+-------------------------------+--------+ + ** |....................legends.....................| + ** +------------------------------------------------+ + ** | watermark | + ** +------------------------------------------------+ + */ + if (im->ylegend[0] != '\0') { Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2; } - if (im->title[0] != '\0') { /* The title is placed "inbetween" two text lines so it ** automatically has some vertical spacing. The horizontal ** spacing is added here, on each side. */ - /* don't care for the with of the title - Xtitle = gfx_get_text_width(im->canvas, 0, - im->text_prop[TEXT_PROP_TITLE].font, - im->text_prop[TEXT_PROP_TITLE].size, - im->tabwidth, - im->title, 0) + 2*Xspacing; */ + /* if necessary, reduce the font size of the title until it fits the image width */ Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10; } if (elements) { - Xmain = im->xsize; - Ymain = im->ysize; if (im->draw_x_grid) { Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5; } if (im->draw_y_grid || im->forceleftspace) { - Xylabel = gfx_get_text_width(im->canvas, 0, + Xylabel = gfx_get_text_width(im, 0, im->text_prop[TEXT_PROP_AXIS].font, im->text_prop[TEXT_PROP_AXIS].size, - im->tabwidth, - "0", 0) * im->unitslength; + im->tabwidth, "0") * im->unitslength; } } -#ifdef WITH_PIECHART - if (piechart) { - im->piesize = im->xsize < im->ysize ? im->xsize : im->ysize; - Xpie = im->piesize; - Ypie = im->piesize; - } -#endif - /* Now calculate the total size. Insert some spacing where - desired. im->xorigin and im->yorigin need to correspond - with the lower left corner of the main graph area or, if - this one is not set, the imaginary box surrounding the - pie chart area. */ + if (im->extra_flags & FULL_SIZE_MODE) { + /* The actual size of the image to draw has been determined by the user. + ** The graph area is the space remaining after accounting for the legend, + ** the watermark, the pie chart, the axis labels, and the title. + */ + im->xorigin = 0; + im->ximg = im->xsize; + im->yimg = im->ysize; + im->yorigin = im->ysize; + Xmain = im->ximg; + Ymain = im->yimg; - /* The legend width cannot yet be determined, as a result we - ** have problems adjusting the image to it. For now, we just - ** forget about it at all; the legend will have to fit in the - ** size already allocated. - */ - im->ximg = Xylabel + Xmain + 2 * Xspacing; + im->yorigin += Ytitle; -#ifdef WITH_PIECHART - im->ximg += Xpie; -#endif + /* Now calculate the total size. Insert some spacing where + desired. im->xorigin and im->yorigin need to correspond + with the lower left corner of the main graph area or, if + this one is not set, the imaginary box surrounding the + pie chart area. */ - if (Xmain) - im->ximg += Xspacing; -#ifdef WITH_PIECHART - if (Xpie) - im->ximg += Xspacing; -#endif + /* Initial size calculation for the main graph area */ + Xmain = im->ximg - (Xylabel + 2 * Xspacing); + if (Xmain) + Xmain -= Xspacing; /* put space between main graph area and right edge */ - im->xorigin = Xspacing + Xylabel; + im->xorigin = Xspacing + Xylabel; - /* the length of the title should not influence with width of the graph - if (Xtitle > im->ximg) im->ximg = Xtitle; */ + /* the length of the title should not influence with width of the graph + if (Xtitle > im->ximg) im->ximg = Xtitle; */ - if (Xvertical) { /* unit description */ - im->ximg += Xvertical; - im->xorigin += Xvertical; - } - xtr(im, 0); - - /* The vertical size is interesting... we need to compare - ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with - ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel} - ** in order to start even thinking about Ylegend or Ywatermark. - ** - ** Do it in three portions: First calculate the inner part, - ** then do the legend, then adjust the total height of the img, - ** adding space for a watermark if one exists; - */ + if (Xvertical) { /* unit description */ + Xmain -= Xvertical; + im->xorigin += Xvertical; + } + im->xsize = Xmain; + xtr(im, 0); - /* reserve space for main and/or pie */ + /* The vertical size of the image is known in advance. The main graph area + ** (Ymain) and im->yorigin must be set according to the space requirements + ** of the legend and the axis labels. + */ - im->yimg = Ymain + Yxlabel; + if (im->extra_flags & NOLEGEND) { + /* set dimensions correctly if using full size mode with no legend */ + im->yorigin = + im->yimg - im->text_prop[TEXT_PROP_AXIS].size * 2.5 - + Yspacing; + Ymain = im->yorigin; + } else { + /* Determine where to place the legends onto the image. + ** Set Ymain and adjust im->yorigin to match the space requirements. + */ + if (leg_place(im, &Ymain) == -1) + return -1; + } -#ifdef WITH_PIECHART - if (im->yimg < Ypie) - im->yimg = Ypie; -#endif - im->yorigin = im->yimg - Yxlabel; + /* remove title space *or* some padding above the graph from the main graph area */ + if (Ytitle) { + Ymain -= Ytitle; + } else { + Ymain -= 1.5 * Yspacing; + } - /* reserve space for the title *or* some padding above the graph */ - if (Ytitle) { - im->yimg += Ytitle; - im->yorigin += Ytitle; - } else { - im->yimg += 1.5 * Yspacing; - im->yorigin += 1.5 * Yspacing; - } - /* reserve space for padding below the graph */ - im->yimg += Yspacing; + /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */ + if (im->watermark[0] != '\0') { + Ymain -= Ywatermark; + } - /* Determine where to place the legends onto the image. - ** Adjust im->yimg to match the space requirements. - */ - if (leg_place(im) == -1) - return -1; + im->ysize = Ymain; - if (im->watermark[0] != '\0') { - im->yimg += Ywatermark; - } -#if 0 - if (Xlegend > im->ximg) { - im->ximg = Xlegend; - /* reposition Pie */ - } -#endif + } else { /* dimension options -width and -height refer to the dimensions of the main graph area */ -#ifdef WITH_PIECHART - /* The pie is placed in the upper right hand corner, - ** just below the title (if any) and with sufficient - ** padding. - */ - if (elements) { - im->pie_x = im->ximg - Xspacing - Xpie / 2; - im->pie_y = im->yorigin - Ymain + Ypie / 2; - } else { - im->pie_x = im->ximg / 2; - im->pie_y = im->yorigin - Ypie / 2; + /* The actual size of the image to draw is determined from + ** several sources. The size given on the command line is + ** the graph area but we need more as we have to draw labels + ** and other things outside the graph area. + */ + + if (im->ylegend[0] != '\0') { + Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2; + } + + + if (im->title[0] != '\0') { + /* The title is placed "inbetween" two text lines so it + ** automatically has some vertical spacing. The horizontal + ** spacing is added here, on each side. + */ + /* don't care for the with of the title + Xtitle = gfx_get_text_width(im->canvas, 0, + im->text_prop[TEXT_PROP_TITLE].font, + im->text_prop[TEXT_PROP_TITLE].size, + im->tabwidth, + im->title, 0) + 2*Xspacing; */ + Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10; + } + + if (elements) { + Xmain = im->xsize; + Ymain = im->ysize; + } + /* Now calculate the total size. Insert some spacing where + desired. im->xorigin and im->yorigin need to correspond + with the lower left corner of the main graph area or, if + this one is not set, the imaginary box surrounding the + pie chart area. */ + + /* The legend width cannot yet be determined, as a result we + ** have problems adjusting the image to it. For now, we just + ** forget about it at all; the legend will have to fit in the + ** size already allocated. + */ + im->ximg = Xylabel + Xmain + 2 * Xspacing; + + if (Xmain) + im->ximg += Xspacing; + + im->xorigin = Xspacing + Xylabel; + + /* the length of the title should not influence with width of the graph + if (Xtitle > im->ximg) im->ximg = Xtitle; */ + + if (Xvertical) { /* unit description */ + im->ximg += Xvertical; + im->xorigin += Xvertical; + } + xtr(im, 0); + + /* The vertical size is interesting... we need to compare + ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with + ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel} + ** in order to start even thinking about Ylegend or Ywatermark. + ** + ** Do it in three portions: First calculate the inner part, + ** then do the legend, then adjust the total height of the img, + ** adding space for a watermark if one exists; + */ + + /* reserve space for main and/or pie */ + + im->yimg = Ymain + Yxlabel; + + + im->yorigin = im->yimg - Yxlabel; + + /* reserve space for the title *or* some padding above the graph */ + if (Ytitle) { + im->yimg += Ytitle; + im->yorigin += Ytitle; + } else { + im->yimg += 1.5 * Yspacing; + im->yorigin += 1.5 * Yspacing; + } + /* reserve space for padding below the graph */ + im->yimg += Yspacing; + + /* Determine where to place the legends onto the image. + ** Adjust im->yimg to match the space requirements. + */ + if (leg_place(im, 0) == -1) + return -1; + + if (im->watermark[0] != '\0') { + im->yimg += Ywatermark; + } } -#endif ytr(im, DNAN); return 0; } -/* from http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm */ -/* yes we are loosing precision by doing tos with floats instead of doubles - but it seems more stable this way. */ +static cairo_status_t cairo_write_func_filehandle( + void *closure, + const unsigned char *data, + unsigned int length) +{ + if (fwrite(data, length, 1, closure) != 1) + return CAIRO_STATUS_WRITE_ERROR; + return CAIRO_STATUS_SUCCESS; +} + +static cairo_status_t cairo_copy_to_buffer( + void *closure, + const unsigned char *data, + unsigned int length) +{ + image_desc_t *im = closure; + + im->rendered_image = + realloc(im->rendered_image, im->rendered_image_size + length); + if (im->rendered_image == NULL) { + return CAIRO_STATUS_WRITE_ERROR; + } + + memcpy(im->rendered_image + im->rendered_image_size, data, length); + + im->rendered_image_size += length; + + return CAIRO_STATUS_SUCCESS; +} + /* draw that picture thing ... */ int graph_paint( image_desc_t *im, @@ -2759,16 +2914,12 @@ int graph_paint( int i, ii; int lazy = lazy_check(im); -#ifdef WITH_PIECHART - int piechart = 0; - double PieStart = 0.0; -#endif - FILE *fo; - gfx_node_t *node; - double areazero = 0.0; graph_desc_t *lastgdes = NULL; + PangoFontMap *font_map = pango_cairo_font_map_get_default(); + + /* if we are lazy and there is nothing to PRINT ... quit now */ if (lazy && im->prt_c == 0) return 0; @@ -2782,15 +2933,6 @@ int graph_paint( if (data_calc(im) == -1) return -1; -#ifdef WITH_PIECHART - /* check if we need to draw a piechart */ - for (i = 0; i < im->gdes_c; i++) { - if (im->gdes[i].gf == GF_PART) { - piechart = 1; - break; - } - } -#endif /* calculate and PRINT and GPRINT definitions. We have to do it at * this point because it will affect the length of the legends @@ -2800,18 +2942,15 @@ int graph_paint( i = print_calc(im, calcpr); if (i < 0) return -1; - if (((i == 0) -#ifdef WITH_PIECHART - && (piechart == 0) -#endif - ) || lazy) + if ((i == 0) || lazy) return 0; -#ifdef WITH_PIECHART - /* If there's only the pie chart to draw, signal this */ - if (i == 0) - piechart = 2; -#endif +/************************************************************** + *** Calculating sizes and locations became a bit confusing *** + *** so I moved this into a separate function. *** + **************************************************************/ + if (graph_size_location(im, i) == -1) + return -1; /* get actual drawing data and find min and max values */ if (data_proc(im) == -1) @@ -2828,56 +2967,79 @@ int graph_paint( if (!calc_horizontal_grid(im)) return -1; - if (im->gridfit) - apply_gridfit(im); + /* reset precalc */ + ytr(im, DNAN); +/* if (im->gridfit) + apply_gridfit(im); */ -/************************************************************** - *** Calculating sizes and locations became a bit confusing *** - *** so I moved this into a separate function. *** - **************************************************************/ - if (graph_size_location(im, i -#ifdef WITH_PIECHART - , piechart -#endif - ) == -1) - return -1; /* the actual graph is created by going through the individual graph elements and then drawing them */ + cairo_surface_destroy(im->surface); - node = gfx_new_area(im->canvas, - 0, 0, - 0, im->yimg, - im->ximg, im->yimg, im->graph_col[GRC_BACK]); + switch (im->imgformat) { + case IF_PNG: + im->surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + im->ximg * im->zoom, + im->yimg * im->zoom); + break; + case IF_PDF: + im->gridfit = 0; + im->surface = strlen(im->graphfile) + ? cairo_pdf_surface_create(im->graphfile, im->ximg * im->zoom, + im->yimg * im->zoom) + : cairo_pdf_surface_create_for_stream(&cairo_copy_to_buffer, im, + im->ximg * im->zoom, + im->yimg * im->zoom); + break; + case IF_EPS: + im->gridfit = 0; + im->surface = strlen(im->graphfile) + ? cairo_ps_surface_create(im->graphfile, im->ximg * im->zoom, + im->yimg * im->zoom) + : cairo_ps_surface_create_for_stream(&cairo_copy_to_buffer, im, + im->ximg * im->zoom, + im->yimg * im->zoom); + break; + case IF_SVG: + im->gridfit = 0; + im->surface = strlen(im->graphfile) + ? cairo_svg_surface_create(im->graphfile, im->ximg * im->zoom, + im->yimg * im->zoom) + : cairo_svg_surface_create_for_stream(&cairo_copy_to_buffer, im, + im->ximg * im->zoom, + im->yimg * im->zoom); + cairo_svg_surface_restrict_to_version(im->surface, + CAIRO_SVG_VERSION_1_1); + break; + }; + im->cr = cairo_create(im->surface); + pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100); + cairo_set_antialias(im->cr, im->graph_antialias); + cairo_scale(im->cr, im->zoom, im->zoom); - gfx_add_point(node, im->ximg, 0); + gfx_new_area(im, + 0, 0, + 0, im->yimg, im->ximg, im->yimg, im->graph_col[GRC_BACK]); -#ifdef WITH_PIECHART - if (piechart != 2) { -#endif - node = gfx_new_area(im->canvas, - im->xorigin, im->yorigin, - im->xorigin + im->xsize, im->yorigin, - im->xorigin + im->xsize, im->yorigin - im->ysize, - im->graph_col[GRC_CANVAS]); - - gfx_add_point(node, im->xorigin, im->yorigin - im->ysize); - - if (im->minval > 0.0) - areazero = im->minval; - if (im->maxval < 0.0) - areazero = im->maxval; -#ifdef WITH_PIECHART - } -#endif + gfx_add_point(im, im->ximg, 0); + gfx_close_path(im); -#ifdef WITH_PIECHART - if (piechart) { - pie_part(im, im->graph_col[GRC_CANVAS], im->pie_x, im->pie_y, - im->piesize * 0.5, 0, 2 * M_PI); - } -#endif + gfx_new_area(im, + im->xorigin, im->yorigin, + im->xorigin + im->xsize, im->yorigin, + im->xorigin + im->xsize, im->yorigin - im->ysize, + im->graph_col[GRC_CANVAS]); + + gfx_add_point(im, im->xorigin, im->yorigin - im->ysize); + gfx_close_path(im); + + if (im->minval > 0.0) + areazero = im->minval; + if (im->maxval < 0.0) + areazero = im->maxval; for (i = 0; i < im->gdes_c; i++) { switch (im->gdes[i].gf) { @@ -2887,6 +3049,7 @@ int graph_paint( case GF_PRINT: case GF_GPRINT: case GF_COMMENT: + case GF_TEXTALIGN: case GF_HRULE: case GF_VRULE: case GF_XPORT: @@ -2897,20 +3060,20 @@ int graph_paint( if (!isnan(im->gdes[i].p_data[ii]) && im->gdes[i].p_data[ii] != 0.0) { if (im->gdes[i].yrule > 0) { - gfx_new_line(im->canvas, - im->xorigin + ii, im->yorigin, - im->xorigin + ii, - im->yorigin - - im->gdes[i].yrule * im->ysize, 1.0, - im->gdes[i].col); + gfx_line(im, + im->xorigin + ii, im->yorigin, + im->xorigin + ii, + im->yorigin - + im->gdes[i].yrule * im->ysize, 1.0, + im->gdes[i].col); } else if (im->gdes[i].yrule < 0) { - gfx_new_line(im->canvas, - im->xorigin + ii, - im->yorigin - im->ysize, - im->xorigin + ii, - im->yorigin - (1 - - im->gdes[i].yrule) * - im->ysize, 1.0, im->gdes[i].col); + gfx_line(im, + im->xorigin + ii, + im->yorigin - im->ysize, + im->xorigin + ii, + im->yorigin - (1 - + im->gdes[i].yrule) * + im->ysize, 1.0, im->gdes[i].col); } } @@ -2941,52 +3104,74 @@ int graph_paint( we draw a square from t-1 to t with the value a. ********************************************************* */ - if (im->gdes[i].col != 0x0) { + if (im->gdes[i].col.alpha != 0.0) { /* GF_LINE and friend */ if (im->gdes[i].gf == GF_LINE) { double last_y = 0.0; + int draw_on = 0; - node = NULL; + cairo_save(im->cr); + cairo_new_path(im->cr); + + cairo_set_line_width(im->cr, im->gdes[i].linewidth); for (ii = 1; ii < im->xsize; ii++) { if (isnan(im->gdes[i].p_data[ii]) || (im->slopemode == 1 && isnan(im->gdes[i].p_data[ii - 1]))) { - node = NULL; + draw_on = 0; continue; } - if (node == NULL) { + if (draw_on == 0) { last_y = ytr(im, im->gdes[i].p_data[ii]); if (im->slopemode == 0) { - node = gfx_new_line(im->canvas, - ii - 1 + im->xorigin, - last_y, ii + im->xorigin, - last_y, - im->gdes[i].linewidth, - im->gdes[i].col); + double x = ii - 1 + im->xorigin; + double y = last_y; + + gfx_line_fit(im, &x, &y); + cairo_move_to(im->cr, x, y); + x = ii + im->xorigin; + y = last_y; + gfx_line_fit(im, &x, &y); + cairo_line_to(im->cr, x, y); } else { - node = gfx_new_line(im->canvas, - ii - 1 + im->xorigin, - ytr(im, - im->gdes[i]. - p_data[ii - 1]), - ii + im->xorigin, last_y, - im->gdes[i].linewidth, - im->gdes[i].col); + double x = ii - 1 + im->xorigin; + double y = ytr(im, + im->gdes[i].p_data[ii - 1]); + + gfx_line_fit(im, &x, &y); + cairo_move_to(im->cr, x, y); + x = ii + im->xorigin; + y = last_y; + gfx_line_fit(im, &x, &y); + cairo_line_to(im->cr, x, y); } + draw_on = 1; } else { - double new_y = ytr(im, im->gdes[i].p_data[ii]); + double x1 = ii + im->xorigin; + double y1 = ytr(im, im->gdes[i].p_data[ii]); if (im->slopemode == 0 - && !AlmostEqual2sComplement(new_y, last_y, - 4)) { - gfx_add_point(node, ii - 1 + im->xorigin, - new_y); + && !AlmostEqual2sComplement(y1, last_y, 4)) { + double x = ii - 1 + im->xorigin; + double y = y1; + + gfx_line_fit(im, &x, &y); + cairo_line_to(im->cr, x, y); }; - last_y = new_y; - gfx_add_point(node, ii + im->xorigin, new_y); + last_y = y1; + gfx_line_fit(im, &x1, &y1); + cairo_line_to(im->cr, x1, y1); }; } + cairo_set_source_rgba(im->cr, im->gdes[i].col.red, + im->gdes[i].col.green, + im->gdes[i].col.blue, + im->gdes[i].col.alpha); + cairo_set_line_cap(im->cr, CAIRO_LINE_CAP_ROUND); + cairo_set_line_join(im->cr, CAIRO_LINE_JOIN_ROUND); + cairo_stroke(im->cr); + cairo_restore(im->cr); } else { int idxI = -1; double *foreY = malloc(sizeof(double) * im->xsize * 2); @@ -3010,11 +3195,11 @@ int graph_paint( 4)) { cntI++; } - node = gfx_new_area(im->canvas, - backX[0], backY[0], - foreX[0], foreY[0], - foreX[cntI], foreY[cntI], - im->gdes[i].col); + gfx_new_area(im, + backX[0], backY[0], + foreX[0], foreY[0], + foreX[cntI], foreY[cntI], + im->gdes[i].col); while (cntI < idxI) { lastI = cntI; cntI++; @@ -3028,9 +3213,9 @@ int graph_paint( 1], 4)) { cntI++; } - gfx_add_point(node, foreX[cntI], foreY[cntI]); + gfx_add_point(im, foreX[cntI], foreY[cntI]); } - gfx_add_point(node, backX[idxI], backY[idxI]); + gfx_add_point(im, backX[idxI], backY[idxI]); while (idxI > 1) { lastI = idxI; idxI--; @@ -3044,10 +3229,11 @@ int graph_paint( 1], 4)) { idxI--; } - gfx_add_point(node, backX[idxI], backY[idxI]); + gfx_add_point(im, backX[idxI], backY[idxI]); } idxI = -1; drawem = 0; + gfx_close_path(im); } if (drawem != 0) { drawem = 0; @@ -3056,10 +3242,6 @@ int graph_paint( if (ii == im->xsize) break; - /* keep things simple for now, just draw these bars - do not try to build a big and complex area */ - - if (im->slopemode == 0 && ii == 0) { continue; } @@ -3077,8 +3259,7 @@ int graph_paint( drawem = 1; continue; } - /* every area has to be wound clock-wise, - so we have to make sur base remains base */ + if (ybase > ytop) { double extra = ytop; @@ -3116,20 +3297,6 @@ int graph_paint( } lastgdes = &(im->gdes[i]); break; -#ifdef WITH_PIECHART - case GF_PART: - if (isnan(im->gdes[i].yrule)) /* fetch variable */ - im->gdes[i].yrule = im->gdes[im->gdes[i].vidx].vf.val; - - if (finite(im->gdes[i].yrule)) { /* even the fetched var can be NaN */ - pie_part(im, im->gdes[i].col, - im->pie_x, im->pie_y, im->piesize * 0.4, - M_PI * 2.0 * PieStart / 100.0, - M_PI * 2.0 * (PieStart + im->gdes[i].yrule) / 100.0); - PieStart += im->gdes[i].yrule; - } - break; -#endif case GF_STACK: rrd_set_error ("STACK should already be turned into LINE or AREA here"); @@ -3138,13 +3305,6 @@ int graph_paint( } /* switch */ } -#ifdef WITH_PIECHART - if (piechart == 2) { - im->draw_x_grid = 0; - im->draw_y_grid = 0; - } -#endif - /* grid_paint also does the text */ if (!(im->extra_flags & ONLY_GRAPH)) @@ -3161,19 +3321,19 @@ int graph_paint( case GF_HRULE: if (im->gdes[i].yrule >= im->minval && im->gdes[i].yrule <= im->maxval) - gfx_new_line(im->canvas, - im->xorigin, ytr(im, im->gdes[i].yrule), - im->xorigin + im->xsize, ytr(im, - im->gdes[i].yrule), - 1.0, im->gdes[i].col); + gfx_line(im, + im->xorigin, ytr(im, im->gdes[i].yrule), + im->xorigin + im->xsize, ytr(im, + im->gdes[i].yrule), + 1.0, im->gdes[i].col); break; case GF_VRULE: if (im->gdes[i].xrule >= im->start && im->gdes[i].xrule <= im->end) - gfx_new_line(im->canvas, - xtr(im, im->gdes[i].xrule), im->yorigin, - xtr(im, im->gdes[i].xrule), - im->yorigin - im->ysize, 1.0, im->gdes[i].col); + gfx_line(im, + xtr(im, im->gdes[i].xrule), im->yorigin, + xtr(im, im->gdes[i].xrule), + im->yorigin - im->ysize, 1.0, im->gdes[i].col); break; default: break; @@ -3181,22 +3341,38 @@ int graph_paint( } - if (strcmp(im->graphfile, "-") == 0) { - fo = im->graphhandle ? im->graphhandle : stdout; -#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__) - /* Change translation mode for stdout to BINARY */ - _setmode(_fileno(fo), O_BINARY); -#endif - } else { - if ((fo = fopen(im->graphfile, "wb")) == NULL) { - rrd_set_error("Opening '%s' for write: %s", im->graphfile, - rrd_strerror(errno)); - return (-1); + switch (im->imgformat) { + case IF_PNG: + { + cairo_status_t status; + + if (strlen(im->graphfile) == 0) { + status = + cairo_surface_write_to_png_stream(im->surface, + &cairo_copy_to_buffer, im); + } else if (strcmp(im->graphfile, "-") == 0) { + status = + cairo_surface_write_to_png_stream(im->surface, + &cairo_write_func_filehandle, + (void *) stdout); + } else { + status = cairo_surface_write_to_png(im->surface, im->graphfile); + } + + if (status != CAIRO_STATUS_SUCCESS) { + rrd_set_error("Could not save png to '%s'", im->graphfile); + return 1; } } - gfx_render(im->canvas, im->ximg, im->yimg, 0x00000000, fo); - if (strcmp(im->graphfile, "-") != 0) - fclose(fo); + break; + default: + if (strlen(im->graphfile)) { + cairo_show_page(im->cr); + } else { + cairo_surface_finish(im->surface); + } + break; + } return 0; } @@ -3233,8 +3409,11 @@ int gdes_alloc( im->gdes[im->gdes_c - 1].data_first = 0; im->gdes[im->gdes_c - 1].p_data = NULL; im->gdes[im->gdes_c - 1].rpnp = NULL; - im->gdes[im->gdes_c - 1].shift = 0; - im->gdes[im->gdes_c - 1].col = 0x0; + im->gdes[im->gdes_c - 1].shift = 0.0; + im->gdes[im->gdes_c - 1].col.red = 0.0; + im->gdes[im->gdes_c - 1].col.green = 0.0; + im->gdes[im->gdes_c - 1].col.blue = 0.0; + im->gdes[im->gdes_c - 1].col.alpha = 0.0; im->gdes[im->gdes_c - 1].legend[0] = '\0'; im->gdes[im->gdes_c - 1].format[0] = '\0'; im->gdes[im->gdes_c - 1].strftm = 0; @@ -3289,6 +3468,10 @@ int rrd_graph( image_desc_t im; rrd_graph_init(&im); + + /* a dummy surface so that we can measure text sizes for placements */ + im.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10); + im.cr = cairo_create(im.surface); im.graphhandle = stream; rrd_graph_options(argc, argv, &im); @@ -3297,11 +3480,17 @@ int rrd_graph( return -1; } + if (optind >= argc) { + rrd_set_error("missing filename"); + return -1; + } + if (strlen(argv[optind]) >= MAXPATH) { rrd_set_error("filename (including path) too long"); im_free(&im); return -1; } + strncpy(im.graphfile, argv[optind], MAXPATH - 1); im.graphfile[MAXPATH - 1] = '\0'; @@ -3352,13 +3541,66 @@ int rrd_graph( } sprintf((*prdata)[0], im.imginfo, filename, - (long) (im.canvas->zoom * im.ximg), - (long) (im.canvas->zoom * im.yimg)); + (long) (im.zoom * im.ximg), (long) (im.zoom * im.yimg)); } im_free(&im); return 0; } +/* a simplified version of the above that just creates the graph in memory + and returns a pointer to it. */ + +unsigned char *rrd_graph_in_memory( + int argc, + char **argv, + char ***prdata, + int *xsize, + int *ysize, + double *ymin, + double *ymax, + size_t * img_size) +{ + image_desc_t im; + + rrd_graph_init(&im); + + /* a dummy surface so that we can measure text sizes for placements */ + im.surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10); + im.cr = cairo_create(im.surface); + + rrd_graph_options(argc, argv, &im); + if (rrd_test_error()) { + im_free(&im); + return NULL; + } + + rrd_graph_script(argc, argv, &im, 1); + if (rrd_test_error()) { + im_free(&im); + return NULL; + } + + /* Everything is now read and the actual work can start */ + + /* by not assigning a name to im.graphfile data will be written to + newly allocated memory on im.rendered_image ... */ + + (*prdata) = NULL; + if (graph_paint(&im, prdata) == -1) { + im_free(&im); + return NULL; + } + + *xsize = im.ximg; + *ysize = im.yimg; + *ymin = im.minval; + *ymax = im.maxval; + *img_size = im.rendered_image_size; + im_free(&im); + + return im.rendered_image; +} + void rrd_graph_init( image_desc_t *im) { @@ -3381,6 +3623,8 @@ void rrd_graph_init( im->yimg = 0; im->xsize = 400; im->ysize = 100; + im->rendered_image_size = 0; + im->rendered_image = NULL; im->step = 0; im->ylegend[0] = '\0'; im->title[0] = '\0'; @@ -3392,6 +3636,10 @@ void rrd_graph_init( im->forceleftspace = 0; im->symbol = ' '; im->viewfactor = 1.0; + im->imgformat = IF_PNG; + im->graphfile[0] = '\0'; + im->cr = NULL; + im->surface = NULL; im->extra_flags = 0; im->rigid = 0; im->gridfit = 1; @@ -3406,10 +3654,19 @@ void rrd_graph_init( im->prt_c = 0; im->gdes_c = 0; im->gdes = NULL; - im->canvas = gfx_new_canvas(); im->grid_dash_on = 1; im->grid_dash_off = 1; im->tabwidth = 40.0; + im->zoom = 1; + im->font_options = cairo_font_options_create(); + im->graph_antialias = CAIRO_ANTIALIAS_GRAY; + + cairo_font_options_set_hint_style(im->font_options, + CAIRO_HINT_STYLE_FULL); + cairo_font_options_set_hint_metrics(im->font_options, + CAIRO_HINT_METRICS_ON); + cairo_font_options_set_antialias(im->font_options, CAIRO_ANTIALIAS_GRAY); + for (i = 0; i < DIM(graph_col); i++) im->graph_col[i] = graph_col[i]; @@ -3463,7 +3720,56 @@ void rrd_graph_options( time_t start_tmp = 0, end_tmp = 0; long long_tmp; struct rrd_time_value start_tv, end_tv; - gfx_color_t color; + long unsigned int color; + char *old_locale = ""; + + /* defines for long options without a short equivalent. should be bytes, + and may not collide with (the ASCII value of) short options */ +#define LONGOPT_UNITS_SI 255 + struct option long_options[] = { + {"start", required_argument, 0, 's'}, + {"end", required_argument, 0, 'e'}, + {"x-grid", required_argument, 0, 'x'}, + {"y-grid", required_argument, 0, 'y'}, + {"vertical-label", required_argument, 0, 'v'}, + {"width", required_argument, 0, 'w'}, + {"height", required_argument, 0, 'h'}, + {"full-size-mode", no_argument, 0, 'D'}, + {"interlaced", no_argument, 0, 'i'}, + {"upper-limit", required_argument, 0, 'u'}, + {"lower-limit", required_argument, 0, 'l'}, + {"rigid", no_argument, 0, 'r'}, + {"base", required_argument, 0, 'b'}, + {"logarithmic", no_argument, 0, 'o'}, + {"color", required_argument, 0, 'c'}, + {"font", required_argument, 0, 'n'}, + {"title", required_argument, 0, 't'}, + {"imginfo", required_argument, 0, 'f'}, + {"imgformat", required_argument, 0, 'a'}, + {"lazy", no_argument, 0, 'z'}, + {"zoom", required_argument, 0, 'm'}, + {"no-legend", no_argument, 0, 'g'}, + {"force-rules-legend", no_argument, 0, 'F'}, + {"only-graph", no_argument, 0, 'j'}, + {"alt-y-grid", no_argument, 0, 'Y'}, + {"no-minor", no_argument, 0, 'I'}, + {"slope-mode", no_argument, 0, 'E'}, + {"alt-autoscale", no_argument, 0, 'A'}, + {"alt-autoscale-min", no_argument, 0, 'J'}, + {"alt-autoscale-max", no_argument, 0, 'M'}, + {"no-gridfit", no_argument, 0, 'N'}, + {"units-exponent", required_argument, 0, 'X'}, + {"units-length", required_argument, 0, 'L'}, + {"units", required_argument, 0, LONGOPT_UNITS_SI}, + {"step", required_argument, 0, 'S'}, + {"tabwidth", required_argument, 0, 'T'}, + {"font-render-mode", required_argument, 0, 'R'}, + {"graph-render-mode", required_argument, 0, 'G'}, + {"font-smoothing-threshold", required_argument, 0, 'B'}, + {"watermark", required_argument, 0, 'W'}, + {"alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */ + {0, 0, 0, 0} + }; optind = 0; opterr = 0; /* initialize getopt */ @@ -3471,59 +3777,13 @@ void rrd_graph_options( parsetime("end-24h", &start_tv); parsetime("now", &end_tv); - /* defines for long options without a short equivalent. should be bytes, - and may not collide with (the ASCII value of) short options */ -#define LONGOPT_UNITS_SI 255 - while (1) { - static struct option long_options[] = { - {"start", required_argument, 0, 's'}, - {"end", required_argument, 0, 'e'}, - {"x-grid", required_argument, 0, 'x'}, - {"y-grid", required_argument, 0, 'y'}, - {"vertical-label", required_argument, 0, 'v'}, - {"width", required_argument, 0, 'w'}, - {"height", required_argument, 0, 'h'}, - {"interlaced", no_argument, 0, 'i'}, - {"upper-limit", required_argument, 0, 'u'}, - {"lower-limit", required_argument, 0, 'l'}, - {"rigid", no_argument, 0, 'r'}, - {"base", required_argument, 0, 'b'}, - {"logarithmic", no_argument, 0, 'o'}, - {"color", required_argument, 0, 'c'}, - {"font", required_argument, 0, 'n'}, - {"title", required_argument, 0, 't'}, - {"imginfo", required_argument, 0, 'f'}, - {"imgformat", required_argument, 0, 'a'}, - {"lazy", no_argument, 0, 'z'}, - {"zoom", required_argument, 0, 'm'}, - {"no-legend", no_argument, 0, 'g'}, - {"force-rules-legend", no_argument, 0, 'F'}, - {"only-graph", no_argument, 0, 'j'}, - {"alt-y-grid", no_argument, 0, 'Y'}, - {"no-minor", no_argument, 0, 'I'}, - {"slope-mode", no_argument, 0, 'E'}, - {"alt-autoscale", no_argument, 0, 'A'}, - {"alt-autoscale-min", no_argument, 0, 'J'}, - {"alt-autoscale-max", no_argument, 0, 'M'}, - {"no-gridfit", no_argument, 0, 'N'}, - {"units-exponent", required_argument, 0, 'X'}, - {"units-length", required_argument, 0, 'L'}, - {"units", required_argument, 0, LONGOPT_UNITS_SI}, - {"step", required_argument, 0, 'S'}, - {"tabwidth", required_argument, 0, 'T'}, - {"font-render-mode", required_argument, 0, 'R'}, - {"font-smoothing-threshold", required_argument, 0, 'B'}, - {"watermark", required_argument, 0, 'W'}, - {"alt-y-mrtg", no_argument, 0, 1000}, /* this has no effect it is just here to save old apps from crashing when they use it */ - {0, 0, 0, 0} - }; int option_index = 0; int opt; int col_start, col_end; opt = getopt_long(argc, argv, - "s:e:x:y:v:w:h:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:", + "s:e:x:y:v:w:h:D:iu:l:rb:oc:n:m:t:f:a:I:zgjFYAMEX:L:S:T:NR:B:W:", long_options, &option_index); if (opt == EOF) @@ -3557,6 +3817,7 @@ void rrd_graph_options( case LONGOPT_UNITS_SI: if (im->extra_flags & FORCE_UNITS) { rrd_set_error("--units can only be used once!"); + setlocale(LC_NUMERIC, old_locale); return; } if (strcmp(optarg, "si") == 0) @@ -3574,10 +3835,14 @@ void rrd_graph_options( im->forceleftspace = 1; break; case 'T': + old_locale = setlocale(LC_NUMERIC, "C"); im->tabwidth = atof(optarg); + setlocale(LC_NUMERIC, old_locale); break; case 'S': + old_locale = setlocale(LC_NUMERIC, "C"); im->step = atoi(optarg); + setlocale(LC_NUMERIC, old_locale); break; case 'N': im->gridfit = 0; @@ -3637,8 +3902,9 @@ void rrd_graph_options( im->draw_y_grid = 0; break; }; - + old_locale = setlocale(LC_NUMERIC, "C"); if (sscanf(optarg, "%lf:%d", &im->ygridstep, &im->ylabfact) == 2) { + setlocale(LC_NUMERIC, old_locale); if (im->ygridstep <= 0) { rrd_set_error("grid step must be > 0"); return; @@ -3647,6 +3913,7 @@ void rrd_graph_options( return; } } else { + setlocale(LC_NUMERIC, old_locale); rrd_set_error("invalid y-grid format"); return; } @@ -3656,10 +3923,14 @@ void rrd_graph_options( im->ylegend[150] = '\0'; break; case 'u': + old_locale = setlocale(LC_NUMERIC, "C"); im->maxval = atof(optarg); + setlocale(LC_NUMERIC, old_locale); break; case 'l': + old_locale = setlocale(LC_NUMERIC, "C"); im->minval = atof(optarg); + setlocale(LC_NUMERIC, old_locale); break; case 'b': im->base = atol(optarg); @@ -3685,8 +3956,11 @@ void rrd_graph_options( } im->ysize = long_tmp; break; + case 'D': + im->extra_flags |= FULL_SIZE_MODE; + break; case 'i': - im->canvas->interlaced = 1; + /* interlaced png not supported at the moment */ break; case 'r': im->rigid = 1; @@ -3695,7 +3969,7 @@ void rrd_graph_options( im->imginfo = optarg; break; case 'a': - if ((int) (im->canvas->imgformat = if_conv(optarg)) == -1) { + if ((int) (im->imgformat = if_conv(optarg)) == -1) { rrd_set_error("unsupported graphics format '%s'", optarg); return; } @@ -3740,7 +4014,7 @@ void rrd_graph_options( return; } if ((ci = grc_conv(col_nam)) != -1) { - im->graph_col[ci] = color; + im->graph_col[ci] = gfx_hex_to_col(color); } else { rrd_set_error("invalid color name '%s'", col_nam); return; @@ -3755,9 +4029,11 @@ void rrd_graph_options( double size = 1; char font[1024] = ""; + old_locale = setlocale(LC_NUMERIC, "C"); if (sscanf(optarg, "%10[A-Z]:%lf:%1000s", prop, &size, font) >= 2) { int sindex, propidx; + setlocale(LC_NUMERIC, old_locale); if ((sindex = text_prop_conv(prop)) != -1) { for (propidx = sindex; propidx < TEXT_PROP_LAST; propidx++) { @@ -3775,14 +4051,17 @@ void rrd_graph_options( return; } } else { + setlocale(LC_NUMERIC, old_locale); rrd_set_error("invalid text property format"); return; } break; } case 'm': - im->canvas->zoom = atof(optarg); - if (im->canvas->zoom <= 0.0) { + old_locale = setlocale(LC_NUMERIC, "C"); + im->zoom = atof(optarg); + setlocale(LC_NUMERIC, old_locale); + if (im->zoom <= 0.0) { rrd_set_error("zoom factor must be > 0"); return; } @@ -3793,20 +4072,38 @@ void rrd_graph_options( break; case 'R': + if (strcmp(optarg, "normal") == 0) { + cairo_font_options_set_antialias(im->font_options, + CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style(im->font_options, + CAIRO_HINT_STYLE_FULL); + } else if (strcmp(optarg, "light") == 0) { + cairo_font_options_set_antialias(im->font_options, + CAIRO_ANTIALIAS_GRAY); + cairo_font_options_set_hint_style(im->font_options, + CAIRO_HINT_STYLE_SLIGHT); + } else if (strcmp(optarg, "mono") == 0) { + cairo_font_options_set_antialias(im->font_options, + CAIRO_ANTIALIAS_NONE); + cairo_font_options_set_hint_style(im->font_options, + CAIRO_HINT_STYLE_FULL); + } else { + rrd_set_error("unknown font-render-mode '%s'", optarg); + return; + } + break; + case 'G': if (strcmp(optarg, "normal") == 0) - im->canvas->aa_type = AA_NORMAL; - else if (strcmp(optarg, "light") == 0) - im->canvas->aa_type = AA_LIGHT; + im->graph_antialias = CAIRO_ANTIALIAS_GRAY; else if (strcmp(optarg, "mono") == 0) - im->canvas->aa_type = AA_NONE; + im->graph_antialias = CAIRO_ANTIALIAS_NONE; else { - rrd_set_error("unknown font-render-mode '%s'", optarg); + rrd_set_error("unknown graph-render-mode '%s'", optarg); return; } break; - case 'B': - im->canvas->font_aa_threshold = atof(optarg); + /* not supported curently */ break; case 'W': @@ -3823,11 +4120,6 @@ void rrd_graph_options( } } - if (optind >= argc) { - rrd_set_error("missing filename"); - return; - } - if (im->logarithmic == 1 && im->minval <= 0) { rrd_set_error ("for a logarithmic yaxis you must specify a lower-limit > 0"); @@ -3875,7 +4167,7 @@ int rrd_graph_color( } else { int n = 0; char *rest; - gfx_color_t col; + long unsigned int col; rest = strstr(color, ":"); if (rest != NULL) @@ -3899,7 +4191,7 @@ int rrd_graph_color( } if (rrd_test_error()) return 0; - gdp->col = col; + gdp->col = gfx_hex_to_col(col); return n; } } @@ -3959,10 +4251,8 @@ int bad_format( int vdef_parse( - gdes, - str) - struct graph_desc_t *gdes; - const char *const str; + struct graph_desc_t *gdes, + const char *const str) { /* A VDEF currently is either "func" or "param,func" * so the parsing is rather simple. Change if needed. @@ -3970,9 +4260,12 @@ int vdef_parse( double param; char func[30]; int n; + char *old_locale; n = 0; + old_locale = setlocale(LC_NUMERIC, "C"); sscanf(str, "%le,%29[A-Z]%n", ¶m, func, &n); + setlocale(LC_NUMERIC, old_locale); if (n == (int) strlen(str)) { /* matched */ ; } else { @@ -3992,6 +4285,8 @@ int vdef_parse( gdes->vf.op = VDEF_MAXIMUM; else if (!strcmp("AVERAGE", func)) gdes->vf.op = VDEF_AVERAGE; + else if (!strcmp("STDEV", func)) + gdes->vf.op = VDEF_STDEV; else if (!strcmp("MINIMUM", func)) gdes->vf.op = VDEF_MINIMUM; else if (!strcmp("TOTAL", func)) @@ -4031,6 +4326,7 @@ int vdef_parse( break; case VDEF_MAXIMUM: case VDEF_AVERAGE: + case VDEF_STDEV: case VDEF_MINIMUM: case VDEF_TOTAL: case VDEF_FIRST: @@ -4054,10 +4350,8 @@ int vdef_parse( int vdef_calc( - im, - gdi) - image_desc_t *im; - int gdi; + image_desc_t *im, + int gdi) { graph_desc_t *src, *dst; rrd_value_t *data; @@ -4121,9 +4415,11 @@ int vdef_calc( } break; case VDEF_TOTAL: + case VDEF_STDEV: case VDEF_AVERAGE:{ int cnt = 0; double sum = 0.0; + double average = 0.0; for (step = 0; step < steps; step++) { if (finite(data[step * src->ds_cnt])) { @@ -4135,9 +4431,19 @@ int vdef_calc( if (dst->vf.op == VDEF_TOTAL) { dst->vf.val = sum * src->step; dst->vf.when = 0; /* no time component */ - } else { + } else if (dst->vf.op == VDEF_AVERAGE) { dst->vf.val = sum / cnt; dst->vf.when = 0; /* no time component */ + } else { + average = sum / cnt; + sum = 0.0; + for (step = 0; step < steps; step++) { + if (finite(data[step * src->ds_cnt])) { + sum += pow((data[step * src->ds_cnt] - average), 2.0); + }; + } + dst->vf.val = pow(sum / cnt, 0.5); + dst->vf.when = 0; /* no time component */ }; } else { dst->vf.val = DNAN; @@ -4257,9 +4563,8 @@ int vdef_calc( /* NaN < -INF < finite_values < INF */ int vdef_percent_compar( - a, - b) - const void *a, *b; + const void *a, + const void *b) { /* Equality is not returned; this doesn't hurt except * (maybe) for a little performance.