1 #include "../rfxswf.h"
2 #include "../graphcut.c"
3 #include "../log.h"
4
sqr(double x)5 static inline double sqr(double x) {return x*x;}
6
draw_line(float * row,float x1,float x2,float y1,float y2,int min,int max,double weight)7 static void draw_line(float*row, float x1, float x2, float y1, float y2, int min, int max, double weight)
8 {
9 if(x2<x1) {int x=x1;x1=x2;x2=x;}
10 if(x1<min || x2>max) {
11 fprintf(stderr, "error: glyph x stroke out of bounds\n");
12 return;
13 }
14 x1 -= min;
15 x2 -= min;
16
17 double d = sqrt(sqr(y2-y1)+sqr(x2-x1));
18 if(floor(x1)==floor(x2)) {
19 row[(int)floor(x1)] += d*weight;
20 } else {
21 double i = d/(x2-x1);
22 int x;
23 int xx1 = ceil(x1);
24 int xx2 = floor(x2);
25 row[xx1] += i*(xx1-x1)*weight;
26 row[xx2] += i*(x2-xx2)*weight;
27 for(x=xx1;x<xx2;x++) {
28 row[x] += i*weight;
29 }
30 }
31 }
32
draw_line_xy(float * row,float * column,float x1,float y1,float x2,float y2,SRECT * area,double weight)33 static void draw_line_xy(float*row,float*column, float x1, float y1, float x2, float y2,SRECT* area, double weight)
34 {
35 draw_line(row, x1, x2, y1, y2, area->xmin, area->xmax, weight);
36 draw_line(column, y1, y2, x1, x2, area->ymin, area->ymax, weight);
37 }
38
find_best(float * _row,int width,int * _x1,int * _x2,int min_size,int from,int to,int num,char debug)39 static void find_best(float*_row, int width, int*_x1, int*_x2, int min_size, int from, int to, int num, char debug)
40 {
41 int x1=-1, x2=-1;
42 float max1=-1e20,max2=-1e20;
43 int t;
44
45 if(from==to) {
46 *_x1 = from;
47 return;
48 }
49
50 float*row = malloc(sizeof(float)*(width+1));
51 int filter_size = 25;
52 float* filter = malloc(sizeof(float)*(filter_size*2+1));
53 double var = filter_size/3;
54 double sum = 0;
55 for(t=-filter_size;t<=filter_size;t++) {
56 double v = t/var;
57 float r = v*v/2;
58 filter[filter_size+t] = exp(-r);
59 sum += filter[filter_size+t];
60 }
61 for(t=-filter_size;t<=filter_size;t++) {
62 filter[filter_size+t]/=sum;
63 }
64
65 //filter[0]=1;filter_size=0;
66
67 for(t=0;t<=width;t++) {
68 int s;
69 double sum = 0;
70 for(s=-filter_size;s<=filter_size;s++) {
71 if(t+s<0) continue;
72 if(t+s>width) continue;
73 sum += _row[t+s]*filter[s+filter_size];
74 }
75 row[t] = sum;
76 }
77 free(filter);
78
79 for(t=from;t<=to;t++) {
80 if(row[t]>max1) {
81 max1 = row[t];
82 x1 = t;
83 }
84 }
85
86
87 if(num<=1) {
88 *_x1=x1;
89 } else {
90 /* this code is slightly wrong, in that it assumes that the glyph distortion problem
91 gets worse when the font sizes get smaller. it doesn't. in fact, the smaller
92 the font size, the more of the scaling bugs disappear (http://www.quiss.org/files/scaletest.swf)
93 A better way would probably to use the font size you need for the two alignzones
94 to come to lie in different pixels, which what I think is what makes the problems
95 appear/disappear.
96 */
97
98 double scale = min_size/1024.0;
99 for(t=from;t<=to;t++) {
100 if(t==x1) {
101 row[t]=-1e20;
102 continue;
103 }
104 double r1 = (t<x1?t:x1)*scale;
105 double r2 = (t<x1?x1:t)*scale;
106 double d1 = r2-r1;
107 double d2 = d1+2;
108 double s = d2/d1;
109 double ext1 = r1-from*scale;
110 double ext2 = to*scale-r2;
111 double add1 = ext1*s - ext1;
112 double add2 = ext2*s - ext2;
113
114 /* don't allow the char to grow more than one pixel */
115 if(add1>=1 || add2>=1) {
116 row[t]=-1e20;
117 }
118 }
119
120 for(t=from;t<=to;t++) {
121 if(row[t]>max2) {
122 max2 = row[t];
123 x2 = t;
124 }
125 }
126
127 if(x1>=0 && x2>=0 && x1>x2) {int x=x1;x1=x2;x2=x;}
128
129 *_x1=x1;
130 *_x2=x2;
131 }
132
133 free(row);
134 }
135
negate_y(SRECT * b)136 static void negate_y(SRECT* b)
137 {
138 // negate y
139 int by1=b->ymin,by2=b->ymax;
140 b->ymin = -by2;
141 b->ymax = -by1;
142 }
143
draw_char(SWFFONT * f,int nr,float * row,float * column,SRECT b,double weight)144 static void draw_char(SWFFONT * f, int nr, float*row, float*column, SRECT b, double weight)
145 {
146 SWFGLYPH*g = &f->glyph[nr];
147
148 SHAPE2*s = swf_ShapeToShape2(g->shape);
149 SHAPELINE*l = s->lines;
150 int x=0,y=0;
151 while(l) {
152 if(l->type == lineTo) {
153 draw_line_xy(row,column,x,-y,l->x,-l->y,&b,weight);
154 } else if(l->type == splineTo) {
155 double x1=x,x2=l->sx,x3=l->x;
156 double y1=y,y2=l->sy,y3=l->y;
157 double c = fabs(x3-2*x2+x1) + fabs(y3-2*y2+y1);
158 int parts = ((int)(sqrt(c)/6))*2+1;
159 float xx=x1,yy=y1;
160 int t;
161 for(t=1;t<=parts;t++) {
162 float nx = ((t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts));
163 float ny = ((t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts));
164 draw_line_xy(row,column,xx,-yy,nx,-ny,&b,weight);
165 xx = nx;
166 yy = ny;
167 }
168 }
169 x = l->x;
170 y = l->y;
171 l = l->next;
172 }
173 swf_Shape2Free(s);
174 free(s);
175 }
176
detect_for_char(SWFFONT * f,float * row,float * column,SRECT font_bbox,SRECT char_bbox)177 static ALIGNZONE detect_for_char(SWFFONT * f, float*row, float*column, SRECT font_bbox, SRECT char_bbox)
178 {
179 ALIGNZONE a = {0xffff,0xffff,0xffff,0xffff};
180 int width = font_bbox.xmax - font_bbox.xmin;
181 int height = font_bbox.ymax - font_bbox.ymin;
182 if(!width || !height)
183 return a;
184
185 /* find two best x values */
186 int x1=-1,y1=-1,x2=-1,y2=-1;
187
188 int nr_x = 0;
189 find_best(row, width, &x1, &x2, f->use->smallest_size,
190 char_bbox.xmin - font_bbox.xmin,
191 char_bbox.xmax - font_bbox.xmin, nr_x,
192 0);
193 if(nr_x>0 && x1>=0) a.x = floatToF16((x1+font_bbox.xmin) / 20480.0);
194 if(nr_x>1 && x2>=0) a.dx = floatToF16((x2-x1) / 20480.0);
195
196 find_best(column, height, &y1, &y2, f->use->smallest_size,
197 char_bbox.ymin - font_bbox.ymin,
198 char_bbox.ymax - font_bbox.ymin, 2,
199 0);
200 if(y1>=0) a.y = floatToF16((y1+font_bbox.ymin) / 20480.0);
201 if(y2>=0) a.dy = floatToF16((y2-y1) / 20480.0);
202 return a;
203 }
204
make_graph(SWFFONT * f)205 static graph_t*make_graph(SWFFONT*f)
206 {
207 FONTUSAGE*use = f->use;
208 graph_t*g = graph_new(f->numchars);
209 int s,t;
210 for(s=1;s<f->numchars;s++) {
211 for(t=0;t<s;t++) {
212 if(f->glyph2ascii) {
213 int c1 = f->glyph2ascii[s];
214 int c2 = f->glyph2ascii[t];
215 if((c1<'a' && c2>='a' && c2<='z') ||
216 (c2<'a' && c1>='a' && c1<='z')) {
217 /* never connect lowercase with any uppercase
218 or punctuation */
219 continue;
220 }
221 }
222
223 int pos1 = swf_FontUseGetPair(f, s, t);
224 int pos2 = swf_FontUseGetPair(f, t, s);
225 if(pos1 || pos2) {
226 int weight1 = pos1?use->neighbors[pos1-1].num:0;
227 int weight2 = pos2?use->neighbors[pos2-1].num:0;
228 int weight = weight1+weight2;
229
230 /*printf("font %d: pair %c and %c\n",
231 f->id, f->glyph2ascii[t], f->glyph2ascii[s]);*/
232 graph_add_edge(&g->nodes[s], &g->nodes[t], weight, weight);
233 }
234 }
235 }
236 return g;
237 }
238
swf_FontCreateAlignZones(SWFFONT * f)239 void swf_FontCreateAlignZones(SWFFONT * f)
240 {
241 if(f->alignzones)
242 return;
243
244 if(!f->layout) {
245 fprintf(stderr, "Error: font needs a layout for alignzones to be detected.");
246 return;
247 }
248
249 f->alignzones = (ALIGNZONE*)rfx_calloc(sizeof(ALIGNZONE)*f->numchars);
250 f->alignzone_flags = FONTALIGN_MEDIUM;
251
252 if(!f->layout || !f->use) {
253 int t;
254 for(t=0;t<f->numchars;t++) {
255 // just align the baseline
256 f->alignzones[t].x = 0xffff;
257 f->alignzones[t].y = 0;
258 f->alignzones[t].dx = 0xffff;
259 f->alignzones[t].dy = 0xffff;//floatToF16(460.80 / 1024.0);
260 }
261 } else {
262 graph_t*g = make_graph(f);
263
264 int num_components = graph_find_components(g);
265 msg("<notice> Building font alignzone information for font %d (%d characters, %d components, %d pairs)\n",
266 f->id, f->numchars, num_components, f->use->num_neighbors);
267
268 SRECT bounds = {0,0,0,0};
269 int t;
270 for(t=0;t<f->numchars;t++) {
271 SRECT b = f->layout->bounds[t];
272 negate_y(&b);
273 swf_ExpandRect2(&bounds, &b);
274 }
275
276 int width = bounds.xmax - bounds.xmin;
277 int height = bounds.ymax - bounds.ymin;
278 float*row = rfx_calloc(sizeof(float)*(width+1));
279 float*global_column = rfx_calloc(sizeof(float)*(height+1));
280 float*column = rfx_calloc(sizeof(float)*(height+1));
281
282 const double SELF_WEIGHT = 0.00; // ignore own char
283
284 int c;
285 for(c=0;c<num_components;c++) {
286 int drawn = 0;
287 memset(global_column, 0, sizeof(float)*(height+1));
288 SRECT local_bounds = {0,0,0,0};
289 for(t=0;t<f->numchars;t++) {
290 if(g->nodes[t].tmp == c) {
291 draw_char(f, t, row, global_column, bounds, 1.0-SELF_WEIGHT);
292 SRECT b = f->layout->bounds[t];
293 negate_y(&b);
294 swf_ExpandRect2(&local_bounds, &b);
295 drawn++;
296 }
297 }
298
299 for(t=0;t<=height;t++) {
300 global_column[t] /= drawn;
301 }
302
303 memcpy(column, global_column, sizeof(float)*(height+1));
304 memset(row, 0, sizeof(float)*(width+1));
305 ALIGNZONE a = detect_for_char(f, row, column, bounds, local_bounds);
306
307 for(t=0;t<f->numchars;t++) {
308 if(g->nodes[t].tmp == c) {
309 f->alignzones[t] = a;
310 }
311 }
312 }
313 free(row);
314 free(column);
315 free(global_column);
316
317 graph_delete(g);
318 }
319 }
320
swf_FontPostprocess(SWF * swf)321 void swf_FontPostprocess(SWF*swf)
322 {
323 TAG*tag = swf->firstTag;
324 while(tag) {
325 TAG*next = tag->next;
326 if(tag->id == ST_DEFINEFONT3) {
327 U16 id = swf_GetDefineID(tag);
328 SWFFONT*font = 0;
329 swf_FontExtract(swf, id, &font);
330 if(!font->alignzones) {
331 swf_FontCreateAlignZones(font);
332 tag = swf_InsertTag(tag, ST_DEFINEFONTALIGNZONES);
333 swf_FontSetAlignZones(tag, font);
334 }
335 swf_FontFree(font);
336 }
337 tag = next;
338 }
339 }
340
swf_FontSetAlignZones(TAG * t,SWFFONT * f)341 void swf_FontSetAlignZones(TAG*t, SWFFONT *f)
342 {
343 swf_SetU16(t, f->id);
344 swf_SetU8(t, f->alignzone_flags);
345 int i;
346 for(i=0;i<f->numchars;i++) {
347 ALIGNZONE*a = &f->alignzones[i];
348 U8 flags = 0;
349 if((a->x & a->dx)!=0xffff)
350 flags |= 1;
351 if((a->y & a->dy)!=0xffff)
352 flags |= 2;
353 int num = 1;
354 if(a->dx != 0xffff || a->dy != 0xffff)
355 num++;
356 swf_SetU8(t, num);
357 if(flags&1) swf_SetU16(t, a->x); else swf_SetU16(t, 0);
358 if(flags&2) swf_SetU16(t, a->y); else swf_SetU16(t, 0);
359 if(num==2) {
360 if((flags&1) && a->dx!=0xffff) swf_SetU16(t, a->dx); else swf_SetU16(t, 0);
361 if((flags&2) && a->dy!=0xffff) swf_SetU16(t, a->dy); else swf_SetU16(t, 0);
362 }
363 swf_SetU8(t, flags);
364 }
365 }
366
367