renamed arts.{c,h} to polyops.{c,h}
[swftools.git] / lib / devices / artsutils.c
1 #include "../../config.h"
2 #include "../rfxswf.h"
3 #include "../gfxdevice.h"
4 #include "../gfxtools.h"
5 #include "../art/libart.h"
6 #include "artsutils.h"
7 #include <assert.h>
8 #include <math.h>
9
10 ArtVpath* gfxline_to_ArtVpath(gfxline_t*line)
11 {
12     ArtVpath *vec = NULL;
13     int pos=0,len=0;
14     gfxline_t*l2;
15     double x=0,y=0;
16
17     /* factor which determines into how many line fragments a spline is converted */
18     double subfraction = 2.4;//0.3
19
20     l2 = line;
21     while(l2) {
22         if(l2->type == gfx_moveTo) {
23             pos ++;
24         } if(l2->type == gfx_lineTo) {
25             pos ++;
26         } if(l2->type == gfx_splineTo) {
27             int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
28             if(!parts) parts = 1;
29             pos += parts + 1;
30         }
31         x = l2->x;
32         y = l2->y;
33         l2 = l2->next;
34     }
35     pos++;
36     len = pos;
37
38     vec = art_new (ArtVpath, len+1);
39
40     pos = 0;
41     l2 = line;
42     while(l2) {
43         if(l2->type == gfx_moveTo) {
44             vec[pos].code = ART_MOVETO;
45             vec[pos].x = l2->x;
46             vec[pos].y = l2->y;
47             pos++; 
48             assert(pos<=len);
49         } else if(l2->type == gfx_lineTo) {
50             vec[pos].code = ART_LINETO;
51             vec[pos].x = l2->x;
52             vec[pos].y = l2->y;
53             pos++; 
54             assert(pos<=len);
55         } else if(l2->type == gfx_splineTo) {
56             int i;
57             int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
58             double stepsize = parts?1.0/parts:0;
59             for(i=0;i<=parts;i++) {
60                 double t = (double)i*stepsize;
61                 vec[pos].code = ART_LINETO;
62                 vec[pos].x = l2->x*t*t + 2*l2->sx*t*(1-t) + x*(1-t)*(1-t);
63                 vec[pos].y = l2->y*t*t + 2*l2->sy*t*(1-t) + y*(1-t)*(1-t);
64                 pos++;
65                 assert(pos<=len);
66             }
67         }
68         x = l2->x;
69         y = l2->y;
70         l2 = l2->next;
71     }
72     vec[pos].code = ART_END;
73     
74     /* fix "dotted" lines */
75     int t;
76     char linepending=0;
77     for(t=0;vec[t].code!=ART_END;t++) {
78         if(t>0 && vec[t-1].code==ART_MOVETO && vec[t].code==ART_LINETO 
79                 && vec[t+1].code!=ART_LINETO
80             && vec[t-1].x == vec[t].x
81             && vec[t-1].y == vec[t].y) {
82             vec[t].x += 0.01;
83         }
84         if(vec[t].code==ART_MOVETO)
85             linepending=0;
86         x = vec[t].x;
87         y = vec[t].y;
88     }
89
90     // Spot adjacent identical points
91     t = 1;
92     while(t < pos)
93     {
94         if ((vec[t-1].x == vec[t].x) && (vec[t-1].y == vec[t].y)) {
95             // adjacent identical points; remove one
96             memcpy(&(vec[t]), &(vec[t + 1]), sizeof(vec[t]) * (pos - t));
97             pos--;
98         } else {
99             t++;
100         }
101     }
102
103     /* adjacency remover disabled for now, pending code inspection */
104     return vec;
105
106     // Check for further non-adjacent identical points. We don't want any
107     // points other than the first and last points to exactly match.
108     //
109     // If we do find matching points, move the second point slightly. This
110     // currently moves the duplicate 2% towards the midpoint of its neighbours
111     // (easier to calculate than 2% down the perpendicular to the line joining
112     // the neighbours) but limiting the change to 0.1 twips to avoid visual
113     // problems when the shapes are large. Note that there is no minimum
114     // change: if the neighbouring points are colinear and equally spaced,
115     // e.g. they were generated as part of a straight spline, then the
116     // duplicate point may not actually move.
117     //
118     // The scan for duplicates algorithm is quadratic in the number of points:
119     // there's probably a better method but the numbers of points is generally
120     // small so this will do for now.
121     {
122         int i = 1, j;
123         for(; i < (pos - 1); ++i)
124         {
125             for (j = 0; j < i; ++j)
126             {
127                 if ((vec[i].x == vec[j].x)
128                     && (vec[i].y == vec[j].y))
129                 {
130                     // points match; shuffle point
131                     double dx = (vec[i-1].x + vec[i+1].x - (vec[i].x * 2.0)) / 100.0;
132                     double dy = (vec[i-1].y + vec[i+1].y - (vec[i].y * 2.0)) / 100.0;
133                     double dxxyy = (dx*dx) + (dy*dy);
134                     if (dxxyy > 0.01)
135                     {
136                         // This is more than 0.1 twip's distance; scale down
137                         double dscale = sqrt(dxxyy) * 10.0;
138                         dx /= dscale;
139                         dy /= dscale;
140                     };
141                     vec[i].x += dx;
142                     vec[i].y += dy;
143                     break;
144                 }
145             }
146         }
147     }
148
149     return vec;
150 }
151
152 void show_path(ArtSVP*path)
153 {
154     int t;
155     printf("Segments: %d\n", path->n_segs);
156     for(t=0;t<path->n_segs;t++) {
157         ArtSVPSeg* seg = &path->segs[t];
158         printf("Segment %d: %d points, %s, BBox: (%f,%f,%f,%f)\n", 
159                 t, seg->n_points, seg->dir==0?"UP  ":"DOWN",
160                 seg->bbox.x0, seg->bbox.y0, seg->bbox.x1, seg->bbox.y1);
161         int p;
162         for(p=0;p<seg->n_points;p++) {
163             ArtPoint* point = &seg->points[p];
164             printf("        (%f,%f)\n", point->x, point->y);
165         }
166     }
167     printf("\n");
168 }
169
170 ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
171 {
172     ArtVpath* vec = gfxline_to_ArtVpath(line);
173     if(perturb) {
174         ArtVpath* vec2 = art_vpath_perturb(vec);
175         free(vec);
176         vec = vec2;
177     }
178     ArtSVP *svp = art_svp_from_vpath(vec);
179     free(vec);
180
181     // We need to make sure that the SVP we now have bounds an area (i.e. the
182     // source line wound anticlockwise) rather than excludes an area (i.e. the
183     // line wound clockwise). It seems that PDF (or xpdf) is less strict about
184     // this for bitmaps than it is for fill areas.
185     //
186     // To check this, we'll sum the cross products of all pairs of adjacent
187     // lines. If the result is positive, the direction is correct; if not, we
188     // need to reverse the sense of the SVP generated. The easiest way to do
189     // this is to flip the up/down flags of all the segments.
190     //
191     // This is approximate; since the gfxline_t structure can contain any
192     // combination of moveTo, lineTo and splineTo in any order, not all pairs
193     // of lines in the shape that share a point need be described next to each
194     // other in the sequence. For ease, we'll consider only pairs of lines
195     // stored as lineTos and splineTos without intervening moveTos.
196     //
197     // TODO is this a valid algorithm? My vector maths is rusty.
198     //
199     // It may be more correct to instead reverse the line before we feed it
200     // into gfxfilltoSVP. However, this seems equivalent and is easier to
201     // implement!
202     double total_cross_product = 0.0;
203     gfxline_t* cursor = line;
204     if (cursor != NULL)
205     {
206         double x_last = cursor->x;
207         double y_last = cursor->y;
208         cursor = cursor->next;
209
210         while((cursor != NULL) && (cursor->next != NULL))
211         {
212             if (((cursor->type == gfx_lineTo) || (cursor->type == gfx_splineTo))
213                 && ((cursor->next->type == gfx_lineTo) || (cursor->next->type == gfx_splineTo)))
214             {
215                 // Process these lines
216                 //
217                 // In this space (x right, y down) the cross-product is
218                 // (x1 * y0) - (x0 * y1)
219                 double x0 = cursor->x - x_last;
220                 double y0 = cursor->y - y_last;
221                 double x1 = cursor->next->x - cursor->x;
222                 double y1 = cursor->next->y - cursor->y;
223                 total_cross_product += (x1 * y0) - (x0 * y1);
224             }
225
226             x_last = cursor->x;
227             y_last = cursor->y;
228             cursor = cursor->next;
229         }
230     }
231     if (total_cross_product < 0.0)
232     {
233         int i = 0;
234         for(; i < svp->n_segs; ++i)
235         {
236             if (svp->segs[i].dir != 0)
237                 svp->segs[i].dir = 0;
238             else
239                 svp->segs[i].dir = 1;
240         }
241     }
242     return svp;
243 }
244 ArtSVP* boxToSVP(double x1, double y1,double x2, double y2)
245 {
246     ArtVpath *vec = art_new (ArtVpath, 5+1);
247     vec[0].code = ART_MOVETO;
248     vec[0].x = x1;
249     vec[0].y = y1;
250     vec[1].code = ART_LINETO;
251     vec[1].x = x1;
252     vec[1].y = y2;
253     vec[2].code = ART_LINETO;
254     vec[2].x = x2;
255     vec[2].y = y2;
256     vec[3].code = ART_LINETO;
257     vec[3].x = x2;
258     vec[3].y = y1;
259     vec[4].code = ART_LINETO;
260     vec[4].x = x1;
261     vec[4].y = y1;
262     vec[5].code = ART_END;
263     vec[5].x = 0;
264     vec[5].y = 0;
265     ArtSVP *svp = art_svp_from_vpath(vec);
266     free(vec);
267     return svp;
268 }
269
270 ArtSVP* gfxstrokeToSVP(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit)
271 {
272     ArtVpath* vec = gfxline_to_ArtVpath(line);
273
274     ArtSVP *svp = art_svp_vpath_stroke (vec,
275                         (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
276                         ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
277                          ((joint_style==gfx_joinBevel)?ART_PATH_STROKE_JOIN_BEVEL:ART_PATH_STROKE_JOIN_BEVEL)),
278                         (cap_style==gfx_capButt)?ART_PATH_STROKE_CAP_BUTT:
279                         ((cap_style==gfx_capRound)?ART_PATH_STROKE_CAP_ROUND:
280                          ((cap_style==gfx_capSquare)?ART_PATH_STROKE_CAP_SQUARE:ART_PATH_STROKE_CAP_SQUARE)),
281                         width, //line_width
282                         miterLimit, //miter_limit
283                         0.05 //flatness
284                         );
285     free(vec);
286     return svp;
287 }
288
289 gfxline_t* SVPtogfxline(ArtSVP*svp)
290 {
291     int size = 0;
292     int t;
293     int pos = 0;
294     for(t=0;t<svp->n_segs;t++) {
295         size += svp->segs[t].n_points + 1;
296     }
297     gfxline_t* lines = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)*size);
298
299     for(t=0;t<svp->n_segs;t++) {
300         ArtSVPSeg* seg = &svp->segs[t];
301         int p;
302         for(p=0;p<seg->n_points;p++) {
303             lines[pos].type = p==0?gfx_moveTo:gfx_lineTo;
304             ArtPoint* point = &seg->points[p];
305             lines[pos].x = point->x;
306             lines[pos].y = point->y;
307             lines[pos].next = &lines[pos+1];
308             pos++;
309         }
310     }
311     if(pos) {
312         lines[pos-1].next = 0;
313         return lines;
314     } else {
315         return 0;
316     }
317 }