win32 compile fixes
[swftools.git] / lib / devices / render.c
index 1865e88..88e33ae 100644 (file)
@@ -1,8 +1,8 @@
-/* gfxdevice_bitmap.cc
+/* render.c
 
    Part of the swftools package.
 
-   Copyright (c) 2005 Matthias Kramm <kramm@quiss.org> 
+   Copyright (c) 2005/2006/2007 Matthias Kramm <kramm@quiss.org> 
  
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
 
 #include <stdlib.h>
 #include <stdio.h>
+#include <math.h>
 #include <memory.h>
 #include "../gfxdevice.h"
 #include "../gfxtools.h"
-#include "../png.h"
 #include "../mem.h"
-
-typedef unsigned int U32;
-typedef unsigned char U8;
+#define PNG_INLINE_EXPORTS
+#include "../types.h"
+#include "../png.c"
+#include "render.h"
 
 typedef gfxcolor_t RGBA;
 
@@ -44,45 +45,29 @@ typedef struct _renderline
 } renderline_t;
 
 typedef struct _internal_result {
-    int width;
-    int height;
-    RGBA* img;
+    gfximage_t img;
     struct _internal_result*next;
 } internal_result_t;
 
 typedef struct _clipbuffer {
     U32*data;
-    int linesize;
-    struct _clipbuffer*prev;
+    struct _clipbuffer*next;
 } clipbuffer_t;
 
-typedef struct _fontlist
-{
-    gfxfont_t*font;
-    char*id;
-    struct _fontlist*next;
-} fontlist_t;
-
 typedef struct _internal {
     int width;
     int height;
     int width2;
     int height2;
+    int bitwidth;
     int multiply;
     int antialize;
+    int zoom;
     int ymin, ymax;
-
-    int depth;
+    int fillwhite;
 
     RGBA* img;
-    int* zbuf; 
-
-    gfxfont_t*font;
-    char*fontid;
-    
-    fontlist_t* fontlist;
 
-    clipbuffer_t*clipbufs;
     clipbuffer_t*clipbuf;
 
     renderline_t*lines;
@@ -182,6 +167,8 @@ static void add_line(gfxdevice_t*dev , double x1, double y1, double x2, double y
 #define PI 3.14159265358979
 static void add_solidline(gfxdevice_t*dev, double x1, double y1, double x2, double y2, double width)
 {
+    /* TODO: handle cap styles */
+
     internal_t*i = (internal_t*)dev->internal;
 
     double dx = x2-x1;
@@ -286,13 +273,13 @@ static void fill_line_solid(RGBA*line, U32*z, int y, int x1, int x2, RGBA col)
         col.r = (col.r*col.a)>>8;
         col.g = (col.g*col.a)>>8;
         col.b = (col.b*col.a)>>8;
