completed transition to smaller polygon struct
[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     int strokes_size;
122     point_t*points;
123     int num_points;
124     int points_size;
125     segment_dir_t dir;
126     char new;
127 } compactpoly_t;
128
129 void finish_segment(compactpoly_t*data)
130 {
131     if(data->num_points <= 1)
132         return;
133     if(data->poly->num_strokes == data->strokes_size) {
134         data->strokes_size <<= 1;
135         assert(data->strokes_size > data->poly->num_strokes);
136         data->poly->strokes = rfx_realloc(data->poly->strokes, sizeof(gfxpolystroke_t)*data->strokes_size);
137     }
138     point_t*p = malloc(sizeof(point_t)*data->num_points);
139     gfxpolystroke_t*s = &data->poly->strokes[data->poly->num_strokes];
140     s->num_points = data->num_points;
141     s->dir = data->dir;
142     s->points = p;
143     assert(data->dir != DIR_UNKNOWN);
144     if(data->dir == DIR_UP) {
145         int t;
146         int s = data->num_points;
147         for(t=0;t<data->num_points;t++) {
148             p[--s] = data->points[t];
149         }
150     } else {
151         memcpy(p, data->points, sizeof(point_t)*data->num_points);
152     }
153 #ifdef CHECKS
154     int t;
155     for(t=0;t<data->num_points-1;t++) {
156         assert(p[t].y<=p[t+1].y);
157     }
158 #endif
159     data->poly->num_strokes++;
160 }
161 static void compactmoveto(polywriter_t*w, int32_t x, int32_t y)
162 {
163     compactpoly_t*data = (compactpoly_t*)w->internal;
164     point_t p;
165     p.x = x;
166     p.y = y;
167     if(p.x != data->last.x || p.y != data->last.y) {
168         data->new = 1;
169     }
170     data->last = p;
171 }
172 static void compactlineto(polywriter_t*w, int32_t x, int32_t y)
173 {
174     compactpoly_t*data = (compactpoly_t*)w->internal;
175     point_t p;
176     p.x = x;
177     p.y = y;
178     if(p.x == data->last.x && p.y == data->last.y)
179         return;
180
181     if(p.y < data->last.y && data->dir != DIR_UP ||
182        p.y > data->last.y && data->dir != DIR_DOWN || 
183        data->new) {
184         finish_segment(data);
185         data->dir = p.y > data->last.y ? DIR_DOWN : DIR_UP;
186         data->points[0] = data->last;
187         data->num_points = 1;
188     }
189
190     if(data->points_size == data->num_points) {
191         data->points_size <<= 1;
192         assert(data->points_size > data->num_points);
193         data->points = rfx_realloc(data->points, sizeof(point_t)*data->points_size);
194     }
195     data->points[data->num_points++] = p;
196     data->last = p;
197 }
198 static void compactsetgridsize(polywriter_t*w, double gridsize)
199 {
200     compactpoly_t*d = (compactpoly_t*)w->internal;
201     d->poly->gridsize = gridsize;
202 }
203 /*static int compare_stroke(const void*_s1, const void*_s2)
204 {
205     gfxpolystroke_t*s1 = (gfxpolystroke_t*)_s1;
206     gfxpolystroke_t*s2 = (gfxpolystroke_t*)_s2;
207     return s1->points[0].y - s2->points[0].y;
208 }*/
209 static void*compactfinish(polywriter_t*w)
210 {
211     compactpoly_t*data = (compactpoly_t*)w->internal;
212     finish_segment(data);
213     data->poly->strokes = (gfxpolystroke_t*)rfx_realloc(data->poly->strokes, sizeof(gfxpolystroke_t)*data->poly->num_strokes);
214     //qsort(data->poly->strokes, data->poly->num_strokes, sizeof(gfxpolystroke_t), compare_stroke);
215     free(data->points);
216     gfxpoly_t*poly = data->poly;
217     free(w->internal);w->internal = 0;
218     return (void*)poly;
219 }
220 void gfxpolywriter_init(polywriter_t*w)
221 {
222     w->moveto = compactmoveto;
223     w->lineto = compactlineto;
224     w->setgridsize = compactsetgridsize;
225     w->finish = compactfinish;
226     compactpoly_t*data = w->internal = rfx_calloc(sizeof(compactpoly_t));
227     data->poly = rfx_calloc(sizeof(gfxpoly_t));
228     data->poly->gridsize = 1.0;
229     data->last.x = data->last.y = 0;
230     data->strokes_size = 16;
231     data->num_points = 0;
232     data->points_size = 16;
233     data->new = 1;
234     data->dir = DIR_UNKNOWN;
235     data->points = (point_t*)rfx_alloc(sizeof(point_t)*data->points_size);
236     data->poly->strokes = (gfxpolystroke_t*)rfx_alloc(sizeof(gfxpolystroke_t)*data->strokes_size);
237 }
238
239 gfxpoly_t* gfxpoly_from_gfxline(gfxline_t*line, double gridsize)
240 {
241     polywriter_t writer;
242     gfxpolywriter_init(&writer);
243     writer.setgridsize(&writer, gridsize);
244     convert_gfxline(line, &writer, gridsize);
245     return (gfxpoly_t*)writer.finish(&writer);
246 }
247 gfxpoly_t* gfxpoly_from_file(const char*filename, double gridsize)
248 {
249     polywriter_t writer;
250     gfxpolywriter_init(&writer);
251     writer.setgridsize(&writer, gridsize);
252     convert_file(filename, &writer, gridsize);
253     return (gfxpoly_t*)writer.finish(&writer);
254 }
255 void gfxpoly_destroy(gfxpoly_t*poly)
256 {
257     int t;
258     for(t=0;t<poly->num_strokes;t++) {
259         free(poly->strokes[t].points);
260         poly->strokes[t].points = 0;
261     }
262     free(poly->strokes);
263     free(poly);
264 }
265