X-Git-Url: http://git.asbjorn.biz/?p=swftools.git;a=blobdiff_plain;f=lib%2Fgfxpoly.c;h=76352af7d7861157a1dc8f08a4c59f38d9cbd6c9;hp=8b914a89c69a22882dab3a3e4b7753ab152d5a60;hb=c41f4433d3e721073c60d55cd923a087761e45f7;hpb=d403c5b5058c049a435bbc439d78f0a5aa35f3aa diff --git a/lib/gfxpoly.c b/lib/gfxpoly.c index 8b914a8..76352af 100644 --- a/lib/gfxpoly.c +++ b/lib/gfxpoly.c @@ -25,9 +25,15 @@ #include "gfxtools.h" #include "gfxpoly.h" #include "mem.h" +#ifdef INTERNAL_LIBART #include "art/libart.h" #include "art/art_svp_intersect.h" #include "art/art_svp_ops.h" +#else +#include +#include +#include +#endif #include "log.h" #include #include @@ -37,6 +43,303 @@ //#define SHEAR //#define DEBUG +//----------------------------------------- svp renderer ---------------------------------------- + +typedef struct { + int xmin; + int ymin; + int xmax; + int ymax; + int width; + int height; +} intbbox_t; + +typedef struct _renderpoint +{ + double x; + signed char direction; +} renderpoint_t; + +typedef struct _renderline +{ + renderpoint_t*points; + int size; + int num; +} renderline_t; + +typedef struct _renderbuf +{ + intbbox_t bbox; + int width; + int height; + double zoom; + renderline_t*lines; +} renderbuf_t; + +static inline void add_pixel(renderbuf_t*buf, double x, int y, signed char direction) +{ + renderpoint_t p; + p.x = x; + p.direction = direction; + + if(x >= buf->bbox.xmax || y >= buf->bbox.ymax || y < buf->bbox.ymin) + return; + renderline_t*l = &buf->lines[y-buf->bbox.ymin]; + + if(l->num == l->size) { + l->size += 32; + l->points = (renderpoint_t*)rfx_realloc(l->points, l->size * sizeof(renderpoint_t)); + } + l->points[l->num] = p; + l->num++; +} +#define CUT 0.5 +#define INT(x) ((int)((x)+16)-16) +static void add_line(renderbuf_t*buf, double x1, double y1, double x2, double y2, signed char direction) +{ + x1 *= buf->zoom; + y1 *= buf->zoom; + x2 *= buf->zoom; + y2 *= buf->zoom; + double diffx, diffy; + double ny1, ny2, stepx; + if(y2 < y1) { + double x,y; + x = x1;x1 = x2;x2=x; + y = y1;y1 = y2;y2=y; + } + diffx = x2 - x1; + diffy = y2 - y1; + ny1 = INT(y1)+CUT; + ny2 = INT(y2)+CUT; + + if(ny1 < y1) { + ny1 = INT(y1) + 1.0 + CUT; + } + if(ny2 >= y2) { + ny2 = INT(y2) - 1.0 + CUT; + } + if(ny1 > ny2) + return; + + stepx = diffx/diffy; + x1 = x1 + (ny1-y1)*stepx; + x2 = x2 + (ny2-y2)*stepx; + + int posy=INT(ny1); + int endy=INT(ny2); + double posx=0; + double startx = x1; + + while(posy<=endy) { + double xx = startx + posx; + add_pixel(buf, xx, posy, direction); + posx+=stepx; + posy++; + } +} + +static int compare_renderpoints(const void * _a, const void * _b) +{ + renderpoint_t*a = (renderpoint_t*)_a; + renderpoint_t*b = (renderpoint_t*)_b; + if(a->x < b->x) return -1; + if(a->x > b->x) return 1; + return 0; +} + +static void fill_bitwise(unsigned char*line, int x1, int x2) +{ + int p1 = x1>>3; + int p2 = x2>>3; + int b1 = 0xff >> (x1&7); + int b2 = 0xff << (1+7-(x2&7)); + if(p1==p2) { + line[p1] |= b1&b2; + } else { + line[p1] |= b1; + memset(&line[p1+1], 255, p2-(p1+1)); + line[p2] = b2; + } +} + +unsigned char* render_svp(ArtSVP*svp, intbbox_t*bbox, double zoom, ArtWindRule rule) +{ + renderbuf_t _buf, *buf=&_buf; + buf->width = (bbox->xmax - bbox->xmin); + buf->height = (bbox->ymax - bbox->ymin); + buf->bbox = *bbox; + buf->zoom = zoom; + int width8 = (buf->width+7) >> 3; + unsigned char* image = (unsigned char*)malloc(width8*buf->height); + memset(image, 0, width8*buf->height); + + buf->lines = (renderline_t*)rfx_alloc(buf->height*sizeof(renderline_t)); + int y; + for(y=0;yheight;y++) { + memset(&buf->lines[y], 0, sizeof(renderline_t)); + buf->lines[y].points = 0; + buf->lines[y].num = 0; + } + + int t; + for(t=0;tn_segs;t++) { + ArtSVPSeg* seg = &svp->segs[t]; + int s; + for(s=0;sn_points-1;s++) { + int dir = seg->dir? 1 : -1; + add_line(buf, seg->points[s].x, seg->points[s].y, seg->points[s+1].x, seg->points[s+1].y, dir); + } + } + for(y=0;yheight;y++) { + renderpoint_t*points = buf->lines[y].points; + unsigned char*line = &image[width8*y]; + int n; + int num = buf->lines[y].num; + int wind = 0; + qsort(points, num, sizeof(renderpoint_t), compare_renderpoints); + int lastx = 0; + int fill = 0; + + for(n=0;nx - bbox->xmin); + + if(x < lastx) + x = lastx; + if(x > buf->width) { + break; + } + if(fill && x!=lastx) { + fill_bitwise(line, lastx, x); + } + wind += p->direction; + if(rule == ART_WIND_RULE_INTERSECT) { + fill = wind>=2; + } else if (rule == ART_WIND_RULE_NONZERO) { + fill = wind!=0; + } else if (rule == ART_WIND_RULE_ODDEVEN) { + fill = wind&1; + } else { // rule == ART_WIND_RULE_POSITIVE + fill = wind>=1; + } + lastx = x; + } + if(fill && lastx!=buf->width) + fill_bitwise(line, lastx, buf->width); + } + + for(y=0;yheight;y++) { + if(buf->lines[y].points) { + free(buf->lines[y].points); + } + memset(&buf->lines[y], 0, sizeof(renderline_t)); + } + free(buf->lines);buf->lines=0; + return image; +} + +#define MAX_WIDTH 8192 +#define MAX_HEIGHT 4096 + +intbbox_t get_svp_bbox(ArtSVP*svp, double zoom) +{ + int t; + intbbox_t b = {0,0,0,0}; + if(svp->n_segs && svp->segs[0].n_points) { + b.xmin = svp->segs[0].points[0].x; + b.ymin = svp->segs[0].points[0].y; + b.xmax = svp->segs[0].points[0].x; + b.ymax = svp->segs[0].points[0].y; + } + for(t=0;tn_segs;t++) { + ArtSVPSeg* seg = &svp->segs[t]; + int s; + for(s=0;sn_points;s++) { + double x = seg->points[s].x*zoom; + double y = seg->points[s].y*zoom; + int x1 = floor(x); + int x2 = ceil(x); + int y1 = floor(y); + int y2 = ceil(y); + if(x1 < b.xmin) b.xmin = x1; + if(y1 < b.ymin) b.ymin = y1; + if(x2 > b.xmax) b.xmax = x2; + if(y2 > b.ymax) b.ymax = y2; + } + } + if(b.xmax > (int)(MAX_WIDTH*zoom)) + b.xmax = (int)(MAX_WIDTH*zoom); + if(b.ymax > (int)(MAX_HEIGHT*zoom)) + b.ymax = (int)(MAX_HEIGHT*zoom); + if(b.xmin < -(int)(MAX_WIDTH*zoom)) + b.xmin = -(int)(MAX_WIDTH*zoom); + if(b.ymin < -(int)(MAX_HEIGHT*zoom)) + b.ymin = -(int)(MAX_HEIGHT*zoom); + + if(b.xmin > b.xmax) + b.xmin = b.xmax; + if(b.ymin > b.ymax) + b.ymin = b.ymax; + + b.width = b.xmax - b.xmin; + b.height = b.ymax - b.ymin; + return b; +} + +#define B11100000 0xe0 +#define B01110000 0x70 +#define B00111000 0x38 +#define B00011100 0x1c +#define B00001110 0x0e +#define B00000111 0x07 +#define B10000000 0x80 +#define B01000000 0x40 +#define B00100000 0x20 +#define B00010000 0x10 +#define B00001000 0x08 +#define B00000100 0x04 +#define B00000010 0x02 +#define B00000001 0x01 + +int compare_bitmaps(intbbox_t*bbox, unsigned char*data1, unsigned char*data2) +{ + if(!data1 || !data2) + return 0; + int x,y; + int height = bbox->height; + int width = bbox->width; + int width8 = (width+7) >> 3; + unsigned char*l1 = &data1[width8]; + unsigned char*l2 = &data2[width8]; + for(y=1;ytype == gfx_moveTo) { vec[pos].code = ART_MOVETO_OPEN; vec[pos].x = l2->x; vec[pos].y = l2->y; + lastmove=pos; pos++; assert(pos<=len); } else if(l2->type == gfx_lineTo) { @@ -98,6 +403,16 @@ static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill) } x = l2->x; y = l2->y; + + /* let closed line segments start w/ MOVETO instead of MOVETO_OPEN */ + if(lastmove>=0 && l2->type!=gfx_moveTo && (!l2->next || l2->next->type == gfx_moveTo)) { + if(vec[lastmove].x == l2->x && + vec[lastmove].y == l2->y) { + assert(vec[lastmove].code == ART_MOVETO_OPEN); + vec[lastmove].code = ART_MOVETO; + } + } + l2 = l2->next; } vec[pos++].code = ART_END; @@ -122,7 +437,7 @@ static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill) } /* Find adjacent identical points. If an ajdacent pair of identical - points is found, the second is removed. + points is found, the second one is removed. So moveto x,y lineto x,y becomes moveto x,y lineto x,y lineto x,y becomes lineto x,y lineto x,y moveto x,y becomes lineto x,y @@ -165,7 +480,7 @@ static ArtVpath* gfxline_to_ArtVpath(gfxline_t*line, char fill) static void shear_svp(ArtSVP*svp, double v) { - /* do a "shearing on the polygon. We do this to eliminate all + /* do a "shearing" on the polygon. We do this to eliminate all horizontal lines (which libart can't handle properly, even though it tries). */ @@ -214,7 +529,13 @@ static double find_shear_value(ArtSVP*svp) } if(!fail) break; - v = lrand48() / 2000000000.0; +#ifdef HAVE_LRAND48 + v = lrand48() / 2000000000.0;; +#elif HAVE_RAND + v = rand() / 2000000000.0; +#else +#error "no lrand48()/rand() implementation found" +#endif tries++; } return v; @@ -347,7 +668,9 @@ int check_svp(ArtSVP*svp) ArtPoint* p2 = &seg->points[p+1]; if(p1->y >= p2->y) { - msg(" bad svp, y in seg is non-increasing %.16f -> %.16f\n", p1->y, p2->y); + if(p1->y > p2->y) { + msg(" bad svp, y in seg is non-increasing %.16f -> %.16f\n", p1->y, p2->y); + } } else { segs[num_segs].y1 = p1->y; segs[num_segs].y2 = p2->y; @@ -363,7 +686,7 @@ int check_svp(ArtSVP*svp) qsort(seg_start, num_segs, sizeof(svp_segment_part_t*), compare_seg_start); qsort(seg_end, num_segs, sizeof(svp_segment_part_t*), compare_seg_end); - double lasty = y[0]+1; + double lasty = num_points?y[0]+1:0; int num_active = 0; int bleed = 0; double bleedy1=0,bleedy2 = 0; @@ -393,7 +716,7 @@ int check_svp(ArtSVP*svp) lasty = y[t]; } if(bleed) { - msg(" svp bleeds from y=%.16f to y=%.16f (%d/%d active segments)\n", + msg(" svp bleeds from y=%.16f to y=%.16f (%d/%d active segments)\n", bleedy1, bleedy2, bleed, num_segs); free(y);free(segs);free(seg_start);free(seg_end); @@ -412,7 +735,6 @@ void write_svp_postscript(const char*filename, ArtSVP*svp) { if(!svp) return; - printf("writing %s\n", filename); FILE*fi = fopen(filename, "wb"); int i, j; double xmin=0,ymin=0,xmax=0,ymax=0; @@ -612,24 +934,34 @@ static ArtSVP* gfxfillToSVP(gfxline_t*line, int perturb) //#endif - -extern const ArtSVP* current_svp; -extern void art_report_error(); -extern int art_error_in_intersector; - ArtSVP* run_intersector(ArtSVP*svp, ArtWindRule rule) { ArtSvpWriter * swr = art_svp_writer_rewind_new(rule); + double zoom = 1.0; + intbbox_t bbox = get_svp_bbox(svp, zoom); + art_svp_intersector(svp, swr); ArtSVP*result = art_svp_writer_rewind_reap(swr); clean_svp(result); if(!check_svp(result)) { - msg(" Error in polygon processing"); current_svp = result; art_report_error(); // might set art_error_in_intersector + } else { + msg(" Comparing polygon renderings of size %dx%d and %dx%d", bbox.width, bbox.height, bbox.width, bbox.height); + unsigned char*data1 = render_svp(svp, &bbox, zoom, rule); + unsigned char*data2 = render_svp(result, &bbox, zoom, ART_WIND_RULE_ODDEVEN); + if(!compare_bitmaps(&bbox, data1, data2)) { + msg(" Bad SVP rewinding result- polygons don't match"); + current_svp = result; + art_report_error(); // might set art_error_in_intersector + } + free(data1); + free(data2); } + if(art_error_in_intersector) { + msg(" Error in polygon processing"); art_svp_free(result); art_error_in_intersector=0; return 0; @@ -675,7 +1007,7 @@ gfxline_t* gfxpoly_to_gfxline(gfxpoly_t*poly) gfxpoly_t* gfxpoly_fillToPoly(gfxline_t*line) { /* I'm not sure whether doing perturbation here is actually - a good idea- if that line has been run through the machine + a good idea- if that line has been run through the machinery several times already, it might be safer to leave it alone, since it should already be in a format libart can handle */ #ifdef PERTURBATE @@ -801,14 +1133,9 @@ gfxline_t* gfxline_circularToEvenOdd(gfxline_t*line) msg(" Converting circular-filled gfxline of %d segments to even-odd filled gfxline", gfxline_len(line)); ArtSVP* svp = gfxfillToSVP(line, 1); - /* TODO: ART_WIND_RULE_POSITIVE means that a shape is visible if - positive and negative line segments add up to something positive. - I *think* that clockwise fill in PDF is defined in a way, however, - that the *last* shape's direction will determine whether something - is filled */ ArtSVP* svp_rewinded; - svp_rewinded = run_intersector(svp, ART_WIND_RULE_POSITIVE); + svp_rewinded = run_intersector(svp, ART_WIND_RULE_NONZERO); if(!svp_rewinded) { art_svp_free(svp); return 0;