small bug in ttf reading
[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     swf_Shape2Free(s);
160     free(s);
161 }
162
163 static ALIGNZONE detect_for_char(SWFFONT * f, int nr, float*row, float*column, SRECT font_bbox, SRECT char_bbox)
164 {
165     ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
166     int width = font_bbox.xmax - font_bbox.xmin;
167     int height = font_bbox.ymax - font_bbox.ymin;
168     if(!width || !height)
169         return a;
170
171     /* find two best x values */
172     int x1=-1,y1=-1,x2=-1,y2=-1;
173
174     int nr_x = 0;
175     find_best(row, width, &x1, &x2, f->use->smallest_size, 
176                 char_bbox.xmin - font_bbox.xmin,
177                 char_bbox.xmax - font_bbox.xmin, nr_x,
178                 0);
179     if(nr_x>0 && x1>=0) a.x  = floatToF16((x1+font_bbox.xmin) / 20480.0);
180     if(nr_x>1 && x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
181
182     find_best(column, height, &y1, &y2, f->use->smallest_size, 
183                 char_bbox.ymin - font_bbox.ymin,
184                 char_bbox.ymax - font_bbox.ymin, 2,
185                 0);
186     if(y1>=0) a.y  = floatToF16((y1+font_bbox.ymin) / 20480.0);
187     if(y2>=0) a.dy = floatToF16((y2-y1) / 20480.0);
188
189     return a;
190 }
191
192 void swf_FontCreateAlignZones(SWFFONT * f)
193 {
194     if(f->alignzones)
195         return;
196
197     if(!f->layout) {
198         fprintf(stderr, "Error: font needs a layout for alignzones to be detected.");
199         return;
200     }
201
202     f->alignzones = (ALIGNZONE*)rfx_calloc(sizeof(ALIGNZONE)*f->numchars);
203     f->alignzone_flags = FONTALIGN_MEDIUM;
204
205     if(!f->layout || !f->use) {
206         int t;
207         for(t=0;t<f->numchars;t++) {
208             // just align the baseline
209             f->alignzones[t].x = 0xffff;
210             f->alignzones[t].y = 0;
211             f->alignzones[t].dx = 0xffff;
212             f->alignzones[t].dy = 0xffff;//floatToF16(460.80 / 1024.0);
213         }
214     } else {
215         SRECT bounds = {0,0,0,0};
216         int t;
217         for(t=0;t<f->numchars;t++) {
218             SRECT b = f->layout->bounds[t];
219             negate_y(&b);
220             swf_ExpandRect2(&bounds, &b);
221         }
222
223         int width = bounds.xmax - bounds.xmin;
224         int height = bounds.ymax - bounds.ymin;
225         float*row = rfx_calloc(sizeof(float)*(width+1));
226         float*column_global = rfx_calloc(sizeof(float)*(height+1));
227         float*column = rfx_calloc(sizeof(float)*(height+1));
228         
229         for(t=0;t<f->numchars;t++) {
230             draw_char(f, t, row, column_global, bounds);
231         }
232         for(t=0;t<=height;t++) {column_global[t]/=f->numchars/2;}
233
234         for(t=0;t<f->numchars;t++) {
235             //memcpy(column, column_global, sizeof(float)*(height+1));
236
237             memset(column, 0, sizeof(float)*(height+1));
238             int s;
239             int drawn = 0;
240             for(s=0;s<f->use->num_neighbors;s++) {
241                 int char1 = f->use->neighbors[s].char1;
242                 int char2 = f->use->neighbors[s].char2;
243                 if(char1 == t || char2 == t) {
244                     int other = t==char1?char2:char1;
245                     draw_char(f, other, row, column, bounds);
246                     drawn++;
247                 }
248             }
249
250             for(s=0;s<=height;s++) {
251                 column[t] /= drawn*2;
252             }
253
254             memset(row, 0, sizeof(float)*(width+1));
255             draw_char(f, t, row, column, bounds);
256             
257             SRECT b = f->layout->bounds[t];
258             negate_y(&b);
259             f->alignzones[t] = detect_for_char(f, t, row, column, bounds, b);
260         }
261         free(row);
262         free(column_global);
263         free(column);
264     }
265 }
266
267 void swf_FontPostprocess(SWF*swf)
268 {
269     TAG*tag = swf->firstTag;
270     while(tag) {
271         TAG*next = tag->next;
272         if(tag->id == ST_DEFINEFONT3) {
273             U16 id = swf_GetDefineID(tag);
274             SWFFONT*font = 0;
275             swf_FontExtract(swf, id, &font);
276             if(!font->alignzones) {
277                 swf_FontCreateAlignZones(font);
278                 tag = swf_InsertTag(tag, ST_DEFINEFONTALIGNZONES);
279                 swf_FontSetAlignZones(tag, font);
280             }
281             swf_FontFree(font);
282         }
283         tag = next;
284     }
285 }
286
287 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
288 {
289     swf_SetU16(t, f->id);
290     swf_SetU8(t, f->alignzone_flags);
291     int i;
292     for(i=0;i<f->numchars;i++) {
293         ALIGNZONE*a = &f->alignzones[i];
294         U8 flags = 0;
295         if((a->x & a->dx)!=0xffff)
296             flags |= 1;
297         if((a->y & a->dy)!=0xffff)
298             flags |= 2;
299         int num = 1;
300         if(a->dx != 0xffff || a->dy != 0xffff)
301             num++;
302         swf_SetU8(t, num);
303         if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
304         if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
305         if(num==2) {
306             if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
307             if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
308         }
309         swf_SetU8(t, flags);
310     }
311 }
312