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