-        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].a = 255;
+               line[x].a = ((line[x].a*ainv)>>8)+col.a;
            }
            bit <<= 1;
            if(!bit) {
@@ -319,27 +306,31 @@ 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) {
+       gfxcolor_t red = {255,255,0,0};
+        fill_line_solid(line, z, y, x1, x2, red);
+        return;
+    }
+    
     double det = m->m00*m->m11 - m->m01*m->m10;
     if(fabs(det) < 0.0005) { 
        /* x direction equals y direction- the image is invisible */
        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;
     
-    if(!b->width || !b->height) {
-       gfxcolor_t red = {255,255,0,0};
-        fill_line_solid(line, z, y, x1, x2, red);
-        return;
-    }
-
     U32 bit = 1<<(x1&31);
     int bitpos = (x1/32);
 
     do {
        if(z[bitpos]&bit) {
            RGBA col;
-           int xx = (int)((  (x - m->tx) * m->m11 - (y - m->ty) * m->m10)*det);
-           int yy = (int)((- (x - m->tx) * m->m01 + (y - m->ty) * m->m00)*det);
+           int xx = (int)(xx1 + x * xinc1);
+           int yy = (int)(yy1 - x * yinc1);
            int ainv;
 
            if(info->clip) {
@@ -357,6 +348,7 @@ static void fill_line_bitmap(RGBA*line, U32*z, int y, int x1, int x2, fillinfo_t
            col = b->data[yy*b->width+xx];
            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;
@@ -404,7 +396,8 @@ void fill(gfxdevice_t*dev, fillinfo_t*fill)
     for(y=i->ymin;y<=i->ymax;y++) {
        renderpoint_t*points = i->lines[y].points;
         RGBA*line = &i->img[i->width2*y];
-        int*zline = &i->zbuf[i->width2*y];
+        U32*zline = &i->clipbuf->data[i->bitwidth*y];
+
        int n;
        int num = i->lines[y].num;
        int lastx;
@@ -429,6 +422,15 @@ void fill(gfxdevice_t*dev, fillinfo_t*fill)
             if(endx == i->width2)
                 break;
         }
+       if(fill->type == filltype_clip) {
+           if(i->clipbuf->next) {
+               U32*line2 = &i->clipbuf->next->data[i->bitwidth*y];
+               int x;
+               for(x=0;x<i->bitwidth;x++)
+                   zline[x] &= line2[x];
+           }
+       }
+
        i->lines[y].num = 0;
     }
 }
@@ -444,10 +446,18 @@ 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;
     }
     return 0;
 }
@@ -456,38 +466,30 @@ void newclip(struct _gfxdevice*dev)
 {
     internal_t*i = (internal_t*)dev->internal;
     
-    clipbuffer_t*c = rfx_calloc(sizeof(clipbuffer_t));
-    c->linesize = ((i->width2+31) / 32);
-    c->data = rfx_calloc(c->linesize * i->height2);
-
-    if(!i->clipbufs) {
-       i->clipbufs = i->clipbuf = c;
-    } else {
-       clipbuffer_t*old = i->clipbuf;
-       i->clipbuf = c;
-       i->clipbuf->prev = old;
-    }
+    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)
+       memcpy(c->data, c->next->data, i->bitwidth*i->height2);
+    else
+       memset(c->data, 0, sizeof(U32)*i->bitwidth*i->height2);
 }
 
 void endclip(struct _gfxdevice*dev)
 {
     internal_t*i = (internal_t*)dev->internal;
     
-    if(!i->clipbufs) {
+    if(!i->clipbuf) {
        fprintf(stderr, "endclip without any active clip buffers");
        return;
     }
-    
-    clipbuffer_t*old = i->clipbuf;
-
-    if(i->clipbuf == i->clipbufs)
-       i->clipbufs = 0;
 
-    i->clipbuf = i->clipbuf->prev;
-
-    old->prev = 0;
-    free(old->data);old->data = 0;
-    free(old);
+    clipbuffer_t*c = i->clipbuf;
+    i->clipbuf = i->clipbuf->next;
+    c->next = 0;
+    free(c->data);c->data = 0;
+    free(c);
 }
 
 void render_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
@@ -495,28 +497,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,y1=y;
-           double x3=line->x,y3=line->y;
+           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);
            fill_solid(dev, color);
         } else if(line->type == gfx_splineTo) {
-           int c,t,parts,qparts;
+           int t,parts;
            double xx,yy;
            
-           double x1=x,y1=y;
-           double x2=line->sx,y2=line->sy;
-           double x3=line->x,y3=line->y;
+           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;
 
@@ -550,17 +550,17 @@ static void draw_line(gfxdevice_t*dev, gfxline_t*line)
 
         if(line->type == gfx_moveTo) {
         } else if(line->type == gfx_lineTo) {
-           double x1=x,y1=y;
-           double x3=line->x,y3=line->y;
+           double x1=x*i->zoom,y1=y*i->zoom;
+           double x3=line->x*i->zoom,y3=line->y*i->zoom;
             
             add_line(dev, x1, y1, x3, y3);
         } else if(line->type == gfx_splineTo) {
            int c,t,parts,qparts;
            double xx,yy;
             
-           double x1=x,y1=y;
-           double x2=line->sx,y2=line->sy;
-           double x3=line->x,y3=line->y;
+           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);
             xx=x1;
@@ -614,13 +614,19 @@ void render_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gf
 
     gfxcolor_t black = {255,0,0,0};
 
+    gfxmatrix_t m2 = *matrix;
+
     draw_line(dev, line);
 
     fillinfo_t info;
     info.type = filltype_bitmap;
     info.image = img;
-    info.matrix = matrix;
+    info.matrix = &m2;
     info.cxform = cxform;
+
+    m2.m00 *= i->zoom; m2.m01 *= i->zoom; m2.tx *= i->zoom;
+    m2.m10 *= i->zoom; m2.m11 *= i->zoom; m2.ty *= i->zoom;
+
     fill(dev, &info);
 }
 
