implemented stroke merging
[swftools.git] / lib / gfxpoly / convert.c
1 #include <stdlib.h>
2 #include <math.h>
3 #include <string.h>
4 #include "../gfxdevice.h"
5 #include "../mem.h"
6 #include "poly.h"
7 #include "convert.h"
8
9 /* factor that determines into how many line fragments a spline is converted */
10 #define SUBFRACTION (2.4)
11
12 static inline int32_t convert_coord(double x, double z)
13 {
14     /* we clamp to 31 bit instead of 32 bit because we use
15        a (x1-x2) shortcut when comparing coordinates
16     */
17     x *= z;
18     if(x < -0x40000000) x = -0x40000000;
19     if(x >  0x3fffffff) x =  0x3fffffff;
20     return ceil(x);
21 }
22
23 static void convert_gfxline(gfxline_t*line, polywriter_t*w, double gridsize)
24 {
25     assert(!line || line[0].type == gfx_moveTo);
26     double lastx=0,lasty=0;
27     double z = 1.0 / gridsize;
28     while(line) {
29         if(line->type == gfx_moveTo) {
30             if(line->next && line->next->type != gfx_moveTo && (line->x!=lastx || line->y!=lasty)) {
31                 w->moveto(w, convert_coord(line->x,z), convert_coord(line->y,z));
32             }
33         } else if(line->type == gfx_lineTo) {
34             w->lineto(w, convert_coord(line->x,z), convert_coord(line->y,z));
35         } else if(line->type == gfx_splineTo) {
36             int parts = (int)(sqrt(fabs(line->x-2*line->sx+lastx) + 
37                                    fabs(line->y-2*line->sy+lasty))*SUBFRACTION);
38             if(!parts) parts = 1;
39             double stepsize = 1.0/parts;
40             int i;
41             for(i=0;i<parts;i++) {
42                 double t = (double)i*stepsize;
43                 double sx = (line->x*t*t + 2*line->sx*t*(1-t) + lastx*(1-t)*(1-t));
44                 double sy = (line->y*t*t + 2*line->sy*t*(1-t) + lasty*(1-t)*(1-t));
45                 w->lineto(w, convert_coord(sx,z), convert_coord(sy,z));
46             }
47             w->lineto(w, convert_coord(line->x,z), convert_coord(line->y,z));
48         }
49         lastx = line->x;
50         lasty = line->y;
51         line = line->next;
52     }
53 }
54
55 static char* readline(FILE*fi)
56 {
57     char c;
58     while(1) {
59         int l = fread(&c, 1, 1, fi);
60         if(!l)
61             return 0;
62         if(c!=10 || c!=13)
63             break;
64     }
65     char line[256];
66     int pos = 0;
67     while(1) {
68         line[pos++] = c;
69         line[pos] = 0;
70         int l = fread(&c, 1, 1, fi);
71         if(!l || c==10 || c==13) {
72             return strdup(line);
73         }
74     }
75 }
76
77 static void convert_file(const char*filename, polywriter_t*w, double gridsize)
78 {
79     FILE*fi = fopen(filename, "rb");
80     if(!fi) {
81         perror(filename);
82     }
83     double z = 1.0 / gridsize;
84     int count = 0;
85     double g = 0;
86     double lastx=0,lasty=0;
87     while(1) {
88         char*line = readline(fi);
89         if(!line)
90             break;
91         double x,y;
92         char s[256];
93         if(sscanf(line, "%lf %lf %s", &x, &y, &s) == 3) {
94             if(s && !strcmp(s,"moveto")) {
95                 w->moveto(w, convert_coord(x,z), convert_coord(y,z));
96                 count++;
97             } else if(s && !strcmp(s,"lineto")) {
98                 w->lineto(w, convert_coord(x,z), convert_coord(y,z));
99                 count++;
100             } else {
101                 fprintf(stderr, "invalid command: %s\n", s);
102             }
103         } else if(sscanf(line, "%% gridsize %lf", &g) == 1) {
104             gridsize = g;
105             z = 1.0 / gridsize;
106             w->setgridsize(w, g);
107         }
108         free(line);
109     }
110     fclose(fi);
111     if(g) {
112         fprintf(stderr, "loaded %d points from %s (gridsize %f)\n", count, filename, g);
113     } else {
114         fprintf(stderr, "loaded %d points from %s\n", count, filename);
115     }
116 }
117
118 typedef struct _compactpoly {
119     gfxpoly_t*poly;
120     point_t last;
121     point_t*points;
122     int num_points;
123     int points_size;
124     segment_dir_t dir;
125     char new;
126 } compactpoly_t;
127
128 void finish_segment(compactpoly_t*data)
129 {
130     if(data->num_points <= 1)
131         return;
132     point_t*p = malloc(sizeof(point_t)*data->num_points);
133     gfxpolystroke_t*s = rfx_calloc(sizeof(gfxpolystroke_t));
134     s->next = data->poly->strokes;
135     data->poly->strokes = s;
136     s->num_points = s->points_size = data->num_points;
137     s->dir = data->dir;
138     s->points = p;
139     assert(data->dir != DIR_UNKNOWN);
140     if(data->dir == DIR_UP) {
141         int t;
142         int s = data->num_points;
143         for(t=0;t<data->num_points;t++) {
144             p[--s] = data->points[t];
145         }
146     } else {
147         memcpy(p, data->points, sizeof(point_t)*data->num_points);
148     }
149 #ifdef CHECKS
150     int t;
151     for(t=0;t<data->num_points-1;t++) {
152         assert(p[t].y<=p[t+1].y);
153     }
154 #endif
155 }
156 static void compactmoveto(polywriter_t*w, int32_t x, int32_t y)
157 {
158     compactpoly_t*data = (compactpoly_t*)w->internal;
159     point_t p;
160     p.x = x;
161     p.y = y;
162     if(p.x != data->last.x || p.y != data->last.y) {
163         data->new = 1;
164     }
165     data->last = p;
166 }
167 static void compactlineto(polywriter_t*w, int32_t x, int32_t y)
168 {
169     compactpoly_t*data = (compactpoly_t*)w->internal;
170     point_t p;
171     p.x = x;
172     p.y = y;
173     if(p.x == data->last.x && p.y == data->last.y)
174         return;
175
176     if(p.y < data->last.y && data->dir != DIR_UP ||
177        p.y > data->last.y && data->dir != DIR_DOWN || 
178        data->new) {
179         finish_segment(data);
180         data->dir = p.y > data->last.y ? DIR_DOWN : DIR_UP;
181         data->points[0] = data->last;
182         data->num_points = 1;
183     }
184     data->new = 0;
185
186     if(data->points_size == data->num_points) {
187         data->points_size <<= 1;
188         assert(data->points_size > data->num_points);
189         data->points = rfx_realloc(data->points, sizeof(point_t)*data->points_size);
190     }
191     data->points[data->num_points++] = p;
192     data->last = p;
193 }
194 static void compactsetgridsize(polywriter_t*w, double gridsize)
195 {
196     compactpoly_t*d = (compactpoly_t*)w->internal;
197     d->poly->gridsize = gridsize;
198 }
199 /*static int compare_stroke(const void*_s1, const void*_s2)
200 {
201     gfxpolystroke_t*s1 = (gfxpolystroke_t*)_s1;
202     gfxpolystroke_t*s2 = (gfxpolystroke_t*)_s2;
203     return s1->points[0].y - s2->points[0].y;
204 }*/
205 static void*compactfinish(polywriter_t*w)
206 {
207     compactpoly_t*data = (compactpoly_t*)w->internal;
208     finish_segment(data);
209     //qsort(data->poly->strokes, data->poly->num_strokes, sizeof(gfxpolystroke_t), compare_stroke);
210     free(data->points);
211     gfxpoly_t*poly = data->poly;
212     free(w->internal);w->internal = 0;
213     return (void*)poly;
214 }
215 void gfxpolywriter_init(polywriter_t*w)
216 {
217     w->moveto = compactmoveto;
218     w->lineto = compactlineto;
219     w->setgridsize = compactsetgridsize;
220     w->finish = compactfinish;
221     compactpoly_t*data = w->internal = rfx_calloc(sizeof(compactpoly_t));
222     data->poly = rfx_calloc(sizeof(gfxpoly_t));
223     data->poly->gridsize = 1.0;
224     data->last.x = data->last.y = 0;
225     data->num_points = 0;
226     data->points_size = 16;
227     data->new = 1;
228     data->dir = DIR_UNKNOWN;
229     data->points = (point_t*)rfx_alloc(sizeof(point_t)*data->points_size);
230     data->poly->strokes = 0;
231 }
232
233 gfxpoly_t* gfxpoly_from_gfxline(gfxline_t*line, double gridsize)
234 {
235     polywriter_t writer;
236     gfxpolywriter_init(&writer);
237     writer.setgridsize(&writer, gridsize);
238     convert_gfxline(line, &writer, gridsize);
239     return (gfxpoly_t*)writer.finish(&writer);
240 }
241 gfxpoly_t* gfxpoly_from_file(const char*filename, double gridsize)
242 {
243     polywriter_t writer;
244     gfxpolywriter_init(&writer);
245     writer.setgridsize(&writer, gridsize);
246     convert_file(filename, &writer, gridsize);
247     return (gfxpoly_t*)writer.finish(&writer);
248 }
249 void gfxpoly_destroy(gfxpoly_t*poly)
250 {
251     int t;
252     gfxpolystroke_t*stroke = poly->strokes;
253     while(stroke) {
254         gfxpolystroke_t*next = stroke->next;
255         free(stroke->points);
256         free(stroke);
257         stroke = next;
258     }
259     free(poly);
260 }
261