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