* fix transparency rendering by rewinding the paths propperly
[rrdtool.git] / src / rrd_gfx.c
index 12607d4..0a44344 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.1.x  Copyright Tobias Oetiker, 1997 - 2003
+ * RRDtool 1.2rc2  Copyright by Tobi Oetiker, 1997-2005
  ****************************************************************************
  * rrd_gfx.c  graphics wrapper for rrdtool
   **************************************************************************/
@@ -46,7 +46,7 @@ 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);
+                               const char *text, int rotation, double tabwidth);
 
 /* create a freetype glyph string */
 static void gfx_string_destroy ( gfx_string string );
@@ -211,10 +211,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 +276,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 (canvas, start, font, size, tabwidth, text, rotation);
+    return gfx_get_text_width_libart (start, font, size, tabwidth, text, rotation);
   case IF_SVG: /* fall through */ 
   case IF_EPS:
   case IF_PDF:
@@ -290,7 +286,7 @@ double gfx_get_text_width ( gfx_canvas_t *canvas,
   }
 }
 
-double gfx_get_text_width_libart ( gfx_canvas_t *canvas,
+double gfx_get_text_width_libart (
                            double start, char* font, double size,
                            double tabwidth, char* text, int rotation){
 
@@ -306,7 +302,7 @@ double gfx_get_text_width_libart ( gfx_canvas_t *canvas,
   error = FT_Set_Char_Size(face,  size*64,size*64,  100,100);
   if ( error ) return -1;
 
-  string = gfx_string_create( face, text, rotation);
+  string = gfx_string_create( face, text, rotation,tabwidth);
   text_width = string->width;
   gfx_string_destroy(string);
   FT_Done_FreeType(library);
@@ -372,7 +368,7 @@ 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)
+        int rotation, double tabwidth)
 {
 
   FT_GlyphSlot  slot = face->glyph;  /* a small shortcut */
@@ -384,6 +380,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 = 0;    
 
   ft_pen.x = 0;   /* start at (0,0) !! */
   ft_pen.y = 0;
@@ -404,14 +401,29 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
   glyph = string->glyphs;
   for (n=0; n<string->count; n++, 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];
+    gottab = 0;
+    if (letter == '\\' && n+1 < string->count && text[n+1] == 't'){
+            /* we have a tab here so skip the backslash and
+               set t to ' ' so that we get a white space */
+            gottab = 1;
+            n++;
+            letter  = ' ';            
+    }            
+    if (letter == '\t'){
+       letter = ' ';
+        gottab = 1 ;
+    }            
     /* initialize each struct gfx_char_s */
     glyph->index = 0;
     glyph->pos.x = 0;
     glyph->pos.y = 0;
     glyph->image = NULL;
 
-    glyph->index = FT_Get_Char_Index( face, text[n] );
+    glyph->index = FT_Get_Char_Index( face, letter );
 
     /* compute glyph origin */
     if ( use_kerning && previous && glyph->index ) {
@@ -422,24 +434,30 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
       ft_pen.y += kerning.y;
     }
 
-    /* store current pen position */
-    glyph->pos.x = ft_pen.x;
-    glyph->pos.y = ft_pen.y;
-
     /* 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);
     if (error) {
-      fprintf (stderr, "couldn't load glyph:  %c\n", text[n]);
+      fprintf (stderr, "couldn't load glyph:  %c\n", letter);
       continue;
     }
     error = FT_Get_Glyph (slot, &glyph->image);
     if (error) {
-      fprintf (stderr, "couldn't get glyph from slot:  %c\n", text[n]);
+      fprintf (stderr, "couldn't get glyph from slot:  %c\n", letter);
       continue;
     }
+    /* if we are in tabbing mode, we replace the tab with a space and shift the position
+       of the space so that its left edge is where the tab was supposed to land us */
+    if (gottab){
+       /* we are in gridfitting mode so the calculations happen in 1/64 pixles */
+        ft_pen.x = tabwidth*64.0 * (float)(1 + (long)(ft_pen.x / (tabwidth * 64.0))) - slot->advance.x;
+    }
+    /* store current pen position */
+    glyph->pos.x = ft_pen.x;
+    glyph->pos.y = ft_pen.y;
+
 
-    ft_pen.x   += slot->advance.x;
+    ft_pen.x   += slot->advance.x;    
     ft_pen.y   += slot->advance.y;
 
     /* rotate glyph */
@@ -452,7 +470,7 @@ 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, FT_RENDER_MODE_NORMAL, 0, 1);
     if (error) {
       fprintf (stderr, "couldn't convert glyph to bitmap\n");
       continue;
@@ -464,9 +482,13 @@ gfx_string gfx_string_create(FT_Face face,const char *text,
   }
 /*  printf ("number of glyphs = %d\n", string->num_glyphs);*/
   compute_string_bbox( string );
-  string->width = string->bbox.xMax - string->bbox.xMin;
+  /* the last character was a tab */  
+  if (gottab) {
+      string->width = ft_pen.x;
+  } else {
+      string->width = string->bbox.xMax - string->bbox.xMin;
+  }
   string->height = string->bbox.yMax - string->bbox.yMin;
-
   return string;
 }
 
@@ -495,25 +517,31 @@ int           gfx_render_png (gfx_canvas_t *canvas,
         switch (node->type) {
         case GFX_LINE:
         case GFX_AREA: {   
-            ArtVpath *vec;
+            ArtVpath *vec,*pvec;
             double dst[6];     
-            ArtSVP *svp;
+            ArtSVP *svp,*usvp,*rsvp;
             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);
             if(node->type == GFX_LINE){
-                svp = art_svp_vpath_stroke ( vec, ART_PATH_STROKE_JOIN_ROUND,
+                svp = art_svp_vpath_stroke ( pvec, ART_PATH_STROKE_JOIN_ROUND,
                                              ART_PATH_STROKE_CAP_ROUND,
                                              node->size*canvas->zoom,1,1);
             } else {
-                svp = art_svp_from_vpath ( vec );
+                svp = art_svp_from_vpath ( pvec );
             }
-            art_free(vec);
-            art_rgb_svp_alpha (svp ,0,0, pys_width, pys_height,
-                               node->color, buffer, rowstride, NULL);
+            art_free(pvec);
+            usvp=art_svp_uncross(svp);
             art_free(svp);
+           rsvp=art_svp_rewind_uncrossed(usvp,ART_WIND_RULE_ODDEVEN); 
+            art_free(usvp); 
+            art_rgb_svp_alpha (rsvp ,0,0, pys_width, pys_height,
+                               node->color, buffer, rowstride, NULL);
+            art_free(rsvp);
             break;
         }
         case GFX_TEXT: {
@@ -548,7 +576,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);
+            string = gfx_string_create (face, node->text, node->angle, node->tabwidth);
             switch(node->halign){
             case GFX_H_RIGHT:  vec.x = -string->bbox.xMax;
                                break;          
@@ -693,12 +721,19 @@ 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_compression_level(png_ptr,1);
+  png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_NO_FILTERS);
+  /* 
+  png_set_filter(png_ptr,PNG_FILTER_TYPE_BASE,PNG_FILTER_SUB);
+  png_set_compression_strategy(png_ptr,Z_HUFFMAN_ONLY);
+  png_set_compression_level(png_ptr,Z_BEST_SPEED); */
+  
   /* Write header data */
   png_write_info (png_ptr, info_ptr);
-
   for (i = 0; i < height; i++)
     row_pointers[i] = (png_bytep) (buffer + i*rowstride);
-
+  
   png_write_image(png_ptr, row_pointers);
   png_write_end(png_ptr, info_ptr);
   png_destroy_write_struct(&png_ptr, &info_ptr);
@@ -1302,7 +1337,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.2rc2 Tobias Oetiker, http://tobi.oetiker.ch\n"
     /* can't like weird chars here */
     "%%Title: (RRDtool output)\n"
     "%%DocumentData: Clean7Bit\n"