added fastlz support in record device
[swftools.git] / lib / devices / render.c
index 20c4dd8..250356a 100644 (file)
 #include <memory.h>
 #include "../gfxdevice.h"
 #include "../gfxtools.h"
-#include "../png.h"
 #include "../mem.h"
-
-typedef unsigned int U32;
-typedef unsigned char U8;
+#include "../types.h"
+#include "../png.h"
+#include "../log.h"
+#include "render.h"
 
 typedef gfxcolor_t RGBA;
 
@@ -47,6 +47,7 @@ typedef struct _renderline
 typedef struct _internal_result {
     gfximage_t img;
     struct _internal_result*next;
+    char palette;
 } internal_result_t;
 
 typedef struct _clipbuffer {
@@ -64,6 +65,9 @@ typedef struct _internal {
     int antialize;
     int zoom;
     int ymin, ymax;
+    int fillwhite;
+
+    char palette;
 
     RGBA* img;
 
@@ -75,15 +79,16 @@ typedef struct _internal {
     internal_result_t*result_next;
 } internal_t;
 
-typedef enum {filltype_solid,filltype_clip,filltype_bitmap} filltype_t;
+typedef enum {filltype_solid,filltype_clip,filltype_bitmap,filltype_gradient} filltype_t;
 
 typedef struct _fillinfo {
-    filltype_t type; //0=solid,1=clip
+    filltype_t type;
     gfxcolor_t*color;
     gfximage_t*image;
     gfxmatrix_t*matrix;
     gfxcxform_t*cxform;
-    char clip;
+    RGBA*gradient;
+    char linear_or_radial;
 } fillinfo_t;
 
 
@@ -269,16 +274,16 @@ static void fill_line_solid(RGBA*line, U32*z, int y, int x1, int x2, RGBA col)
 
     if(col.a!=255) {
         int ainv = 255-col.a;
-        col.r = (col.r*col.a)>>8;
-        col.g = (col.g*col.a)>>8;
-        col.b = (col.b*col.a)>>8;
-        col.a = 255;
+        col.r = (col.r*col.a)/255;
+        col.g = (col.g*col.a)/255;
+        col.b = (col.b*col.a)/255;
         do {
            if(z[bitpos]&bit) {
-               line[x].r = ((line[x].r*ainv)>>8)+col.r;
-               line[x].g = ((line[x].g*ainv)>>8)+col.g;
-               line[x].b = ((line[x].b*ainv)>>8)+col.b;
-               line[x].a = 255;
+               line[x].r = ((line[x].r*ainv)/255)+col.r;
+               line[x].g = ((line[x].g*ainv)/255)+col.g;
+               line[x].b = ((line[x].b*ainv)/255)+col.b;
+               //line[x].a = 255;
+               line[x].a = ((line[x].a*ainv)/255)+col.a;
            }
            bit <<= 1;
            if(!bit) {
@@ -305,7 +310,7 @@ static void fill_line_bitmap(RGBA*line, U32*z, int y, int x1, int x2, fillinfo_t
     gfxmatrix_t*m = info->matrix;
     gfximage_t*b = info->image;
     
-    if(!b->width || !b->height) {
+    if(!b || !b->width || !b->height) {
        gfxcolor_t red = {255,255,0,0};
         fill_line_solid(line, z, y, x1, x2, red);
         return;
@@ -332,7 +337,7 @@ static void fill_line_bitmap(RGBA*line, U32*z, int y, int x1, int x2, fillinfo_t
            int yy = (int)(yy1 - x * yinc1);
            int ainv;
 
-           if(info->clip) {
+           if(info->linear_or_radial) {
                if(xx<0) xx=0;
                if(xx>=b->width) xx = b->width-1;
                if(yy<0) yy=0;
@@ -348,9 +353,65 @@ static void fill_line_bitmap(RGBA*line, U32*z, int y, int x1, int x2, fillinfo_t
            ainv = 255-col.a;
 
            /* needs bitmap with premultiplied alpha */
-           line[x].r = ((line[x].r*ainv)>>8)+col.r;
-           line[x].g = ((line[x].g*ainv)>>8)+col.g;
-           line[x].b = ((line[x].b*ainv)>>8)+col.b;
+           line[x].r = ((line[x].r*ainv)/255)+col.r;
+           line[x].g = ((line[x].g*ainv)/255)+col.g;
+           line[x].b = ((line[x].b*ainv)/255)+col.b;
+           line[x].a = 255;
+       }
+       bit <<= 1;
+       if(!bit) {
+           bit = 1;bitpos++;
+       }
+    } while(++x<x2);
+}
+
+static void fill_line_gradient(RGBA*line, U32*z, int y, int x1, int x2, fillinfo_t*info)
+{
+    int x = x1;
+
+    gfxmatrix_t*m = info->matrix;
+    RGBA*g= info->gradient;
+    
+    double det = m->m00*m->m11 - m->m01*m->m10;
+    if(fabs(det) < 0.0005) { 
+       /* x direction equals y direction */
+       return;
+    }
+    
+    det = 1.0/det;
+    double xx1 =  (  (-m->tx) * m->m11 - (y - m->ty) * m->m10) * det;
+    double yy1 =  (- (-m->tx) * m->m01 + (y - m->ty) * m->m00) * det;
+    double xinc1 = m->m11 * det;
+    double yinc1 = m->m01 * det;
+    
+    U32 bit = 1<<(x1&31);
+    int bitpos = (x1/32);
+
+    do {
+       if(z[bitpos]&bit) {
+           RGBA col;
+           int ainv;
+
+            int pos = 0;
+            if(info->linear_or_radial) {
+                double xx = xx1 + x * xinc1;
+                double yy = yy1 + y * yinc1;
+                double r = sqrt(xx*xx + yy*yy);
+                if(r>1) r = 1;
+                pos = (int)(r*255.999);
+            } else {
+                double r = xx1 + x * xinc1;
+                if(r>1) r = 1;
+                if(r<-1) r = -1;
+                pos = (int)((r+1)*127.999);
+            }
+           col = g[pos];
+           ainv = 255-col.a;
+
+           /* needs bitmap with premultiplied alpha */
+           line[x].r = ((line[x].r*ainv)/255)+col.r;
+           line[x].g = ((line[x].g*ainv)/255)+col.g;
+           line[x].b = ((line[x].b*ainv)/255)+col.b;
            line[x].a = 255;
        }
        bit <<= 1;
@@ -384,7 +445,8 @@ void fill_line(gfxdevice_t*dev, RGBA*line, U32*zline, int y, int startx, int end
        fill_line_clip(line, zline, y, startx, endx);
     else if(fill->type == filltype_bitmap)
        fill_line_bitmap(line, zline, y, startx, endx, fill);
-    // etc.
+    else if(fill->type == filltype_gradient)
+       fill_line_gradient(line, zline, y, startx, endx, fill);
 }
 
 void fill(gfxdevice_t*dev, fillinfo_t*fill)
@@ -437,6 +499,7 @@ void fill(gfxdevice_t*dev, fillinfo_t*fill)
 void fill_solid(gfxdevice_t*dev, gfxcolor_t* color)
 {
     fillinfo_t info;
+    memset(&info, 0, sizeof(info));
     info.type = filltype_solid;
     info.color = color;
     fill(dev, &info);
@@ -445,13 +508,21 @@ void fill_solid(gfxdevice_t*dev, gfxcolor_t* color)
 int render_setparameter(struct _gfxdevice*dev, const char*key, const char*value)
 {
     internal_t*i = (internal_t*)dev->internal;
-    if(!strcmp(key, "antialize")) {
+    if(!strcmp(key, "antialize") || !strcmp(key, "antialise")) {
        i->antialize = atoi(value);
        i->zoom = i->antialize * i->multiply;
+       return 1;
     } else if(!strcmp(key, "multiply")) {
        i->multiply = atoi(value);
        i->zoom = i->antialize * i->multiply;
        fprintf(stderr, "Warning: multiply not implemented yet\n");
+       return 1;
+    } else if(!strcmp(key, "fillwhite")) {
+       i->fillwhite = atoi(value);
+       return 1;
+    } else if(!strcmp(key, "palette")) {
+       i->palette = atoi(value);
+       return 1;
     }
     return 0;
 }
@@ -460,8 +531,8 @@ void newclip(struct _gfxdevice*dev)
 {
     internal_t*i = (internal_t*)dev->internal;
     
-    clipbuffer_t*c = rfx_calloc(sizeof(clipbuffer_t));
-    c->data = rfx_calloc(sizeof(U32) * i->bitwidth * i->height2);
+    clipbuffer_t*c = (clipbuffer_t*)rfx_calloc(sizeof(clipbuffer_t));
+    c->data = (U32*)rfx_calloc(sizeof(U32) * i->bitwidth * i->height2);
     c->next = i->clipbuf;
     i->clipbuf = c;
     if(c->next)
@@ -470,12 +541,13 @@ void newclip(struct _gfxdevice*dev)
        memset(c->data, 0, sizeof(U32)*i->bitwidth*i->height2);
 }
 
-void endclip(struct _gfxdevice*dev)
+void endclip(struct _gfxdevice*dev, char removelast)
 {
     internal_t*i = (internal_t*)dev->internal;
-    
-    if(!i->clipbuf) {
-       fprintf(stderr, "endclip without any active clip buffers");
+   
+    /* test for at least one cliplevel (the one we created ourselves) */
+    if(!i->clipbuf || (!i->clipbuf->next && !removelast)) {
+       fprintf(stderr, "endclip without any active clip buffers\n");
        return;
     }
 
@@ -491,28 +563,26 @@ void render_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxc
     internal_t*i = (internal_t*)dev->internal;
     double x,y;
     
-    if(cap_style != gfx_capRound || joint_style != gfx_joinRound) {
+    /*if(cap_style != gfx_capRound || joint_style != gfx_joinRound) {
        fprintf(stderr, "Warning: cap/joint style != round not yet supported\n");
-    }
+    }*/
 
     while(line) {
-        int x1,y1,x2,y2,x3,y3;
-
         if(line->type == gfx_moveTo) {
         } else if(line->type == gfx_lineTo) {
            double x1=x*i->zoom,y1=y*i->zoom;
            double x3=line->x*i->zoom,y3=line->y*i->zoom;
-           add_solidline(dev, x1, y1, x3, y3, width * i->multiply);
+           add_solidline(dev, x1, y1, x3, y3, width * i->zoom);
            fill_solid(dev, color);
         } else if(line->type == gfx_splineTo) {
-           int c,t,parts,qparts;
+           int t,parts;
            double xx,yy;
            
            double x1=x*i->zoom,y1=y*i->zoom;
            double x2=line->sx*i->zoom,y2=line->sy*i->zoom;
            double x3=line->x*i->zoom,y3=line->y*i->zoom;
             
-            c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
+            double c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
             xx=x1;
            yy=y1;
 
@@ -523,7 +593,7 @@ void render_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxc
                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
                 
-               add_solidline(dev, xx, yy, nx, ny, width * i->multiply);
+               add_solidline(dev, xx, yy, nx, ny, width * i->zoom);
                fill_solid(dev, color);
                 xx = nx;
                 yy = ny;
@@ -562,7 +632,7 @@ static void draw_line(gfxdevice_t*dev, gfxline_t*line)
             xx=x1;
            yy=y1;
 
-            parts = (int)(sqrt(c)/3);
+            parts = (int)(sqrt(c));
             if(!parts) parts = 1;
 
             for(t=1;t<=parts;t++) {
@@ -584,6 +654,7 @@ void render_startclip(struct _gfxdevice*dev, gfxline_t*line)
 {
     internal_t*i = (internal_t*)dev->internal;
     fillinfo_t info;
+    memset(&info, 0, sizeof(info));
     newclip(dev);
     info.type = filltype_clip;
     draw_line(dev, line);
@@ -593,7 +664,7 @@ void render_startclip(struct _gfxdevice*dev, gfxline_t*line)
 void render_endclip(struct _gfxdevice*dev)
 {
     internal_t*i = (internal_t*)dev->internal;
-    endclip(dev);
+    endclip(dev, 0);
 }
 
 void render_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
@@ -608,13 +679,12 @@ void render_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gf
 {
     internal_t*i = (internal_t*)dev->internal;
 
-    gfxcolor_t black = {255,0,0,0};
-
     gfxmatrix_t m2 = *matrix;
 
     draw_line(dev, line);
 
     fillinfo_t info;
+    memset(&info, 0, sizeof(info));
     info.type = filltype_bitmap;
     info.image = img;
     info.matrix = &m2;
@@ -630,10 +700,58 @@ void render_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gr
 {
     internal_t*i = (internal_t*)dev->internal;
     
-    gfxcolor_t black = {255,0,0,0};
+    gfxmatrix_t m2 = *matrix;
 
     draw_line(dev, line);
-    fill_solid(dev, &black);
+
+    RGBA g[256];
+    fillinfo_t info;
+    memset(&info, 0, sizeof(info));
+    info.type = filltype_gradient;
+    info.gradient = g;
+    info.matrix = &m2;
+
+    m2.m00 *= i->zoom; m2.m01 *= i->zoom; m2.tx *= i->zoom;
+    m2.m10 *= i->zoom; m2.m11 *= i->zoom; m2.ty *= i->zoom;
+
+    info.linear_or_radial = type == gfxgradient_radial;
+
+    int pos = 0;
+    gfxcolor_t color = {0,0,0,0};
+    pos=0;
+    while(gradient) {
+        int nextpos = gradient->pos*256;
+        int t;
+        if(nextpos>256) {
+            msg("<error> Invalid gradient- contains values > 1.0");
+            return;
+        }
+        
+        gfxcolor_t nextcolor = gradient->color;
+        if(nextpos!=pos) {
+            double p0 = 1.0;
+            double p1 = 0.0;
+            double step = 1.0/(nextpos-pos);
+            int t;
+            for(t=pos;t<nextpos;t++) {
+                g[t].r = color.r*p0 + nextcolor.r*p1;
+                g[t].g = color.g*p0 + nextcolor.g*p1;
+                g[t].b = color.b*p0 + nextcolor.b*p1;
+                g[t].a = color.a*p0 + nextcolor.a*p1;
+                p0 -= step;
+                p1 += step;
+            }
+        }
+        color=nextcolor;
+
+        pos = nextpos;
+        gradient = gradient->next;
+    }
+    if(pos!=256) {
+        msg("<error> Invalid gradient- doesn't end with 1.0");
+    }
+
+    fill(dev, &info);
 }
 
 void render_addfont(struct _gfxdevice*dev, gfxfont_t*font)
@@ -643,6 +761,12 @@ void render_addfont(struct _gfxdevice*dev, gfxfont_t*font)
 void render_drawchar(struct _gfxdevice*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
 {
     internal_t*i = (internal_t*)dev->internal;
+    if(!font)
+       return;
+
+    /* align characters to whole pixels */
+    matrix->tx = (int)(matrix->tx * i->antialize) / i->antialize;
+    matrix->ty = (int)(matrix->ty * i->antialize) / i->antialize;
 
     gfxglyph_t*glyph = &font->glyphs[glyphnr];
     gfxline_t*line2 = gfxline_clone(glyph->line);
@@ -658,31 +782,88 @@ void render_result_write(gfxresult_t*r, int filedesc)
 {
     internal_result_t*i= (internal_result_t*)r->internal;
 }
-int render_result_save(gfxresult_t*r, char*filename)
+int render_result_save(gfxresult_t*r, const char*filename)
 {
     internal_result_t*i= (internal_result_t*)r->internal;
+    if(!i) {
+       return 0; // no pages drawn
+    }
     if(i->next) {
        int nr=0;
+       char filenamebuf[256];
+       char*origname = strdup(filename);
+       int l = strlen(origname);
+       if(l>3 && strchr("gG",origname[l-1]) && strchr("nN",filename[l-2]) &&
+               strchr("pP",origname[l-3]) && filename[l-4]=='.') {
+           origname[l-4] = 0;
+       }
        while(i->next) {
-           writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+           sprintf(filenamebuf, "%s.%d.png", origname, nr);
+            if(!i->palette) {
+               writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+            } else {
+               writePalettePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+            }
            nr++;
        }
+       free(origname);
     } else {
-       writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+        if(!i->palette) {
+           writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+       } else {
+           writePalettePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+       }
     }
     return 1;
 }
-void*render_result_get(gfxresult_t*r, char*name)
+char*gfximage_asXPM(gfximage_t*img, int depth)
+{
+    int d= 256/depth;
+    char*str = (char*)malloc(img->width*img->height*4 + 500 + 16*depth*depth*depth);
+    char*p = str;
+    p+= sprintf(p, "static char *noname[] = {\n\"%d %d 262144 3\",\n", img->width, img->height);
+    int r,g,b;
+    for(r=0;r<depth;r++)
+    for(g=0;g<depth;g++)
+    for(b=0;b<depth;b++) {
+       p += sprintf(p, "\"%c%c%c c #%02x%02x%02x\",\n", r+32,g+32,b+32, r*d,g*d,b*d);
+    }
+    int y;
+    for(y=0;y<img->height;y++)  {
+       p+=sprintf(p, "\"");
+       gfxcolor_t*col = &img->data[y*img->height];
+       int x;
+       for(x=0;x<img->width;x++) {
+           p+=sprintf(p, "%c%c%c", 32+(col->r/d), 32+(col->g/d), 32+(col->b/d));
+       }
+       p+=sprintf(p, "\",\n");
+    }
+    *p = 0;
+    return p;
+}
+void*render_result_get(gfxresult_t*r, const char*name)
 {
     internal_result_t*i= (internal_result_t*)r->internal;
-    if(!strncmp(name,"page",4)) {
+    if(!strncmp(name,"xpm",3)) {
+       int pagenr = atoi(&name[3]);
+       if(pagenr<0)
+           pagenr=0;
+       while(pagenr>0) {
+           i = i->next;
+           if(!i)
+               return 0;
+            pagenr--;
+       }
+       return gfximage_asXPM(&i->img, 64);
+    } else if(!strncmp(name,"page",4)) {
        int pagenr = atoi(&name[4]);
        if(pagenr<0)
-           return 0;
+           pagenr=0;
        while(pagenr>0) {
            i = i->next;
            if(!i)
                return 0;
+            pagenr--;
        }
        return &i->img;
     }
@@ -695,10 +876,15 @@ void render_result_destroy(gfxresult_t*r)
     while(i) {
        internal_result_t*next = i->next;
        free(i->img.data);i->img.data = 0;
-       free(i);
+
+        /* FIXME memleak
+           the following rfx_free causes a segfault on WIN32 machines,
+           if executed */
+        //rfx_free(i);
+
        i = next;
     }
-    free(r);
+    rfx_free(r);
 }
 
 gfxresult_t* render_finish(struct _gfxdevice*dev)
@@ -741,6 +927,10 @@ void render_startpage(struct _gfxdevice*dev, int width, int height)
         i->lines[y].num = 0;
     }
     i->img = (RGBA*)rfx_calloc(sizeof(RGBA)*i->width2*i->height2);
+    if(i->fillwhite) {
+       memset(i->img, 0xff, sizeof(RGBA)*i->width2*i->height2);
+    }
+
     i->ymin = 0x7fffffff;
     i->ymax = -0x80000000;
 
@@ -752,7 +942,7 @@ void render_startpage(struct _gfxdevice*dev, int width, int height)
 
 static void store_image(internal_t*i, internal_result_t*ir)
 {
-    ir->img.data = malloc(i->width*i->height*sizeof(RGBA));
+    ir->img.data = (gfxcolor_t*)malloc(i->width*i->height*sizeof(gfxcolor_t));
     ir->img.width = i->width;
     ir->img.height = i->height;
 
@@ -813,13 +1003,19 @@ void render_endpage(struct _gfxdevice*dev)
        exit(1);
     }
 
-    endclip(dev);
+    endclip(dev, 1);
+    int unclosed = 0;
     while(i->clipbuf) {
-       fprintf(stderr, "Warning: unclosed clip while processing endpage()\n");
-       endclip(dev);
+       endclip(dev, 1);
+        unclosed++;
+    }
+
+    if(unclosed) {
+        fprintf(stderr, "Warning: %d unclosed clip(s) while processing endpage()\n", unclosed);
     }
     
     internal_result_t*ir= (internal_result_t*)rfx_calloc(sizeof(internal_result_t));
+    ir->palette = i->palette;
 
     int y,x;
 
@@ -845,7 +1041,7 @@ void render_endpage(struct _gfxdevice*dev)
     i->height2 = 0;
 }
 
-void render_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
+void render_drawlink(struct _gfxdevice*dev, gfxline_t*line, const char*action)
 {
     /* not supported for this output device */
 }
@@ -853,7 +1049,6 @@ void render_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
 void gfxdevice_render_init(gfxdevice_t*dev)
 {
     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
-    int y;
     memset(dev, 0, sizeof(gfxdevice_t));
     
     dev->name = "render";
@@ -883,3 +1078,10 @@ void gfxdevice_render_init(gfxdevice_t*dev)
     dev->finish = render_finish;
 }
 
+
+gfxdevice_t* gfxdevice_render_new()
+{
+    gfxdevice_t* d = (gfxdevice_t*)malloc(sizeof(gfxdevice_t));
+    gfxdevice_render_init(d);
+    return d;
+}