generate align zones during postprocessing
[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             for(s=0;s<f->use->num_neighbors;s++) {
239                 int char1 = f->use->neighbors[s].char1;
240                 int char2 = f->use->neighbors[s].char2;
241                 if(char1 == t || char2 == t) {
242                     int other = t==char1?char2:char1;
243                     draw_char(f, other, row, column, bounds);
244                     drawn++;
245                 }
246             }
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_FontPostprocess(SWF*swf)
266 {
267     TAG*tag = swf->firstTag;
268     while(tag) {
269         TAG*next = tag->next;
270         if(tag->id == ST_DEFINEFONT3) {
271             U16 id = swf_GetDefineID(tag);
272             SWFFONT*font = 0;
273             swf_FontExtract(swf, id, &font);
274             if(!font->alignzones) {
275                 swf_FontCreateAlignZones(font);
276                 tag = swf_InsertTag(tag, ST_DEFINEFONTALIGNZONES);
277                 swf_FontSetAlignZones(tag, font);
278             }
279             swf_FontFree(font);
280         }
281         tag = next;
282     }
283 }
284
285 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
286 {
287     swf_SetU16(t, f->id);
288     swf_SetU8(t, f->alignzone_flags);
289     int i;
290     for(i=0;i<f->numchars;i++) {
291         ALIGNZONE*a = &f->alignzones[i];
292         U8 flags = 0;
293         if((a->x & a->dx)!=0xffff)
294             flags |= 1;
295         if((a->y & a->dy)!=0xffff)
296             flags |= 2;
297         int num = 1;
298         if(a->dx != 0xffff || a->dy != 0xffff)
299             num++;
300         swf_SetU8(t, num);
301         if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
302         if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
303         if(num==2) {
304             if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
305             if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
306         }
307         swf_SetU8(t, flags);
308     }
309 }
310