19619036c8da726fb4f0ca365ffb760879933c88
[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 = 20;
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 = 1;
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         int t;
214         SRECT bounds = {0,0,0,0};
215         
216         for(t=0;t<f->numchars;t++) {
217             SRECT b = f->layout->bounds[t];
218             negate_y(&b);
219             swf_ExpandRect2(&bounds, &b);
220         }
221
222         int width = bounds.xmax - bounds.xmin;
223         int height = bounds.ymax - bounds.ymin;
224         float*row = rfx_calloc(sizeof(float)*(width+1));
225         float*column_global = rfx_calloc(sizeof(float)*(height+1));
226         float*column = rfx_calloc(sizeof(float)*(height+1));
227         
228         for(t=0;t<f->numchars;t++) {
229             draw_char(f, t, row, column_global, bounds);
230         }
231         for(t=0;t<=height;t++) {column_global[t]/=f->numchars/2;}
232
233         for(t=0;t<f->numchars;t++) {
234             memcpy(column, column_global, sizeof(float)*(height+1));
235             memset(row, 0, sizeof(float)*(width+1));
236             draw_char(f, t, row, column, bounds);
237             
238             SRECT b = f->layout->bounds[t];
239             negate_y(&b);
240             f->alignzones[t] = detect_for_char(f, t, row, column, bounds, b);
241         }
242         free(row);
243         free(column_global);
244         free(column);
245     }
246 }
247
248 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
249 {
250     swf_SetU16(t, f->id);
251     swf_SetU8(t, f->alignzone_flags);
252     int i;
253     for(i=0;i<f->numchars;i++) {
254         ALIGNZONE*a = &f->alignzones[i];
255         U8 flags = 0;
256         if((a->x & a->dx)!=0xffff)
257             flags |= 1;
258         if((a->y & a->dy)!=0xffff)
259             flags |= 2;
260         int num = 1;
261         if(a->dx != 0xffff || a->dy != 0xffff)
262             num++;
263         swf_SetU8(t, num);
264         if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
265         if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
266         if(num==2) {
267             if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
268             if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
269         }
270         swf_SetU8(t, flags);
271     }
272 }
273
274