@@ -634,54 +640,21 @@ void render_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gr
     fill_solid(dev, &black);
 }
 
-void render_addfont(struct _gfxdevice*dev, char*fontid, gfxfont_t*font)
+void render_addfont(struct _gfxdevice*dev, gfxfont_t*font)
 {
-    internal_t*i = (internal_t*)dev->internal;
-    
-    fontlist_t*last=0,*l = i->fontlist;
-    while(l) {
-       last = l;
-       if(!strcmp((char*)l->id, fontid)) {
-           return; // we already know this font
-       }
-       l = l->next;
-    }
-    l = (fontlist_t*)rfx_calloc(sizeof(fontlist_t));
-    l->font = font;
-    l->next = 0;
-    if(last) {
-       last->next = l;
-    } else {
-       i->fontlist = l;
-    }
 }
 
-void render_drawchar(struct _gfxdevice*dev, char*fontid, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
+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;
 
-    if(i->font && i->fontid && !strcmp(fontid, i->fontid)) {
-       // current font is correct
-    } else {
-       fontlist_t*l = i->fontlist;
-       i->font = 0;
-       i->fontid = 0;
-       while(l) {
-           if(!strcmp((char*)l->id, i->fontid)) {
-               i->font = l->font;
-               i->fontid = l->id;
-               break;
-           }
-           l = l->next;
-       }
-       if(i->font == 0) {
-           fprintf(stderr, "Unknown font id: %s", fontid);
-           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 = &i->font->glyphs[glyphnr];
-    
+    gfxglyph_t*glyph = &font->glyphs[glyphnr];
     gfxline_t*line2 = gfxline_clone(glyph->line);
     gfxline_transform(line2, matrix);
     draw_line(dev, line2);
@@ -695,28 +668,93 @@ 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, i->width, i->height);
+           sprintf(filenamebuf, "%s.%d.png", origname, nr);
+           writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
            nr++;
        }
+       free(origname);
     } else {
-       writePNG(filename, (unsigned char*)i->img, i->width, i->height);
+       writePNG(filename, (unsigned char*)i->img.data, i->img.width, i->img.height);
+    }
+    return 1;
+}
+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");
+    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, char*name)
+void*render_result_get(gfxresult_t*r, const char*name)
 {
     internal_result_t*i= (internal_result_t*)r->internal;
+    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;
+       }
+       return gfximage_asXPM(&i->img, 64);
+    } else if(!strncmp(name,"page",4)) {
+       int pagenr = atoi(&name[4]);
+       if(pagenr<0)
+           pagenr=0;
+       while(pagenr>0) {
+           i = i->next;
+           if(!i)
+               return 0;
+       }
+       return &i->img;
+    }
     return 0;
 }
 void render_result_destroy(gfxresult_t*r)
 {
     internal_result_t*i= (internal_result_t*)r->internal;
-    free(i); r->internal = 0;
+    r->internal = 0;
+    while(i) {
+       internal_result_t*next = i->next;
+       free(i->img.data);i->img.data = 0;
+       free(i);
+       i = next;
+    }
     free(r);
 }
 
@@ -733,6 +771,7 @@ gfxresult_t* render_finish(struct _gfxdevice*dev)
     res->destroy = render_result_destroy;
 
     free(dev->internal); dev->internal = 0; i = 0;
+
     return res;
 }
 
@@ -746,10 +785,11 @@ void render_startpage(struct _gfxdevice*dev, int width, int height)
        exit(1);
     }
     
-    i->width = width;
-    i->height = height;
-    i->width2 = width*i->antialize*i->multiply;
-    i->height2 = height*i->antialize*i->multiply;
+    i->width = width*i->multiply;
+    i->height = height*i->multiply;
+    i->width2 = width*i->zoom;
+    i->height2 = height*i->zoom;
+    i->bitwidth = (i->width2+31)/32;
 
     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
     for(y=0;y<i->height2;y++) {
@@ -757,12 +797,72 @@ void render_startpage(struct _gfxdevice*dev, int width, int height)
         i->lines[y].points = 0;
         i->lines[y].num = 0;
     }
