prepare for the release of rrdtool-1.2.3
[rrdtool.git] / src / rrd_gfx.c
index 49b9368..93341e7 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2.x  Copyright Tobias Oetiker, 1997 - 2005
+ * RRDtool 1.2.3  Copyright by Tobi Oetiker, 1997-2005
  ****************************************************************************
  * rrd_gfx.c  graphics wrapper for rrdtool
   **************************************************************************/
@@ -45,8 +45,8 @@ struct gfx_string_s {
 static void compute_string_bbox(gfx_string string);
 
 /* create a freetype glyph string */
-gfx_string gfx_string_create ( FT_Face face,
-                               const char *text, int rotation, double tabwidth);
+gfx_string gfx_string_create ( gfx_canvas_t *canvas, FT_Face face,
+                               const char *text, int rotation, double tabwidth, double size);
 
 /* create a freetype glyph string */
 static void gfx_string_destroy ( gfx_string string );
@@ -62,7 +62,6 @@ gfx_node_t *gfx_new_node( gfx_canvas_t *canvas,enum gfx_en type){
   node->points = 0;
   node->points_max =0;
   node->closed_path = 0;
-  node->svp = NULL;         /* svp */
   node->filename = NULL;             /* font or image filename */
   node->text = NULL;
   node->x = 0.0;
@@ -89,7 +88,9 @@ gfx_canvas_t *gfx_new_canvas (void) {
     canvas->imgformat = IF_PNG; /* we default to PNG output */
     canvas->interlaced = 0;
     canvas->zoom = 1.0;
-    return canvas;    
+    canvas->font_aa_threshold = -1.0;
+    canvas->aa_type = AA_NORMAL;
+    return canvas;
 }
 
 /* create a new line */
@@ -114,7 +115,7 @@ gfx_node_t  *gfx_new_dashed_line(gfx_canvas_t *canvas,
   if (vec == NULL) return NULL;
   vec[0].code = ART_MOVETO_OPEN; vec[0].x=X0+LINEOFFSET; vec[0].y=Y0+LINEOFFSET;
   vec[1].code = ART_LINETO; vec[1].x=X1+LINEOFFSET; vec[1].y=Y1+LINEOFFSET;
-  vec[2].code = ART_END;
+  vec[2].code = ART_END; vec[2].x=0;vec[2].y=0;
   
   node->points = 3;
   node->points_max = 3;
@@ -143,7 +144,7 @@ gfx_node_t   *gfx_new_area   (gfx_canvas_t *canvas,
   vec[1].code = ART_LINETO; vec[1].x=X1; vec[1].y=Y1;
   vec[2].code = ART_LINETO; vec[2].x=X2; vec[2].y=Y2;
   vec[3].code = ART_LINETO; vec[3].x=X0; vec[3].y=Y0;
-  vec[4].code = ART_END;
+  vec[4].code = ART_END; vec[4].x=0; vec[4].y=0;
   
   node->points = 5;
   node->points_max = 5;
@@ -211,10 +212,6 @@ gfx_node_t   *gfx_new_text   (gfx_canvas_t *canvas,
                              enum gfx_v_align_en v_align,
                               char* text){
    gfx_node_t *node = gfx_new_node(canvas,GFX_TEXT);
-/*   if (angle != 0.0){*/
-       /* currently we only support 0 and 270 */
-/*       angle = 270.0;
-   }*/
    
    node->text = strdup(text);
    node->size = size;
@@ -280,7 +277,7 @@ double gfx_get_text_width ( gfx_canvas_t *canvas,
                            double tabwidth, char* text, int rotation){
   switch (canvas->imgformat) {
   case IF_PNG: 
-    return gfx_get_text_width_libart (start, font, size, tabwidth, text, rotation);
+    return gfx_get_text_width_libart (canvas, start, font, size, tabwidth, text, rotation);
   case IF_SVG: /* fall through */ 
   case IF_EPS:
   case IF_PDF:
@@ -291,8 +288,8 @@ double gfx_get_text_width ( gfx_canvas_t *canvas,
 }
 
 double gfx_get_text_width_libart (
-                           double start, char* font, double size,
-                           double tabwidth, char* text, int rotation){
+                           gfx_canvas_t *canvas, double start, char* font, double size,
+                           double tabwidth, char* text, int rotation ){
 
   int           error;
   double        text_width=0;
@@ -306,7 +303,7 @@ double gfx_get_text_width_libart (
   error = FT_Set_Char_Size(face,  size*64,size*64,  100,100);
   if ( error ) return -1;
 
-  string = gfx_string_create( face, text, rotation,tabwidth);
+  string = gfx_string_create( canvas, face, text, rotation, tabwidth, size );
   text_width = string->width;
   gfx_string_destroy(string);
   FT_Done_FreeType(library);
@@ -325,14 +322,6 @@ static void gfx_libart_close_path(gfx_node_t *node, ArtVpath **vec)
     art_vpath_add_point (vec, &points, &points_max, ART_END, 0, 0);
 }
 
-static void gfx_round_scaled_coordinates(ArtVpath *vec)
-{
-    while (vec->code != ART_END) {
-       vec->x = floor(vec->x - LINEOFFSET + 0.5) + LINEOFFSET;
-       vec->y = floor(vec->y - LINEOFFSET + 0.5) + LINEOFFSET;
-       vec++;
-    }
-}
 
 /* find bbox of a string */
 static void compute_string_bbox(gfx_string string) {
@@ -371,8 +360,8 @@ static void compute_string_bbox(gfx_string string) {
 } 
 
 /* create a free type glyph string */
-gfx_string gfx_string_create(FT_Face face,const char *text,
-        int rotation, double tabwidth)
+gfx_string gfx_string_create(gfx_canvas_t *canvas, FT_Face face,const char *text,
+        int rotation, double tabwidth, double size )
 {
 
   FT_GlyphSlot  slot = face->glyph;  /* a small shortcut */
@@ -384,8 +373,7 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
   gfx_char      glyph;          /* current glyph in table */
   unsigned int  n;
   int           error;
-  int        gottab;    
-
+  int        gottab = 0;    
   ft_pen.x = 0;   /* start at (0,0) !! */
   ft_pen.y = 0;
 
@@ -403,12 +391,13 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
   use_kerning = FT_HAS_KERNING(face);
   previous    = 0;
   glyph = string->glyphs;
-  for (n=0; n<string->count; n++, glyph++) {
+  for (n=0; n<string->count;glyph++) {
     FT_Vector   vec;
     /* handle the tabs ...
        have a witespace glyph inserted, but set its width such that the distance
     of the new right edge is x times tabwidth from 0,0 where x is an integer. */    
-    char letter = text[n];
+    unsigned char letter = text[n];
+          
     gottab = 0;
     if (letter == '\\' && n+1 < string->count && text[n+1] == 't'){
             /* we have a tab here so skip the backslash and
@@ -440,7 +429,10 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
 
     /* load the glyph image (in its native format) */
     /* for now, we take a monochrome glyph bitmap */
-    error = FT_Load_Glyph (face, glyph->index, FT_LOAD_DEFAULT);
+    error = FT_Load_Glyph (face, glyph->index, size > canvas->font_aa_threshold ?
+                            canvas->aa_type == AA_NORMAL ? FT_LOAD_TARGET_NORMAL :
+                            canvas->aa_type == AA_LIGHT ? FT_LOAD_TARGET_LIGHT :
+                            FT_LOAD_TARGET_MONO : FT_LOAD_TARGET_MONO);
     if (error) {
       fprintf (stderr, "couldn't load glyph:  %c\n", letter);
       continue;
@@ -474,7 +466,10 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
     }
 
     /* convert to a bitmap - destroy native image */
-    error = FT_Glyph_To_Bitmap (&glyph->image, FT_RENDER_MODE_NORMAL, 0, 1);
+    error = FT_Glyph_To_Bitmap (&glyph->image, size > canvas->font_aa_threshold ?
+                            canvas->aa_type == AA_NORMAL ? FT_RENDER_MODE_NORMAL :
+                            canvas->aa_type == AA_LIGHT ? FT_RENDER_MODE_LIGHT :
+                            FT_RENDER_MODE_MONO : FT_RENDER_MODE_MONO, 0, 1);
     if (error) {
       fprintf (stderr, "couldn't convert glyph to bitmap\n");
       continue;
@@ -483,16 +478,19 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
     /* increment number of glyphs */
     previous = glyph->index;
     string->num_glyphs++;
+    n++;
+    
   }
 /*  printf ("number of glyphs = %d\n", string->num_glyphs);*/
   compute_string_bbox( string );
   /* the last character was a tab */  
-  if (gottab) {
+  /* if (gottab) { */
       string->width = ft_pen.x;
-  } else {
+  /* } else {
       string->width = string->bbox.xMax - string->bbox.xMin;
-  }
+  } */
   string->height = string->bbox.yMax - string->bbox.yMin;
+
   return string;
 }
 
@@ -508,14 +506,23 @@ int           gfx_render_png (gfx_canvas_t *canvas,
     
     FT_Library    library;
     gfx_node_t *node = canvas->firstnode;    
+    /*
     art_u8 red = background >> 24, green = (background >> 16) & 0xff;
     art_u8 blue = (background >> 8) & 0xff, alpha = ( background & 0xff );
+    */
     unsigned long pys_width = width * canvas->zoom;
     unsigned long pys_height = height * canvas->zoom;
-    const int bytes_per_pixel = 3;
+    const int bytes_per_pixel = 4;
     unsigned long rowstride = pys_width*bytes_per_pixel; /* bytes per pixel */
-    art_u8 *buffer = art_new (art_u8, rowstride*pys_height);
-    art_rgb_run_alpha (buffer, red, green, blue, alpha, pys_width*pys_height);
+    
+    /* fill that buffer with out background color */
+    gfx_color_t *buffp = art_new (gfx_color_t, pys_width*pys_height);
+    art_u8 *buffer = (art_u8 *)buffp;
+    unsigned long i;
+    for (i=0;i<pys_width*pys_height;
+        i++){
+       *(buffp++)=background;
+    }
     FT_Init_FreeType( &library );
     while(node){
         switch (node->type) {
@@ -528,24 +535,34 @@ int           gfx_render_png (gfx_canvas_t *canvas,
             vec = art_vpath_affine_transform(node->path,dst);
            if (node->closed_path)
                gfx_libart_close_path(node, &vec);
-           gfx_round_scaled_coordinates(vec);
+           /* gfx_round_scaled_coordinates(vec); */
+            /* pvec = art_vpath_perturb(vec);
+              art_free(vec); */
             if(node->type == GFX_LINE){
                 svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
                                              ART_PATH_STROKE_CAP_ROUND,
-                                             node->size*canvas->zoom,1,1);
+                                             node->size*canvas->zoom,4,0.25);
             } else {
-                svp = art_svp_from_vpath ( vec );
+                svp  = art_svp_from_vpath ( vec );
+               /* this takes time and is unnecessary since we make
+                  sure elsewhere that the areas are going clock-whise */
+               /*  svpt = art_svp_uncross( svp );
+                    art_svp_free(svp);
+                   svp  = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO); 
+                    art_svp_free(svpt);
+                 */
             }
             art_free(vec);
-            art_rgb_svp_alpha (svp ,0,0, pys_width, pys_height,
-                               node->color, buffer, rowstride, NULL);
-            art_free(svp);
+           /* this is from gnome since libart does not have this yet */
+            gnome_print_art_rgba_svp_alpha (svp ,0,0, pys_width, pys_height,
+                                node->color, buffer, rowstride, NULL);
+            art_svp_free(svp);
             break;
         }
         case GFX_TEXT: {
             unsigned int  n;
             int  error;
-            art_u8 fcolor[3],falpha;
+            art_u8 fcolor[4],falpha;
             FT_Face       face;
             gfx_char      glyph;
             gfx_string    string;
@@ -553,7 +570,7 @@ int           gfx_render_png (gfx_canvas_t *canvas,
 
             float pen_x = 0.0 , pen_y = 0.0;
             /* double x,y; */
-            long   ix,iy,iz;
+            long   ix,iy;
             
             fcolor[0] = node->color >> 24;
             fcolor[1] = (node->color >> 16) & 0xff;
@@ -563,8 +580,10 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                                  (char *)node->filename,
                                  0,
                                  &face );
-           if ( error ) break;
-
+           if ( error ) {
+               rrd_set_error("failed to load %s",node->filename);
+               break;
+           }
             error = FT_Set_Char_Size(face,   /* handle to face object            */
                                      (long)(node->size*64),
                                      (long)(node->size*64),
@@ -574,7 +593,7 @@ int           gfx_render_png (gfx_canvas_t *canvas,
             pen_x = node->x * canvas->zoom;
             pen_y = node->y * canvas->zoom;
 
-            string = gfx_string_create (face, node->text, node->angle, node->tabwidth);
+            string = gfx_string_create (canvas, face, node->text, node->angle, node->tabwidth, node->size);
             switch(node->halign){
             case GFX_H_RIGHT:  vec.x = -string->bbox.xMax;
                                break;          
@@ -605,7 +624,7 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                 int gr;
                 FT_Glyph        image;
                 FT_BitmapGlyph  bit;
-
+               /* long buf_x,comp_n; */
                /* make copy to transform */
                 if (! glyph->image) {
                   fprintf (stderr, "no image\n");
@@ -622,9 +641,67 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                 FT_Vector_Transform (&vec, &string->transform);
 
                 bit = (FT_BitmapGlyph) image;
-
                 gr = bit->bitmap.num_grays -1;
-                for (iy=0; iy < bit->bitmap.rows; iy++){
+/* 
+               buf_x = (pen_x + 0.5) + (double)bit->left;
+               comp_n = buf_x + bit->bitmap.width > pys_width ? pys_width - buf_x : bit->bitmap.width;
+                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+               buf_x *=  bytes_per_pixel ;
+               for (iy=0; iy < bit->bitmap.rows; iy++){                    
+                   long buf_y = iy+(pen_y+0.5)-(double)bit->top;
+                   if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                    buf_y *= rowstride;
+                   for (ix=0;ix < bit->bitmap.width;ix++){             
+                       *(letter + (ix*bytes_per_pixel+3)) = *(bit->bitmap.buffer + iy * bit->bitmap.width + ix);
+                   }
+                   art_rgba_rgba_composite(buffer + buf_y + buf_x ,letter,comp_n);
+                }
+                art_free(letter);
+*/
+                switch ( bit->bitmap.pixel_mode ) {
+                    case FT_PIXEL_MODE_GRAY:
+                        for (iy=0; iy < bit->bitmap.rows; iy++){
+                            long buf_y = iy+(pen_y+0.5)-bit->top;
+                            if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                            buf_y *= rowstride;
+                            for (ix=0;ix < bit->bitmap.width;ix++){
+                                long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+                                art_u8 font_alpha;
+
+                                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+                                buf_x *=  bytes_per_pixel ;
+                                font_alpha =  *(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix);
+                    if (font_alpha > 0){
+                                    fcolor[3] =  (art_u8)((double)font_alpha / gr * falpha);
+                        art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+                                }
+                            }
+                        }
+                        break;
+
+                    case FT_PIXEL_MODE_MONO:
+                        for (iy=0; iy < bit->bitmap.rows; iy++){
+                            long buf_y = iy+(pen_y+0.5)-bit->top;
+                            if (buf_y < 0 || buf_y >= (long)pys_height) continue;
+                            buf_y *= rowstride;
+                            for (ix=0;ix < bit->bitmap.width;ix++){
+                                long buf_x = ix + (pen_x + 0.5) + (double)bit->left ;
+
+                                if (buf_x < 0 || buf_x >= (long)pys_width) continue;
+                                buf_x *=  bytes_per_pixel ;
+                                if ( (fcolor[3] = falpha * ((*(bit->bitmap.buffer + iy * bit->bitmap.pitch + ix/8) >> (7 - (ix % 8))) & 1)) > 0 )
+                                    art_rgba_rgba_composite(buffer + buf_y + buf_x ,fcolor,1);
+                            }
+                        }
+                        break;
+
+                        default:
+                            rrd_set_error("unknown freetype pixel mode: %d", bit->bitmap.pixel_mode);
+                            break;
+                }
+
+/*
+                for (iy=0; iy < bit->bitmap.rows; iy++){                   
                     long buf_y = iy+(pen_y+0.5)-bit->top;
                     if (buf_y < 0 || buf_y >= (long)pys_height) continue;
                     buf_y *= rowstride;
@@ -643,6 +720,7 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                         }
                     }
                 }
+*/
                 FT_Done_Glyph (image);
             }
             gfx_string_destroy(string);
@@ -664,12 +742,12 @@ gfx_destroy    (gfx_canvas_t *canvas){
   while(node){
     next = node->next;
     art_free(node->path);
-    art_free(node->svp);
     free(node->text);
     free(node->filename);
     art_free(node);
     node = next;
   }
+  art_free(canvas);
   return 0;
 }
  
@@ -696,6 +774,7 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
 
   if (info_ptr == NULL)
     {
+      png_free(png_ptr,row_pointers);
       png_destroy_write_struct(&png_ptr,  (png_infopp)NULL);
       return (1);
     }
@@ -709,7 +788,7 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
 
   png_init_io(png_ptr, fp);
   png_set_IHDR (png_ptr, info_ptr,width, height,
-                8, PNG_COLOR_TYPE_RGB,
+                8, PNG_COLOR_TYPE_RGB_ALPHA,
                 PNG_INTERLACE_NONE,
                 PNG_COMPRESSION_TYPE_DEFAULT,
                 PNG_FILTER_TYPE_DEFAULT);
@@ -719,9 +798,11 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
   text[0].compression = PNG_TEXT_COMPRESSION_NONE;
   png_set_text (png_ptr, info_ptr, text, 1);
 
-  /* lets make this fast */
+  /* lets make this fast while ending up with some increass in image size */
+  png_set_filter(png_ptr,0,PNG_FILTER_NONE);
+  /* png_set_filter(png_ptr,0,PNG_FILTER_SUB); */
   png_set_compression_level(png_ptr,1);
-  png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
+  /* png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY); */
   /* 
   png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_FILTER_SUB);
   png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
@@ -734,6 +815,7 @@ static int gfx_save_png (art_u8 *buffer, FILE *fp,  long width, long height, lon
   
   png_write_image(png_ptr, row_pointers);
   png_write_end(png_ptr, info_ptr);
+  png_free(png_ptr,row_pointers);
   png_destroy_write_struct(&png_ptr, &info_ptr);
   return 1;
 }
@@ -1131,7 +1213,7 @@ static void svg_text(FILE *fp, gfx_node_t *node)
      fputs(",", fp);
      svg_write_number(fp, y);
      fputs(") rotate(", fp);
-     svg_write_number(fp, node->angle);
+     svg_write_number(fp, -node->angle);
      fputs(")\"", fp);
      x = y = 0;
      svg_close_tag(fp);
@@ -1335,7 +1417,7 @@ static int eps_prologue(eps_state *state)
   gfx_node_t *node;
   fputs(
     "%!PS-Adobe-3.0 EPSF-3.0\n"
-    "%%Creator: RRDtool 1.1.x, Tobias Oetiker, http://tobi.oetiker.ch\n"
+    "%%Creator: RRDtool 1.2.3 Tobias Oetiker, http://tobi.oetiker.ch\n"
     /* can't like weird chars here */
     "%%Title: (RRDtool output)\n"
     "%%DocumentData: Clean7Bit\n"
@@ -1527,7 +1609,7 @@ static void eps_write_text(eps_state *state, gfx_node_t *node)
     fputc(' ', fp);
     svg_write_number(fp, y);
     fputs(" translate ", fp);
-    svg_write_number(fp, -node->angle);
+    svg_write_number(fp, node->angle);
     fputs(" rotate 0 ", fp);
     svg_write_number(fp, ydelta);
     fputs(" moveto ", fp);
@@ -1919,7 +2001,7 @@ static void pdf_write_text(pdf_state *state, gfx_node_t *node,
   }
   pdf_set_fill_color(s, node->color);
   if (node->angle != 0) {
-    double a = 2 * M_PI * -node->angle / 360.0;
+    double a = 2 * M_PI * node->angle / 360.0;
     double new_x, new_y;
     cos_a = cos(a);
     sin_a = sin(a);