fixed line width handling
[swftools.git] / lib / modules / swfrender.c
index e085937..f17a1a2 100644 (file)
@@ -79,7 +79,7 @@ typedef struct _renderbuf_internal
 {
     renderline_t*lines;
     bitmap_t*bitmaps;
-    char antialize;
+    int antialize;
     int multiply;
     int width2,height2;
     int shapes;
@@ -116,7 +116,7 @@ static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2,
     double ny1, ny2, stepx;
 /*    if(DEBUG&4) {
         int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
-        printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
+        printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]\n", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
     }*/
     assert(p->shapeline);
 
@@ -172,7 +172,7 @@ static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2,
     }
 }
 #define PI 3.14159265358979
-static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
+static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, double width, renderpoint_t*p)
 {
     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
 
@@ -194,8 +194,10 @@ static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double
 #else
     /* That's what Macromedia's Player seems to do at zoom level 0.  */
     /* TODO: needs testing */
-    if(width<20)
-       width = 20;
+
+    /* TODO: how does this interact with scaling? */
+    if(width * i->multiply < 20)
+       width = 20 / i->multiply;
 #endif
 
     sd = (double)dx*(double)dx+(double)dy*(double)dy;
@@ -274,7 +276,7 @@ static int compare_renderpoints(const void * _a, const void * _b)
     return 0;
 }
 
