replaced libart with new polygon code
[swftools.git] / lib / devices / polyops.c
index 1a71c1f..e6dd671 100644 (file)
 #include <unistd.h>
 #endif
 #include <memory.h>
+#include <assert.h>
 #include <string.h>
 #include <math.h>
 #include "../mem.h"
 #include "../gfxdevice.h"
 #include "../gfxtools.h"
 #include "../gfxpoly.h"
+#include "../log.h"
 #include "polyops.h"
 
 typedef struct _clip {
     gfxpoly_t*poly;
+    int openclips;
     struct _clip*next;
 } clip_t;
 
@@ -42,6 +45,9 @@ typedef struct _internal {
     gfxdevice_t*out;
     clip_t*clip;
     gfxpoly_t*polyunion;
+    
+    int good_polygons;
+    int bad_polygons;
 } internal_t;
 
 static int verbose = 0;
@@ -54,7 +60,7 @@ static void dbg(char*format, ...)
     int l;
     va_list arglist;
     va_start(arglist, format);
-    vsprintf(buf, format, arglist);
+    vsnprintf(buf, sizeof(buf)-1, format, arglist);
     va_end(arglist);
     l = strlen(buf);
     while(l && buf[l-1]=='\n') {
@@ -85,19 +91,56 @@ void polyops_startclip(struct _gfxdevice*dev, gfxline_t*line)
     dbg("polyops_startclip");
     internal_t*i = (internal_t*)dev->internal;
 
-    gfxpoly_t* poly = gfxpoly_fillToPoly(line);
-
-    if(i->clip) {
-       gfxpoly_t*old = i->clip->poly;
-       clip_t*n = i->clip;
-       i->clip = (clip_t*)rfx_calloc(sizeof(clip_t));
-       i->clip->next = n;
-       i->clip->poly = gfxpoly_intersect(poly, old);
-       gfxpoly_free(poly);
-    } else {
-       i->clip = (clip_t*)rfx_calloc(sizeof(clip_t));
-       i->clip->poly = poly;
+    gfxpoly_t* oldclip = i->clip?i->clip->poly:0;
+    gfxpoly_t* poly = gfxpoly_from_fill(line, DEFAULT_GRID);
+    if(poly) 
+        i->good_polygons++;
+    else
+        i->bad_polygons++;
+
+    gfxpoly_t* currentclip = 0;
+    int type = 0;
+
+    /* we can't rely on gfxpoly actually being able to convert
+       a gfxline into a gfxpoly- for polygons which are too
+       complex or just degenerate, this might fail. So handle
+       all the cases where polygon conversion or intersection
+       might go awry */
+    if(!poly && !oldclip) {
+       i->out->startclip(i->out,line);
+       currentclip = 0;
+       type = 1;
+    } else if(!poly && oldclip) {
+       gfxline_t*oldclipline = gfxline_from_gfxpoly(oldclip);
+       i->out->startclip(i->out,oldclipline);
+       i->out->startclip(i->out,line);
+       currentclip = 0;
+       type = 2;
+    } else if(poly && oldclip) {
+       gfxpoly_t*intersection = gfxpoly_intersect(poly, oldclip);
+       if(intersection) {
+            i->good_polygons++;
+           // this case is what usually happens 
+           gfxpoly_destroy(poly);poly=0;
+           currentclip = intersection;
+           type = 0;
+       } else {
+            i->bad_polygons++;
+           gfxline_t*oldclipline = gfxline_from_gfxpoly(oldclip);
+           i->out->startclip(i->out, oldclipline);
+           currentclip = poly;
+           type = 1;
+       }
+    } else if(poly && !oldclip) {
+       currentclip = poly;
+       type = 0;
     }
+
+    clip_t*n = i->clip;
+    i->clip = (clip_t*)rfx_calloc(sizeof(clip_t));
+    i->clip->next = n;
+    i->clip->poly = currentclip;
+    i->clip->openclips = type;
 }
 
 void polyops_endclip(struct _gfxdevice*dev)
@@ -105,42 +148,91 @@ void polyops_endclip(struct _gfxdevice*dev)
     dbg("polyops_endclip");
     internal_t*i = (internal_t*)dev->internal;
 
-    if(i->clip) {
-       clip_t*old = i->clip;
-       i->clip = i->clip->next;
-       gfxpoly_free(old->poly);old->poly = 0;
-       old->next = 0;free(old);
-    } else {
-       fprintf(stderr, "Error: endclip without startclip\n");
+    if(!i->clip) {
+       msg("<error> endclip without startclip (in: polyops)\n");
+       return;
+    }
+
+    clip_t*old = i->clip;
+    i->clip = i->clip->next;
+    if(old->poly) {
+       gfxpoly_destroy(old->poly);old->poly = 0;
     }
+    int t;
+    for(t=0;t<old->openclips;t++)
+       i->out->endclip(i->out);
+
+    old->next = 0;free(old);
 }
 
 static void addtounion(struct _gfxdevice*dev, gfxpoly_t*poly)
 {
     internal_t*i = (internal_t*)dev->internal;
-    if(i->polyunion) {
+    if(poly && i->polyunion) {
        gfxpoly_t*old = i->polyunion;
-       i->polyunion = gfxpoly_union(poly,i->polyunion);
-       gfxpoly_free(old);
+       gfxpoly_t*newpoly = gfxpoly_union(poly,i->polyunion);
+       i->polyunion = newpoly;
+       gfxpoly_destroy(old);
     }
 }
 
-void polyops_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
+static gfxline_t* handle_poly(gfxdevice_t*dev, gfxpoly_t*poly, char*ok)
 {
-    dbg("polyops_stroke");
     internal_t*i = (internal_t*)dev->internal;
-    //i->out->stroke(i->out, line, width, color, cap_style, joint_style, miterLimit);
-    gfxpoly_t* poly = gfxpoly_strokeToPoly(line, width, cap_style, joint_style, miterLimit);
-    if(i->clip) {
+    if(i->clip && i->clip->poly) {
        gfxpoly_t*old = poly;
-       poly = gfxpoly_intersect(poly, i->clip->poly);
-       gfxpoly_free(old);
+       if(poly) {
+           poly = gfxpoly_intersect(poly, i->clip->poly);
+           gfxpoly_destroy(old);
+       }
     }
+
+    if(poly) 
+        i->good_polygons++;
+    else
+        i->bad_polygons++;
+
     addtounion(dev, poly);
-    gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
-    if(i->out) i->out->fill(i->out, gfxline, color);
-    gfxline_free(gfxline);
-    gfxpoly_free(poly);
+    gfxline_t*gfxline = 0;
+    if(poly) {
+       // this is the case where everything went right
+       gfxline_t*line = gfxline_from_gfxpoly(poly);
+       gfxpoly_destroy(poly);
+        *ok = 1;
+       return line;
+    } else {
+       if(i->clip && i->clip->poly) {
+           /* convert current clipping from a polygon to an
+              actual "startclip" written to the output */
+           assert(i->clip->openclips <= 1);
+           gfxline_t*clipline = gfxline_from_gfxpoly(i->clip->poly);
+           i->out->startclip(i->out, clipline);
+           gfxline_free(clipline);
+           gfxpoly_destroy(i->clip->poly);i->clip->poly = 0;
+           i->clip->openclips++;
+           return 0;
+       } else {
+           return 0;
+       }
+    }
+}
+
+void polyops_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
+{
+    dbg("polyops_stroke");
+    internal_t*i = (internal_t*)dev->internal;
+
+    gfxpoly_t* poly = gfxpoly_from_stroke(line, width, cap_style, joint_style, miterLimit, DEFAULT_GRID);
+    char ok = 0;
+    gfxline_t*line2 = handle_poly(dev, poly, &ok);
+
+    if(ok) {
+       if(i->out && line2) i->out->fill(i->out, line2, color);
+       gfxline_free(line2);
+    } else {
+        msg("<error> ..");
+       if(i->out) i->out->stroke(i->out, line, width, color, cap_style, joint_style, miterLimit);
+    }
 }
 
 void polyops_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
@@ -148,53 +240,50 @@ void polyops_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
     dbg("polyops_fill");
     internal_t*i = (internal_t*)dev->internal;
 
-    gfxpoly_t*poly = gfxpoly_fillToPoly(line);
+    gfxpoly_t*poly = gfxpoly_from_fill(line, DEFAULT_GRID);
+    char ok = 0;
+    gfxline_t*line2 = handle_poly(dev, poly, &ok);
 
-    if(i->clip) {
-       gfxpoly_t*old = poly;
-       poly  = gfxpoly_intersect(poly, i->clip->poly);
-       gfxpoly_free(poly);
+    if(ok) {
+       if(i->out && line2) i->out->fill(i->out, line2, color);
+       gfxline_free(line2);
+    } else {
+       if(i->out) i->out->fill(i->out, line, color);
     }
-    addtounion(dev,poly);
-    gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
-    if(i->out) i->out->fill(i->out, gfxline, color);
-    gfxline_free(gfxline);
-    gfxpoly_free(poly);
 }
 
 void polyops_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
 {
     dbg("polyops_fillbitmap");
     internal_t*i = (internal_t*)dev->internal;
-    gfxpoly_t* poly = gfxpoly_fillToPoly(line);
+    
+    gfxpoly_t*poly = gfxpoly_from_fill(line, DEFAULT_GRID);
+    char ok = 0;
+    gfxline_t*line2 = handle_poly(dev, poly, &ok);
 
-    if(i->clip) {
-       gfxpoly_t*old = poly;
-       poly = gfxpoly_intersect(poly, i->clip->poly);
-       gfxpoly_free(old);
+    if(ok) {
+       if(i->out && line2) i->out->fillbitmap(i->out, line2, img, matrix, cxform);
+       gfxline_free(line2);
+    } else {
+       if(i->out) i->out->fillbitmap(i->out, line, img, matrix, cxform);
     }
-    addtounion(dev,poly);
-    gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
-    if(i->out) i->out->fillbitmap(i->out, gfxline, img, matrix, cxform);
-    gfxline_free(gfxline);
-    gfxpoly_free(poly);
 }
 
 void polyops_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
 {
     dbg("polyops_fillgradient");
     internal_t*i = (internal_t*)dev->internal;
-    gfxpoly_t* poly  = gfxpoly_fillToPoly(line);
-    if(i->clip) {
-       gfxpoly_t*old = poly;
-       poly  = gfxpoly_intersect(poly, i->clip->poly);
-       gfxpoly_free(old);
+    
+    gfxpoly_t*poly = gfxpoly_from_fill(line, DEFAULT_GRID);
+    char ok = 0;
+    gfxline_t*line2 = handle_poly(dev, poly, &ok);
+
+    if(ok) {
+       if(i->out && line2) i->out->fillgradient(i->out, line2, gradient, type, matrix);
+       gfxline_free(line2);
+    } else {
+       if(i->out) i->out->fillgradient(i->out, line, gradient, type, matrix);
     }
-    addtounion(dev,poly);
-    gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
-    if(i->out) i->out->fillgradient(i->out, gfxline, gradient, type, matrix);
-    gfxline_free(gfxline);
-    gfxpoly_free(poly);
 }
 
 void polyops_addfont(struct _gfxdevice*dev, gfxfont_t*font)
@@ -213,24 +302,26 @@ void polyops_drawchar(struct _gfxdevice*dev, gfxfont_t*font, int glyphnr, gfxcol
     gfxline_t*glyph = gfxline_clone(font->glyphs[glyphnr].line);
     gfxline_transform(glyph, matrix);
 
-    if(i->clip) {
+    if(i->clip && i->clip->poly) {
        gfxbbox_t bbox = gfxline_getbbox(glyph);
-       gfxpoly_t*dummybox = gfxpoly_createbox(bbox.xmin,bbox.ymin,bbox.xmax,bbox.ymax);
-       gfxpoly_t*poly = gfxpoly_intersect(dummybox, i->clip->poly);
-       gfxline_t*gfxline = gfxpoly_to_gfxline(poly);
-       gfxbbox_t bbox2 = gfxline_getbbox(gfxline);
-       double w = bbox2.xmax - bbox2.xmin;
-       double h = bbox2.ymax - bbox2.ymin;
-       
-       addtounion(dev, poly); // TODO: use the whole char, not just the bbox?
-
-       if(w < 0.001 || h < 0.001) /* character was clipped completely */ {
-       } else if(fabs((bbox.xmax - bbox.xmin) - w) > 0.05 ||
-                  fabs((bbox.ymax - bbox.ymin) - h) > 0.05) {
-           /* notable change in character size: character was clipped 
-              TODO: handle diagonal cuts
-            */
-           polyops_fill(dev, glyph, color);
+       gfxpoly_t*dummybox = gfxpoly_createbox(bbox.xmin,bbox.ymin,bbox.xmax,bbox.ymax, DEFAULT_GRID);
+
+        char ok=0;
+       gfxline_t*gfxline = handle_poly(dev, dummybox, &ok);
+       if(ok) {
+           gfxbbox_t bbox2 = gfxline_getbbox(gfxline);
+           double w = bbox2.xmax - bbox2.xmin;
+           double h = bbox2.ymax - bbox2.ymin;
+           if(w < 0.001 || h < 0.001) /* character was clipped completely */ {
+           } else if(fabs((bbox.xmax - bbox.xmin) - w) > 0.05 ||
+                     fabs((bbox.ymax - bbox.ymin) - h) > 0.05) {
+               /* notable change in character size: character was clipped 
+                  TODO: how to deal with diagonal cuts?
+                */
+               polyops_fill(dev, glyph, color);
+           } else {
+               if(i->out) i->out->drawchar(i->out, font, glyphnr, color, matrix);
+           }
        } else {
            if(i->out) i->out->drawchar(i->out, font, glyphnr, color, matrix);
        }
@@ -259,6 +350,15 @@ gfxresult_t* polyops_finish(struct _gfxdevice*dev)
 {
     dbg("polyops_finish");
     internal_t*i = (internal_t*)dev->internal;
+
+
+    if(i->polyunion) {
+       gfxpoly_destroy(i->polyunion);i->polyunion=0;
+    } else {
+        if(i->bad_polygons) {
+            msg("<notice> --flatten success rate: %.1f%% (%d failed polygons)", i->good_polygons*100.0 / (i->good_polygons + i->bad_polygons), i->bad_polygons);
+        }
+    }
     if(i->out) {
        return i->out->finish(i->out);
     } else {
@@ -269,7 +369,7 @@ gfxresult_t* polyops_finish(struct _gfxdevice*dev)
 gfxline_t*gfxdevice_union_getunion(struct _gfxdevice*dev)
 {
     internal_t*i = (internal_t*)dev->internal;
-    return gfxpoly_to_gfxline(i->polyunion);
+    return gfxline_from_gfxpoly(i->polyunion);
 }
 
 void gfxdevice_removeclippings_init(gfxdevice_t*dev, gfxdevice_t*out)
@@ -325,6 +425,7 @@ void gfxdevice_union_init(gfxdevice_t*dev,gfxdevice_t*out)
     dev->finish = polyops_finish;
 
     i->out = out;
-    i->polyunion = gfxpoly_strokeToPoly(0, 0, gfx_capButt, gfx_joinMiter, 0);
+    /* create empty polygon */
+    i->polyunion = gfxpoly_from_stroke(0, 0, gfx_capButt, gfx_joinMiter, 0, DEFAULT_GRID);
 }