more horizontal refactoring
[swftools.git] / lib / modules / swfalignzones.c
index a102a9b..36ddd34 100644 (file)
@@ -34,13 +34,13 @@ static void draw_line_xy(float*row,float*column, float x1, float y1, float x2, f
     draw_line(column, y1, y2, x1, x2, area->ymin, area->ymax);
 }
 
-static void find_best(float*_row, int width, int*_x1, int*_x2, int min_size, int min, int max, char debug)
+static void find_best(float*_row, int width, int*_x1, int*_x2, int min_size, int from, int to, int num, char debug)
 {
     int x1=-1, x2=-1;
     float max1=-1e20,max2=-1e20;
     int t;
     float*row = malloc(sizeof(float)*(width+1));
-    int filter_size = 20;
+    int filter_size = 10;
     float* filter = malloc(sizeof(float)*(filter_size*2+1));
     double var = filter_size/3;
     for(t=-filter_size;t<=filter_size;t++) {
@@ -60,80 +60,76 @@ static void find_best(float*_row, int width, int*_x1, int*_x2, int min_size, int
        }
        row[t] = sum;
     }
+    free(filter);
 
-    for(t=0;t<=width;t++) {
+    for(t=from;t<=to;t++) {
        if(row[t]>max1) {
            max1 = row[t];
            x1 = t;
        }
     }
 
-    double scale = min_size/1024.0;
-    for(t=0;t<=width;t++) {
-       if(t==x1) {
-           row[t]=-1e20;
-           continue;
-       }
-       double r1 = (t<x1?t:x1)*scale;
-       double r2 = (t<x1?x1:t)*scale;
-       double d1 = r2-r1;
-       double d2 = d1+2;
-       double s = d2/d1;
-       double ext1 = r1;
-       double ext2 = width*scale-r2;
-       double add1 = ext1*s - ext1;
-       double add2 = ext2*s - ext2;
-
-       /* don't allow the char to grow more than one pixel */
-       if(add1>=1 || add2>=1) {
-           row[t]=-1e20;
+
+    if(num<=1) {
+       *_x1=x1;
+    } else {
+       /* this code is slightly wrong, in that it assumes that the glyph distortion problem 
+          gets worse when the font sizes get smaller. it doesn't. in fact, the smaller
+          the font size, the more of the scaling bugs disappear (http://www.quiss.org/files/scaletest.swf)
+          A better way would probably to use the font size you need for the two alignzones
+          to come to lie in different pixels, which what I think is what makes the problems
+          appear/disappear.
+       */
+
+       double scale = min_size/1024.0;
+       for(t=from;t<=to;t++) {
+           if(t==x1) {
+               row[t]=-1e20;
+               continue;
+           }
+           double r1 = (t<x1?t:x1)*scale;
+           double r2 = (t<x1?x1:t)*scale;
+           double d1 = r2-r1;
+           double d2 = d1+2;
+           double s = d2/d1;
+           double ext1 = r1-from*scale;
+           double ext2 = to*scale-r2;
+           double add1 = ext1*s - ext1;
+           double add2 = ext2*s - ext2;
+
+           /* don't allow the char to grow more than one pixel */
+           if(add1>=1 || add2>=1) {
+               row[t]=-1e20;
+           }
        }
-    }
 
-    for(t=0;t<=width;t++) {
-       if(row[t]>max2) {
-           max2 = row[t];
-           x2 = t;
+       for(t=from;t<=to;t++) {
+           if(row[t]>max2) {
+               max2 = row[t];
+               x2 = t;
+           }
        }
-    }
 
-    if(x1>=0 && x2>=0 && x1>x2) {int x=x1;x1=x2;x2=x;}
-    *_x1=x1;
-    *_x2=x2;
+       if(x1>=0 && x2>=0 && x1>x2) {int x=x1;x1=x2;x2=x;}
+    
+       *_x1=x1;
+       *_x2=x2;
+    }
+    
     free(row);
 }
 
-static int find_min_distance(int min_size, int height)
+static void negate_y(SRECT* b)
 {
-    /* find a minimum distance between two alignzones so
-       that when they both, due to pixel snapping, move 
-       into opposite directions, the subsequent character
-       scaling doesn't cause the upper/lower end of the char
-       to move outwards more than m pixels */
-    int m = 4;
-    double scale = min_size / 1024.0;
-    double max_move = (2 * 1.0 / scale)/m;
-    return (int)(height*max_move);
+    // negate y
+    int by1=b->ymin,by2=b->ymax;
+    b->ymin = -by2;
+    b->ymax = -by1;
 }
 
-static ALIGNZONE detect_for_char(SWFFONT * f, int nr)
+static void draw_char(SWFFONT * f, int nr, float*row, float*column, SRECT b)
 {
     SWFGLYPH*g = &f->glyph[nr];
-    SRECT b = f->layout->bounds[nr];
-    ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
-
-    // negate y
-    int by1=b.ymin,by2=b.ymax;
-    b.ymin = -by2;
-    b.ymax = -by1;
-
-    int width = b.xmax - b.xmin + 1;
-    int height = b.ymax - b.ymin + 1;
-    if(!width || !height)
-       return a;
-           
-    float*row = rfx_calloc(sizeof(float)*width);
-    float*column = rfx_calloc(sizeof(float)*height);
 
     SHAPE2*s = swf_ShapeToShape2(g->shape);
     SHAPELINE*l = s->lines;
@@ -160,19 +156,36 @@ static ALIGNZONE detect_for_char(SWFFONT * f, int nr)
        y = l->y;
        l = l->next;
     }
+    swf_Shape2Free(s);
+    free(s);
+}
+
+static ALIGNZONE detect_for_char(SWFFONT * f, int nr, float*row, float*column, SRECT font_bbox, SRECT char_bbox)
+{
+    ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
+    int width = font_bbox.xmax - font_bbox.xmin;
+    int height = font_bbox.ymax - font_bbox.ymin;
+    if(!width || !height)
+       return a;
 
-    int t;
-    
     /* find two best x values */
     int x1=-1,y1=-1,x2=-1,y2=-1;
-    find_best(row,width,&x1,&x2,f->use->smallest_size,b.xmin,b.xmax,0);
-    char debug = f->glyph2ascii[nr]=='o' && f->id==7;
-    find_best(column,height,&y1,&y2,f->use->smallest_size,b.ymin,b.ymax,debug);
 
-    //if(x1>=0) a.x  = floatToF16((x1+b.xmin) / 20480.0);
-    //if(x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
-    if(y1>=0) a.y  = floatToF16((y1+b.ymin) / 20480.0);
+    int nr_x = 0;
+    find_best(row, width, &x1, &x2, f->use->smallest_size, 
+               char_bbox.xmin - font_bbox.xmin,
+               char_bbox.xmax - font_bbox.xmin, nr_x,
+               0);
+    if(nr_x>0 && x1>=0) a.x  = floatToF16((x1+font_bbox.xmin) / 20480.0);
+    if(nr_x>1 && x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
+
+    find_best(column, height, &y1, &y2, f->use->smallest_size, 
+               char_bbox.ymin - font_bbox.ymin,
+               char_bbox.ymax - font_bbox.ymin, 2,
+               0);
+    if(y1>=0) a.y  = floatToF16((y1+font_bbox.ymin) / 20480.0);
     if(y2>=0) a.dy = floatToF16((y2-y1) / 20480.0);
+
     return a;
 }
 
@@ -186,11 +199,10 @@ void swf_FontCreateAlignZones(SWFFONT * f)
        return;
     }
 
-    
     f->alignzones = (ALIGNZONE*)rfx_calloc(sizeof(ALIGNZONE)*f->numchars);
     f->alignzone_flags = FONTALIGN_MEDIUM;
 
-    if(!f->layout) {
+    if(!f->layout || !f->use) {
        int t;
        for(t=0;t<f->numchars;t++) {
            // just align the baseline
@@ -200,17 +212,76 @@ void swf_FontCreateAlignZones(SWFFONT * f)
            f->alignzones[t].dy = 0xffff;//floatToF16(460.80 / 1024.0);
        }
     } else {
+       SRECT bounds = {0,0,0,0};
        int t;
        for(t=0;t<f->numchars;t++) {
-           f->alignzones[t] = detect_for_char(f, t);
+           SRECT b = f->layout->bounds[t];
+           negate_y(&b);
+           swf_ExpandRect2(&bounds, &b);
+       }
+
+       int width = bounds.xmax - bounds.xmin;
+       int height = bounds.ymax - bounds.ymin;
+       float*row = rfx_calloc(sizeof(float)*(width+1));
+       float*column_global = rfx_calloc(sizeof(float)*(height+1));
+       float*column = rfx_calloc(sizeof(float)*(height+1));
+       
+       for(t=0;t<f->numchars;t++) {
+           draw_char(f, t, row, column_global, bounds);
        }
+       for(t=0;t<=height;t++) {column_global[t]/=f->numchars/2;}
+
+       for(t=0;t<f->numchars;t++) {
+           //memcpy(column, column_global, sizeof(float)*(height+1));
+
+           memset(column, 0, sizeof(float)*(height+1));
+           int s;
+           int drawn = 0;
+           for(s=0;s<f->use->num_neighbors;s++) {
+               int char1 = f->use->neighbors[s].char1;
+               int char2 = f->use->neighbors[s].char2;
+               if(char1 == t || char2 == t) {
+                   int other = t==char1?char2:char1;
+                   draw_char(f, other, row, column, bounds);
+                   drawn++;
+               }
+           }
+
+           for(s=0;s<=height;s++) {
+               column[t] /= drawn*2;
+           }
+
+           memset(row, 0, sizeof(float)*(width+1));
+           draw_char(f, t, row, column, bounds);
+           
+           SRECT b = f->layout->bounds[t];
+           negate_y(&b);
+           f->alignzones[t] = detect_for_char(f, t, row, column, bounds, b);
+       }
+       free(row);
+       free(column_global);
+       free(column);
     }
+}
 
-/*
-    "-^_~\xad\xaf+`\xac\xb7\xf7" //chars for which to detect one y value
-    "#=:;\xb1" //chars for which to detect two y values
-    "\"\xa8" //chars for which to detect two x values
-*/
+void swf_FontPostprocess(SWF*swf)
+{
+    TAG*tag = swf->firstTag;
+    while(tag) {
+       TAG*next = tag->next;
+       if(tag->id == ST_DEFINEFONT3) {
+           U16 id = swf_GetDefineID(tag);
+           SWFFONT*font = 0;
+           swf_FontExtract(swf, id, &font);
+           if(!font->alignzones) {
+               swf_FontCreateAlignZones(font);
+               tag = swf_InsertTag(tag, ST_DEFINEFONTALIGNZONES);
+               swf_FontSetAlignZones(tag, font);
+           }
+           swf_FontFree(font);
+       }
+       tag = next;
+    }
 }
 
 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
@@ -239,4 +310,3 @@ void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
     }
 }
 
-