-void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
+void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, int antialize, int multiply)
 {
     renderbuf_internal*i;
     int y;
@@ -285,10 +287,12 @@ void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, c
     buf->posy = posy;
     buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
     i = (renderbuf_internal*)buf->internal;
-    i->antialize = !!antialize;
-    i->multiply = antialize?multiply*2:multiply;
-    i->height2 = antialize?2*buf->height:buf->height;
-    i->width2 = antialize?2*buf->width:buf->width;
+    if(antialize < 1)
+       antialize = 1;
+    i->antialize = antialize;
+    i->multiply = multiply*antialize;
+    i->height2 = antialize*buf->height;
+    i->width2 = antialize*buf->width;
     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
     for(y=0;y<i->height2;y++) {
        memset(&i->lines[y], 0, sizeof(renderline_t));
@@ -304,12 +308,12 @@ void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, c
 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
 {
     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
-    if(i->shapes) {
-       fprintf(stderr, "rfxswf: Warning: swf_Render_SetBackground() called after drawing shapes\n");
-    }
     int x,xx,y,yy;
     int xstep=width*65536/i->width2;
     int ystep=height*65536/i->height2;
+    if(i->shapes) {
+       fprintf(stderr, "rfxswf: Warning: swf_Render_SetBackground() called after drawing shapes\n");
+    }
     for(y=0,yy=0;y<i->height2;y++,yy+=ystep) {
        RGBA*src = &img[(yy>>16) * width];
        RGBA*line = &i->img[y * i->width2];
@@ -391,6 +395,13 @@ static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
 
 void swf_Process(RENDERBUF*dest, U32 clipdepth);
 
+double matrixsize(MATRIX*m)
+{
+    double l1 = sqrt((m->sx /65536.0) * (m->sx /65536.0) + (m->r0 /65536.0) * (m->r0/65536.0) );
+    double l2 = sqrt((m->r1 /65536.0) * (m->r1 /65536.0) + (m->sy /65536.0) * (m->sy/65536.0) );
+    return sqrt(l1*l2);
+}
+
 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
 {
     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
@@ -402,6 +413,7 @@ void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _dept
     SHAPE2* lshape = 0;
     renderpoint_t p, lp;
     U32 clipdepth;
+    double widthmultiply = matrixsize(m);
 
     memset(&p, 0, sizeof(renderpoint_t));
     memset(&lp, 0, sizeof(renderpoint_t));
@@ -422,12 +434,12 @@ void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _dept
         for(t=0;t<s2->numfillstyles;t++) {
             MATRIX nm;
             swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
-            nm.sx *= i->multiply;
+            /*nm.sx *= i->multiply;
             nm.sy *= i->multiply;
             nm.r0 *= i->multiply;
             nm.r1 *= i->multiply;
             nm.tx *= i->multiply;
-            nm.ty *= i->multiply;
+            nm.ty *= i->multiply;*/
             s2->fillstyles[t].m = nm;
         }
     }
@@ -438,6 +450,7 @@ void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _dept
         lp.depth = (_depth << 16)+1;
     }
 
+
     while(line)
     {
         int x1,y1,x2,y2,x3,y3;
@@ -449,7 +462,7 @@ void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _dept
             
             if(line->linestyle && ! clipdepth) {
                 lp.shapeline = &lshape->lines[line->linestyle-1];
-                add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
+                add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
                 lp.depth++;
             }
             if(line->fillstyle0 || line->fillstyle1) {
@@ -478,7 +491,7 @@ void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _dept
                 
                 if(line->linestyle && ! clipdepth) {
                     lp.shapeline = &lshape->lines[line->linestyle-1];
-                    add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
+                    add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
                     lp.depth++;
                 }
                 if(line->fillstyle0 || line->fillstyle1) {
@@ -509,6 +522,7 @@ void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _dept
 
 static RGBA color_red = {255,255,0,0};
 static RGBA color_white = {255,255,255,255};
+static RGBA color_black = {255,0,0,0};
 
 static void fill_clip(RGBA*line, int*z, int y, int x1, int x2, U32 depth)
 {
@@ -551,20 +565,22 @@ static void fill_solid(RGBA*line, int*z, int y, int x1, int x2, RGBA col, U32 de
     }
 }
 
-static void fill_bitmap(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clipbitmap, U32 depth)
+static void fill_bitmap(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clipbitmap, U32 depth, double fmultiply)
 {
     int x = x1;
-    double m11=m->sx/65536.0, m21=m->r1/65536.0;
-    double m12=m->r0/65536.0, m22=m->sy/65536.0;
-    double rx = m->tx/20.0;
-    double ry = m->ty/20.0;
+    
+    double m11= m->sx*fmultiply/65536.0, m21= m->r1*fmultiply/65536.0;
+    double m12= m->r0*fmultiply/65536.0, m22= m->sy*fmultiply/65536.0;
+    double rx = m->tx*fmultiply/20.0;
+    double ry = m->ty*fmultiply/20.0;
+
     double det = m11*m22 - m12*m21;
     if(fabs(det) < 0.0005) { 
        /* x direction equals y direction- the image is invisible */
        return;
     }
     det = 20.0/det;
-    
+
     if(!b->width || !b->height) {
         fill_solid(line, z, y, x1, x2, color_red, depth);
         return;
@@ -585,6 +601,8 @@ static void fill_bitmap(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, bitma
            } else {
                xx %= b->width;
                yy %= b->height;
+               if(xx<0) xx += b->width;
+               if(yy<0) yy += b->height;
            }
 
            col = b->data[yy*b->width+xx];
@@ -652,7 +670,7 @@ static void fill(RENDERBUF*dest, RGBA*line, int*zline, int y, int x1, int x2, st
                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
                     fill_solid(line, zline, y, x1, x2, color_red, l->p->depth);
                 } else {
-                    fill_bitmap(line, zline, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0, l->p->depth);
+                    fill_bitmap(line, zline, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0, l->p->depth, i->multiply);
                 }
             } else {
                 fprintf(stderr, "Undefined fillmode: %02x\n", f->type);
@@ -871,47 +889,66 @@ RGBA* swf_Render(RENDERBUF*dest)
     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
     int y;
-    RGBA*line2=0;
-    
-    for(y=0;y<i->height2;y++) {
-        int n;
-        RGBA*line = &i->img[y*i->width2];
-
-        if(!i->antialize) {
-            memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
-        } else {
-            if(y&1) {
-                int x;
-               RGBA*line1=line;
-               if(!line2)
-                   line2=line1;
-                RGBA* p = &img[(y/2)*dest->width];
-                for(x=0;x<dest->width;x++) {
-                    RGBA*p1 = &line1[x*2];
-                    RGBA*p2 = &line1[x*2+1];
-                    RGBA*p3 = &line2[x*2];
-                    RGBA*p4 = &line2[x*2+1];
-                    p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
-                    p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
-                    p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
-                    p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
-                }
-            }
-        }
-       line2=line;
+    int antialize = i->antialize;
+   
+    if(antialize <= 1) /* no antializing */ {
+       for(y=0;y<i->height2;y++) {
+           RGBA*line = &i->img[y*i->width2];
+           memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
+       }
+    } else {
+       RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*antialize);
+       int q = antialize*antialize;
+       int ypos = 0;
+       for(y=0;y<i->height2;y++) {
+           int n;
+           ypos = y % antialize;
+           lines[ypos] = &i->img[y*i->width2];
+           if(ypos == antialize-1) {
+               RGBA*out = &img[(y / antialize)*dest->width];
+               int x;
+               int r,g,b,a;
+               for(x=0;x<dest->width;x++) {
+                   int xpos = x*antialize;
+                   int yp;
+                   U32 r=0,g=0,b=0,a=0;
+                   for(yp=0;yp<antialize;yp++) {
+                       RGBA*lp = &lines[yp][xpos];
+                       int xp;
+                       for(xp=0;xp<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);
     }
-
     return img;
 }
 
 typedef struct
 {
+    int numchars;
+    SHAPE2**glyphs;
+} font_t;
+
+typedef struct
+{
     TAG*tag;
     SRECT*bbox;
     enum {none_type, shape_type, image_type, text_type, font_type} type;
     union {
         SHAPE2*shape;
-        SWFFONT*font;
+        font_t*font;
     } obj;
 } character_t;
 
@@ -954,11 +991,65 @@ int compare_placements(const void *v1, const void *v2)
     }*/
 }
 
+typedef struct textcallbackblock
+{
+    character_t*idtable;
+    U16 depth;
+    U16 clipdepth;
+    CXFORM* cxform;
+    MATRIX m;
+    RENDERBUF*buf;
+} textcallbackblock_t;
+
+static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize, 
+                   int xstart, int ystart, RGBA* color)
+{
+    textcallbackblock_t * info = (textcallbackblock_t*)self;
+    font_t*font = 0;
+    int t;
+    if(!info->idtable[fontid].obj.font) {
+       fprintf(stderr, "Font %d unknown\n", fontid);
+       return;
+    } else {
+       font  = info->idtable[fontid].obj.font;
+    }
+    for(t=0;t<nr;t++) {
+       int x = xstart + xpos[t];
+       int y = ystart;
+       MATRIX m = info->m;
+       SPOINT p;
+       
+       p.x = x; p.y = y; 
+       p = swf_TurnPoint(p, &m);
+       
+       m.sx = (m.sx * fontsize) / 1024;
+       m.sy = (m.sy * fontsize) / 1024;
+       m.r0 = (m.r0 * fontsize) / 1024;
+       m.r1 = (m.r1 * fontsize) / 1024;
+       m.tx += p.x;
+       m.ty += p.y;
+
+       if(chars[t]<0 || chars[t]>= font->numchars) {
+           fprintf(stderr, "Character out of range: %d\n", chars[t]);
+       } else {
+           SHAPE2*shape = font->glyphs[chars[t]];
+           shape->fillstyles[0].color = *color; //q&d
+           /*printf("Rendering char %d (size %d, x:%d, y:%d) color:%02x%02x%02x%02x\n", chars[t], fontsize, x, y,
+                   color->a, color->r, color->g, color->b);
+           swf_DumpMatrix(stdout, &m);
+           swf_DumpShape(shape);*/
+           swf_RenderShape(info->buf, shape, &m, info->cxform, info->depth, info->clipdepth);
+       }
+    }
+}
+
 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
 {
     TAG*tag;
     int t;
     int numplacements;
+    RGBA color;
+    SWFPLACEOBJECT* placements;
     
     character_t* idtable = rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
     SWFPLACEOBJECT** depthtable = rfx_calloc(sizeof(SWFPLACEOBJECT*)*65536); // depth to placeobject mapping
@@ -972,11 +1063,11 @@ void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
        }
        tag = tag->next;
     }
-    SWFPLACEOBJECT* placements = rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
+    placements = rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
     numplacements = 0;
 
     /* set background color */
-    RGBA color = swf_GetSWFBackgroundColor(swf);
+    color = swf_GetSWFBackgroundColor(swf);
     swf_Render_SetBackgroundColor(buf, color);
 
     /* parse definitions */
@@ -1001,8 +1092,23 @@ void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
                free(data);
             } else if(tag->id == ST_DEFINEFONT ||
                       tag->id == ST_DEFINEFONT2) {
-                //swf_FontExtract(swf,id,&idtable[id].font);
-                idtable[id].obj.font = 0;
+               int t;
+               SWFFONT*swffont;
+               font_t*font = rfx_calloc(sizeof(font_t));
+               idtable[id].obj.font = font;
+                swf_FontExtract(swf,id,&swffont);
+               font->numchars = swffont->numchars;
+               font->glyphs = rfx_calloc(sizeof(SHAPE2*)*font->numchars);
+               for(t=0;t<font->numchars;t++) {
+                   if(!swffont->glyph[t].shape->fillstyle.n) {
+                       /* the actual fill color will be overwritten while rendering */
+                       swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
+                   }
+                   font->glyphs[t] = swf_ShapeToShape2(swffont->glyph[t].shape);
+               }
+               swf_FontFree(swffont);
+                idtable[id].type = font_type;
+
             } else if(tag->id == ST_DEFINEFONTINFO ||
                       tag->id == ST_DEFINEFONTINFO2) {
                 idtable[id].type = font_type;
@@ -1033,10 +1139,30 @@ void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
         }
 
         if(idtable[id].type == shape_type) {
-            SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
+            //SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
             swf_RenderShape(buf, idtable[id].obj.shape, &p->matrix, &p->cxform, p->depth, p->clipdepth);
         } else if(idtable[id].type == text_type) {
-           /* TODO */
+           TAG* tag = idtable[id].tag;
+           textcallbackblock_t info;
+           MATRIX m;
+
+           swf_SetTagPos(tag, 0);
+           swf_GetU16(tag);
+           swf_GetRect(tag,0);
+           swf_GetMatrix(tag,&m);
+           swf_MatrixJoin(&info.m, &m, &p->matrix);
+           /*printf("Text matrix:\n");
+           swf_DumpMatrix(stdout, &m);
+           printf("Placement matrix:\n");
+           swf_DumpMatrix(stdout, &p->matrix);*/
+
+           info.idtable = idtable;
+           info.depth = p->depth;
+           info.cxform = &p->cxform;
+           info.clipdepth = p->clipdepth;
+           info.buf = buf;
+           
+           swf_ParseDefineText(tag, textcallback, &info);
         } else {
             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
         }
@@ -1054,7 +1180,22 @@ void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
                 swf_Shape2Free(shape); // FIXME
                 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
             }
-        }
+        } else if(idtable[t].type == font_type) {
+           font_t* font = idtable[t].obj.font;
+           if(font) {
+               if(font->glyphs) {
+                   int t;
+                   for(t=0;t<font->numchars;t++) {
+                       swf_Shape2Free(font->glyphs[t]);
+                       free(font->glyphs[t]); font->glyphs[t] = 0;
+                   }
+                   free(font->glyphs);
+                   font->glyphs = 0;
+               }
+               free(idtable[t].obj.font); idtable[t].obj.font = 0;
+               font = 0;
+           }
+       }
     }
     free(placements);
     free(idtable);