a102a9bf328321a9b03a4739001d2095e6947ad1
[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 min, int max, 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
64     for(t=0;t<=width;t++) {
65         if(row[t]>max1) {
66             max1 = row[t];
67             x1 = t;
68         }
69     }
70
71     double scale = min_size/1024.0;
72     for(t=0;t<=width;t++) {
73         if(t==x1) {
74             row[t]=-1e20;
75             continue;
76         }
77         double r1 = (t<x1?t:x1)*scale;
78         double r2 = (t<x1?x1:t)*scale;
79         double d1 = r2-r1;
80         double d2 = d1+2;
81         double s = d2/d1;
82         double ext1 = r1;
83         double ext2 = width*scale-r2;
84         double add1 = ext1*s - ext1;
85         double add2 = ext2*s - ext2;
86
87         /* don't allow the char to grow more than one pixel */
88         if(add1>=1 || add2>=1) {
89             row[t]=-1e20;
90         }
91     }
92
93     for(t=0;t<=width;t++) {
94         if(row[t]>max2) {
95             max2 = row[t];
96             x2 = t;
97         }
98     }
99
100     if(x1>=0 && x2>=0 && x1>x2) {int x=x1;x1=x2;x2=x;}
101     *_x1=x1;
102     *_x2=x2;
103     free(row);
104 }
105
106 static int find_min_distance(int min_size, int height)
107 {
108     /* find a minimum distance between two alignzones so
109        that when they both, due to pixel snapping, move 
110        into opposite directions, the subsequent character
111        scaling doesn't cause the upper/lower end of the char
112        to move outwards more than m pixels */
113     int m = 4;
114     double scale = min_size / 1024.0;
115     double max_move = (2 * 1.0 / scale)/m;
116     return (int)(height*max_move);
117 }
118
119 static ALIGNZONE detect_for_char(SWFFONT * f, int nr)
120 {
121     SWFGLYPH*g = &f->glyph[nr];
122     SRECT b = f->layout->bounds[nr];
123     ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
124
125     // negate y
126     int by1=b.ymin,by2=b.ymax;
127     b.ymin = -by2;
128     b.ymax = -by1;
129
130     int width = b.xmax - b.xmin + 1;
131     int height = b.ymax - b.ymin + 1;
132     if(!width || !height)
133         return a;
134             
135     float*row = rfx_calloc(sizeof(float)*width);
136     float*column = rfx_calloc(sizeof(float)*height);
137
138     SHAPE2*s = swf_ShapeToShape2(g->shape);
139     SHAPELINE*l = s->lines;
140     int x=0,y=0;
141     while(l) {
142         if(l->type == lineTo) {
143             draw_line_xy(row,column,x,-y,l->x,-l->y,&b);
144         } else if(l->type == splineTo) {
145             double x1=x,x2=l->sx,x3=l->x;
146             double y1=y,y2=l->sy,y3=l->y;
147             double c = fabs(x3-2*x2+x1) + fabs(y3-2*y2+y1);
148             int parts = ((int)(sqrt(c)/6))*2+1;
149             float xx=x1,yy=y1;
150             int t;
151             for(t=1;t<=parts;t++) {
152                 float nx = ((t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts));
153                 float ny = ((t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts));
154                 draw_line_xy(row,column,xx,-yy,nx,-ny,&b);
155                 xx = nx;
156                 yy = ny;
157             }
158         }
159         x = l->x;
160         y = l->y;
161         l = l->next;
162     }
163
164     int t;
165     
166     /* find two best x values */
167     int x1=-1,y1=-1,x2=-1,y2=-1;
168     find_best(row,width,&x1,&x2,f->use->smallest_size,b.xmin,b.xmax,0);
169     char debug = f->glyph2ascii[nr]=='o' && f->id==7;
170     find_best(column,height,&y1,&y2,f->use->smallest_size,b.ymin,b.ymax,debug);
171
172     //if(x1>=0) a.x  = floatToF16((x1+b.xmin) / 20480.0);
173     //if(x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
174     if(y1>=0) a.y  = floatToF16((y1+b.ymin) / 20480.0);
175     if(y2>=0) a.dy = floatToF16((y2-y1) / 20480.0);
176     return a;
177 }
178
179 void swf_FontCreateAlignZones(SWFFONT * f)
180 {
181     if(f->alignzones)
182         return;
183
184     if(!f->layout) {
185         fprintf(stderr, "Error: font needs a layout for alignzones to be detected.");
186         return;
187     }
188
189     
190     f->alignzones = (ALIGNZONE*)rfx_calloc(sizeof(ALIGNZONE)*f->numchars);
191     f->alignzone_flags = FONTALIGN_MEDIUM;
192
193     if(!f->layout) {
194         int t;
195         for(t=0;t<f->numchars;t++) {
196             // just align the baseline
197             f->alignzones[t].x = 0xffff;
198             f->alignzones[t].y = 0;
199             f->alignzones[t].dx = 0xffff;
200             f->alignzones[t].dy = 0xffff;//floatToF16(460.80 / 1024.0);
201         }
202     } else {
203         int t;
204         for(t=0;t<f->numchars;t++) {
205             f->alignzones[t] = detect_for_char(f, t);
206         }
207     }
208
209 /*
210     "-^_~\xad\xaf+`\xac\xb7\xf7" //chars for which to detect one y value
211     "#=:;\xb1" //chars for which to detect two y values
212     "\"\xa8" //chars for which to detect two x values
213 */
214 }
215
216 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
217 {
218     swf_SetU16(t, f->id);
219     swf_SetU8(t, f->alignzone_flags);
220     int i;
221     for(i=0;i<f->numchars;i++) {
222         ALIGNZONE*a = &f->alignzones[i];
223         U8 flags = 0;
224         if((a->x & a->dx)!=0xffff)
225             flags |= 1;
226         if((a->y & a->dy)!=0xffff)
227             flags |= 2;
228         int num = 1;
229         if(a->dx != 0xffff || a->dy != 0xffff)
230             num++;
231         swf_SetU8(t, num);
232         if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
233         if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
234         if(num==2) {
235             if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
236             if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
237         }
238         swf_SetU8(t, flags);
239     }
240 }
241
242