don't generate align zones for unused fonts. fixed some mem leaks.
[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         double scale = min_size/1024.0;
77         for(t=from;t<=to;t++) {
78             if(t==x1) {
79                 row[t]=-1e20;
80                 continue;
81             }
82             double r1 = (t<x1?t:x1)*scale;
83             double r2 = (t<x1?x1:t)*scale;
84             double d1 = r2-r1;
85             double d2 = d1+2;
86             double s = d2/d1;
87             double ext1 = r1-from*scale;
88             double ext2 = to*scale-r2;
89             double add1 = ext1*s - ext1;
90             double add2 = ext2*s - ext2;
91
92             /* don't allow the char to grow more than one pixel */
93             if(add1>=1 || add2>=1) {
94                 row[t]=-1e20;
95             }
96         }
97
98         for(t=from;t<=to;t++) {
99             if(row[t]>max2) {
100                 max2 = row[t];
101                 x2 = t;
102             }
103         }
104
105         if(x1>=0 && x2>=0 && x1>x2) {int x=x1;x1=x2;x2=x;}
106     
107         *_x1=x1;
108         *_x2=x2;
109     }
110     
111     free(row);
112 }
113
114 static void negate_y(SRECT* b)
115 {
116     // negate y
117     int by1=b->ymin,by2=b->ymax;
118     b->ymin = -by2;
119     b->ymax = -by1;
120 }
121
122 static void draw_char(SWFFONT * f, int nr, float*row, float*column, SRECT b)
123 {
124     SWFGLYPH*g = &f->glyph[nr];
125
126     SHAPE2*s = swf_ShapeToShape2(g->shape);
127     SHAPELINE*l = s->lines;
128     int x=0,y=0;
129     while(l) {
130         if(l->type == lineTo) {
131             draw_line_xy(row,column,x,-y,l->x,-l->y,&b);
132         } else if(l->type == splineTo) {
133             double x1=x,x2=l->sx,x3=l->x;
134             double y1=y,y2=l->sy,y3=l->y;
135             double c = fabs(x3-2*x2+x1) + fabs(y3-2*y2+y1);
136             int parts = ((int)(sqrt(c)/6))*2+1;
137             float xx=x1,yy=y1;
138             int t;
139             for(t=1;t<=parts;t++) {
140                 float nx = ((t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts));
141                 float ny = ((t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts));
142                 draw_line_xy(row,column,xx,-yy,nx,-ny,&b);
143                 xx = nx;
144                 yy = ny;
145             }
146         }
147         x = l->x;
148         y = l->y;
149         l = l->next;
150     }
151 }
152
153 static ALIGNZONE detect_for_char(SWFFONT * f, int nr, float*row, float*column, SRECT font_bbox, SRECT char_bbox)
154 {
155     ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
156     int width = font_bbox.xmax - font_bbox.xmin;
157     int height = font_bbox.ymax - font_bbox.ymin;
158     if(!width || !height)
159         return a;
160
161     /* find two best x values */
162     int x1=-1,y1=-1,x2=-1,y2=-1;
163
164     int nr_x = 1;
165     find_best(row, width, &x1, &x2, f->use->smallest_size, 
166                 char_bbox.xmin - font_bbox.xmin,
167                 char_bbox.xmax - font_bbox.xmin, nr_x,
168                 0);
169     if(nr_x>0 && x1>=0) a.x  = floatToF16((x1+font_bbox.xmin) / 20480.0);
170     if(nr_x>1 && x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
171
172     find_best(column, height, &y1, &y2, f->use->smallest_size, 
173                 char_bbox.ymin - font_bbox.ymin,
174                 char_bbox.ymax - font_bbox.ymin, 2,
175                 0);
176     if(y1>=0) a.y  = floatToF16((y1+font_bbox.ymin) / 20480.0);
177     if(y2>=0) a.dy = floatToF16((y2-y1) / 20480.0);
178
179     return a;
180 }
181
182 void swf_FontCreateAlignZones(SWFFONT * f)
183 {
184     if(f->alignzones)
185         return;
186
187     if(!f->layout) {
188         fprintf(stderr, "Error: font needs a layout for alignzones to be detected.");
189         return;
190     }
191
192     f->alignzones = (ALIGNZONE*)rfx_calloc(sizeof(ALIGNZONE)*f->numchars);
193     f->alignzone_flags = FONTALIGN_MEDIUM;
194
195     if(!f->layout || !f->use) {
196         int t;
197         for(t=0;t<f->numchars;t++) {
198             // just align the baseline
199             f->alignzones[t].x = 0xffff;
200             f->alignzones[t].y = 0;
201             f->alignzones[t].dx = 0xffff;
202             f->alignzones[t].dy = 0xffff;//floatToF16(460.80 / 1024.0);
203         }
204     } else {
205         int t;
206         SRECT bounds = {0,0,0,0};
207         
208         for(t=0;t<f->numchars;t++) {
209             SRECT b = f->layout->bounds[t];
210             negate_y(&b);
211             swf_ExpandRect2(&bounds, &b);
212         }
213
214         int width = bounds.xmax - bounds.xmin;
215         int height = bounds.ymax - bounds.ymin;
216         float*row = rfx_calloc(sizeof(float)*(width+1));
217         float*column_global = rfx_calloc(sizeof(float)*(height+1));
218         float*column = rfx_calloc(sizeof(float)*(height+1));
219         
220         for(t=0;t<f->numchars;t++) {
221             draw_char(f, t, row, column_global, bounds);
222         }
223         for(t=0;t<=height;t++) {column_global[t]/=f->numchars/2;}
224
225         for(t=0;t<f->numchars;t++) {
226             memcpy(column, column_global, sizeof(float)*(height+1));
227             memset(row, 0, sizeof(float)*(width+1));
228             draw_char(f, t, row, column, bounds);
229             
230             SRECT b = f->layout->bounds[t];
231             negate_y(&b);
232             f->alignzones[t] = detect_for_char(f, t, row, column, bounds, b);
233         }
234         free(row);
235         free(column_global);
236         free(column);
237     }
238 }
239
240 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
241 {
242     swf_SetU16(t, f->id);
243     swf_SetU8(t, f->alignzone_flags);
244     int i;
245     for(i=0;i<f->numchars;i++) {
246         ALIGNZONE*a = &f->alignzones[i];
247         U8 flags = 0;
248         if((a->x & a->dx)!=0xffff)
249             flags |= 1;
250         if((a->y & a->dy)!=0xffff)
251             flags |= 2;
252         int num = 1;
253         if(a->dx != 0xffff || a->dy != 0xffff)
254             num++;
255         swf_SetU8(t, num);
256         if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
257         if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
258         if(num==2) {
259             if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
260             if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
261         }
262         swf_SetU8(t, flags);
263     }
264 }
265
266