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