#ifdef HAVE_GD #include "mgstat.h" #include #include #include #include #define IMG_WIDTH 512 /* image width */ #define IMG_HEIGHT 256 /* image height */ #define G_MARG_X 16 /* horizontal margin */ #define G_MARG_Y 20 /* vertical margin */ #define G_WIDTH (IMG_WIDTH - G_MARG_X*2) /* width of a chart */ #define G_HEIGHT (IMG_HEIGHT-G_MARG_Y*2) /* height of a chart */ #define G_END_X (IMG_WIDTH - G_MARG_X) /* coord of graph end */ #define G_END_Y (IMG_HEIGHT - G_MARG_Y) #define SPACE_X 6 #define BLACK 3 #define WHITE 4 #define BGCOLOR 5 #define GRAY 6 #define LIGHT_GRAY 7 struct gd_image { gdImagePtr im; unsigned int max_el; int colors[16]; unsigned long long max; }; static void titles(struct gd_image *, int); static struct gd_image *new_image(void) { struct gd_image *p; p = calloc(1, sizeof *p); if(!p) errx(1, "%s: cannot allocate memory.", __FUNCTION__); return p; } /* * Simulate 3D look by drawing border lines around rectangle. */ static void border_3d(struct gd_image *g, int x, int y, int width, int height) { gdImageLine(g->im, x, y, x , y+height-1, g->colors[WHITE]); gdImageLine(g->im, x, y, x+width-1, y, g->colors[WHITE]); gdImageLine(g->im, x+width-1, y+1, x+width-1 , y+height-1, g->colors[BLACK]); gdImageLine(g->im, x+1, y+height-1, x+width-1 , y+height-1, g->colors[BLACK]); } #define BLOCK_WIDTH 20 /* width of a little blocks in a legend */ #define BLOCK_HEIGHT 10 /* height of a little blocks in a legend */ #define BLOCK_Y 4 /* vertical coordinate of these blocks */ #define BLOCK_X G_MARG_X /* horiontal coordinate */ static void legend(struct gd_image *p, char **descr, int x, int i) { gdImageFilledRectangle(p->im, x, BLOCK_Y, x+BLOCK_WIDTH, BLOCK_Y+BLOCK_HEIGHT, p->colors[i]); gdImageString(p->im, gdFontSmall, x+BLOCK_WIDTH+5, BLOCK_Y, descr[i], p->colors[BLACK]); } static void alloc_colors(struct gd_image *p, int type) { p->colors[BGCOLOR] = gdImageColorAllocate(p->im, 220, 216, 198); if(type == CHART_BYTES) { p->colors[0] = gdImageColorAllocate(p->im, 232, 153, 153); p->colors[1] = gdImageColorAllocate(p->im, 156, 200, 165); } else { p->colors[0] = gdImageColorAllocate(p->im, 153, 153 , 212); p->colors[1] = gdImageColorAllocate(p->im, 153, 232, 232); } p->colors[BLACK] = gdImageColorAllocate(p->im, 0, 0, 0); p->colors[WHITE] = gdImageColorAllocate(p->im, 255, 255, 255); p->colors[GRAY] = gdImageColorAllocate(p->im, 105, 105, 105); p->colors[LIGHT_GRAY] = gdImageColorAllocate(p->im, 195, 195, 195); } #define FONT_SIZE 5 /* * Create gif, allocate colors, draw 3d border, titles and legend. */ struct gd_image *open_graph(int nrel, int type, unsigned long long max) { struct gd_image *p; char *descr[2], buf[16]; p = new_image(); p->im = gdImageCreate(IMG_WIDTH, IMG_HEIGHT); alloc_colors(p, type); if(type == CHART_BYTES) { descr[0] = "Actual bytes"; descr[1] = "Bytes sent"; } else { descr[0] = "Total hits"; descr[1] = "Compressable"; } border_3d(p, 0, 0, IMG_WIDTH, IMG_HEIGHT); border_3d(p, 1, 1, IMG_WIDTH-2, IMG_HEIGHT-2); legend(p, descr, BLOCK_X, 0); legend(p, descr, 130, 1); p->max_el = nrel; p->max = max; titles(p, nrel); snprintf(buf, sizeof buf, "%lld", p->max); gdImageStringUp(p->im, gdFontSmall, 2, (strlen(buf)+1)*FONT_SIZE+G_MARG_Y, buf, p->colors[BLACK]); gdImageLine(p->im, G_MARG_X, G_MARG_Y + G_HEIGHT/2-2, G_END_X, G_MARG_Y + G_HEIGHT/2-2, p->colors[LIGHT_GRAY]); snprintf(buf, sizeof buf, "%lld", p->max/2); gdImageStringUp(p->im, gdFontSmall, 2, (strlen(buf)+1)*FONT_SIZE+ G_MARG_Y+G_HEIGHT/2-2, buf, p->colors[BLACK]); return p; } void close_graph(struct gd_image *g, char *s) { FILE *f; char buf[256]; gdImageRectangle(g->im, G_MARG_X-1, G_MARG_Y-2, G_END_X, G_END_Y+1, g->colors[GRAY]); snprintf(buf, sizeof buf, "%s/%s", out_dir, s); f = fopen(buf, "wb"); if(!f) errx(1, "%s: cannot open %s: %s",__FUNCTION__, buf, strerror(errno)); #ifdef HAVE_GD_GIF gdImageGif(g->im, f); #else gdImagePng(g->im, f); #endif fclose(f); gdImageDestroy(g->im); free(g); #ifdef DEBUG printf("%s: Writing %s\n", __FUNCTION__, s); #endif } static inline int calc_x(int x, int pos, int max) { return (pos * (x/max)); } /* * Draw bar at specified position 'pos' for a value 'val'. * 'series' specifies offset in the same position. */ void draw_val(struct gd_image *g, int pos, int series, unsigned long long val) { int size, offset, x1, x2, y1; float y, d; if(!val && !g->max) return; d = (float)(val*G_HEIGHT)/g->max; y = (float)val*100/g->max; size = G_WIDTH/g->max_el; x1 = pos*size + series*2; x2 = (pos+1)*size + series*2 - SPACE_X; y1 = G_HEIGHT - d + G_MARG_Y; // if(y == 100) y1 += 2; offset = (G_WIDTH-(size * g->max_el))/2; #ifdef DEBUG fprintf(stderr, "%lld val, %lld max\n", val, g->max); fflush(stderr); #endif gdImageFilledRectangle(g->im, offset + G_MARG_X + x1, y1, offset+G_MARG_X+ x2, G_END_Y, g->colors[series]); border_3d(g, offset+G_MARG_X + x1, y1, size-SPACE_X+1, G_END_Y-y1+2); #ifdef DEBUG printf(" %d pos, %d x1, %d, %d, %d\n", pos, x1, size, G_WIDTH, g->max_el); #endif } /* * Draw horizontal titles - day numbers or months. */ static void titles(struct gd_image *g, int c) { int i, x; int size; int offset; char buf[8]; size = G_WIDTH/g->max_el; offset = (G_WIDTH-(size * g->max_el))/2; for(i = 0; i < g->max_el; i++) { if(i < 9) x = 3; else x = 0; if(c == 12) { /* we have to print months names */ snprintf(buf, sizeof buf, "%s", m_name[i]); x = 9; } else snprintf(buf, sizeof buf, "%d", i + 1); gdImageString(g->im, gdFontSmall, offset + x + G_MARG_X + calc_x(G_WIDTH , i , g->max_el), 240, buf , g->colors[BLACK]); } #ifdef DEBUG for(i = 0; i < 5; i++) { printf("-- %d %d\n", i, calc_x(20, i, 5)); } #endif } #endif