prepare for the release of rrdtool-1.2.4
[rrdtool.git] / src / rrd_gfx.c
index f1176ab..fb93240 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2rc9  Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.4  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 );
@@ -88,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 */
@@ -275,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:
@@ -286,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;
@@ -301,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);
@@ -358,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 */
@@ -372,7 +374,6 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
   unsigned int  n;
   int           error;
   int        gottab = 0;    
-
   ft_pen.x = 0;   /* start at (0,0) !! */
   ft_pen.y = 0;
 
@@ -390,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
@@ -427,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;
@@ -461,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;
@@ -470,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;
 }
 
@@ -503,12 +514,12 @@ int           gfx_render_png (gfx_canvas_t *canvas,
     unsigned long pys_height = height * canvas->zoom;
     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);
+    
     /* fill that buffer with out background color */
-    gfx_color_t *buffp;
-    long i;
-    for (i=0,buffp=(gfx_color_t *)buffer;
-         i<pys_width*pys_height;
+    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;
     }
@@ -517,28 +528,31 @@ int           gfx_render_png (gfx_canvas_t *canvas,
         switch (node->type) {
         case GFX_LINE:
         case GFX_AREA: {   
-            ArtVpath *vec,*pvec;
+            ArtVpath *vec;
             double dst[6];     
-            ArtSVP *svp,*svpt;
+            ArtSVP *svp;
             art_affine_scale(dst,canvas->zoom,canvas->zoom);
             vec = art_vpath_affine_transform(node->path,dst);
            if (node->closed_path)
                gfx_libart_close_path(node, &vec);
            /* gfx_round_scaled_coordinates(vec); */
-            pvec = art_vpath_perturb(vec);
-           art_free(vec);
+            /* pvec = art_vpath_perturb(vec);
+              art_free(vec); */
             if(node->type == GFX_LINE){
-                svp = art_svp_vpath_stroke ( pvec, ART_PATH_STROKE_JOIN_ROUND,
+                svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
                                              ART_PATH_STROKE_CAP_ROUND,
                                              node->size*canvas->zoom,4,0.25);
             } else {
-                svp  = art_svp_from_vpath ( pvec );
-                svpt = art_svp_uncross( svp );
-                art_svp_free(svp);
-               svp  = art_svp_rewind_uncrossed(svpt,ART_WIND_RULE_NONZERO); 
-                art_svp_free(svpt);
+                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(pvec);
+            art_free(vec);
            /* 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);
@@ -579,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;          
@@ -644,23 +658,46 @@ int           gfx_render_png (gfx_canvas_t *canvas,
                 }
                 art_free(letter);
 */
-
-                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.width + 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);
+                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;
                 }
 
 /*
@@ -761,8 +798,9 @@ 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 */
-  /* png_set_filter(png_ptr,0,PNG_FILTER_NONE); */
+  /* 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_compression_strategy(png_ptr,Z_HUFFMAN_ONLY); */
   /* 
@@ -1175,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);
@@ -1379,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.2rc9 Tobias Oetiker, http://tobi.oetiker.ch\n"
+    "%%Creator: RRDtool 1.2.4 Tobias Oetiker, http://tobi.oetiker.ch\n"
     /* can't like weird chars here */
     "%%Title: (RRDtool output)\n"
     "%%DocumentData: Clean7Bit\n"
@@ -1571,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);
@@ -1963,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);