fixed off-by-one bug
[swftools.git] / lib / gfxpoly.c
bloba687e63094cf553ac4ef1a50cdd3ece69b0b81c1
1 /* gfxpoly.c
3 Various boolean polygon functions.
5 Part of the swftools package.
7 Copyright (c) 2005 Matthias Kramm <kramm@quiss.org>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
23 #include "../config.h"
24 #include "gfxdevice.h"
25 #include "gfxtools.h"
26 #include "gfxpoly.h"
27 #include "mem.h"
28 #include "art/libart.h"
29 #include "art/art_svp_intersect.h"
30 #include "art/art_svp_ops.h"
31 #include "log.h"
32 #include <assert.h>
33 #include <memory.h>
34 #include <math.h>
36 #define PERTURBATE
37 //#define SHEAR
38 //#define DEBUG
40 //----------------------------------------- svp renderer ----------------------------------------
42 typedef struct {
43 int xmin;
44 int ymin;
45 int xmax;
46 int ymax;
47 int width;
48 int height;
49 } intbbox_t;
51 typedef struct _renderpoint
53 double x;
54 signed char direction;
55 } renderpoint_t;
57 typedef struct _renderline
59 renderpoint_t*points;
60 int size;
61 int num;
62 } renderline_t;
64 typedef struct _renderbuf
66 intbbox_t bbox;
67 int width;
68 int height;
69 double zoom;
70 renderline_t*lines;
71 } renderbuf_t;
73 static inline void add_pixel(renderbuf_t*buf, double x, int y, signed char direction)
75 renderpoint_t p;
76 p.x = x;
77 p.direction = direction;
79 if(x >= buf->bbox.xmax || y >= buf->bbox.ymax || y < buf->bbox.ymin)
80 return;
81 renderline_t*l = &buf->lines[y-buf->bbox.ymin];
83 if(l->num == l->size) {
84 l->size += 32;
85 l->points = (renderpoint_t*)rfx_realloc(l->points, l->size * sizeof(renderpoint_t));
87 l->points[l->num] = p;
88 l->num++;
90 #define CUT 0.5
91 #define INT(x) ((int)((x)+16)-16)
92 static void add_line(renderbuf_t*buf, double x1, double y1, double x2, double y2, signed char direction)
94 x1 *= buf->zoom;
95 y1 *= buf->zoom;
96 x2 *= buf->zoom;
97 y2 *= buf->zoom;
98 double diffx, diffy;
99 double ny1, ny2, stepx;
100 if(y2 < y1) {
101 double x,y;
102 x = x1;x1 = x2;x2=x;
103 y = y1;y1 = y2;y2=y;
105 diffx = x2 - x1;
106 diffy = y2 - y1;
107 ny1 = INT(y1)+CUT;
108 ny2 = INT(y2)+CUT;
110 if(ny1 < y1) {
111 ny1 = INT(y1) + 1.0 + CUT;
113 if(ny2 >= y2) {
114 ny2 = INT(y2) - 1.0 + CUT;
116 if(ny1 > ny2)
117 return;
119 stepx = diffx/diffy;
120 x1 = x1 + (ny1-y1)*stepx;
121 x2 = x2 + (ny2-y2)*stepx;
123 int posy=INT(ny1);
124 int endy=INT(ny2);
125 double posx=0;
126 double startx = x1;
128 while(posy<=endy) {
129 double xx = startx + posx;
130 add_pixel(buf, xx, posy, direction);
131 posx+=stepx;
132 posy++;
136 static int compare_renderpoints(const void * _a, const void * _b)
138 renderpoint_t*a = (renderpoint_t*)_a;
139 renderpoint_t*b = (renderpoint_t*)_b;
140 if(a->x < b->x) return -1;
141 if(a->x > b->x) return 1;
142 return 0;
145 static void fill_bitwise(unsigned char*line, int x1, int x2)
147 int p1 = x1>>3;
148 int p2 = x2>>3;
149 int b1 = 0xff >> (x1&7);
150 int b2 = 0xff << (1+7-(x2&7));
151 if(p1==p2) {
152 line[p1] |= b1&b2;
153 } else {
154 line[p1] |= b1;
155 memset(&line[p1+1], 255, p2-(p1+1));
156 line[p2] = b2;
160 unsigned char* render_svp(ArtSVP*svp, intbbox_t*bbox, double zoom, ArtWindRule rule)
162 renderbuf_t _buf, *buf=&_buf;
163 buf->width = (bbox->xmax - bbox->xmin);
164 buf->height = (bbox->ymax - bbox->ymin);
165 buf->bbox = *bbox;
166 buf->zoom = zoom;
167 int width8 = (buf->width+7) >> 3;
168 unsigned char* image = (unsigned char*)malloc(width8*buf->height);
169 memset(image, 0, width8*buf->height);
171 buf->lines = (renderline_t*)rfx_alloc(buf->height*sizeof(renderline_t));
172 int y;
173 for(y=0;y<buf->height;y++) {
174 memset(&buf->lines[y], 0, sizeof(renderline_t));
175 buf->lines[y].points = 0;
176 buf->lines[y].num = 0;
179 int t;
180 for(t=0;t<svp->n_segs;t++) {
181 ArtSVPSeg* seg = &svp->segs[t];
182 int s;
183 for(s=0;s<seg->n_points-1;s++) {
184 int dir = seg->dir? 1 : -1;
185 add_line(buf, seg->points[s].x, seg->points[s].y, seg->points[s+1].x, seg->points[s+1].y, dir);
188 for(y=0;y<buf->height;y++) {
189 renderpoint_t*points = buf->lines[y].points;
190 unsigned char*line = &image[width8*y];
191 int n;
192 int num = buf->lines[y].num;
193 int wind = 0;
194 qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
195 int lastx = 0;
196 int fill = 0;
198 for(n=0;n<num;n++) {
199 renderpoint_t*p = &points[n];
200 int x = (int)(p->x - bbox->xmin);
202 if(x < lastx)
203 x = lastx;
204 if(x > buf->width) {
205 break;
207 if(fill && x!=lastx) {
208 fill_bitwise(line, lastx, x);
210 wind += p->direction;
211 if(rule == ART_WIND_RULE_INTERSECT) {
212 fill = wind>=2;
213 } else if (rule == ART_WIND_RULE_NONZERO) {
214 fill = wind!=0;
215 } else if (rule == ART_WIND_RULE_ODDEVEN) {
216 fill = wind&1;
217 } else { // rule == ART_WIND_RULE_POSITIVE
218 fill = wind>=1;
220 lastx = x;
222 if(fill && lastx!=buf->width)
223 fill_bitwise(line, lastx, buf->width);
226 for(y=0;y<buf->height;y++) {
227 if(buf->lines[y].points) {
228 free(buf->lines[y].points);
230 memset(&buf->lines[y], 0, sizeof(renderline_t));
232 free(buf->lines);buf->lines=0;
233 return image;
236 #define MAX_WIDTH 8192
237 #define MAX_HEIGHT 4096
239 intbbox_t get_svp_bbox(ArtSVP*svp, double zoom)
241 int t;
242 intbbox_t b = {0,0,0,0};
243 if(svp->n_segs && svp->segs[0].n_points) {
244 b.xmin = svp->segs[0].points[0].x;
245 b.ymin = svp->segs[0].points[0].y;
246 b.xmax = svp->segs[0].points[0].x;
247 b.ymax = svp->segs[0].points[0].y;
249 for(t=0;t<svp->n_segs;t++) {
250 ArtSVPSeg* seg = &svp->segs[t];
251 int s;
252 for(s=0;s<seg->n_points;s++) {
253 double x = seg->points[s].x*zoom;
254 double y = seg->points[s].y*zoom;
255 int x1 = floor(x);
256 int x2 = ceil(x);
257 int y1 = floor(y);
258 int y2 = ceil(y);
259 if(x1 < b.xmin) b.xmin = x1;
260 if(y1 < b.ymin) b.ymin = y1;
261 if(x2 > b.xmax) b.xmax = x2;
262 if(y2 > b.ymax) b.ymax = y2;
265 if(b.xmax > (int)(MAX_WIDTH*zoom))
266 b.xmax = (int)(MAX_WIDTH*zoom);
267 if(b.ymax > (int)(MAX_HEIGHT*zoom))
268 b.ymax = (int)(MAX_HEIGHT*zoom);
269 if(b.xmin < -(int)(MAX_WIDTH*zoom))
270 b.xmin = -(int)(MAX_WIDTH*zoom);
271 if(b.ymin < -(int)(MAX_HEIGHT*zoom))
272 b.ymin = -(int)(MAX_HEIGHT*zoom);
274 if(b.xmin > b.xmax)
275 b.xmin = b.xmax;
276 if(b.ymin > b.ymax)
277 b.ymin = b.ymax;
279 b.width = b.xmax - b.xmin;
280 b.height = b.ymax - b.ymin;
281 return b;
284 #define B11100000 0xe0
285 #define B01110000 0x70
286 #define B00111000 0x38
287 #define B00011100 0x1c
288 #define B00001110 0x0e
289 #define B00000111 0x07
290 #define B10000000 0x80
291 #define B01000000 0x40
292 #define B00100000 0x20
293 #define B00010000 0x10
294 #define B00001000 0x08
295 #define B00000100 0x04
296 #define B00000010 0x02
297 #define B00000001 0x01
299 static int compare_bitmaps(intbbox_t*bbox, unsigned char*data1, unsigned char*data2)
301 if(!data1 || !data2)
302 return 0;
303 int x,y;
304 int height = bbox->height;
305 int width = bbox->width;
306 int width8 = (width+7) >> 3;
307 unsigned char*l1 = &data1[width8];
308 unsigned char*l2 = &data2[width8];
309 for(y=1;y<height-1;y++) {
310 for(x=0;x<width8;x++) {
311 unsigned a = l1[x-width8] & l1[x] & l1[x+width8];
312 unsigned b = l2[x-width8] & l2[x] & l2[x+width8];
314 if((a&B11100000) && !(l2[x]&B01000000)) return 0;
315 if((a&B01110000) && !(l2[x]&B00100000)) return 0;
316 if((a&B00111000) && !(l2[x]&B00010000)) return 0;
317 if((a&B00011100) && !(l2[x]&B00001000)) return 0;
318 if((a&B00001110) && !(l2[x]&B00000100)) return 0;
319 if((a&B00000111) && !(l2[x]&B00000010)) return 0;
321 if((b&B11100000) && !(l1[x]&B01000000)) return 0;
322 if((b&B01110000) && !(l1[x]&B00100000)) return 0;
323 if((b&B00111000) && !(l1[x]&B00010000)) return 0;
324 if((b&B00011100) && !(l1[x]&B00001000)) return 0;
325 if((b&B00001110) && !(l1[x]&B00000100)) return 0;
326 if((b&B00000111) && !(l1[x]&B00000010)) return 0;
328 l1 += width8;
329 l2 += width8;
331 return 1;
335 //-----------------------------------------------------------------------------------------------
337 static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill)
339 ArtVpath *vec = NULL;
340 int pos=0,len=0;
341 gfxline_t*l2;
342 double x=0,y=0;
344 /* factor which determines into how many line fragments a spline is converted */
345 double subfraction = 2.4;//0.3
347 l2 = line;
348 while(l2) {
349 if(l2->type == gfx_moveTo) {
350 pos ++;
351 } else if(l2->type == gfx_lineTo) {
352 pos ++;
353 } else if(l2->type == gfx_splineTo) {
354 int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
355 if(!parts) parts = 1;
356 pos += parts + 1;
358 x = l2->x;
359 y = l2->y;
360 l2 = l2->next;
362 pos++;
363 len = pos;
365 vec = art_new (ArtVpath, len+1);
367 pos = 0;
368 l2 = line;
369 int lastmove=-1;
370 while(l2) {
371 if(l2->type == gfx_moveTo) {
372 vec[pos].code = ART_MOVETO_OPEN;
373 vec[pos].x = l2->x;
374 vec[pos].y = l2->y;
375 lastmove=pos;
376 pos++;
377 assert(pos<=len);
378 } else if(l2->type == gfx_lineTo) {
379 vec[pos].code = ART_LINETO;
380 vec[pos].x = l2->x;
381 vec[pos].y = l2->y;
382 pos++;
383 assert(pos<=len);
384 } else if(l2->type == gfx_splineTo) {
385 int i;
386 int parts = (int)(sqrt(fabs(l2->x-2*l2->sx+x) + fabs(l2->y-2*l2->sy+y))*subfraction);
387 if(!parts) parts = 1;
388 double stepsize = 1.0/parts;
389 for(i=0;i<=parts;i++) {
390 double t = (double)i*stepsize;
391 vec[pos].code = ART_LINETO;
392 vec[pos].x = l2->x*t*t + 2*l2->sx*t*(1-t) + x*(1-t)*(1-t);
393 vec[pos].y = l2->y*t*t + 2*l2->sy*t*(1-t) + y*(1-t)*(1-t);
394 pos++;
395 assert(pos<=len);
398 x = l2->x;
399 y = l2->y;
401 /* let closed line segments start w/ MOVETO instead of MOVETO_OPEN */
402 if(lastmove>=0 && l2->type!=gfx_moveTo && (!l2->next || l2->next->type == gfx_moveTo)) {
403 if(vec[lastmove].x == l2->x &&
404 vec[lastmove].y == l2->y) {
405 assert(vec[lastmove].code == ART_MOVETO_OPEN);
406 vec[lastmove].code = ART_MOVETO;
410 l2 = l2->next;
412 vec[pos++].code = ART_END;
413 assert(pos == len);
415 if(!fill) {
416 /* Fix "dotted" lines. Those are lines where singular points are created
417 by a moveto x,y lineto x,y combination. We "fix" these by shifting the
418 point in the lineto a little bit to the right
419 These should only occur in strokes, not in fills, so do this only
420 when we know we're not filling.
422 int t;
423 for(t=0;vec[t].code!=ART_END;t++) {
424 if(t>0 && (vec[t-1].code==ART_MOVETO_OPEN || vec[t-1].code==ART_MOVETO)
425 && vec[t].code==ART_LINETO && vec[t+1].code!=ART_LINETO &&
426 vec[t-1].x == vec[t].x &&
427 vec[t-1].y == vec[t].y) {
428 vec[t].x += 0.01;
433 /* Find adjacent identical points. If an ajdacent pair of identical
434 points is found, the second one is removed.
435 So moveto x,y lineto x,y becomes moveto x,y
436 lineto x,y lineto x,y becomes lineto x,y
437 lineto x,y moveto x,y becomes lineto x,y
438 moveto x,y moveto x,y becomes moveto x,y
439 lineto x,y lineto x2,y2 becomes lineto x2,y2 (if dir(x,y) ~= dir(x2,y2))
441 pos = 0;
442 int outpos = 0;
443 while(1)
445 if(vec[pos].code == ART_END) {
446 vec[outpos++] = vec[pos++];
447 break;
450 char samedir = 0, samepoint = 0;
451 if(outpos) {
452 double dx = vec[pos].x-vec[pos-1].x;
453 double dy = vec[pos].y-vec[pos-1].y;
454 /*if(pos<len-1 && vec[pos].code == ART_LINETO && vec[pos+1].code == ART_LINETO) {
455 double dx2 = vec[pos+1].x-vec[pos].x;
456 double dy2 = vec[pos+1].y-vec[pos].y;
457 if(fabs(dx*dy2 - dy*dx2) < 0.0001 && dx*dx2 + dy*dy2 >= 0) {
458 samedir=1;
461 if(fabs(dx) + fabs(dy) < 0.0001) {
462 samepoint=1;
465 if(!samepoint && !samedir) {
466 vec[outpos++] = vec[pos++];
467 } else {
468 pos++; // skip
472 return vec;
475 static void shear_svp(ArtSVP*svp, double v)
477 /* do a "shearing" on the polygon. We do this to eliminate all
478 horizontal lines (which libart can't handle properly, even though
479 it tries). */
481 int t;
482 for(t=0;t<svp->n_segs;t++) {
483 ArtSVPSeg* seg = &svp->segs[t];
484 int s;
485 for(s=0;s<seg->n_points;s++) {
486 ArtPoint* point = &seg->points[s];
487 point->y += point->x*v;
492 static double find_shear_value(ArtSVP*svp)
494 /* We try random values until we find one
495 where there's no slope below a given value, or if that fails,
496 at least no slope of 0 */
498 double v = 0;
499 int tries = 0;
500 while(1) {
501 char fail = 0;
502 int t;
503 for(t=0;t<svp->n_segs;t++) {
504 ArtSVPSeg* seg = &svp->segs[t];
505 int s;
506 for(s=0;s<seg->n_points-1;s++) {
507 ArtPoint* p1 = &seg->points[s];
508 ArtPoint* p2 = &seg->points[s+1];
509 double dx = p2->x - p1->x;
510 double dy = p2->y - p1->y;
511 dy += dx*v;
512 if(dy==0) {
513 fail = 1;
514 break;
516 if(tries<100 && dx && fabs(dy / dx) < 1e-5) {
517 fail = 1;
518 break;
521 if(fail)
522 break;
524 if(!fail)
525 break;
526 #ifdef HAVE_LRAND48
527 v = lrand48() / 2000000000.0;;
528 #elif HAVE_RAND
529 v = rand() / 2000000000.0;
530 #else
531 #error "no lrand48()/rand() implementation found"
532 #endif
533 tries++;
535 return v;
538 void show_path(ArtSVP*path)
540 int t;
541 printf("Segments: %d\n", path->n_segs);
542 for(t=0;t<path->n_segs;t++) {
543 ArtSVPSeg* seg = &path->segs[t];
544 printf("Segment %d: %d points, %s, BBox: (%f,%f,%f,%f)\n",
545 t, seg->n_points, seg->dir==0?"UP ":"DOWN",
546 seg->bbox.x0, seg->bbox.y0, seg->bbox.x1, seg->bbox.y1);
547 int p;
548 for(p=0;p<seg->n_points;p++) {
549 ArtPoint* point = &seg->points[p];
550 printf(" (%f,%f)\n", point->x, point->y);
553 printf("\n");
557 typedef struct svp_segment_part
559 double y1;
560 double y2;
561 char active;
562 } svp_segment_part_t;
564 int compare_double(const void*_y1, const void*_y2)
566 const double*y1 = _y1;
567 const double*y2 = _y2;
568 if(*y1<*y2) return -1;
569 if(*y1>*y2) return 1;
570 else return 0;
573 int compare_seg_start(const void*_s1, const void*_s2)
575 svp_segment_part_t*s1 = *(svp_segment_part_t**)_s1;
576 svp_segment_part_t*s2 = *(svp_segment_part_t**)_s2;
577 if(s1->y1 < s2->y1) return -1;
578 if(s1->y1 > s2->y1) return 1;
579 else return 0;
582 int compare_seg_end(const void*_s1, const void*_s2)
584 svp_segment_part_t*s1 = *(svp_segment_part_t**)_s1;
585 svp_segment_part_t*s2 = *(svp_segment_part_t**)_s2;
586 if(s1->y2 < s2->y2) return -1;
587 if(s1->y2 > s2->y2) return 1;
588 else return 0;
591 void clean_svp(ArtSVP*svp)
593 int t;
594 int oldpoints = 0;
595 int newpoints = 0;
596 int oldsegs = 0;
597 int newsegs = 0;
598 for(t=0;t<svp->n_segs;t++) {
599 ArtSVPSeg* seg = &svp->segs[t];
600 int p;
601 int pos=0;
602 double lasty = 0;
603 oldpoints += seg->n_points;
604 for(p=0;p<seg->n_points;p++) {
605 ArtPoint* p1 = &seg->points[p];
606 if(!p || lasty!=p1->y) {
607 seg->points[pos] = seg->points[p];
608 pos++;
609 lasty = p1->y;
612 seg->n_points = pos;
613 newpoints += seg->n_points;
615 int pos = 0;
616 oldsegs = svp->n_segs;
617 for(t=0;t<svp->n_segs;t++) {
618 if(svp->segs[t].n_points > 1) {
619 svp->segs[pos++] = svp->segs[t];
622 svp->n_segs = pos;
623 newsegs = svp->n_segs;
624 if(newsegs < oldsegs || newpoints < oldpoints) {
625 msg("<verbose> Simplified polygon from %d points to %d points, %d to %d segs",
626 oldpoints, newpoints, oldsegs, newsegs);
630 int check_svp(ArtSVP*svp)
632 /* count number of y coordinates and segs */
633 int t;
634 int num_points = 0;
635 int num_segs = 0;
636 for(t=0;t<svp->n_segs;t++) {
637 if(!svp->segs[t].n_points) {
638 msg("<error> svp contains segment with zero points\n");
639 return 0;
641 num_points += svp->segs[t].n_points;
642 num_segs += svp->segs[t].n_points - 1;
645 /* create segs and ys */
646 double*y = malloc(sizeof(double)*num_points);
647 svp_segment_part_t*segs = malloc(sizeof(svp_segment_part_t)*num_segs);
648 svp_segment_part_t**seg_start = malloc(sizeof(svp_segment_part_t*)*num_segs);
649 svp_segment_part_t**seg_end = malloc(sizeof(svp_segment_part_t*)*num_segs);
651 int c1=0;
652 num_segs = 0;
653 for(t=0;t<svp->n_segs;t++) {
654 ArtSVPSeg* seg = &svp->segs[t];
655 int p;
656 for(p=0;p<seg->n_points;p++) {
657 y[c1++] = seg->points[p].y;
658 assert(c1 <= num_points);
660 for(p=0;p<seg->n_points-1;p++) {
661 ArtPoint* p1 = &seg->points[p];
662 ArtPoint* p2 = &seg->points[p+1];
664 if(p1->y >= p2->y) {
665 if(p1->y > p2->y) {
666 msg("<error> bad svp, y in seg is non-increasing %.16f -> %.16f\n", p1->y, p2->y);
668 } else {
669 segs[num_segs].y1 = p1->y;
670 segs[num_segs].y2 = p2->y;
671 segs[num_segs].active = 0;
672 seg_start[num_segs] = &segs[num_segs];
673 seg_end[num_segs] = &segs[num_segs];
674 num_segs++;
679 qsort(y, num_points, sizeof(double), compare_double);
680 qsort(seg_start, num_segs, sizeof(svp_segment_part_t*), compare_seg_start);
681 qsort(seg_end, num_segs, sizeof(svp_segment_part_t*), compare_seg_end);
683 double lasty = num_points?y[0]+1:0;
684 int num_active = 0;
685 int bleed = 0;
686 double bleedy1=0,bleedy2 = 0;
687 for(t=0;t<num_points;t++) {
688 if(lasty == y[t])
689 continue;
690 int s;
691 for(s=0;s<num_segs;s++) {
692 if(segs[s].y1==y[t]) {
693 /* segment is starting */
694 segs[s].active = 1;
695 num_active++;
696 } else if(segs[s].y2==y[t]) {
697 segs[s].active = 0;
698 num_active--;
701 if(num_active&1) {
702 bleed = num_active;
703 bleedy1 = y[t];
704 } else {
705 if(bleed) {
706 bleedy2 = y[t];
707 break;
710 lasty = y[t];
712 if(bleed) {
713 msg("<verbose> svp bleeds from y=%.16f to y=%.16f (%d/%d active segments)\n",
714 bleedy1, bleedy2,
715 bleed, num_segs);
716 free(y);free(segs);free(seg_start);free(seg_end);
717 return 0;
720 free(y);
721 free(segs);
722 free(seg_start);
723 free(seg_end);
725 return 1;
728 void write_svp_postscript(const char*filename, ArtSVP*svp)
730 if(!svp)
731 return;
732 FILE*fi = fopen(filename, "wb");
733 int i, j;
734 double xmin=0,ymin=0,xmax=0,ymax=0;
735 fprintf(fi, "%% begin\n");
736 for (i = 0; i < svp->n_segs; i++) {
737 for (j = 0; j < svp->segs[i].n_points; j++) {
738 double x = svp->segs[i].points[j].x;
739 double y = svp->segs[i].points[j].y;
740 if(i==0 && j==0) {
741 xmin = xmax = x;
742 ymin = ymax = y;
743 } else {
744 if(x < xmin) xmin = x;
745 if(x > xmax) xmax = x;
746 if(y < ymin) ymin = y;
747 if(y > ymax) ymax = y;
751 if(xmax == xmin) xmax=xmin+1;
752 if(ymax == ymin) ymax=ymin+1;
754 for (i = 0; i < svp->n_segs; i++)
756 fprintf(fi, "%g setgray\n", svp->segs[i].dir ? 0.7 : 0);
757 for (j = 0; j < svp->segs[i].n_points; j++)
759 //fprintf(fi, "%g %g %s\n",
760 // 20 + 550*(svp->segs[i].points[j].x-xmin)/(xmax-xmin),
761 // 820 - 800*(svp->segs[i].points[j].y-ymin)/(ymax-ymin),
762 // j ? "lineto" : "moveto");
763 fprintf(fi, "%.32f %.32f %s\n",
764 svp->segs[i].points[j].x,
765 svp->segs[i].points[j].y,
766 j ? "lineto" : "moveto");
768 fprintf(fi, "stroke\n");
771 fprintf(fi, "showpage\n");
772 fclose(fi);
775 void write_vpath_postscript(const char*filename, ArtVpath*path)
777 FILE*fi = fopen(filename, "wb");
778 int i, j;
779 double xmin=0,ymin=0,xmax=0,ymax=0;
780 fprintf(fi, "%% begin\n");
781 ArtVpath*p = path;
782 char first = 1;
783 while(p->code != ART_END) {
784 if(p->code == ART_MOVETO || p->code == ART_MOVETO_OPEN) {
785 if(!first)
786 fprintf(fi, "stroke\n");
787 first = 0;
788 fprintf(fi, "0.0 setgray\n");
789 fprintf(fi, "%.32f %.32f moveto\n", p->x, p->y);
790 } else {
791 fprintf(fi, "%.32f %.32f lineto\n", p->x, p->y);
793 p++;
795 if(!first)
796 fprintf(fi, "stroke\n");
797 fprintf(fi, "showpage\n");
798 fclose(fi);
801 void write_gfxline_postscript(const char*filename, gfxline_t*line)
803 FILE*fi = fopen(filename, "wb");
804 int i, j;
805 fprintf(fi, "%% begin\n");
806 char first = 1;
807 while(line) {
808 if(line->type == gfx_moveTo) {
809 if(!first)
810 fprintf(fi, "stroke\n");
811 first = 0;
812 fprintf(fi, "0.0 setgray\n");
813 fprintf(fi, "%.32f %.32f moveto\n", line->x, line->y);
814 } else {
815 fprintf(fi, "%.32f %.32f lineto\n", line->x, line->y);
817 line = line->next;
819 if(!first)
820 fprintf(fi, "stroke\n");
821 fprintf(fi, "showpage\n");
822 fclose(fi);
825 static int vpath_len(ArtVpath*svp)
827 int len = 0;
828 while(svp->code != ART_END) {
829 svp ++;
830 len ++;
832 return len;
835 int gfxline_len(gfxline_t*line)
837 gfxline_t*i = line;
838 int len = 0;
839 while(i) {
840 len ++;
841 i = i->next;
843 return len;
846 static ArtVpath*pvpath= 0;
847 static int cmppos(const void*_p1, const void*_p2)
849 int*p1 = (int*)_p1;
850 int*p2 = (int*)_p2;
851 ArtVpath*v1 = &pvpath[*p1];
852 ArtVpath*v2 = &pvpath[*p2];
853 if(v1->y < v2->y)
854 return -1;
855 else if(v1->y > v2->y)
856 return 1;
857 else if(v1->x < v2->x)
858 return -2;
859 else if(v1->x > v2->x)
860 return 2;
861 else
862 return 0;
865 #define PERTURBATION 2e-3
866 static void my_perturb(ArtVpath*vpath)
868 int t;
869 int len = vpath_len(vpath);
870 int*pos = (int*)malloc(len*sizeof(int));
871 for(t=0;t<len;t++)
872 pos[t] = t;
873 pvpath = vpath;
874 qsort(pos, len, sizeof(int), cmppos);
875 t=0;
876 while(t<len) {
877 int t2=t+1;
878 while(t2<len && !cmppos(&pos[t],&pos[t2])) {
879 t2++;
882 double dx = (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5;
883 double dy = (PERTURBATION * rand ()) / RAND_MAX - PERTURBATION * 0.5;
884 int s;
885 for(s=t;s<t2;s++) {
886 vpath[pos[s]].x += dx;
887 vpath[pos[s]].y += dy;
889 t = t2;
891 free(pos);
892 pvpath = 0;
895 static ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb)
897 ArtVpath* vec = gfxline_to_ArtVpath(line, 1);
898 msg("<verbose> Casting gfxline of %d segments (%d line segments) to a gfxpoly", gfxline_len(line), vpath_len(vec));
900 if(perturb) {
901 //ArtVpath* vec2 = art_vpath_perturb(vec);
902 //free(vec);
903 //vec = vec2;
904 my_perturb(vec);
906 ArtSVP *svp = art_svp_from_vpath(vec);
907 free(vec);
909 return svp;
912 //#ifdef SHEAR
913 // double shear = find_shear_value(svp);
914 // gfxline_t*line = gfxline_from_gfxpoly((gfxpoly_t*)svp);
915 // gfxline_t*l = line;
916 // while(l) {
917 // l->y += l->x*shear;
918 // l->sy += l->sx*shear;
919 // l= l->next;
920 // }
921 // svp = (ArtSVP*)gfxpoly_fillToPoly(line);
922 // printf("shearing svp by %.16f\n", shear);
923 //#endif
924 // ....
925 //#ifdef SHEAR
926 // art_svp_free(svp);
927 // shear_svp(result, -shear);
928 //#endif
931 ArtSVP* run_intersector(ArtSVP*svp, ArtWindRule rule)
933 ArtSvpWriter * swr = art_svp_writer_rewind_new(rule);
935 double zoom = 1.0;
936 intbbox_t bbox = get_svp_bbox(svp, zoom);
938 art_svp_intersector(svp, swr);
939 ArtSVP*result = art_svp_writer_rewind_reap(swr);
940 clean_svp(result);
941 if(!check_svp(result)) {
942 current_svp = result;
943 art_report_error(); // might set art_error_in_intersector
944 } else {
945 msg("<verbose> Comparing polygon renderings of size %dx%d and %dx%d", bbox.width, bbox.height, bbox.width, bbox.height);
946 unsigned char*data1 = render_svp(svp, &bbox, zoom, rule);
947 unsigned char*data2 = render_svp(result, &bbox, zoom, ART_WIND_RULE_ODDEVEN);
948 if(!compare_bitmaps(&bbox, data1, data2)) {
949 msg("<verbose> Bad SVP rewinding result- polygons don't match");
950 current_svp = result;
951 art_report_error(); // might set art_error_in_intersector
953 free(data1);
954 free(data2);
957 if(art_error_in_intersector) {
958 msg("<verbose> Error in polygon processing");
959 art_svp_free(result);
960 art_error_in_intersector=0;
961 return 0;
963 return result;
966 gfxline_t* gfxline_from_gfxpoly(gfxpoly_t*poly)
968 ArtSVP*svp = (ArtSVP*)poly;
969 int size = 0;
970 int t;
971 int pos = 0;
973 msg("<verbose> Casting polygon of %d segments back to gfxline", svp->n_segs);
975 for(t=0;t<svp->n_segs;t++) {
976 size += svp->segs[t].n_points;
978 size = size + 1;
979 gfxline_t* lines = (gfxline_t*)rfx_alloc(sizeof(gfxline_t)*size);
981 for(t=0;t<svp->n_segs;t++) {
982 ArtSVPSeg* seg = &svp->segs[t];
983 int p;
984 for(p=0;p<seg->n_points;p++) {
985 lines[pos].type = p==0?gfx_moveTo:gfx_lineTo;
986 ArtPoint* point = &seg->points[p];
987 lines[pos].x = point->x;
988 lines[pos].y = point->y;
989 lines[pos].next = &lines[pos+1];
990 pos++;
993 if(pos) {
994 lines[pos-1].next = 0;
995 return lines;
996 } else {
997 return 0;
1001 gfxpoly_t* gfxpoly_from_fill(gfxline_t*line, double gridsize)
1003 /* I'm not sure whether doing perturbation here is actually
1004 a good idea- if that line has been run through the machinery
1005 several times already, it might be safer to leave it alone,
1006 since it should already be in a format libart can handle */
1007 #ifdef PERTURBATE
1008 ArtSVP* svp = gfxfillToSVP(line, 1);
1009 #else
1010 ArtSVP* svp = gfxfillToSVP(line, 0);
1011 #endif
1013 #ifdef DEBUG
1014 char filename[80];
1015 static int counter = 0;
1016 sprintf(filename, "svp%d.ps", counter);
1017 write_svp_postscript(filename, svp);
1018 sprintf(filename, "gfxline%d.ps", counter);
1019 write_gfxline_postscript(filename, line);
1020 #endif
1022 /* we do xor-filling by default, so dir is always 1
1023 (actually for oddeven rewinding it makes no difference, but
1024 it's "cleaner")
1026 int t;
1027 for(t=0; t<svp->n_segs; t++) {
1028 svp->segs[t].dir = 1;
1031 /* for some reason, we need to rewind / self-intersect the polygons that gfxfillToSVP
1032 returns- art probably uses a different fill mode (circular?) for vpaths */
1033 ArtSVP*svp_uncrossed=0;
1035 #ifdef DEBUG
1036 sprintf(filename, "svp%d_in.ps", counter);
1037 write_svp_postscript(filename, svp);
1038 counter++;
1039 #endif
1041 svp_uncrossed = run_intersector(svp, ART_WIND_RULE_ODDEVEN);
1043 art_svp_free(svp);
1044 svp=svp_uncrossed;
1046 return (gfxpoly_t*)svp;
1049 gfxpoly_t* gfxpoly_intersect(gfxpoly_t*poly1, gfxpoly_t*poly2)
1051 ArtSvpWriter *swr;
1053 static int counter = 0;
1055 ArtSVP* svp1 = (ArtSVP*)poly1;
1056 ArtSVP* svp2 = (ArtSVP*)poly2;
1057 msg("<verbose> Intersecting two polygons of %d and %d segments", svp1->n_segs, svp2->n_segs);
1059 #ifdef DEBUG
1060 char filename[80];
1061 sprintf(filename, "isvp%d_src1.ps", counter);
1062 write_svp_postscript(filename, svp1);
1063 sprintf(filename, "isvp%d_src2.ps", counter);
1064 write_svp_postscript(filename, svp2);
1065 #endif
1067 ArtSVP* svp3 = art_svp_merge (svp1, svp2);
1069 #ifdef DEBUG
1070 sprintf(filename, "isvp%d_src.ps", counter);
1071 write_svp_postscript(filename, svp3);
1072 #endif
1074 //write_svp_postscript("svp.ps", svp3);
1075 ArtSVP*svp_new = run_intersector(svp3, ART_WIND_RULE_INTERSECT);
1077 art_free (svp3); /* shallow free because svp3 contains shared segments */
1079 #ifdef DEBUG
1080 sprintf(filename, "isvp%d.ps", counter);
1081 write_svp_postscript(filename, svp_new);
1082 #endif
1084 counter++;
1086 //write_svp_postscript("svp_new.ps", svp_new);
1088 return (gfxpoly_t*)svp_new;
1091 gfxpoly_t* gfxpoly_union(gfxpoly_t*poly1, gfxpoly_t*poly2)
1093 ArtSVP* svp1 = (ArtSVP*)poly1;
1094 ArtSVP* svp2 = (ArtSVP*)poly2;
1095 msg("<verbose> Unifying two polygons of %d and %d segments", svp1->n_segs, svp2->n_segs);
1097 ArtSVP* svp = art_svp_union(svp1, svp2);
1098 return (gfxpoly_t*)svp;
1101 gfxpoly_t* gfxpoly_from_stroke(gfxline_t*line, gfxcoord_t width, gfx_capType cap_style, gfx_joinType joint_style, double miterLimit, double gridsize)
1103 ArtVpath* vec = gfxline_to_ArtVpath(line, 0);
1104 msg("<verbose> Casting gfxline of %d segments to a stroke-polygon", gfxline_len(line));
1106 ArtVpath* vec2 = art_vpath_perturb(vec);
1107 free(vec);
1108 vec = vec2;
1110 ArtSVP *svp = art_svp_vpath_stroke (vec,
1111 (joint_style==gfx_joinMiter)?ART_PATH_STROKE_JOIN_MITER:
1112 ((joint_style==gfx_joinRound)?ART_PATH_STROKE_JOIN_ROUND:
1113 ((joint_style==gfx_joinBevel)?ART_PATH_STROKE_JOIN_BEVEL:ART_PATH_STROKE_JOIN_BEVEL)),
1114 (cap_style==gfx_capButt)?ART_PATH_STROKE_CAP_BUTT:
1115 ((cap_style==gfx_capRound)?ART_PATH_STROKE_CAP_ROUND:
1116 ((cap_style==gfx_capSquare)?ART_PATH_STROKE_CAP_SQUARE:ART_PATH_STROKE_CAP_SQUARE)),
1117 width, //line_width
1118 miterLimit, //miter_limit
1119 0.05 //flatness
1121 free(vec);
1122 return (gfxpoly_t*)svp;
1125 gfxline_t* gfxpoly_circular_to_evenodd(gfxline_t*line, double gridsize)
1127 msg("<verbose> Converting circular-filled gfxline of %d segments to even-odd filled gfxline", gfxline_len(line));
1128 ArtSVP* svp = gfxfillToSVP(line, 1);
1130 ArtSVP* svp_rewinded;
1132 svp_rewinded = run_intersector(svp, ART_WIND_RULE_NONZERO);
1133 if(!svp_rewinded) {
1134 art_svp_free(svp);
1135 return 0;
1138 gfxline_t* result = gfxline_from_gfxpoly((gfxpoly_t*)svp_rewinded);
1139 art_svp_free(svp);
1140 art_svp_free(svp_rewinded);
1141 return result;
1144 gfxpoly_t* gfxpoly_createbox(double x1, double y1,double x2, double y2, double gridsize)
1146 ArtVpath *vec = art_new (ArtVpath, 5+1);
1147 vec[0].code = ART_MOVETO;
1148 vec[0].x = x1;
1149 vec[0].y = y1;
1150 vec[1].code = ART_LINETO;
1151 vec[1].x = x1;
1152 vec[1].y = y2;
1153 vec[2].code = ART_LINETO;
1154 vec[2].x = x2;
1155 vec[2].y = y2;
1156 vec[3].code = ART_LINETO;
1157 vec[3].x = x2;
1158 vec[3].y = y1;
1159 vec[4].code = ART_LINETO;
1160 vec[4].x = x1;
1161 vec[4].y = y1;
1162 vec[5].code = ART_END;
1163 vec[5].x = 0;
1164 vec[5].y = 0;
1165 ArtSVP *svp = art_svp_from_vpath(vec);
1166 free(vec);
1167 return (gfxpoly_t*)svp;
1170 void gfxpoly_destroy(gfxpoly_t*poly)
1172 ArtSVP*svp = (ArtSVP*)poly;
1173 art_svp_free(svp);