-    i->zbuf = (int*)rfx_calloc(sizeof(int)*i->width2*i->height2);
     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;
 
+
+    /* initialize initial clipping field, which doesn't clip anything yet */
     newclip(dev);
+    memset(i->clipbuf->data, 255, sizeof(U32)*i->bitwidth*i->height2);
+}
+
+static void store_image(internal_t*i, internal_result_t*ir)
+{
+    ir->img.data = (gfxcolor_t*)malloc(i->width*i->height*sizeof(gfxcolor_t));
+    ir->img.width = i->width;
+    ir->img.height = i->height;
+
+    gfxcolor_t*dest = ir->img.data;
+
+    if(i->antialize <= 1) /* no antializing */ {
+       int y;
+       for(y=0;y<i->height;y++) {
+           RGBA*line = &i->img[y*i->width];
+           memcpy(&dest[y*i->width], line, sizeof(RGBA)*i->width);
+       }
+    } else {
+       RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*i->antialize);
+       int q = i->antialize*i->antialize;
+       int ypos = 0;
+       int y;
+       int y2=0;
+       for(y=0;y<i->height2;y++) {
+           int n;
+           ypos = y % i->antialize;
+           lines[ypos] = &i->img[y*i->width2];
+           if(ypos == i->antialize-1) {
+               RGBA*out = &dest[(y2++)*i->width];
+               int x;
+               int r,g,b,a;
+               for(x=0;x<i->width;x++) {
+                   int xpos = x*i->antialize;
+                   int yp;
+                   U32 r=0,g=0,b=0,a=0;
+                   for(yp=0;yp<i->antialize;yp++) {
+                       RGBA*lp = &lines[yp][xpos];
+                       int xp;
+                       for(xp=0;xp<i->antialize;xp++) {
+                           RGBA*p = &lp[xp];
+                           r += p->r;
+                           g += p->g;
+                           b += p->b;
+                           a += p->a;
+                       }
+                   }
+                   out[x].r = r / q;
+                   out[x].g = g / q;
+                   out[x].b = b / q;
+                   out[x].a = a / q;
+               }
+           }
+       }
+       rfx_free(lines);
+    }
 }
 
 void render_endpage(struct _gfxdevice*dev)
@@ -775,15 +875,17 @@ void render_endpage(struct _gfxdevice*dev)
     }
 
     endclip(dev);
-    while(i->clipbufs) {
+    while(i->clipbuf) {
        fprintf(stderr, "Warning: unclosed clip while processing endpage()\n");
        endclip(dev);
     }
     
     internal_result_t*ir= (internal_result_t*)rfx_calloc(sizeof(internal_result_t));
-    ir->width = i->width;
-    ir->height = i->height;
-    ir->img = i->img; i->img = 0;
+
+    int y,x;
+
+    store_image(i, ir);
+
     ir->next = 0;
     if(i->result_next) {
        i->result_next->next = ir;
@@ -793,15 +895,18 @@ void render_endpage(struct _gfxdevice*dev)
     }
     i->result_next = ir;
 
-    rfx_free(i->lines);i->lines=0; //FIXME
-    rfx_free(i->zbuf);i->zbuf = 0;
+    for(y=0;y<i->height2;y++) {
+       rfx_free(i->lines[y].points); i->lines[y].points = 0;
+    }
+    rfx_free(i->lines);i->lines=0;
+
     if(i->img) {rfx_free(i->img);i->img = 0;}
 
     i->width2 = 0;
     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 */
 }
@@ -809,8 +914,10 @@ 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";
+
     dev->internal = i;
     
     i->width = 0;
@@ -819,6 +926,7 @@ void gfxdevice_render_init(gfxdevice_t*dev)
     i->height2 = 0;
     i->antialize = 1;
     i->multiply = 1;
+    i->zoom = 1;
 
     dev->setparameter = render_setparameter;
     dev->startpage = render_startpage;