diff --git a/src/rrd_graph.c b/src/rrd_graph.c
index f33cfc3dd8b981f1f820cb3494f6dc237a8c7bb4..d3aa62697bed75bd563ada45ddcde66ed0018dff 100644 (file)
--- a/src/rrd_graph.c
+++ b/src/rrd_graph.c
/****************************************************************************
- * RRDtool 1.2.23 Copyright by Tobi Oetiker, 1997-2007
+ * RRDtool 1.3.2 Copyright by Tobi Oetiker, 1997-2008
****************************************************************************
* rrd__graph.c produce graphs from data in rrdfiles
****************************************************************************/
#ifdef WIN32
#include "strftime.h"
#endif
+
#include "rrd_tool.h"
+/* for basename */
+#ifdef HAVE_LIBGEN_H
+# include <libgen.h>
+#else
+#include "plbasename.h"
+#endif
+
#if defined(WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
#include <io.h>
#include <fcntl.h>
#endif
-#ifdef HAVE_TIME_H
#include <time.h>
-#endif
-#ifdef HAVE_LOCALE_H
#include <locale.h>
-#endif
#include "rrd_graph.h"
+#include "rrd_client.h"
/* some constant definitions */
#ifndef RRD_DEFAULT_FONT
/* there is special code later to pick Cour.ttf when running on windows */
-#define RRD_DEFAULT_FONT "DejaVuSansMono-Roman.ttf"
+#define RRD_DEFAULT_FONT "DejaVu Sans Mono,Bitstream Vera Sans Mono,monospace,Courier"
#endif
text_prop_t text_prop[] = {
- {8.0, RRD_DEFAULT_FONT}
+ {8.0, RRD_DEFAULT_FONT,NULL}
, /* default */
- {9.0, RRD_DEFAULT_FONT}
+ {9.0, RRD_DEFAULT_FONT,NULL}
, /* title */
- {7.0, RRD_DEFAULT_FONT}
+ {7.0, RRD_DEFAULT_FONT,NULL}
, /* axis */
- {8.0, RRD_DEFAULT_FONT}
+ {8.0, RRD_DEFAULT_FONT,NULL}
, /* unit */
- {8.0, RRD_DEFAULT_FONT} /* legend */
+ {8.0, RRD_DEFAULT_FONT,NULL} /* legend */
+ ,
+ {5.5, RRD_DEFAULT_FONT,NULL} /* watermark */
};
xlab_t xlab[] = {
,
{60, 0, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 2, 0, "%H:%M"}
,
- {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 4, 0, "%a %H:%M"}
+ {60, 24 * 3600, TMT_MINUTE, 30, TMT_HOUR, 2, TMT_HOUR, 6, 0, "%a %H:%M"}
,
{180, 0, TMT_HOUR, 1, TMT_HOUR, 6, TMT_HOUR, 6, 0, "%H:%M"}
,
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 */
};
yval = im->yorigin - pixie * (log10(value) - log10(im->minval));
}
}
- /* make sure we don't return anything too unreasonable. GD lib can
- get terribly slow when drawing lines outside its scope. This is
- especially problematic in connection with the rigid option */
- if (!im->rigid) {
- /* keep yval as-is */
- } else if (yval > im->yorigin) {
- yval = im->yorigin + 0.00001;
- } else if (yval < im->yorigin - im->ysize) {
- yval = im->yorigin - im->ysize - 0.00001;
- }
return yval;
}
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 (enum gf_en)(-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 (enum gfx_if_en)(-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 (enum tmt_en)(-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)
-
- return -1;
+ 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 (enum grc_en)(-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);
+ conv_if(WATERMARK, TEXT_PROP_WATERMARK);
+ return (enum text_prop_en)(-1);
}
image_desc_t *im)
{
unsigned long i, ii;
+ cairo_status_t status = (cairo_status_t) 0;
if (im == NULL)
return 0;
+
+ if (im->daemon_addr != NULL)
+ free(im->daemon_addr);
+
for (i = 0; i < (unsigned) im->gdes_c; i++) {
if (im->gdes[i].data_first) {
/* careful here, because a single pointer can occur several times */
free(im->gdes[i].ds_namv);
}
}
+ /* free allocated memory used for dashed lines */
+ if (im->gdes[i].p_dashes != NULL)
+ free(im->gdes[i].p_dashes);
+
free(im->gdes[i].p_data);
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->rendered_image) {
+ free(im->rendered_image);
+ }
+
+ if (im->layout) {
+ g_object_unref (im->layout);
+ }
+
+ if (im->surface)
+ cairo_surface_destroy(im->surface);
+
+ if (status)
+ fprintf(stderr, "OOPS: Cairo has issues it can't even die: %s\n",
+ cairo_status_to_string(status));
+
return 0;
}
if (im->unitsexponent != 9999) {
/* unitsexponent = 9, 6, 3, 0, -3, -6, -9, etc */
- viewdigits = floor(im->unitsexponent / 3);
+ viewdigits = floor((double)(im->unitsexponent / 3));
} else {
viewdigits = digits;
}
#endif
}
+
void apply_gridfit(
image_desc_t *im)
{
ytr(im, DNAN); /* reset precalc */
log10_range = log10(im->maxval) - log10(im->minval);
}
- /* make sure first y=10^x gridline is located on
- integer pixel position by moving scale slightly
+ /* make sure first y=10^x gridline is located on
+ integer pixel position by moving scale slightly
downwards (sub-pixel movement) */
ypix = ytr(im, ya) + im->ysize; /* add im->ysize so it always is positive */
ypixfrac = ypix - floor(ypix);
else {
switch (cf) {
case CF_HWPREDICT:
+ case CF_MHWPREDICT:
case CF_DEVSEASONAL:
case CF_DEVPREDICT:
case CF_SEASONAL:
} else {
switch (cf) {
case CF_HWPREDICT:
+ case CF_MHWPREDICT:
case CF_DEVSEASONAL:
case CF_DEVPREDICT:
case CF_SEASONAL:
}
-/* get the data required for the graphs from the
+/* get the data required for the graphs from the
relevant rrds ... */
int data_fetch(
if (!skip) {
unsigned long ft_step = im->gdes[i].step; /* ft_step will record what we got from fetch */
+ /* Flush the file if
+ * - a connection to the daemon has been established
+ * - this is the first occurrence of that RRD file
+ */
+ if (rrdc_is_connected(im->daemon_addr))
+ {
+ int status;
+
+ status = 0;
+ for (ii = 0; ii < i; ii++)
+ {
+ if (strcmp (im->gdes[i].rrd, im->gdes[ii].rrd) == 0)
+ {
+ status = 1;
+ break;
+ }
+ }
+
+ if (status == 0)
+ {
+ status = rrdc_flush (im->gdes[i].rrd);
+ if (status != 0)
+ {
+ rrd_set_error ("rrdc_flush (%s) failed with status %i.",
+ im->gdes[i].rrd, status);
+ return (-1);
+ }
+ }
+ } /* if (rrdc_is_connected()) */
+
if ((rrd_fetch_fn(im->gdes[i].rrd,
im->gdes[i].cf,
&im->gdes[i].start,
/* evaluate the expressions in the CDEF functions */
/*************************************************************
- * CDEF stuff
+ * CDEF stuff
*************************************************************/
long find_var_wrapper(
return -1;
}
-/* find the largest common denominator for all the numbers
+/* find the greatest common divisor for all the numbers
in the 0 terminated num array */
long lcd(
long *num)
/* add one entry to the array that keeps track of the step sizes of the
* data sources going into the CDEF. */
if ((steparray =
- rrd_realloc(steparray,
+ (long*)rrd_realloc(steparray,
(++stepcnt +
1) * sizeof(*steparray))) == NULL) {
rrd_set_error("realloc steparray");
*/
im->gdes[gdi].step = lcd(steparray);
free(steparray);
- if ((im->gdes[gdi].data = malloc(((im->gdes[gdi].end -
+ if ((im->gdes[gdi].data = (rrd_value_t*)malloc(((im->gdes[gdi].end -
im->gdes[gdi].start)
/ im->gdes[gdi].step)
* sizeof(double))) == NULL) {
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 int AlmostEqual2sComplement(
+ float A,
+ float B,
+ int maxUlps)
+{
+
+ int aInt = *(int *) &A;
+ int bInt = *(int *) &B;
+ int intDiff;
+
+ /* Make sure maxUlps is non-negative and small enough that the
+ default NAN won't compare as equal to anything. */
+
+ /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
+
+ /* Make aInt lexicographically ordered as a twos-complement int */
+
+ if (aInt < 0)
+ aInt = 0x80000000l - aInt;
+
+ /* Make bInt lexicographically ordered as a twos-complement int */
+
+ if (bInt < 0)
+ bInt = 0x80000000l - bInt;
+
+ intDiff = abs(aInt - bInt);
+
+ if (intDiff <= maxUlps)
+ return 1;
+
+ return 0;
+}
+
/* massage data so, that we get one value for each x coordinate in the graph */
int data_proc(
image_desc_t *im)
{
long i, ii;
double pixstep = (double) (im->end - im->start)
- / (double) im->xsize; /* how much time
+ / (double) im->xsize; /* how much time
passes in one pixel */
double paintval;
double minval = DNAN, maxval = DNAN;
for (i = 0; i < im->gdes_c; i++) {
if ((im->gdes[i].gf == GF_LINE) ||
(im->gdes[i].gf == GF_AREA) || (im->gdes[i].gf == GF_TICK)) {
- if ((im->gdes[i].p_data = malloc((im->xsize + 1)
+ if ((im->gdes[i].p_data = (rrd_value_t*)malloc((im->xsize + 1)
* sizeof(rrd_value_t))) == NULL) {
rrd_set_error("malloc data_proc");
return -1;
lets set these to dummy values then ... */
if (im->logarithmic) {
- if (isnan(minval))
- minval = 0.2;
- if (isnan(maxval))
+ if (isnan(minval) || isnan(maxval) || maxval <= 0) {
+ minval = 0.0; /* catching this right away below */
maxval = 5.1;
+ }
+ /* in logarithm mode, where minval is smaller or equal
+ to 0 make the beast just way smaller than maxval */
+ if (minval <= 0) {
+ minval = maxval / 10e8;
+ }
} else {
- if (isnan(minval))
+ if (isnan(minval) || isnan(maxval)) {
minval = 0.0;
- if (isnan(maxval))
maxval = 1.0;
+ }
}
- /* adjust min and max values */
+ /* adjust min and max values given by the user */
+ /* for logscale we add something on top */
if (isnan(im->minval)
- /* don't adjust low-end with log scale *//* why not? */
|| ((!im->rigid) && im->minval > minval)
) {
if (im->logarithmic)
- im->minval = minval * 0.5;
+ im->minval = minval / 2.0;
else
im->minval = minval;
}
else
im->maxval = maxval;
}
+
/* make sure min is smaller than max */
if (im->minval > im->maxval) {
- im->minval = 0.99 * im->maxval;
+ if (im->minval > 0)
+ im->minval = 0.99 * im->maxval;
+ else
+ im->minval = 1.01 * im->maxval;
}
/* make sure min and max are not equal */
- if (im->minval == im->maxval) {
- im->maxval *= 1.01;
- if (!im->logarithmic) {
- im->minval *= 0.99;
- }
+ if (AlmostEqual2sComplement(im->minval, im->maxval, 4)) {
+ if (im->maxval > 0)
+ im->maxval *= 1.01;
+ else
+ im->maxval *= 0.99;
+
/* make sure min and max are not both zero */
- if (im->maxval == 0.0) {
+ if (AlmostEqual2sComplement(im->maxval, 0, 4)) {
im->maxval = 1.0;
}
}
/* calculate values required for PRINT and GPRINT functions */
int print_calc(
- image_desc_t *im,
- char ***prdata)
+ image_desc_t *im)
{
long i, ii, validsteps;
double printval;
double magfact = -1;
char *si_symb = "";
char *percent_s;
- int prlines = 1;
+ int prline_cnt = 0;
/* wow initializing tmvdef is quite a task :-) */
time_t now = time(NULL);
localtime_r(&now, &tmvdef);
- if (im->imginfo)
- prlines++;
for (i = 0; i < im->gdes_c; i++) {
vidx = im->gdes[i].vidx;
switch (im->gdes[i].gf) {
case GF_PRINT:
- prlines++;
- if (((*prdata) =
- rrd_realloc((*prdata), prlines * sizeof(char *))) == NULL) {
- rrd_set_error("realloc prdata");
- return 0;
- }
case GF_GPRINT:
/* PRINT and GPRINT can now print VDEF generated values.
* There's no need to do any calculations on them as these
switch (im->gdes[i].cf) {
case CF_HWPREDICT:
+ case CF_MHWPREDICT:
case CF_DEVPREDICT:
case CF_DEVSEASONAL:
case CF_SEASONAL:
}
if (im->gdes[i].gf == GF_PRINT) {
- (*prdata)[prlines - 2] =
- malloc((FMT_LEG_LEN + 2) * sizeof(char));
- (*prdata)[prlines - 1] = NULL;
+ rrd_infoval_t prline;
+
if (im->gdes[i].strftm) {
- strftime((*prdata)[prlines - 2], FMT_LEG_LEN,
- im->gdes[i].format, &tmvdef);
+ prline.u_str = (char*)malloc((FMT_LEG_LEN + 2) * sizeof(char));
+ strftime(prline.u_str,
+ FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
+ } else if (bad_format(im->gdes[i].format)) {
+ rrd_set_error
+ ("bad format for PRINT in '%s'", im->gdes[i].format);
+ return -1;
} else {
- if (bad_format(im->gdes[i].format)) {
- rrd_set_error("bad format for PRINT in '%s'",
- im->gdes[i].format);
- return -1;
- }
-#ifdef HAVE_SNPRINTF
- snprintf((*prdata)[prlines - 2], FMT_LEG_LEN,
- im->gdes[i].format, printval, si_symb);
-#else
- sprintf((*prdata)[prlines - 2], im->gdes[i].format,
- printval, si_symb);
-#endif
+ prline.u_str =
+ sprintf_alloc(im->gdes[i].format, printval, si_symb);
}
+ grinfo_push(im,
+ sprintf_alloc
+ ("print[%ld]", prline_cnt++), RD_I_STR, prline);
+ free(prline.u_str);
} else {
/* GF_GPRINT */
if (im->gdes[i].strftm) {
- strftime(im->gdes[i].legend, FMT_LEG_LEN,
- im->gdes[i].format, &tmvdef);
+ strftime(im->gdes[i].legend,
+ FMT_LEG_LEN, im->gdes[i].format, &tmvdef);
} else {
if (bad_format(im->gdes[i].format)) {
- rrd_set_error("bad format for GPRINT in '%s'",
- im->gdes[i].format);
+ rrd_set_error
+ ("bad format for GPRINT in '%s'",
+ im->gdes[i].format);
return -1;
}
#ifdef HAVE_SNPRINTF
- snprintf(im->gdes[i].legend, FMT_LEG_LEN - 2,
+ snprintf(im->gdes[i].legend,
+ FMT_LEG_LEN - 2,
im->gdes[i].format, printval, si_symb);
#else
- sprintf(im->gdes[i].legend, im->gdes[i].format, printval,
- si_symb);
+ sprintf(im->gdes[i].legend,
+ im->gdes[i].format, printval, si_symb);
#endif
}
graphelement = 1;
graphelement = 1;
break;
case GF_COMMENT:
+ case GF_TEXTALIGN:
case GF_DEF:
case GF_CDEF:
case GF_VDEF:
}
+
/* place legends with color spots */
int leg_place(
- image_desc_t *im)
+ image_desc_t *im,
+ int calc_width)
{
/* graph labels */
int interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
int border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
int fill = 0, fill_last;
+ double legendwidth; // = im->ximg - 2 * border;
int leg_c = 0;
- int leg_x = border, leg_y = im->yimg;
- int leg_y_prev = im->yimg;
+ double leg_x = border;
+ int leg_y = 0; //im->yimg;
+ int leg_y_prev = 0; // im->yimg;
int leg_cc;
- int glue = 0;
+ double glue = 0;
int i, ii, mark = 0;
- char prt_fctn; /*special printfunctions */
+ char default_txtalign = TXA_JUSTIFIED; /*default line orientation */
int *legspace;
+ char *tab;
+ char saved_legend[FMT_LEG_LEN + 5];
+
+ if(calc_width){
+ legendwidth = 0;
+ }
+ else{
+ legendwidth = im->legendwidth - 2 * border;
+ }
- if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
- if ((legspace = malloc(im->gdes_c * sizeof(int))) == NULL) {
+
+ if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
+ if ((legspace = (int*)malloc(im->gdes_c * sizeof(int))) == NULL) {
rrd_set_error("malloc for legspace");
return -1;
}
for (i = 0; i < im->gdes_c; i++) {
- fill_last = fill;
+ char prt_fctn; /*special printfunctions */
+ if(calc_width){
+ strcpy(saved_legend, im->gdes[i].legend);
+ }
- /* hid legends for rules which are not displayed */
+ fill_last = fill;
+ /* 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 &&
- (im->gdes[i].yrule < im->minval
- || im->gdes[i].yrule > im->maxval))
+ if (im->gdes[i].gf == GF_HRULE
+ && (im->gdes[i].yrule <
+ im->minval || im->gdes[i].yrule > im->maxval))
im->gdes[i].legend[0] = '\0';
-
- if (im->gdes[i].gf == GF_VRULE &&
- (im->gdes[i].xrule < im->start
- || im->gdes[i].xrule > im->end))
+ if (im->gdes[i].gf == GF_VRULE
+ && (im->gdes[i].xrule <
+ im->start || im->gdes[i].xrule > im->end))
im->gdes[i].legend[0] = '\0';
}
- leg_cc = strlen(im->gdes[i].legend);
+ /* turn \\t into tab */
+ while ((tab = strstr(im->gdes[i].legend, "\\t"))) {
+ memmove(tab, tab + 1, strlen(tab));
+ tab[0] = (char) 9;
+ }
- /* is there a controle code ant the end of the legend string ? */
- /* and it is not a tab \\t */
- if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\'
- && im->gdes[i].legend[leg_cc - 1] != 't') {
+ leg_cc = strlen(im->gdes[i].legend);
+ /* is there a controle code at the end of the legend string ? */
+ if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
prt_fctn = im->gdes[i].legend[leg_cc - 1];
leg_cc -= 2;
im->gdes[i].legend[leg_cc] = '\0';
prt_fctn != 'r' &&
prt_fctn != 'j' &&
prt_fctn != 'c' &&
- prt_fctn != 's' &&
- prt_fctn != 't' && prt_fctn != '\0' && prt_fctn != 'g') {
+ prt_fctn != 's' && prt_fctn != '\0' && prt_fctn != 'g') {
free(legspace);
- rrd_set_error("Unknown control code at the end of '%s\\%c'",
- im->gdes[i].legend, prt_fctn);
+ rrd_set_error
+ ("Unknown control code at the end of '%s\\%c'",
+ im->gdes[i].legend, prt_fctn);
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) {
- legspace[i] = (prt_fctn == 'g' ? 0 : interleg);
+ /* 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,
- im->text_prop[TEXT_PROP_LEGEND].
- font,
- im->text_prop[TEXT_PROP_LEGEND].
- size, im->tabwidth,
- im->gdes[i].legend, 0);
+ fill +=
+ gfx_get_text_width(im,
+ fill + border,
+ im->
+ text_prop
+ [TEXT_PROP_LEGEND].
+ font_desc,
+ im->tabwidth, im->gdes[i].legend);
leg_c++;
} else {
legspace[i] = 0;
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(calc_width && (fill > legendwidth)){
+ legendwidth = fill;
+ }
+ if (i == im->gdes_c - 1 || fill > legendwidth) {
+ /* 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 (fill > legendwidth) {
if (leg_c > 1) {
/* go back one */
i--;
fill = fill_last;
leg_c--;
- prt_fctn = 'j';
- } else {
- prt_fctn = 'l';
}
-
+ }
+ if (leg_c == 1 && prt_fctn == 'j') {
+ prt_fctn = 'l';
}
}
-
if (prt_fctn != '\0') {
leg_x = border;
if (leg_c >= 2 && prt_fctn == 'j') {
- glue = (im->ximg - fill - 2 * border) / (leg_c - 1);
+ glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
} else {
glue = 0;
}
if (prt_fctn == 'c')
- leg_x = (im->ximg - fill) / 2.0;
+ leg_x = (double)(legendwidth - fill) / 2.0;
if (prt_fctn == 'r')
- leg_x = im->ximg - fill - border;
-
+ leg_x = legendwidth - fill - border;
for (ii = mark; ii <= i; ii++) {
if (im->gdes[ii].legend[0] == '\0')
continue; /* skip empty legends */
im->gdes[ii].leg_x = leg_x;
- im->gdes[ii].leg_y = leg_y;
+ im->gdes[ii].leg_y = leg_y + border;
leg_x +=
- gfx_get_text_width(im->canvas, leg_x,
- im->text_prop[TEXT_PROP_LEGEND].
- font,
- im->text_prop[TEXT_PROP_LEGEND].
- size, im->tabwidth,
- im->gdes[ii].legend, 0)
- + legspace[ii]
+ (double)gfx_get_text_width(im, leg_x,
+ im->
+ text_prop
+ [TEXT_PROP_LEGEND].
+ font_desc,
+ im->tabwidth, im->gdes[ii].legend)
+ +(double)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(calc_width && (fill > legendwidth)){
+ legendwidth = fill;
+ }
fill = 0;
leg_c = 0;
mark = ii;
}
+
+ if(calc_width){
+ strcpy(im->gdes[i].legend, saved_legend);
+ }
+ }
+
+ if(calc_width){
+ im->legendwidth = legendwidth + 2 * border;
}
- 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;
+ else{
+ im->legendheight = leg_y + border * 0.6;
}
free(legspace);
}
/* the xaxis labels are determined from the number of seconds per pixel
in the requested graph */
-
-
int calc_horizontal_grid(
- image_desc_t *im)
+ image_desc_t
+ *im)
{
double range;
double scaledrange;
im->ygrid_scale.labfact = 2;
range = im->maxval - im->minval;
scaledrange = range / im->magfact;
-
/* does the scale of this graph make it impossible to put lines
on it? If so, give up. */
if (isnan(scaledrange)) {
im->viewfactor / im->magfact));
if (decimals <= 0) /* everything is small. make place for zero */
decimals = 1;
-
im->ygrid_scale.gridstep =
pow((double) 10,
floor(log10(range * im->viewfactor / im->magfact))) /
im->viewfactor * im->magfact;
-
if (im->ygrid_scale.gridstep == 0) /* range is one -> 0.1 is reasonable scale */
im->ygrid_scale.gridstep = 0.1;
/* should have at least 5 lines but no more then 15 */
- if (range / im->ygrid_scale.gridstep < 5)
+ if (range / im->ygrid_scale.gridstep < 5
+ && im->ygrid_scale.gridstep >= 30)
im->ygrid_scale.gridstep /= 10;
if (range / im->ygrid_scale.gridstep > 15)
im->ygrid_scale.gridstep *= 10;
if (range / im->ygrid_scale.gridstep > 5) {
im->ygrid_scale.labfact = 1;
- if (range / im->ygrid_scale.gridstep > 8)
+ if (range / im->ygrid_scale.gridstep > 8
+ || im->ygrid_scale.gridstep <
+ 1.8 * im->text_prop[TEXT_PROP_AXIS].size)
im->ygrid_scale.labfact = 2;
} else {
im->ygrid_scale.gridstep /= 5;
if (im->unitslength < len + 2)
im->unitslength = len + 2;
- sprintf(im->ygrid_scale.labfmt, "%%%d.%df%s", len,
+ sprintf(im->ygrid_scale.labfmt,
+ "%%%d.%df%s", len,
-fractionals, (im->symbol != ' ' ? " %c" : ""));
} else {
int len = decimals + 1;
if (im->unitslength < len + 2)
im->unitslength = len + 2;
- sprintf(im->ygrid_scale.labfmt, "%%%d.0f%s", len,
- (im->symbol != ' ' ? " %c" : ""));
+ sprintf(im->ygrid_scale.labfmt,
+ "%%%d.0f%s", len, (im->symbol != ' ' ? " %c" : ""));
}
- } else {
+ } else { /* classic rrd grid */
for (i = 0; ylab[i].grid > 0; i++) {
pixel = im->ysize / (scaledrange / ylab[i].grid);
gridind = i;
- if (pixel > 7)
+ if (pixel >= 5)
break;
}
for (i = 0; i < 4; i++) {
if (pixel * ylab[gridind].lfac[i] >=
- 2.5 * im->text_prop[TEXT_PROP_AXIS].size) {
+ 1.8 * im->text_prop[TEXT_PROP_AXIS].size) {
im->ygrid_scale.labfact = ylab[gridind].lfac[i];
break;
}
}
int draw_horizontal_grid(
- image_desc_t *im)
+ image_desc_t
+ *im)
{
int i;
double scaledstep;
int nlabels = 0;
double X0 = im->xorigin;
double X1 = im->xorigin + im->xsize;
-
int sgrid = (int) (im->minval / im->ygrid_scale.gridstep - 1);
int egrid = (int) (im->maxval / im->ygrid_scale.gridstep + 1);
double MaxY;
+ double second_axis_magfact = 0;
+ char *second_axis_symb = "";
scaledstep =
- im->ygrid_scale.gridstep / (double) im->magfact *
- (double) im->viewfactor;
+ im->ygrid_scale.gridstep /
+ (double) im->magfact * (double) im->viewfactor;
MaxY = scaledstep * (double) egrid;
for (i = sgrid; i <= egrid; i++) {
- double Y0 = ytr(im, im->ygrid_scale.gridstep * i);
- double YN = ytr(im, im->ygrid_scale.gridstep * (i + 1));
+ double Y0 = ytr(im,
+ im->ygrid_scale.gridstep * i);
+ double YN = ytr(im,
+ im->ygrid_scale.gridstep * (i + 1));
- if (floor(Y0 + 0.5) >= im->yorigin - im->ysize
- && floor(Y0 + 0.5) <= im->yorigin) {
+ if (floor(Y0 + 0.5) >=
+ im->yorigin - im->ysize && floor(Y0 + 0.5) <= im->yorigin) {
/* Make sure at least 2 grid labels are shown, even if it doesn't agree
with the chosen settings. Add a label if required by settings, or if
there is only one label so far and the next grid line is out of bounds. */
&& (YN < im->yorigin - im->ysize || YN > im->yorigin))) {
if (im->symbol == ' ') {
if (im->extra_flags & ALTYGRID) {
- sprintf(graph_label, im->ygrid_scale.labfmt,
+ sprintf(graph_label,
+ im->ygrid_scale.labfmt,
scaledstep * (double) i);
} else {
if (MaxY < 10) {
char sisym = (i == 0 ? ' ' : im->symbol);
if (im->extra_flags & ALTYGRID) {
- sprintf(graph_label, im->ygrid_scale.labfmt,
+ sprintf(graph_label,
+ im->ygrid_scale.labfmt,
scaledstep * (double) i, sisym);
} else {
if (MaxY < 10) {
}
}
nlabels++;
+ if (im->second_axis_scale != 0){
+ char graph_label_right[100];
+ double sval = im->ygrid_scale.gridstep*(double)i*im->second_axis_scale+im->second_axis_shift;
+ if (im->second_axis_format[0] == '\0'){
+ if (!second_axis_magfact){
+ double dummy = im->ygrid_scale.gridstep*(double)(sgrid+egrid)/2.0*im->second_axis_scale+im->second_axis_shift;
+ auto_scale(im,&dummy,&second_axis_symb,&second_axis_magfact);
+ }
+ sval /= second_axis_magfact;
- 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);
+ if(MaxY < 10) {
+ sprintf(graph_label_right,"%5.1f %s",sval,second_axis_symb);
+ } else {
+ sprintf(graph_label_right,"%5.0f %s",sval,second_axis_symb);
+ }
+ }
+ else {
+ sprintf(graph_label_right,im->second_axis_format,sval);
+ }
+ gfx_text ( im,
+ X1+7, Y0,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font_desc,
+ im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+ graph_label_right );
+ }
+ gfx_text(im,
+ X0 -
+ im->
+ text_prop[TEXT_PROP_AXIS].
+ size, Y0,
+ im->graph_col[GRC_FONT],
+ im->
+ text_prop[TEXT_PROP_AXIS].
+ font_desc,
+ 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);
}
}
}
double mnt;
int iexp;
- iexp = floor(log(fabs(x)) / log(10));
+ iexp = floor(log((double)fabs(x)) / log((double)10));
mnt = x / pow(10.0, iexp);
if (mnt >= 10.0) {
iexp++;
return mnt;
}
-static int AlmostEqual2sComplement(
- float A,
- float B,
- int maxUlps)
-{
-
- int aInt = *(int *) &A;
- int bInt = *(int *) &B;
- int intDiff;
-
- /* Make sure maxUlps is non-negative and small enough that the
- default NAN won't compare as equal to anything. */
-
- /* assert(maxUlps > 0 && maxUlps < 4 * 1024 * 1024); */
-
- /* Make aInt lexicographically ordered as a twos-complement int */
-
- if (aInt < 0)
- aInt = 0x80000000l - aInt;
-
- /* Make bInt lexicographically ordered as a twos-complement int */
-
- if (bInt < 0)
- bInt = 0x80000000l - bInt;
-
- intDiff = abs(aInt - bInt);
-
- if (intDiff <= maxUlps)
- return 1;
-
- return 0;
-}
/* logaritmic horizontal grid */
int horizontal_log_grid(
- image_desc_t *im)
+ image_desc_t
+ *im)
{
double yloglab[][10] = {
- {1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
- {1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
- {1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0},
- {1.0, 2.0, 4.0, 6.0, 8.0, 10., 0.0, 0.0, 0.0, 0.0},
- {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.},
- {0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* last line */
+ {
+ 1.0, 10., 0.0, 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0}, {
+ 1.0, 5.0, 10., 0.0, 0.0, 0.0, 0.0,
+ 0.0, 0.0, 0.0}, {
+ 1.0, 2.0, 5.0, 7.0, 10., 0.0, 0.0,
+ 0.0, 0.0, 0.0}, {
+ 1.0, 2.0, 4.0,
+ 6.0, 8.0, 10.,
+ 0.0,
+ 0.0, 0.0, 0.0}, {
+ 1.0,
+ 2.0,
+ 3.0,
+ 4.0,
+ 5.0,
+ 6.0,
+ 7.0,
+ 8.0,
+ 9.0,
+ 10.},
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* last line */
};
-
int i, j, val_exp, min_exp;
double nex; /* number of decades in data */
double logscale; /* scale in logarithmic space */
nex = log10(im->maxval / im->minval);
logscale = im->ysize / nex;
-
/* major spacing for data with high dynamic range */
while (logscale * exfrac < 3 * im->text_prop[TEXT_PROP_LEGEND].size) {
if (exfrac == 1)
mid++;
for (i = 0; yloglab[mid][i + 1] < 10.0; i++);
mspac = logscale * log10(10.0 / yloglab[mid][i]);
- } while (mspac > 2 * im->text_prop[TEXT_PROP_LEGEND].size
- && yloglab[mid][0] > 0);
+ }
+ while (mspac >
+ 2 * im->text_prop[TEXT_PROP_LEGEND].size && yloglab[mid][0] > 0);
if (mid)
mid--;
-
/* find first value in yloglab */
for (flab = 0;
yloglab[mid][flab] < 10
val_exp = tmp;
if (val_exp % exfrac)
val_exp += abs(-val_exp % exfrac);
-
X0 = im->xorigin;
X1 = im->xorigin + im->xsize;
-
/* draw grid */
pre_value = DNAN;
while (1) {
value = yloglab[mid][flab] * pow(10.0, val_exp);
if (AlmostEqual2sComplement(value, pre_value, 4))
break; /* it seems we are not converging */
-
pre_value = value;
-
Y0 = ytr(im, value);
if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
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) {
int scale;
else
pvalue = pow(10.0, ((val_exp + 1) % 3) + 2);
pvalue *= yloglab[mid][flab];
-
- if (((scale + si_symbcenter) < (int) sizeof(si_symbol)) &&
- ((scale + si_symbcenter) >= 0))
+ if (((scale + si_symbcenter) < (int) sizeof(si_symbol))
+ && ((scale + si_symbcenter) >= 0))
symbol = si_symbol[scale + si_symbcenter];
else
symbol = '?';
-
sprintf(graph_label, "%3.0f %c", pvalue, symbol);
- } else
+ } 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);
+ }
+ if (im->second_axis_scale != 0){
+ char graph_label_right[100];
+ double sval = value*im->second_axis_scale+im->second_axis_shift;
+ if (im->second_axis_format[0] == '\0'){
+ if (im->extra_flags & FORCE_UNITS_SI) {
+ double mfac = 1;
+ char *symb = "";
+ auto_scale(im,&sval,&symb,&mfac);
+ sprintf(graph_label_right,"%4.0f %s", sval,symb);
+ }
+ else {
+ sprintf(graph_label_right,"%3.0e", sval);
+ }
+ }
+ else {
+ sprintf(graph_label_right,im->second_axis_format,sval);
+ }
+
+ gfx_text ( im,
+ X1+7, Y0,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_AXIS].font_desc,
+ im->tabwidth,0.0, GFX_H_LEFT, GFX_V_CENTER,
+ graph_label_right );
+ }
+ gfx_text(im,
+ X0 -
+ im->
+ text_prop[TEXT_PROP_AXIS].
+ size, Y0,
+ im->graph_col[GRC_FONT],
+ im->
+ text_prop[TEXT_PROP_AXIS].
+ font_desc,
+ im->tabwidth, 0.0,
+ GFX_H_RIGHT, GFX_V_CENTER, graph_label);
/* minor grid */
if (mid < 4 && exfrac == 1) {
/* find first and last minor line behind current major line
value = i * pow(10.0, min_exp);
if (value < im->minval)
continue;
-
Y0 = ytr(im, value);
if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
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) {
value = pow(10.0, i);
if (value < im->minval)
continue;
-
Y0 = ytr(im, value);
if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
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);
}
}
value = i * pow(10.0, min_exp);
if (value < im->minval)
continue;
-
Y0 = ytr(im, value);
if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
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 */
value = pow(10.0, i);
if (value < im->minval)
continue;
-
Y0 = ytr(im, value);
if (floor(Y0 + 0.5) <= im->yorigin - im->ysize)
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);
}
}
/* the type of time grid is determined by finding
the number of seconds per pixel in the graph */
-
-
if (im->xlab_user.minsec == -1) {
factor = (im->end - im->start) / im->xsize;
xlab_sel = 0;
- while (xlab[xlab_sel + 1].minsec != -1
- && xlab[xlab_sel + 1].minsec <= factor) {
+ while (xlab[xlab_sel + 1].minsec !=
+ -1 && xlab[xlab_sel + 1].minsec <= factor) {
xlab_sel++;
} /* pick the last one */
- while (xlab[xlab_sel - 1].minsec == xlab[xlab_sel].minsec
+ while (xlab[xlab_sel - 1].minsec ==
+ xlab[xlab_sel].minsec
&& xlab[xlab_sel].length > (im->end - im->start)) {
xlab_sel--;
} /* go back to the smallest size */
/* y coords are the same for every line ... */
Y0 = im->yorigin;
Y1 = im->yorigin - im->ysize;
-
-
/* paint the minor grid */
if (!(im->extra_flags & NOMINOR)) {
for (ti = find_first_time(im->start,
- im->xlab_user.gridtm,
- im->xlab_user.gridst),
- timajor = find_first_time(im->start,
- im->xlab_user.mgridtm,
- im->xlab_user.mgridst);
+ im->
+ xlab_user.
+ gridtm,
+ im->
+ xlab_user.
+ gridst),
+ timajor =
+ find_first_time(im->start,
+ im->xlab_user.
+ mgridtm,
+ im->xlab_user.
+ mgridst);
ti < im->end;
ti =
find_next_time(ti, im->xlab_user.gridtm, im->xlab_user.gridst)
continue;
while (timajor < ti) {
timajor = find_next_time(timajor,
- im->xlab_user.mgridtm,
- im->xlab_user.mgridst);
+ im->
+ xlab_user.
+ mgridtm, im->xlab_user.mgridst);
}
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);
}
}
/* paint the major grid */
for (ti = find_first_time(im->start,
- im->xlab_user.mgridtm,
- im->xlab_user.mgridst);
+ im->
+ xlab_user.
+ mgridtm,
+ im->
+ xlab_user.
+ mgridst);
ti < im->end;
ti = find_next_time(ti, im->xlab_user.mgridtm, im->xlab_user.mgridst)
) {
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 */
- for (ti = find_first_time(im->start - im->xlab_user.precis / 2,
- im->xlab_user.labtm,
- im->xlab_user.labst);
- ti <= im->end - im->xlab_user.precis / 2;
+ for (ti =
+ find_first_time(im->start -
+ im->xlab_user.
+ precis / 2,
+ im->xlab_user.
+ labtm,
+ im->xlab_user.
+ labst);
+ ti <=
+ im->end -
+ im->xlab_user.precis / 2;
ti = find_next_time(ti, im->xlab_user.labtm, im->xlab_user.labst)
) {
tilab = ti + im->xlab_user.precis / 2; /* correct time for the label */
/* are we inside the graph ? */
if (tilab < im->start || tilab > im->end)
continue;
-
#if HAVE_STRFTIME
localtime_r(&tilab, &tm);
strftime(graph_label, 99, im->xlab_user.stst, &tm);
#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_desc,
+ im->tabwidth, 0.0,
+ GFX_H_CENTER, GFX_V_TOP, graph_label);
}
}
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_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 - 4,
+ im->yorigin,
+ im->xorigin + im->xsize +
+ 4, im->yorigin, 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_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_close_path(im);
+ 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);
+ if (im->second_axis_scale != 0){
+ gfx_line ( im, im->xorigin+im->xsize,im->yorigin+4,
+ im->xorigin+im->xsize,im->yorigin-im->ysize-4,
+ MGRIDWIDTH, im->graph_col[GRC_AXIS]);
+ gfx_new_area ( im,
+ im->xorigin+im->xsize-2, im->yorigin-im->ysize-2,
+ im->xorigin+im->xsize+3, im->yorigin-im->ysize-2,
+ im->xorigin+im->xsize, im->yorigin-im->ysize-7, /* LINEOFFSET */
+ im->graph_col[GRC_ARROW]);
+ gfx_close_path(im);
+ }
}
long i;
int res = 0;
double X0, Y0; /* points for filled graph and more */
- gfx_node_t *node;
-
- /* 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 ); */
-
-
+ struct gfx_color_t water_color;
+
+ if (im->draw_3d_border > 0) {
+ /* draw 3d border */
+ i = im->draw_3d_border;
+ gfx_new_area(im, 0, im->yimg,
+ i, im->yimg - i, i, i, im->graph_col[GRC_SHADEA]);
+ gfx_add_point(im, im->ximg - i, i);
+ gfx_add_point(im, im->ximg, 0);
+ gfx_add_point(im, 0, 0);
+ gfx_close_path(im);
+ gfx_new_area(im, i, im->yimg - i,
+ im->ximg - i,
+ im->yimg - i, im->ximg - i, i, 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)
vertical_grid(im);
-
if (im->draw_y_grid == 1) {
if (im->logarithmic) {
res = horizontal_log_grid(im);
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_desc,
+ 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),
+ if (im->ylegend[0] != '\0'){
+ gfx_text(im,
+ im->xOriginLegendY+10,
+ im->yOriginLegendY,
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);
+ im->
+ text_prop[TEXT_PROP_UNIT].
+ font_desc,
+ im->tabwidth,
+ RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
+
+ }
+ if (im->second_axis_legend[0] != '\0'){
+ gfx_text( im,
+ im->xOriginLegendY2+10,
+ im->yOriginLegendY2,
+ im->graph_col[GRC_FONT],
+ im->text_prop[TEXT_PROP_UNIT].font_desc,
+ im->tabwidth,
+ RRDGRAPH_YLEGEND_ANGLE,
+ GFX_H_CENTER, GFX_V_CENTER,
+ im->second_axis_legend);
+ }
/* 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->xOriginTitle, im->yOriginTitle+6,
+ im->graph_col[GRC_FONT],
+ im->
+ text_prop[TEXT_PROP_TITLE].
+ font_desc,
+ 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");
-
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ water_color = im->graph_col[GRC_FONT];
+ water_color.alpha = 0.3;
+ double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
+ gfx_text(im, xpos, 5,
+ water_color,
+ im->
+ text_prop[TEXT_PROP_WATERMARK].
+ font_desc, 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);
+ water_color = im->graph_col[GRC_FONT];
+ water_color.alpha = 0.3;
+ gfx_text(im,
+ im->ximg / 2, im->yimg - 6,
+ water_color,
+ im->
+ text_prop[TEXT_PROP_WATERMARK].
+ font_desc, im->tabwidth, 0,
+ GFX_H_CENTER, GFX_V_BOTTOM, im->watermark);
}
/* graph labels */
- if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
+ if (!(im->extra_flags & NOLEGEND) && !(im->extra_flags & ONLY_GRAPH)) {
for (i = 0; i < im->gdes_c; i++) {
if (im->gdes[i].legend[0] == '\0')
continue;
-
/* 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);
+ X0 = im->xOriginLegend + im->gdes[i].leg_x;
+ Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
+ gfx_text(im, X0, Y0,
+ im->graph_col[GRC_FONT],
+ im->
+ text_prop
+ [TEXT_PROP_LEGEND].font_desc,
+ 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;
-
- boxH = gfx_get_text_width(im->canvas, 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;
-
- /* 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);
+ double boxH, boxV;
+ double X1, Y1;
+
+ boxH = gfx_get_text_width(im, 0,
+ im->
+ text_prop
+ [TEXT_PROP_LEGEND].
+ font_desc,
+ im->tabwidth, "o") * 1.2;
+ boxV = boxH;
+ /* shift the box up a bit */
+ Y0 -= boxV * 0.4;
+ if (im->gdes[i].gf == GF_HRULE) { /* [-] */
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, 1.0);
+ gfx_line(im,
+ X0, Y0 - boxV / 2,
+ X0 + boxH, Y0 - boxV / 2,
+ 1.0, im->gdes[i].col);
+ gfx_close_path(im);
+ } else if (im->gdes[i].gf == GF_VRULE) { /* [|] */
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, 1.0);
+ gfx_line(im,
+ X0 + boxH / 2, Y0,
+ X0 + boxH / 2, Y0 - boxV,
+ 1.0, im->gdes[i].col);
+ gfx_close_path(im);
+ } else if (im->gdes[i].gf == GF_LINE) { /* [/] */
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+ gfx_line(im,
+ X0, Y0,
+ X0 + boxH, Y0 - boxV,
+ im->gdes[i].linewidth, im->gdes[i].col);
+ gfx_close_path(im);
+ } else {
+ /* make sure transparent colors show up the same way as in the graph */
+ 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);
+ }
+ if (im->gdes[i].dash) {
+ /* make box borders in legend dashed if the graph is dashed */
+ double dashes[] = {
+ 3.0
+ };
+ cairo_set_dash(im->cr, dashes, 1, 0.0);
+ }
+ cairo_stroke(im->cr);
+ cairo_restore(im->cr);
}
}
}
if (im->lazy == 0)
return 0; /* no lazy option */
+ if (strlen(im->graphfile) == 0)
+ return 0; /* inmemory option */
if (stat(im->graphfile, &imgstat) != 0)
return 0; /* can't stat */
/* one pixel in the existing graph is more then what we would
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;
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
- )
+ image_desc_t
+ *im,
+ int elements)
{
/* 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
+ ** and other things outside the graph area. If the option
+ ** --full-size-mode is selected the size defines the total
+ ** image size and the size available for the graph is
+ ** calculated.
*/
- /* +-+-------------------------------------------+
- ** |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 |
- ** +---------------------------------------------+
+ /** +---+-----------------------------------+
+ ** | y |...............graph title.........|
+ ** | +---+-------------------------------+
+ ** | a | y | |
+ ** | x | | |
+ ** | i | a | |
+ ** | s | x | main graph area |
+ ** | | i | |
+ ** | t | s | |
+ ** | i | | |
+ ** | t | l | |
+ ** | l | b +-------------------------------+
+ ** | e | l | x axis labels |
+ ** +---+---+-------------------------------+
+ ** |....................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;
+ int Xvertical = 0, Xvertical2 = 0, Ytitle =
+ 0, Xylabel = 0, Xmain = 0, Ymain =
+ 0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
+
+ // no legends and no the shall be plotted it's easy
if (im->extra_flags & ONLY_GRAPH) {
im->xorigin = 0;
im->ximg = im->xsize;
return 0;
}
+ if(im->watermark[0] != '\0') {
+ Ywatermark = im->text_prop[TEXT_PROP_WATERMARK].size * 2;
+ }
+
+ // calculate the width of the left vertical legend
if (im->ylegend[0] != '\0') {
Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
}
+ // calculate the width of the right vertical legend
+ if (im->second_axis_legend[0] != '\0') {
+ Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
+ }
+ else{
+ Xvertical2 = Xspacing;
+ }
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;
}
+ else{
+ // we have no title; get a little clearing from the top
+ Ytitle = 1.5 * Yspacing;
+ }
if (elements) {
- Xmain = im->xsize;
- Ymain = im->ysize;
if (im->draw_x_grid) {
+ // calculate the height of the horizontal labelling
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,
- im->text_prop[TEXT_PROP_AXIS].font,
- im->text_prop[TEXT_PROP_AXIS].size,
- im->tabwidth,
- "0", 0) * im->unitslength;
+ // calculate the width of the vertical labelling
+ Xylabel =
+ gfx_get_text_width(im, 0,
+ im->text_prop[TEXT_PROP_AXIS].font_desc,
+ 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. */
+ // add some space to the labelling
+ Xylabel += Xspacing;
- /* 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.
+ /* If the legend is printed besides the graph the width has to be
+ ** calculated first. Placing the legend north or south of the
+ ** graph requires the width calculation first, so the legend is
+ ** skipped for the moment.
*/
- im->ximg = Xylabel + Xmain + 2 * Xspacing;
+ im->legendheight = 0;
+ im->legendwidth = 0;
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == WEST || im->legendposition == EAST){
+ if (leg_place(im, 1) == -1){
+ return -1;
+ }
+ }
+ }
-#ifdef WITH_PIECHART
- im->ximg += Xpie;
-#endif
+ if (im->extra_flags & FULL_SIZE_MODE) {
- if (Xmain)
- im->ximg += Xspacing;
-#ifdef WITH_PIECHART
- if (Xpie)
- im->ximg += Xspacing;
-#endif
+ /* 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 axis labels, and the title.
+ */
+ im->ximg = im->xsize;
+ im->yimg = im->ysize;
+ Xmain = im->ximg;
+ Ymain = im->yimg;
+
+ /* 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. */
+ /* Initial size calculation for the main graph area */
+
+ Xmain -= Xylabel;// + Xspacing;
+ if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+ Xmain -= im->legendwidth;// + Xspacing;
+ }
+ if (im->second_axis_scale != 0){
+ Xmain -= Xylabel;
+ }
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ Xmain -= Xspacing;
+ }
- im->xorigin = Xspacing + Xylabel;
+ Xmain -= Xvertical + Xvertical2;
- /* the length of the title should not influence with width of the graph
- if (Xtitle > im->ximg) im->ximg = Xtitle; */
+ /* limit the remaining space to 0 */
+ if(Xmain < 1){
+ Xmain = 1;
+ }
+ im->xsize = Xmain;
- if (Xvertical) { /* unit description */
- im->ximg += Xvertical;
- im->xorigin += Xvertical;
- }
- xtr(im, 0);
+ /* Putting the legend north or south, the height can now be calculated */
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == NORTH || im->legendposition == SOUTH){
+ im->legendwidth = im->ximg;
+ if (leg_place(im, 0) == -1){
+ return -1;
+ }
+ }
+ }
- /* 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( (im->legendposition == NORTH || im->legendposition == SOUTH) && !(im->extra_flags & NOLEGEND) ){
+ Ymain -= Yxlabel + im->legendheight;
+ }
+ else{
+ Ymain -= Yxlabel;
+ }
- /* reserve space for main and/or pie */
+ /* reserve space for the title *or* some padding above the graph */
+ Ymain -= Ytitle;
- im->yimg = Ymain + Yxlabel;
+ /* reserve space for padding below the graph */
+ if (im->extra_flags & NOLEGEND) {
+ Ymain -= Yspacing;
+ }
-#ifdef WITH_PIECHART
- if (im->yimg < Ypie)
- im->yimg = Ypie;
-#endif
+ if (im->watermark[0] != '\0') {
+ Ymain -= Ywatermark;
+ }
+ /* limit the remaining height to 0 */
+ if(Ymain < 1){
+ Ymain = 1;
+ }
+ im->ysize = Ymain;
+ } else { /* dimension options -width and -height refer to the dimensions of the main graph area */
- im->yorigin = im->yimg - Yxlabel;
+ /* 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.
+ */
- /* 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;
+ if (elements) {
+ Xmain = im->xsize; // + Xspacing;
+ Ymain = im->ysize;
+ }
- /* 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->ximg = Xmain + Xylabel;
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ im->ximg += Xspacing;
+ }
- if (im->watermark[0] != '\0') {
- im->yimg += Ywatermark;
+ if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
+ im->ximg += im->legendwidth;// + Xspacing;
+ }
+ if (im->second_axis_scale != 0){
+ im->ximg += Xylabel;
+ }
+
+ im->ximg += Xvertical + Xvertical2;
+
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == NORTH || im->legendposition == SOUTH){
+ im->legendwidth = im->ximg;
+ if (leg_place(im, 0) == -1){
+ return -1;
+ }
+ }
+ }
+
+ im->yimg = Ymain + Yxlabel;
+ if( (im->legendposition == NORTH || im->legendposition == SOUTH) && !(im->extra_flags & NOLEGEND) ){
+ im->yimg += im->legendheight;
+ }
+
+ /* reserve space for the title *or* some padding above the graph */
+ if (Ytitle) {
+ im->yimg += Ytitle;
+ } else {
+ im->yimg += 1.5 * Yspacing;
+ }
+ /* reserve space for padding below the graph */
+ if (im->extra_flags & NOLEGEND) {
+ im->yimg += Yspacing;
+ }
+
+ if (im->watermark[0] != '\0') {
+ im->yimg += Ywatermark;
+ }
}
-#if 0
- if (Xlegend > im->ximg) {
- im->ximg = Xlegend;
- /* reposition Pie */
+
+
+ /* In case of putting the legend in west or east position the first
+ ** legend calculation might lead to wrong positions if some items
+ ** are not aligned on the left hand side (e.g. centered) as the
+ ** legendwidth wight have been increased after the item was placed.
+ ** In this case the positions have to be recalculated.
+ */
+ if (!(im->extra_flags & NOLEGEND)) {
+ if(im->legendposition == WEST || im->legendposition == EAST){
+ if (leg_place(im, 0) == -1){
+ return -1;
+ }
+ }
}
-#endif
-#ifdef WITH_PIECHART
- /* The pie is placed in the upper right hand corner,
- ** just below the title (if any) and with sufficient
- ** padding.
+ /* After calculating all dimensions
+ ** it is now possible to calculate
+ ** all offsets.
*/
- 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;
+ switch(im->legendposition){
+ case NORTH:
+ im->xOriginTitle = Xvertical + Xylabel + (im->xsize / 2);
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = 0;
+ im->yOriginLegend = Ytitle;
+
+ im->xOriginLegendY = 0;
+ im->yOriginLegendY = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+ im->xorigin = Xvertical + Xylabel;
+ im->yorigin = Ytitle + im->legendheight + Ymain;
+
+ im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain / 2) + Yxlabel;
+
+ break;
+
+ case WEST:
+ im->xOriginTitle = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = 0;
+ im->yOriginLegend = Ytitle;
+
+ im->xOriginLegendY = im->legendwidth;
+ im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+ im->xorigin = im->legendwidth + Xvertical + Xylabel;
+ im->yorigin = Ytitle + Ymain;
+
+ im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+ break;
+
+ case SOUTH:
+ im->xOriginTitle = Xvertical + Xylabel + im->xsize / 2;
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = 0;
+ im->yOriginLegend = Ytitle + Ymain + Yxlabel;
+
+ im->xOriginLegendY = 0;
+ im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+ im->xorigin = Xvertical + Xylabel;
+ im->yorigin = Ytitle + Ymain;
+
+ im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+ break;
+
+ case EAST:
+ im->xOriginTitle = Xvertical + Xylabel + im->xsize / 2;
+ im->yOriginTitle = 0;
+
+ im->xOriginLegend = Xvertical + Xylabel + Xmain + Xvertical2;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegend += Xylabel;
+ }
+ im->yOriginLegend = Ytitle;
+
+ im->xOriginLegendY = 0;
+ im->yOriginLegendY = Ytitle + (Ymain / 2);
+
+ im->xorigin = Xvertical + Xylabel;
+ im->yorigin = Ytitle + Ymain;
+
+ im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
+ if (im->second_axis_scale != 0){
+ im->xOriginLegendY2 += Xylabel;
+ }
+ im->yOriginLegendY2 = Ytitle + (Ymain / 2);
+
+ if (!(im->extra_flags & NO_RRDTOOL_TAG)){
+ im->xOriginTitle += Xspacing;
+ im->xOriginLegend += Xspacing;
+ im->xOriginLegendY += Xspacing;
+ im->xorigin += Xspacing;
+ im->xOriginLegendY2 += Xspacing;
+ }
+ break;
}
-#endif
+ xtr(im, 0);
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_output(
+ void *closure,
+ const unsigned char
+ *data,
+ unsigned int length)
+{
+ image_desc_t *im = (image_desc_t*)closure;
+
+ im->rendered_image =
+ (unsigned char*)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,
- char ***calcpr)
+ image_desc_t *im)
{
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;
+ rrd_infoval_t info;
- /* if we are lazy and there is nothing to PRINT ... quit now */
- if (lazy && im->prt_c == 0)
- return 0;
+// PangoFontMap *font_map = pango_cairo_font_map_get_default();
/* pull the data from the rrd files ... */
-
if (data_fetch(im) == -1)
return -1;
-
/* evaluate VDEF and CDEF operations ... */
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
- * if there are no graph elements we stop here ...
- * if we are lazy, try to quit ...
+ * if there are no graph elements (i==0) we stop here ...
+ * if we are lazy, try to quit ...
*/
- i = print_calc(im, calcpr);
+ i = print_calc(im);
if (i < 0)
return -1;
- if (((i == 0)
-#ifdef WITH_PIECHART
- && (piechart == 0)
-#endif
- ) || lazy)
- return 0;
-#ifdef WITH_PIECHART
- /* If there's only the pie chart to draw, signal this */
+ /* if we want and can be lazy ... quit now */
if (i == 0)
- piechart = 2;
-#endif
+ return 0;
+
+/**************************************************************
+ *** 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;
+
+ info.u_cnt = im->xorigin;
+ grinfo_push(im, sprintf_alloc("graph_left"), RD_I_CNT, info);
+ info.u_cnt = im->yorigin - im->ysize;
+ grinfo_push(im, sprintf_alloc("graph_top"), RD_I_CNT, info);
+ info.u_cnt = im->xsize;
+ grinfo_push(im, sprintf_alloc("graph_width"), RD_I_CNT, info);
+ info.u_cnt = im->ysize;
+ grinfo_push(im, sprintf_alloc("graph_height"), RD_I_CNT, info);
+ info.u_cnt = im->ximg;
+ grinfo_push(im, sprintf_alloc("image_width"), RD_I_CNT, info);
+ info.u_cnt = im->yimg;
+ grinfo_push(im, sprintf_alloc("image_height"), RD_I_CNT, info);
+ info.u_cnt = im->start;
+ grinfo_push(im, sprintf_alloc("graph_start"), RD_I_CNT, info);
+ info.u_cnt = im->end;
+ grinfo_push(im, sprintf_alloc("graph_end"), RD_I_CNT, info);
+
+ /* if we want and can be lazy ... quit now */
+ if (lazy)
+ return 0;
/* get actual drawing data and find min and max values */
if (data_proc(im) == -1)
return -1;
-
if (!im->logarithmic) {
si_unit(im);
}
+
/* identify si magnitude Kilo, Mega Giga ? */
if (!im->rigid && !im->logarithmic)
expand_range(im); /* make sure the upper and lower limit are
sensible values */
- if (!calc_horizontal_grid(im))
- return -1;
-
- if (im->gridfit)
- apply_gridfit(im);
+ info.u_val = im->minval;
+ grinfo_push(im, sprintf_alloc("value_min"), RD_I_VAL, info);
+ info.u_val = im->maxval;
+ grinfo_push(im, sprintf_alloc("value_max"), RD_I_VAL, info);
-/**************************************************************
- *** 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)
+ if (!calc_horizontal_grid(im))
return -1;
-
+ /* reset precalc */
+ ytr(im, DNAN);
+/* if (im->gridfit)
+ apply_gridfit(im); */
/* the actual graph is created by going through the individual
graph elements and then drawing them */
-
- node = gfx_new_area(im->canvas,
- 0, 0,
- 0, im->yimg,
- im->ximg, im->yimg, im->graph_col[GRC_BACK]);
-
- gfx_add_point(node, im->ximg, 0);
-
-#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
-
-#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
-
+ cairo_surface_destroy(im->surface);
+ 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_output, 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_output, 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_output, im, im->ximg * im->zoom, im->yimg * im->zoom);
+ cairo_svg_surface_restrict_to_version
+ (im->surface, CAIRO_SVG_VERSION_1_1);
+ break;
+ };
+ cairo_destroy(im->cr);
+ im->cr = cairo_create(im->surface);
+ cairo_set_antialias(im->cr, im->graph_antialias);
+ cairo_scale(im->cr, im->zoom, im->zoom);
+// pango_cairo_font_map_set_resolution(PANGO_CAIRO_FONT_MAP(font_map), 100);
+ gfx_new_area(im, 0, 0, 0, im->yimg,
+ im->ximg, im->yimg, im->graph_col[GRC_BACK]);
+ gfx_add_point(im, im->ximg, 0);
+ gfx_close_path(im);
+ 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);
+ cairo_rectangle(im->cr, im->xorigin, im->yorigin - im->ysize - 1.0,
+ im->xsize, im->ysize + 2.0);
+ cairo_clip(im->cr);
+ 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) {
case GF_CDEF:
case GF_PRINT:
case GF_GPRINT:
case GF_COMMENT:
+ case GF_TEXTALIGN:
case GF_HRULE:
case GF_VRULE:
case GF_XPORT:
break;
case GF_TICK:
for (ii = 0; ii < im->xsize; ii++) {
- if (!isnan(im->gdes[i].p_data[ii]) &&
- im->gdes[i].p_data[ii] != 0.0) {
+ 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 + 1.0,
+ 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 - 1.0,
+ im->xorigin + ii,
+ im->yorigin - im->ysize -
+ im->gdes[i].
+ yrule *
+ im->ysize, 1.0, im->gdes[i].col);
}
}
}
} /* for */
/* *******************************************************
- a ___. (a,t)
+ a ___. (a,t)
| | ___
____| | | |
| |___|
- -------|--t-1--t--------------------------------
+ -------|--t-1--t--------------------------------
- if we know the value at time t was a then
+ if we know the value at time t was a then
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;
+
+ cairo_save(im->cr);
+ cairo_new_path(im->cr);
+ cairo_set_line_width(im->cr, im->gdes[i].linewidth);
+ if (im->gdes[i].dash) {
+ cairo_set_dash(im->cr,
+ im->gdes[i].p_dashes,
+ im->gdes[i].ndash, im->gdes[i].offset);
+ }
- node = NULL;
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);
- double *foreX = malloc(sizeof(double) * im->xsize * 2);
- double *backY = malloc(sizeof(double) * im->xsize * 2);
- double *backX = malloc(sizeof(double) * im->xsize * 2);
+ double *foreY =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
+ double *foreX =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
+ double *backY =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
+ double *backX =
+ (double *) malloc(sizeof(double) * im->xsize * 2);
int drawem = 0;
for (ii = 0; ii <= im->xsize; ii++) {
int lastI = 0;
while (cntI < idxI
- && AlmostEqual2sComplement(foreY[lastI],
- foreY[cntI], 4)
- && AlmostEqual2sComplement(foreY[lastI],
- foreY[cntI + 1],
- 4)) {
+ &&
+ AlmostEqual2sComplement(foreY
+ [lastI],
+ foreY[cntI], 4)
+ &&
+ AlmostEqual2sComplement(foreY
+ [lastI],
+ foreY
+ [cntI + 1], 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++;
while (cntI < idxI
&&
- AlmostEqual2sComplement(foreY[lastI],
+ AlmostEqual2sComplement(foreY
+ [lastI],
foreY[cntI], 4)
&&
- AlmostEqual2sComplement(foreY[lastI],
- foreY[cntI +
- 1], 4)) {
+ AlmostEqual2sComplement(foreY
+ [lastI],
+ foreY
+ [cntI
+ + 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--;
while (idxI > 1
&&
- AlmostEqual2sComplement(backY[lastI],
+ AlmostEqual2sComplement(backY
+ [lastI],
backY[idxI], 4)
&&
- AlmostEqual2sComplement(backY[lastI],
- backY[idxI -
- 1], 4)) {
+ AlmostEqual2sComplement(backY
+ [lastI],
+ backY
+ [idxI
+ - 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;
}
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;
}
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;
}
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");
return -1;
break;
-
} /* switch */
}
-#ifdef WITH_PIECHART
- if (piechart == 2) {
- im->draw_x_grid = 0;
- im->draw_y_grid = 0;
- }
-#endif
-
+ cairo_reset_clip(im->cr);
/* grid_paint also does the text */
if (!(im->extra_flags & ONLY_GRAPH))
grid_paint(im);
-
-
if (!(im->extra_flags & ONLY_GRAPH))
axis_paint(im);
-
/* the RULES are the last thing to paint ... */
for (i = 0; i < im->gdes_c; i++) {
switch (im->gdes[i].gf) {
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);
+ && im->gdes[i].yrule <= im->maxval) {
+ cairo_save(im->cr);
+ if (im->gdes[i].dash) {
+ cairo_set_dash(im->cr,
+ im->gdes[i].p_dashes,
+ im->gdes[i].ndash, im->gdes[i].offset);
+ }
+ 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);
+ cairo_stroke(im->cr);
+ cairo_restore(im->cr);
+ }
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);
+ && im->gdes[i].xrule <= im->end) {
+ cairo_save(im->cr);
+ if (im->gdes[i].dash) {
+ cairo_set_dash(im->cr,
+ im->gdes[i].p_dashes,
+ im->gdes[i].ndash, im->gdes[i].offset);
+ }
+ 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);
+ cairo_stroke(im->cr);
+ cairo_restore(im->cr);
+ }
break;
default:
break;
}
- 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;
+
+ status = strlen(im->graphfile) ?
+ cairo_surface_write_to_png(im->surface, im->graphfile)
+ : cairo_surface_write_to_png_stream(im->surface, &cairo_output,
+ im);
+
+ if (status != CAIRO_STATUS_SUCCESS) {
+ rrd_set_error("Could not save png to '%s'", im->graphfile);
+ return 1;
}
+ break;
}
- gfx_render(im->canvas, im->ximg, im->yimg, 0x00000000, fo);
- if (strcmp(im->graphfile, "-") != 0)
- fclose(fo);
+ default:
+ if (strlen(im->graphfile)) {
+ cairo_show_page(im->cr);
+ } else {
+ cairo_surface_finish(im->surface);
+ }
+ break;
+ }
+
return 0;
}
/*****************************************************
- * graph stuff
+ * graph stuff
*****************************************************/
int gdes_alloc(
{
im->gdes_c++;
- if ((im->gdes = (graph_desc_t *) rrd_realloc(im->gdes, (im->gdes_c)
- * sizeof(graph_desc_t))) ==
- NULL) {
+ if ((im->gdes = (graph_desc_t *)
+ rrd_realloc(im->gdes, (im->gdes_c)
+ * sizeof(graph_desc_t))) == NULL) {
rrd_set_error("realloc graph_descs");
return -1;
}
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].p_dashes = NULL;
+ im->gdes[im->gdes_c - 1].shift = 0.0;
+ im->gdes[im->gdes_c - 1].dash = 0;
+ im->gdes[im->gdes_c - 1].ndash = 0;
+ im->gdes[im->gdes_c - 1].offset = 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;
im->gdes[im->gdes_c - 1].ds = -1;
im->gdes[im->gdes_c - 1].cf_reduce = CF_AVERAGE;
im->gdes[im->gdes_c - 1].cf = CF_AVERAGE;
- im->gdes[im->gdes_c - 1].p_data = NULL;
im->gdes[im->gdes_c - 1].yrule = DNAN;
im->gdes[im->gdes_c - 1].xrule = 0;
return 0;
int inp, outp = 0;
for (inp = 0; inp < len && input[inp] != ':' && input[inp] != '\0'; inp++) {
- if (input[inp] == '\\' &&
- input[inp + 1] != '\0' &&
- (input[inp + 1] == '\\' || input[inp + 1] == ':')) {
+ if (input[inp] == '\\'
+ && input[inp + 1] != '\0'
+ && (input[inp + 1] == '\\' || input[inp + 1] == ':')) {
output[outp++] = input[++inp];
} else {
output[outp++] = input[inp];
}
}
- output[outp] = '\0';
- return inp;
+ output[outp] = '\0';
+ return inp;
+}
+
+/* Now just a wrapper around rrd_graph_v */
+int rrd_graph(
+ int argc,
+ char **argv,
+ char ***prdata,
+ int *xsize,
+ int *ysize,
+ FILE * stream,
+ double *ymin,
+ double *ymax)
+{
+ int prlines = 0;
+ rrd_info_t *grinfo = NULL;
+ rrd_info_t *walker;
+
+ grinfo = rrd_graph_v(argc, argv);
+ if (grinfo == NULL)
+ return -1;
+ walker = grinfo;
+ (*prdata) = NULL;
+ while (walker) {
+ if (strcmp(walker->key, "image_info") == 0) {
+ prlines++;
+ if (((*prdata) =
+ (char**)rrd_realloc((*prdata),
+ (prlines + 1) * sizeof(char *))) == NULL) {
+ rrd_set_error("realloc prdata");
+ return 0;
+ }
+ /* imginfo goes to position 0 in the prdata array */
+ (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+ + 2) * sizeof(char));
+ strcpy((*prdata)[prlines - 1], walker->value.u_str);
+ (*prdata)[prlines] = NULL;
+ }
+ /* skip anything else */
+ walker = walker->next;
+ }
+ walker = grinfo;
+ *xsize = 0;
+ *ysize = 0;
+ *ymin = 0;
+ *ymax = 0;
+ while (walker) {
+ if (strcmp(walker->key, "image_width") == 0) {
+ *xsize = walker->value.u_cnt;
+ } else if (strcmp(walker->key, "image_height") == 0) {
+ *ysize = walker->value.u_cnt;
+ } else if (strcmp(walker->key, "value_min") == 0) {
+ *ymin = walker->value.u_val;
+ } else if (strcmp(walker->key, "value_max") == 0) {
+ *ymax = walker->value.u_val;
+ } else if (strncmp(walker->key, "print", 5) == 0) { /* keys are prdate[0..] */
+ prlines++;
+ if (((*prdata) =
+ (char**)rrd_realloc((*prdata),
+ (prlines + 1) * sizeof(char *))) == NULL) {
+ rrd_set_error("realloc prdata");
+ return 0;
+ }
+ (*prdata)[prlines - 1] = (char*)malloc((strlen(walker->value.u_str)
+ + 2) * sizeof(char));
+ (*prdata)[prlines] = NULL;
+ strcpy((*prdata)[prlines - 1], walker->value.u_str);
+ } else if (strcmp(walker->key, "image") == 0) {
+ if ( fwrite(walker->value.u_blo.ptr, walker->value.u_blo.size, 1,
+ (stream ? stream : stdout)) == 0 && ferror(stream ? stream : stdout)){
+ rrd_set_error("writing image");
+ return 0;
+ }
+ }
+ /* skip anything else */
+ walker = walker->next;
+ }
+ rrd_info_free(grinfo);
+ return 0;
}
+
/* Some surgery done on this function, it became ridiculously big.
** Things moved:
** - initializing now in rrd_graph_init()
** - options parsing now in rrd_graph_options()
** - script parsing now in rrd_graph_script()
*/
-int rrd_graph(
+rrd_info_t *rrd_graph_v(
int argc,
- char **argv,
- char ***prdata,
- int *xsize,
- int *ysize,
- FILE * stream,
- double *ymin,
- double *ymax)
+ char **argv)
{
image_desc_t im;
-
+ rrd_info_t *grinfo;
+ char *old_locale;
rrd_graph_init(&im);
- im.graphhandle = stream;
-
+ /* a dummy surface so that we can measure text sizes for placements */
+ old_locale = setlocale(LC_NUMERIC, "C");
rrd_graph_options(argc, argv, &im);
if (rrd_test_error()) {
+ rrd_info_free(im.grinfo);
im_free(&im);
- return -1;
+ return NULL;
+ }
+
+ if (optind >= argc) {
+ rrd_info_free(im.grinfo);
+ im_free(&im);
+ rrd_set_error("missing filename");
+ return NULL;
}
if (strlen(argv[optind]) >= MAXPATH) {
rrd_set_error("filename (including path) too long");
+ rrd_info_free(im.grinfo);
im_free(&im);
- return -1;
+ return NULL;
}
+
strncpy(im.graphfile, argv[optind], MAXPATH - 1);
im.graphfile[MAXPATH - 1] = '\0';
+ if (strcmp(im.graphfile, "-") == 0) {
+ im.graphfile[0] = '\0';
+ }
+
rrd_graph_script(argc, argv, &im, 1);
+ setlocale(LC_NUMERIC, old_locale); /* reenable locale for rendering the graph */
+
if (rrd_test_error()) {
+ rrd_info_free(im.grinfo);
im_free(&im);
- return -1;
+ return NULL;
}
/* Everything is now read and the actual work can start */
- (*prdata) = NULL;
- if (graph_paint(&im, prdata) == -1) {
+ if (graph_paint(&im) == -1) {
+ rrd_info_free(im.grinfo);
im_free(&im);
- return -1;
+ return NULL;
}
+
/* The image is generated and needs to be output.
** Also, if needed, print a line with information about the image.
*/
- *xsize = im.ximg;
- *ysize = im.yimg;
- *ymin = im.minval;
- *ymax = im.maxval;
if (im.imginfo) {
+ rrd_infoval_t info;
+ char *path;
char *filename;
- if (!(*prdata)) {
- /* maybe prdata is not allocated yet ... lets do it now */
- if ((*prdata = calloc(2, sizeof(char *))) == NULL) {
- rrd_set_error("malloc imginfo");
- return -1;
- };
- }
- if (((*prdata)[0] =
- malloc((strlen(im.imginfo) + 200 +
- strlen(im.graphfile)) * sizeof(char)))
- == NULL) {
- rrd_set_error("malloc imginfo");
- return -1;
- }
- filename = im.graphfile + strlen(im.graphfile);
- while (filename > im.graphfile) {
- if (*(filename - 1) == '/' || *(filename - 1) == '\\')
- break;
- filename--;
- }
+ path = strdup(im.graphfile);
+ filename = basename(path);
+ info.u_str =
+ sprintf_alloc(im.imginfo,
+ filename,
+ (long) (im.zoom *
+ im.ximg), (long) (im.zoom * im.yimg));
+ grinfo_push(&im, sprintf_alloc("image_info"), RD_I_STR, info);
+ free(info.u_str);
+ free(path);
+ }
+ if (im.rendered_image) {
+ rrd_infoval_t img;
- sprintf((*prdata)[0], im.imginfo, filename,
- (long) (im.canvas->zoom * im.ximg),
- (long) (im.canvas->zoom * im.yimg));
+ img.u_blo.size = im.rendered_image_size;
+ img.u_blo.ptr = im.rendered_image;
+ grinfo_push(&im, sprintf_alloc("image"), RD_I_BLO, img);
}
+ grinfo = im.grinfo;
im_free(&im);
- return 0;
+ return grinfo;
+}
+
+static void
+rrd_set_font_desc (
+ image_desc_t *im,int prop,char *font, double size ){
+ if (font){
+ strncpy(im->text_prop[prop].font, font, sizeof(text_prop[prop].font) - 1);
+ im->text_prop[prop].font[sizeof(text_prop[prop].font) - 1] = '\0';
+ im->text_prop[prop].font_desc = pango_font_description_from_string( font );
+ };
+ if (size > 0){
+ im->text_prop[prop].size = size;
+ };
+ if (im->text_prop[prop].font_desc && im->text_prop[prop].size ){
+ pango_font_description_set_size(im->text_prop[prop].font_desc, im->text_prop[prop].size * PANGO_SCALE);
+ };
}
void rrd_graph_init(
- image_desc_t *im)
+ image_desc_t
+ *im)
{
unsigned int i;
+ char *deffont = getenv("RRD_DEFAULT_FONT");
+ static PangoFontMap *fontmap = NULL;
+ PangoContext *context;
#ifdef HAVE_TZSET
tzset();
#endif
-#ifdef HAVE_SETLOCALE
- setlocale(LC_TIME, "");
-#ifdef HAVE_MBSTOWCS
- setlocale(LC_CTYPE, "");
-#endif
-#endif
- im->yorigin = 0;
- im->xorigin = 0;
- im->minval = 0;
- im->xlab_user.minsec = -1;
- im->ximg = 0;
- im->yimg = 0;
- im->xsize = 400;
- im->ysize = 100;
- im->step = 0;
- im->ylegend[0] = '\0';
- im->title[0] = '\0';
- im->watermark[0] = '\0';
- im->minval = DNAN;
- im->maxval = DNAN;
- im->unitsexponent = 9999;
- im->unitslength = 6;
- im->forceleftspace = 0;
- im->symbol = ' ';
- im->viewfactor = 1.0;
+
+ im->base = 1000;
+ im->daemon_addr = NULL;
+ im->draw_x_grid = 1;
+ im->draw_y_grid = 1;
+ im->draw_3d_border = 2;
im->extra_flags = 0;
- im->rigid = 0;
+ im->font_options = cairo_font_options_create();
+ im->forceleftspace = 0;
+ im->gdes_c = 0;
+ im->gdes = NULL;
+ im->graph_antialias = CAIRO_ANTIALIAS_GRAY;
+ im->grid_dash_off = 1;
+ im->grid_dash_on = 1;
im->gridfit = 1;
+ im->grinfo = (rrd_info_t *) NULL;
+ im->grinfo_current = (rrd_info_t *) NULL;
+ im->imgformat = IF_PNG;
im->imginfo = NULL;
im->lazy = 0;
- im->slopemode = 0;
+ im->legenddirection = TOP_DOWN;
+ im->legendheight = 0;
+ im->legendposition = SOUTH;
+ im->legendwidth = 0;
im->logarithmic = 0;
- im->ygridstep = DNAN;
- im->draw_x_grid = 1;
- im->draw_y_grid = 1;
- im->base = 1000;
+ im->maxval = DNAN;
+ im->minval = 0;
+ im->minval = DNAN;
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->rigid = 0;
+ im->rendered_image_size = 0;
+ im->rendered_image = NULL;
+ im->slopemode = 0;
+ im->step = 0;
+ im->symbol = ' ';
im->tabwidth = 40.0;
+ im->title[0] = '\0';
+ im->unitsexponent = 9999;
+ im->unitslength = 6;
+ im->viewfactor = 1.0;
+ im->watermark[0] = '\0';
+ im->with_markup = 0;
+ im->ximg = 0;
+ im->xlab_user.minsec = -1;
+ im->xorigin = 0;
+ im->xOriginLegend = 0;
+ im->xOriginLegendY = 0;
+ im->xOriginLegendY2 = 0;
+ im->xOriginTitle = 0;
+ im->xsize = 400;
+ im->ygridstep = DNAN;
+ im->yimg = 0;
+ im->ylegend[0] = '\0';
+ im->second_axis_scale = 0; /* 0 disables it */
+ im->second_axis_shift = 0; /* no shift by default */
+ im->second_axis_legend[0] = '\0';
+ im->second_axis_format[0] = '\0';
+ im->yorigin = 0;
+ im->yOriginLegend = 0;
+ im->yOriginLegendY = 0;
+ im->yOriginLegendY2 = 0;
+ im->yOriginTitle = 0;
+ im->ysize = 100;
+ im->zoom = 1;
- for (i = 0; i < DIM(graph_col); i++)
- im->graph_col[i] = graph_col[i];
+ im->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+ im->cr = cairo_create(im->surface);
-#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__CYGWIN32__)
- {
- char *windir;
- char rrd_win_default_font[1000];
-
- windir = getenv("windir");
- /* %windir% is something like D:\windows or C:\winnt */
- if (windir != NULL) {
- strncpy(rrd_win_default_font, windir, 500);
- rrd_win_default_font[500] = '\0';
- strcat(rrd_win_default_font, "\\fonts\\");
- strcat(rrd_win_default_font, RRD_DEFAULT_FONT);
- for (i = 0; i < DIM(text_prop); i++) {
- strncpy(text_prop[i].font, rrd_win_default_font,
- sizeof(text_prop[i].font) - 1);
- text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0';
- }
- }
- }
-#endif
- {
- char *deffont;
-
- deffont = getenv("RRD_DEFAULT_FONT");
- if (deffont != NULL) {
- for (i = 0; i < DIM(text_prop); i++) {
- strncpy(text_prop[i].font, deffont,
- sizeof(text_prop[i].font) - 1);
- text_prop[i].font[sizeof(text_prop[i].font) - 1] = '\0';
- }
- }
- }
for (i = 0; i < DIM(text_prop); i++) {
- im->text_prop[i].size = text_prop[i].size;
- strcpy(im->text_prop[i].font, text_prop[i].font);
+ im->text_prop[i].size = -1;
+ rrd_set_font_desc(im,i, deffont ? deffont : text_prop[i].font,text_prop[i].size);
}
+
+ if (fontmap == NULL){
+ fontmap = pango_cairo_font_map_get_default();
+ }
+
+ context = pango_cairo_font_map_create_context((PangoCairoFontMap*)fontmap);
+
+ pango_cairo_context_set_resolution(context, 100);
+
+ pango_cairo_update_context(im->cr,context);
+
+ im->layout = pango_layout_new(context);
+
+// im->layout = pango_cairo_create_layout(im->cr);
+
+
+ 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];
+
+
}
+
void rrd_graph_options(
int argc,
char *argv[],
- image_desc_t *im)
+ image_desc_t
+ *im)
{
int stroff;
char *parsetime_error = NULL;
char scan_gtm[12], scan_mtm[12], scan_ltm[12], col_nam[12];
time_t start_tmp = 0, end_tmp = 0;
long long_tmp;
- struct rrd_time_value start_tv, end_tv;
- gfx_color_t color;
-
- optind = 0;
- opterr = 0; /* initialize getopt */
-
- parsetime("end-24h", &start_tv);
- parsetime("now", &end_tv);
+ rrd_time_value_t start_tv, end_tv;
+ long unsigned int color;
/* 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
+/* *INDENT-OFF* */
+ struct option long_options[] = {
+ { "alt-autoscale", no_argument, 0, 'A'},
+ { "imgformat", required_argument, 0, 'a'},
+ { "font-smoothing-threshold", required_argument, 0, 'B'},
+ { "base", required_argument, 0, 'b'},
+ { "color", required_argument, 0, 'c'},
+ { "full-size-mode", no_argument, 0, 'D'},
+ { "daemon", required_argument, 0, 'd'},
+ { "slope-mode", no_argument, 0, 'E'},
+ { "end", required_argument, 0, 'e'},
+ { "force-rules-legend", no_argument, 0, 'F'},
+ { "imginfo", required_argument, 0, 'f'},
+ { "graph-render-mode", required_argument, 0, 'G'},
+ { "no-legend", no_argument, 0, 'g'},
+ { "height", required_argument, 0, 'h'},
+ { "no-minor", no_argument, 0, 'I'},
+ { "interlaced", no_argument, 0, 'i'},
+ { "alt-autoscale-min", no_argument, 0, 'J'},
+ { "only-graph", no_argument, 0, 'j'},
+ { "units-length", required_argument, 0, 'L'},
+ { "lower-limit", required_argument, 0, 'l'},
+ { "alt-autoscale-max", no_argument, 0, 'M'},
+ { "zoom", required_argument, 0, 'm'},
+ { "no-gridfit", no_argument, 0, 'N'},
+ { "font", required_argument, 0, 'n'},
+ { "logarithmic", no_argument, 0, 'o'},
+ { "pango-markup", no_argument, 0, 'P'},
+ { "font-render-mode", required_argument, 0, 'R'},
+ { "rigid", no_argument, 0, 'r'},
+ { "step", required_argument, 0, 'S'},
+ { "start", required_argument, 0, 's'},
+ { "tabwidth", required_argument, 0, 'T'},
+ { "title", required_argument, 0, 't'},
+ { "upper-limit", required_argument, 0, 'u'},
+ { "vertical-label", required_argument, 0, 'v'},
+ { "watermark", required_argument, 0, 'W'},
+ { "width", required_argument, 0, 'w'},
+ { "units-exponent", required_argument, 0, 'X'},
+ { "x-grid", required_argument, 0, 'x'},
+ { "alt-y-grid", no_argument, 0, 'Y'},
+ { "y-grid", required_argument, 0, 'y'},
+ { "lazy", no_argument, 0, 'z'},
+ { "units", required_argument, 0, LONGOPT_UNITS_SI},
+ { "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 */
+ { "disable-rrdtool-tag",no_argument, 0, 1001},
+ { "right-axis", required_argument, 0, 1002},
+ { "right-axis-label", required_argument, 0, 1003},
+ { "right-axis-format", required_argument, 0, 1004},
+ { "legend-position", required_argument, 0, 1005},
+ { "legend-direction", required_argument, 0, 1006},
+ { "border", required_argument, 0, 1007},
+ { "grid-dash", required_argument, 0, 1008},
+ { 0, 0, 0, 0}
+};
+/* *INDENT-ON* */
+
+ optind = 0;
+ opterr = 0; /* initialize getopt */
+ rrd_parsetime("end-24h", &start_tv);
+ rrd_parsetime("now", &end_tv);
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:",
+ "Aa:B:b:c:Dd:Ee:Ff:G:gh:IiJjL:l:Nn:Bb:oPR:rS:s:T:t:u:v:W:w:X:x:Yy:z",
long_options, &option_index);
-
if (opt == EOF)
break;
-
switch (opt) {
case 'I':
im->extra_flags |= NOMINOR;
case 'g':
im->extra_flags |= NOLEGEND;
break;
+ case 1005:
+ if (strcmp(optarg, "north") == 0) {
+ im->legendposition = NORTH;
+ } else if (strcmp(optarg, "west") == 0) {
+ im->legendposition = WEST;
+ } else if (strcmp(optarg, "south") == 0) {
+ im->legendposition = SOUTH;
+ } else if (strcmp(optarg, "east") == 0) {
+ im->legendposition = EAST;
+ } else {
+ rrd_set_error("unknown legend-position '%s'", optarg);
+ return;
+ }
+ break;
+ case 1006:
+ if (strcmp(optarg, "topdown") == 0) {
+ im->legenddirection = TOP_DOWN;
+ } else if (strcmp(optarg, "bottomup") == 0) {
+ im->legenddirection = BOTTOM_UP;
+ } else {
+ rrd_set_error("unknown legend-position '%s'", optarg);
+ return;
+ }
+ break;
case 'F':
im->extra_flags |= FORCE_RULES_LEGEND;
break;
+ case 1001:
+ im->extra_flags |= NO_RRDTOOL_TAG;
+ break;
case LONGOPT_UNITS_SI:
if (im->extra_flags & FORCE_UNITS) {
rrd_set_error("--units can only be used once!");
case 'N':
im->gridfit = 0;
break;
+ case 'P':
+ im->with_markup = 1;
+ break;
case 's':
- if ((parsetime_error = parsetime(optarg, &start_tv))) {
+ if ((parsetime_error = rrd_parsetime(optarg, &start_tv))) {
rrd_set_error("start time: %s", parsetime_error);
return;
}
break;
case 'e':
- if ((parsetime_error = parsetime(optarg, &end_tv))) {
+ if ((parsetime_error = rrd_parsetime(optarg, &end_tv))) {
rrd_set_error("end time: %s", parsetime_error);
return;
}
im->draw_x_grid = 0;
break;
};
-
if (sscanf(optarg,
"%10[A-Z]:%ld:%10[A-Z]:%ld:%10[A-Z]:%ld:%ld:%n",
scan_gtm,
strncpy(im->xlab_form, optarg + stroff,
sizeof(im->xlab_form) - 1);
im->xlab_form[sizeof(im->xlab_form) - 1] = '\0';
- if ((int) (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) {
+ if ((int)
+ (im->xlab_user.gridtm = tmt_conv(scan_gtm)) == -1) {
rrd_set_error("unknown keyword %s", scan_gtm);
return;
- } else if ((int) (im->xlab_user.mgridtm = tmt_conv(scan_mtm))
+ } else if ((int)
+ (im->xlab_user.mgridtm = tmt_conv(scan_mtm))
== -1) {
rrd_set_error("unknown keyword %s", scan_mtm);
return;
- } else if ((int) (im->xlab_user.labtm = tmt_conv(scan_ltm)) ==
- -1) {
+ } else if ((int)
+ (im->xlab_user.labtm = tmt_conv(scan_ltm)) == -1) {
rrd_set_error("unknown keyword %s", scan_ltm);
return;
}
im->draw_y_grid = 0;
break;
};
-
if (sscanf(optarg, "%lf:%d", &im->ygridstep, &im->ylabfact) == 2) {
if (im->ygridstep <= 0) {
rrd_set_error("grid step must be > 0");
return;
}
break;
+ case 1007:
+ im->draw_3d_border = atoi(optarg);
+ break;
+ case 1008: /* grid-dash */
+ if(sscanf(optarg,
+ "%lf:%lf",
+ &im->grid_dash_on,
+ &im->grid_dash_off) != 2) {
+ rrd_set_error("expected grid-dash format float:float");
+ return;
+ }
+ break;
+ case 1002: /* right y axis */
+
+ if(sscanf(optarg,
+ "%lf:%lf",
+ &im->second_axis_scale,
+ &im->second_axis_shift) == 2) {
+ if(im->second_axis_scale==0){
+ rrd_set_error("the second_axis_scale must not be 0");
+ return;
+ }
+ } else {
+ rrd_set_error("invalid right-axis format expected scale:shift");
+ return;
+ }
+ break;
+ case 1003:
+ strncpy(im->second_axis_legend,optarg,150);
+ im->second_axis_legend[150]='\0';
+ break;
+ case 1004:
+ if (bad_format(optarg)){
+ rrd_set_error("use either %le or %lf formats");
+ return;
+ }
+ strncpy(im->second_axis_format,optarg,150);
+ im->second_axis_format[150]='\0';
+ break;
case 'v':
strncpy(im->ylegend, optarg, 150);
im->ylegend[150] = '\0';
}
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;
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;
}
case 'E':
im->slopemode = 1;
break;
-
case 'o':
im->logarithmic = 1;
break;
switch (col_len) {
case 3:
- color = (((color & 0xF00) * 0x110000) |
- ((color & 0x0F0) * 0x011000) |
- ((color & 0x00F) * 0x001100) | 0x000000FF);
+ color =
+ (((color & 0xF00) * 0x110000) | ((color & 0x0F0) *
+ 0x011000) |
+ ((color & 0x00F)
+ * 0x001100)
+ | 0x000000FF);
break;
case 4:
- color = (((color & 0xF000) * 0x11000) |
- ((color & 0x0F00) * 0x01100) |
- ((color & 0x00F0) * 0x00110) |
- ((color & 0x000F) * 0x00011)
+ color =
+ (((color & 0xF000) *
+ 0x11000) | ((color & 0x0F00) *
+ 0x01100) | ((color &
+ 0x00F0) *
+ 0x00110) |
+ ((color & 0x000F) * 0x00011)
);
break;
case 6:
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;
case 'n':{
char prop[15];
double size = 1;
- char font[1024] = "";
+ int end;
- if (sscanf(optarg, "%10[A-Z]:%lf:%1000s", prop, &size, font) >= 2) {
+ if (sscanf(optarg, "%10[A-Z]:%lf%n", prop, &size, &end) >= 2) {
int sindex, propidx;
if ((sindex = text_prop_conv(prop)) != -1) {
- for (propidx = sindex; propidx < TEXT_PROP_LAST;
- propidx++) {
+ for (propidx = sindex;
+ propidx < TEXT_PROP_LAST; propidx++) {
if (size > 0) {
- im->text_prop[propidx].size = size;
+ rrd_set_font_desc(im,propidx,NULL,size);
}
- if (strlen(font) > 0) {
- strcpy(im->text_prop[propidx].font, font);
+ if ((int) strlen(optarg) > end+2) {
+ if (optarg[end] == ':') {
+ rrd_set_font_desc(im,propidx,optarg + end + 1,0);
+ } else {
+ rrd_set_error
+ ("expected : after font size in '%s'",
+ optarg);
+ return;
+ }
}
+ /* only run the for loop for DEFAULT (0) for
+ all others, we break here. woodo programming */
if (propidx == sindex && sindex != 0)
break;
}
break;
}
case 'm':
- im->canvas->zoom = atof(optarg);
- if (im->canvas->zoom <= 0.0) {
+ im->zoom = atof(optarg);
+ if (im->zoom <= 0.0) {
rrd_set_error("zoom factor must be > 0");
return;
}
strncpy(im->title, optarg, 150);
im->title[150] = '\0';
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':
strncpy(im->watermark, optarg, 100);
im->watermark[99] = '\0';
break;
+ case 'd':
+ {
+ if (im->daemon_addr != NULL)
+ {
+ rrd_set_error ("You cannot specify --daemon "
+ "more than once.");
+ return;
+ }
+
+ im->daemon_addr = strdup(optarg);
+ if (im->daemon_addr == NULL)
+ {
+ rrd_set_error("strdup failed");
+ return;
+ }
+ break;
+ }
case '?':
if (optopt != 0)
rrd_set_error("unknown option '%c'", optopt);
rrd_set_error("unknown option '%s'", argv[optind - 1]);
return;
}
- }
+ } /* while (1) */
- if (optind >= argc) {
- rrd_set_error("missing filename");
- return;
+ { /* try to connect to rrdcached */
+ int status = rrdc_connect(im->daemon_addr);
+ if (status != 0) return;
}
- if (im->logarithmic == 1 && im->minval <= 0) {
+ pango_cairo_context_set_font_options(pango_layout_get_context(im->layout), im->font_options);
+ pango_layout_context_changed(im->layout);
+
+
+
+ if (im->logarithmic && im->minval <= 0) {
rrd_set_error
("for a logarithmic yaxis you must specify a lower-limit > 0");
return;
}
- if (proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
- /* error string is set in parsetime.c */
+ if (rrd_proc_start_end(&start_tv, &end_tv, &start_tmp, &end_tmp) == -1) {
+ /* error string is set in rrd_parsetime.c */
return;
}
if (start_tmp < 3600 * 24 * 365 * 10) {
- rrd_set_error("the first entry to fetch should be after 1980 (%ld)",
- start_tmp);
+ rrd_set_error
+ ("the first entry to fetch should be after 1980 (%ld)",
+ start_tmp);
return;
}
if (end_tmp < start_tmp) {
- rrd_set_error("start (%ld) should be less than end (%ld)",
- start_tmp, end_tmp);
+ rrd_set_error
+ ("start (%ld) should be less than end (%ld)", start_tmp, end_tmp);
return;
}
}
int rrd_graph_color(
- image_desc_t *im,
+ image_desc_t
+ *im,
char *var,
char *err,
int optional)
} else {
int n = 0;
char *rest;
- gfx_color_t col;
+ long unsigned int col;
rest = strstr(color, ":");
if (rest != NULL)
n = rest - color;
else
n = strlen(color);
-
switch (n) {
case 7:
sscanf(color, "#%6lx%n", &col, &n);
}
if (rrd_test_error())
return 0;
- gdp->col = col;
+ gdp->col = gfx_hex_to_col(col);
return n;
}
}
/* line cannot end with percent char */
if (*ptr == '\0')
return 1;
-
/* '%s', '%S' and '%%' are allowed */
if (*ptr == 's' || *ptr == 'S' || *ptr == '%')
ptr++;
-
/* %c is allowed (but use only with vdef!) */
else if (*ptr == 'c') {
ptr++;
/* optional padding character */
if (*ptr == ' ' || *ptr == '+' || *ptr == '-')
ptr++;
-
/* This should take care of 'm.n' with all three optional */
while (*ptr >= '0' && *ptr <= '9')
ptr++;
ptr++;
while (*ptr >= '0' && *ptr <= '9')
ptr++;
-
/* Either 'le', 'lf' or 'lg' must follow here */
if (*ptr++ != 'l')
return 1;
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.
if (n == (int) strlen(str)) { /* matched */
param = DNAN;
} else {
- rrd_set_error("Unknown function string '%s' in VDEF '%s'", str,
- gdes->vname);
+ rrd_set_error
+ ("Unknown function string '%s' in VDEF '%s'",
+ str, gdes->vname);
return -1;
}
}
if (!strcmp("PERCENT", func))
gdes->vf.op = VDEF_PERCENT;
+ else if (!strcmp("PERCENTNAN", func))
+ gdes->vf.op = VDEF_PERCENTNAN;
else if (!strcmp("MAXIMUM", func))
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))
else if (!strcmp("LSLCORREL", func))
gdes->vf.op = VDEF_LSLCORREL;
else {
- rrd_set_error("Unknown function '%s' in VDEF '%s'\n", func,
- gdes->vname);
+ rrd_set_error
+ ("Unknown function '%s' in VDEF '%s'\n", func, gdes->vname);
return -1;
};
-
switch (gdes->vf.op) {
case VDEF_PERCENT:
+ case VDEF_PERCENTNAN:
if (isnan(param)) { /* no parameter given */
- rrd_set_error("Function '%s' needs parameter in VDEF '%s'\n",
- func, gdes->vname);
+ rrd_set_error
+ ("Function '%s' needs parameter in VDEF '%s'\n",
+ func, gdes->vname);
return -1;
};
if (param >= 0.0 && param <= 100.0) {
gdes->vf.val = DNAN; /* undefined */
gdes->vf.when = 0; /* undefined */
} else {
- rrd_set_error("Parameter '%f' out of range in VDEF '%s'\n", param,
- gdes->vname);
+ rrd_set_error
+ ("Parameter '%f' out of range in VDEF '%s'\n",
+ param, gdes->vname);
return -1;
};
break;
case VDEF_MAXIMUM:
case VDEF_AVERAGE:
+ case VDEF_STDEV:
case VDEF_MINIMUM:
case VDEF_TOTAL:
case VDEF_FIRST:
gdes->vf.val = DNAN;
gdes->vf.when = 0;
} else {
- rrd_set_error("Function '%s' needs no parameter in VDEF '%s'\n",
- func, gdes->vname);
+ rrd_set_error
+ ("Function '%s' needs no parameter in VDEF '%s'\n",
+ func, gdes->vname);
return -1;
};
break;
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;
dst = &im->gdes[gdi];
src = &im->gdes[dst->vidx];
data = src->data + src->ds;
- steps = (src->end - src->start) / src->step;
+ steps = (src->end - src->start) / src->step;
#if 0
- printf("DEBUG: start == %lu, end == %lu, %lu steps\n", src->start,
- src->end, steps);
+ printf
+ ("DEBUG: start == %lu, end == %lu, %lu steps\n",
+ src->start, src->end, steps);
#endif
-
switch (dst->vf.op) {
case VDEF_PERCENT:{
rrd_value_t *array;
int field;
-
-
- if ((array = malloc(steps * sizeof(double))) == NULL) {
+ if ((array = (rrd_value_t*)malloc(steps * sizeof(double))) == NULL) {
rrd_set_error("malloc VDEV_PERCENT");
return -1;
}
array[step] = data[step * src->ds_cnt];
}
qsort(array, step, sizeof(double), vdef_percent_compar);
-
- field = (steps - 1) * dst->vf.param / 100;
+ field = round((dst->vf.param * (double)(steps - 1)) / 100.0);
dst->vf.val = array[field];
dst->vf.when = 0; /* no time component */
free(array);
#if 0
for (step = 0; step < steps; step++)
- printf("DEBUG: %3li:%10.2f %c\n", step, array[step],
- step == field ? '*' : ' ');
+ printf("DEBUG: %3li:%10.2f %c\n",
+ step, array[step], step == field ? '*' : ' ');
#endif
+ }
+ break;
+ case VDEF_PERCENTNAN:{
+ rrd_value_t *array;
+ int field;
+ /* count number of "valid" values */
+ int nancount=0;
+ for (step = 0; step < steps; step++) {
+ if (!isnan(data[step * src->ds_cnt])) { nancount++; }
+ }
+ /* and allocate it */
+ if ((array = (rrd_value_t*)malloc(nancount * sizeof(double))) == NULL) {
+ rrd_set_error("malloc VDEV_PERCENT");
+ return -1;
+ }
+ /* and fill it in */
+ field=0;
+ for (step = 0; step < steps; step++) {
+ if (!isnan(data[step * src->ds_cnt])) {
+ array[field] = data[step * src->ds_cnt];
+ field++;
+ }
+ }
+ qsort(array, nancount, sizeof(double), vdef_percent_compar);
+ field = round( dst->vf.param * (double)(nancount - 1) / 100.0);
+ dst->vf.val = array[field];
+ dst->vf.when = 0; /* no time component */
+ free(array);
}
break;
case VDEF_MAXIMUM:
}
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])) {
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;
SUMxy = 0;
SUMxx = 0;
SUMyy = 0;
-
for (step = 0; step < steps; step++) {
if (finite(data[step * src->ds_cnt])) {
cnt++;
y_intercept = (SUMy - slope * SUMx) / cnt;
correl =
(SUMxy -
- (SUMx * SUMy) / cnt) / sqrt((SUMxx -
- (SUMx * SUMx) / cnt) * (SUMyy -
- (SUMy *
- SUMy) /
- cnt));
-
+ (SUMx * SUMy) / cnt) /
+ sqrt((SUMxx -
+ (SUMx * SUMx) / cnt) * (SUMyy - (SUMy * SUMy) / cnt));
if (cnt) {
if (dst->vf.op == VDEF_LSLSLOPE) {
dst->vf.val = slope;
dst->vf.val = correl;
dst->vf.when = 0;
};
-
} else {
dst->vf.val = DNAN;
dst->vf.when = 0;
/* 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.
return -1;
if (isnan(*(double *) b))
return 1;
-
/* NaN doesn't reach this part so INF and -INF are extremes.
* The sign from isinf() is compatible with the sign we return
*/
return isinf(*(double *) a);
if (isinf(*(double *) b))
return isinf(*(double *) b);
-
/* If we reach this, both values must be finite */
if (*(double *) a < *(double *) b)
return -1;
else
return 1;
}
+
+void grinfo_push(
+ image_desc_t *im,
+ char *key,
+ rrd_info_type_t type,
+ rrd_infoval_t value)
+{
+ im->grinfo_current = rrd_info_push(im->grinfo_current, key, type, value);
+ if (im->grinfo == NULL) {
+ im->grinfo = im->grinfo_current;
+ }
+}