added glyph pairing to align zone detector
[swftools.git] / lib / modules / swfalignzones.c
1 #include "../rfxswf.h"
2
3 static inline double sqr(double x) {return x*x;}
4
5 static void draw_line(float*row, float x1, float x2, float y1, float y2, int min, int max)
6 {
7     if(x2<x1) {int x=x1;x1=x2;x2=x;}
8     if(x1<min || x2>max) {
9         fprintf(stderr, "error: glyph x stroke out of bounds\n");
10         return;
11     }
12     x1 -= min;
13     x2 -= min;
14
15     double d = sqrt(sqr(y2-y1)+sqr(x2-x1));
16     if(floor(x1)==floor(x2)) {
17         row[(int)floor(x1)] += d;
18     } else {
19         double i = d/(x2-x1);
20         int x;
21         int xx1 = ceil(x1);
22         int xx2 = floor(x2);
23         row[xx1] += i*(xx1-x1);
24         row[xx2] += i*(x2-xx2);
25         for(x=xx1;x<xx2;x++) {
26             row[x]+=i;
27         }
28     }
29 }
30
31 static void draw_line_xy(float*row,float*column, float x1, float y1, float x2, float y2,SRECT* area)
32 {
33     draw_line(row, x1, x2, y1, y2, area->xmin, area->xmax);
34     draw_line(column, y1, y2, x1, x2, area->ymin, area->ymax);
35 }
36
37 static void find_best(float*_row, int width, int*_x1, int*_x2, int min_size, int from, int to, int num, char debug)
38 {
39     int x1=-1, x2=-1;
40     float max1=-1e20,max2=-1e20;
41     int t;
42     float*row = malloc(sizeof(float)*(width+1));
43     int filter_size = 10;
44     float* filter = malloc(sizeof(float)*(filter_size*2+1));
45     double var = filter_size/3;
46     for(t=-filter_size;t<=filter_size;t++) {
47         double v = t/var;
48         float r = v*v/2;
49         filter[filter_size+t] = exp(-r);
50     }
51     //filter[0]=1;filter_size=0;
52
53     for(t=0;t<=width;t++) {
54         int s;
55         double sum = 0;
56         for(s=-filter_size;s<=filter_size;s++) {
57             if(t+s<0) continue;
58             if(t+s>width) continue;
59             sum += _row[t+s]*filter[s+filter_size];
60         }
61         row[t] = sum;
62     }
63     free(filter);
64
65     for(t=from;t<=to;t++) {
66         if(row[t]>max1) {
67             max1 = row[t];
68             x1 = t;
69         }
70     }
71
72
73     if(num<=1) {
74         *_x1=x1;
75     } else {
76         /* this code is slightly wrong, in that it assumes that the glyph distortion problem 
77            gets worse when the font sizes get smaller. it doesn't. in fact, the smaller
78            the font size, the more of the scaling bugs disappear (http://www.quiss.org/files/scaletest.swf)
79            A better way would probably to use the font size you need for the two alignzones
80            to come to lie in different pixels, which what I think is what makes the problems
81            appear/disappear.
82         */
83
84         double scale = min_size/1024.0;
85         for(t=from;t<=to;t++) {
86             if(t==x1) {
87                 row[t]=-1e20;
88                 continue;
89             }
90             double r1 = (t<x1?t:x1)*scale;
91             double r2 = (t<x1?x1:t)*scale;
92             double d1 = r2-r1;
93             double d2 = d1+2;
94             double s = d2/d1;
95             double ext1 = r1-from*scale;
96             double ext2 = to*scale-r2;
97             double add1 = ext1*s - ext1;
98             double add2 = ext2*s - ext2;
99
100             /* don't allow the char to grow more than one pixel */
101             if(add1>=1 || add2>=1) {
102                 row[t]=-1e20;
103             }
104         }
105
106         for(t=from;t<=to;t++) {
107             if(row[t]>max2) {
108                 max2 = row[t];
109                 x2 = t;
110             }
111         }
112
113         if(x1>=0 && x2>=0 && x1>x2) {int x=x1;x1=x2;x2=x;}
114     
115         *_x1=x1;
116         *_x2=x2;
117     }
118     
119     free(row);
120 }
121
122 static void negate_y(SRECT* b)
123 {
124     // negate y
125     int by1=b->ymin,by2=b->ymax;
126     b->ymin = -by2;
127     b->ymax = -by1;
128 }
129
130 static void draw_char(SWFFONT * f, int nr, float*row, float*column, SRECT b)
131 {
132     SWFGLYPH*g = &f->glyph[nr];
133
134     SHAPE2*s = swf_ShapeToShape2(g->shape);
135     SHAPELINE*l = s->lines;
136     int x=0,y=0;
137     while(l) {
138         if(l->type == lineTo) {
139             draw_line_xy(row,column,x,-y,l->x,-l->y,&b);
140         } else if(l->type == splineTo) {
141             double x1=x,x2=l->sx,x3=l->x;
142             double y1=y,y2=l->sy,y3=l->y;
143             double c = fabs(x3-2*x2+x1) + fabs(y3-2*y2+y1);
144             int parts = ((int)(sqrt(c)/6))*2+1;
145             float xx=x1,yy=y1;
146             int t;
147             for(t=1;t<=parts;t++) {
148                 float nx = ((t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts));
149                 float ny = ((t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts));
150                 draw_line_xy(row,column,xx,-yy,nx,-ny,&b);
151                 xx = nx;
152                 yy = ny;
153             }
154         }
155         x = l->x;
156         y = l->y;
157         l = l->next;
158     }
159 }
160
161 static ALIGNZONE detect_for_char(SWFFONT * f, int nr, float*row, float*column, SRECT font_bbox, SRECT char_bbox)
162 {
163     ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
164     int width = font_bbox.xmax - font_bbox.xmin;
165     int height = font_bbox.ymax - font_bbox.ymin;
166     if(!width || !height)
167         return a;
168
169     /* find two best x values */
170     int x1=-1,y1=-1,x2=-1,y2=-1;
171
172     int nr_x = 0;
173     find_best(row, width, &x1, &x2, f->use->smallest_size, 
174                 char_bbox.xmin - font_bbox.xmin,
175                 char_bbox.xmax - font_bbox.xmin, nr_x,
176                 0);
177     if(nr_x>0 && x1>=0) a.x  = floatToF16((x1+font_bbox.xmin) / 20480.0);
178     if(nr_x>1 && x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
179
180     find_best(column, height, &y1, &y2, f->use->smallest_size, 
181                 char_bbox.ymin - font_bbox.ymin,
182                 char_bbox.ymax - font_bbox.ymin, 2,
183                 0);
184     if(y1>=0) a.y  = floatToF16((y1+font_bbox.ymin) / 20480.0);
185     if(y2>=0) a.dy = floatToF16((y2-y1) / 20480.0);
186
187     return a;
188 }
189
190 void swf_FontCreateAlignZones(SWFFONT * f)
191 {
192     if(f->alignzones)
193         return;
194
195     if(!f->layout) {
196         fprintf(stderr, "Error: font needs a layout for alignzones to be detected.");
197         return;
198     }
199
200     f->alignzones = (ALIGNZONE*)rfx_calloc(sizeof(ALIGNZONE)*f->numchars);
201     f->alignzone_flags = FONTALIGN_MEDIUM;
202
203     if(!f->layout || !f->use) {
204         int t;
205         for(t=0;t<f->numchars;t++) {
206             // just align the baseline
207             f->alignzones[t].x = 0xffff;
208             f->alignzones[t].y = 0;
209             f->alignzones[t].dx = 0xffff;
210             f->alignzones[t].dy = 0xffff;//floatToF16(460.80 / 1024.0);
211         }
212     } else {
213         SRECT bounds = {0,0,0,0};
214         int t;
215         for(t=0;t<f->numchars;t++) {
216             SRECT b = f->layout->bounds[t];
217             negate_y(&b);
218             swf_ExpandRect2(&bounds, &b);
219         }
220
221         int width = bounds.xmax - bounds.xmin;
222         int height = bounds.ymax - bounds.ymin;
223         float*row = rfx_calloc(sizeof(float)*(width+1));
224         float*column_global = rfx_calloc(sizeof(float)*(height+1));
225         float*column = rfx_calloc(sizeof(float)*(height+1));
226         
227         for(t=0;t<f->numchars;t++) {
228             draw_char(f, t, row, column_global, bounds);
229         }
230         for(t=0;t<=height;t++) {column_global[t]/=f->numchars/2;}
231
232         for(t=0;t<f->numchars;t++) {
233             //memcpy(column, column_global, sizeof(float)*(height+1));
234
235             memset(column, 0, sizeof(float)*(height+1));
236             int s;
237             int drawn = 0;
238             printf("[font %d] pairing %c with ", f->id, f->glyph2ascii[t]);
239             for(s=0;s<f->use->num_neighbors;s++) {
240                 if(f->use->neighbors[s].char2 == t) {
241                     printf("%c ", f->glyph2ascii[f->use->neighbors[s].char1]);
242                     draw_char(f, f->use->neighbors[s].char1, row, column, bounds);
243                     drawn++;
244                 }
245             }
246             printf("\n");
247
248             for(s=0;s<=height;s++) {
249                 column[t] /= drawn*2;
250             }
251
252             memset(row, 0, sizeof(float)*(width+1));
253             draw_char(f, t, row, column, bounds);
254             
255             SRECT b = f->layout->bounds[t];
256             negate_y(&b);
257             f->alignzones[t] = detect_for_char(f, t, row, column, bounds, b);
258         }
259         free(row);
260         free(column_global);
261         free(column);
262     }
263 }
264
265 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
266 {
267     swf_SetU16(t, f->id);
268     swf_SetU8(t, f->alignzone_flags);
269     int i;
270     for(i=0;i<f->numchars;i++) {
271         ALIGNZONE*a = &f->alignzones[i];
272         U8 flags = 0;
273         if((a->x & a->dx)!=0xffff)
274             flags |= 1;
275         if((a->y & a->dy)!=0xffff)
276             flags |= 2;
277         int num = 1;
278         if(a->dx != 0xffff || a->dy != 0xffff)
279             num++;
280         swf_SetU8(t, num);
281         if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
282         if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
283         if(num==2) {
284             if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
285             if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
286         }
287         swf_SetU8(t, flags);
288     }
289 }
290
291