/* swfrender.c functions for rendering swf content Extension module for the rfxswf library. Part of the swftools package. Copyright (c) 2004 Mederra Oy Copyright (c) 2004 Matthias Kramm 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 the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include /* one bit flag: */ #define clip_type 0 #define fill_type 1 typedef struct _renderpoint { float x; U32 depth; SHAPELINE*shapeline; SHAPE2*s; } renderpoint_t; /* enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type; float fx; int x; U32 depth; U32 clipdepth; // solidfill; RGBA color; // texturefill bitmap_t* bitmap; // gradientfill gradient_t* gradient; // texture- & gradientfill; U32 x,y; U32 dx,dy; */ typedef struct _renderline { TAG*points; //incremented in 128 byte steps int num; U32 pending_clipdepth; } renderline_t; typedef struct _bitmap { int width; int height; RGBA*data; int id; struct _bitmap*next; } bitmap_t; typedef struct _renderbuf_internal { renderline_t*lines; bitmap_t*bitmaps; int antialize; int multiply; int width2,height2; int shapes; int ymin, ymax; RGBA* img; int* zbuf; } renderbuf_internal; #define DEBUG 0 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p) { renderbuf_internal*i = (renderbuf_internal*)dest->internal; if(x >= i->width2 || y >= i->height2 || y<0) return; p->x = x; if(yymin) i->ymin = y; if(y>i->ymax) i->ymax = y; i->lines[y].num++; swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t)); } /* set this to 0.777777 or something if the "both fillstyles set while not inside shape" problem appears to often */ #define CUT 0.5 #define INT(x) ((int)((x)+16)-16) static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p) { renderbuf_internal*i = (renderbuf_internal*)buf->internal; double diffx, diffy; 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]\n", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0); }*/ assert(p->shapeline); y1=y1*i->multiply; y2=y2*i->multiply; x1=x1*i->multiply; x2=x2*i->multiply; y1 = y1/20.0; y2 = y2/20.0; x1 = x1/20.0; x2 = x2/20.0; if(y2 < y1) { double x; double 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) { float xx = (float)(startx + posx); add_pixel(buf, xx ,posy, p); posx+=stepx; posy++; } } } #define PI 3.14159265358979 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; double dx = x2-x1; double dy = y2-y1; double sd; double d; int t; int segments; double lastx,lasty; double vx,vy; double xx,yy; /* Make sure the line is always at least one pixel wide */ #ifdef LINEMODE1 /* That's what Macromedia's Player does at least at zoom level >= 1. */ width += 20; #else /* That's what Macromedia's Player seems to do at zoom level 0. */ /* TODO: needs testing */ /* 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; d = sqrt(sd); if(!dx && !dy) { vx = 1; vy = 0; } else { vx = ( dy/d); vy = (-dx/d); } segments = width/2; if(segments < 2) segments = 2; segments = 8; vx=vx*width*0.5; vy=vy*width*0.5; xx = x2+vx; yy = y2+vy; add_line(buf, x1+vx, y1+vy, xx, yy, p); lastx = xx; lasty = yy; for(t=1;tx < b->x) return -1; if(a->x > b->x) return 1; return 0; } void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, int antialize, int multiply) { renderbuf_internal*i; int y; memset(buf, 0, sizeof(RENDERBUF)); buf->width = width*multiply; buf->height = height*multiply; buf->posx = posx; buf->posy = posy; buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal)); i = (renderbuf_internal*)buf->internal; 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;yheight2;y++) { memset(&i->lines[y], 0, sizeof(renderline_t)); i->lines[y].points = swf_InsertTag(0, 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); i->shapes = 0; i->ymin = 0x7fffffff; i->ymax = -0x80000000; } void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height) { renderbuf_internal*i = (renderbuf_internal*)buf->internal; 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;yheight2;y++,yy+=ystep) { RGBA*src = &img[(yy>>16) * width]; RGBA*line = &i->img[y * i->width2]; for(x=0,xx=0;xwidth2;x++,xx+=xstep) { line[x] = src[xx>>16]; } } } void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color) { swf_Render_SetBackground(buf, &color, 1, 1); } void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height) { renderbuf_internal*i = (renderbuf_internal*)buf->internal; bitmap_t*bm = rfx_calloc(sizeof(bitmap_t)); bm->id = id; bm->width = width; bm->height = height; bm->data = rfx_alloc(width*height*4); memcpy(bm->data, img, width*height*4); bm->next = i->bitmaps; i->bitmaps = bm; } void swf_Render_ClearCanvas(RENDERBUF*dest) { renderbuf_internal*i = (renderbuf_internal*)dest->internal; int y; for(y=0;yheight2;y++) { swf_ClearTag(i->lines[y].points); } memset(i->zbuf, 0, sizeof(int)*i->width2*i->height2); memset(i->img, 0, sizeof(RGBA)*i->width2*i->height2); } void swf_Render_Delete(RENDERBUF*dest) { renderbuf_internal*i = (renderbuf_internal*)dest->internal; int y; bitmap_t*b = i->bitmaps; /* delete canvas */ rfx_free(i->zbuf); rfx_free(i->img); /* delete line buffers */ for(y=0;yheight2;y++) { swf_DeleteTag(i->lines[y].points); i->lines[y].points = 0; } /* delete bitmaps */ while(b) { bitmap_t*next = b->next; free(b->data);b->data=0; rfx_free(b); b = next; } rfx_free(i->lines); i->lines = 0; rfx_free(dest->internal); dest->internal = 0; } static SHAPE2* linestyle2fillstyle(SHAPE2*shape) { SHAPE2*s = rfx_calloc(sizeof(SHAPE2)); int t; s->numfillstyles = shape->numlinestyles; s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles); s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles); for(t=0;tnumlinestyles;t++) { s->lines[t].fillstyle0 = t+1; s->fillstyles[t].type = FILL_SOLID; s->fillstyles[t].color = shape->linestyles[t].color; } return s; } 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; SHAPELINE*line; int x=0,y=0; MATRIX mat = *m; SHAPE2* s2 = 0; 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)); clipdepth = _clipdepth? _clipdepth << 16 | 0xffff : 0; p.depth = _depth << 16; mat.tx -= dest->posx*20; mat.ty -= dest->posy*20; s2 = swf_Shape2Clone(shape); line = s2->lines; if(shape->numfillstyles) { int t; p.s = s2; /* multiply fillstyles matrices with placement matrix- important for texture and gradient fill */ for(t=0;tnumfillstyles;t++) { MATRIX nm; swf_MatrixJoin(&nm, &mat, &s2->fillstyles[t].m); /*nm.sx *= i->multiply; nm.sy *= i->multiply; nm.r0 *= i->multiply; nm.r1 *= i->multiply; nm.tx *= i->multiply; nm.ty *= i->multiply;*/ s2->fillstyles[t].m = nm; } } if(shape->numlinestyles) { lshape = linestyle2fillstyle(shape); lp.s = lshape; lp.depth = (_depth << 16)+1; } while(line) { int x1,y1,x2,y2,x3,y3; if(line->type == moveTo) { } else if(line->type == lineTo) { transform_point(&mat, x, y, &x1, &y1); transform_point(&mat, line->x, line->y, &x3, &y3); if(line->linestyle && ! clipdepth) { lp.shapeline = &lshape->lines[line->linestyle-1]; add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp); lp.depth++; } if(line->fillstyle0 || line->fillstyle1) { assert(shape->numfillstyles); p.shapeline = line; add_line(dest, x1, y1, x3, y3, &p); } } else if(line->type == splineTo) { int c,t,parts,qparts; double xx,yy; transform_point(&mat, x, y, &x1, &y1); transform_point(&mat, line->sx, line->sy, &x2, &y2); transform_point(&mat, line->x, line->y, &x3, &y3); c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1); xx=x1; yy=y1; parts = (int)(sqrt(c)/3); if(!parts) parts = 1; for(t=1;t<=parts;t++) { 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); if(line->linestyle && ! clipdepth) { lp.shapeline = &lshape->lines[line->linestyle-1]; add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp); lp.depth++; } if(line->fillstyle0 || line->fillstyle1) { assert(shape->numfillstyles); p.shapeline = line; add_line(dest, xx, yy, nx, ny, &p); } xx = nx; yy = ny; } } x = line->x; y = line->y; line = line->next; } swf_Process(dest, clipdepth); if(s2) { swf_Shape2Free(s2);rfx_free(s2);s2=0; } if(lshape) { swf_Shape2Free(lshape);rfx_free(lshape);lshape=0; } } 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) { int x = x1; if(x1>=x2) return; do { if(depth > z[x]) { z[x] = depth; } } while(++x>8; col.g = (col.g*col.a)>>8; col.b = (col.b*col.a)>>8; col.a = 255; do { if(depth >= z[x]) { 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; z[x] = depth; } } while(++x= z[x]) { line[x] = col; z[x] = depth; } } while(++xsx*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; } do { if(depth >= z[x]) { RGBA col; int xx = (int)(( (x - rx) * m22 - (y - ry) * m21)*det); int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det); int ainv; if(clipbitmap) { if(xx<0) xx=0; if(xx>=b->width) xx = b->width-1; if(yy<0) yy=0; if(yy>=b->height) yy = b->height-1; } 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]; ainv = 255-col.a; 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; z[x] = depth; } } while(++xinternal; int clip=1; layer_t*l = fillstate->layers; if(x1>=x2) //zero width? nothing to do. return; while(l) { if(l->fillid == 0) { /* not filled. TODO: we should never add those in the first place */ if(DEBUG&2) printf("(not filled)"); } else if(l->fillid > l->p->s->numfillstyles) { fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->numlinestyles); } else if(clipdepth) { /* filled region- not used for clipping */ clip = 0; } else { FILLSTYLE*f; if(DEBUG&2) printf("(%d -> %d style %d)", x1, x2, l->fillid); f = &l->p->s->fillstyles[l->fillid-1]; if(f->type == FILL_SOLID) { /* plain color fill */ fill_solid(line, zline, y, x1, x2, f->color, l->p->depth); } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) { /* TODO: optimize (do this in add_pixel()?) */ bitmap_t* b = i->bitmaps; while(b && b->id != f->id_bitmap) { b = b->next; } if(!b) { 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, i->multiply); } } else { fprintf(stderr, "Undefined fillmode: %02x\n", f->type); } } l = l->next; } if(clip && clipdepth) { fill_clip(line, zline, y, x1, x2, clipdepth); } } static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after) { layer_t*last=0,*l = state->layers; while(l && l->p->depth < depth) { last = l; l = l->next; } *before = last; if(l && l->p->depth == depth) *self = l; else *after = l; } static void delete_layer(state_t*state, layer_t*todel) { layer_t*before=todel->prev; layer_t*next = todel->next; rfx_free(todel); if(!before) { state->layers = next; if(next) next->prev = 0; } else { before->next = next; if(before->next) before->next->prev = before; } } static void add_layer(state_t*state, layer_t*before, layer_t*toadd) { if(!before) { toadd->next = state->layers; toadd->prev = 0; state->layers=toadd; } else { toadd->next = before->next; toadd->prev = before; before->next = toadd; } if(toadd->next) toadd->next->prev = toadd; } static void free_layers(state_t* state) { layer_t*l = state->layers; while(l) { layer_t*next = l->next; rfx_free(l); l = next; } } static void change_state(int y, state_t* state, renderpoint_t*p) { layer_t*before=0, *self=0, *after=0; if(DEBUG&2) { printf("[(%d,%d)/%d/%d-%d]", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1); } search_layer(state, p->depth, &before, &self, &after); if(self) { /* shape update */ if(self->fillid<0/*??*/ || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) { /* filling ends */ if(DEBUG&2) printf(""); delete_layer(state, self); } else { /*both fill0 and fill1 are set- exchange the two, updating the layer */ if(self->fillid == p->shapeline->fillstyle0) { self->fillid = p->shapeline->fillstyle1; self->p = p; if(DEBUG&2) printf(""); } else if(self->fillid == p->shapeline->fillstyle1) { self->fillid = p->shapeline->fillstyle0; self->p = p; if(DEBUG&2) printf(""); } else { /* buggy shape. keep everything as-is. */ if(DEBUG&2) printf(""); //fprintf(stderr, "\n", y); } } return; } else { layer_t* n = 0; if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) { /* this is a hack- a better way would be to make sure that we always get (0,32), (32, 33), (33, 0) in the right order if they happen to fall on the same pixel. (not: (0,32), (33, 0), (32, 33)) */ fprintf(stderr, "\n", y); return; } n = rfx_calloc(sizeof(layer_t)); if(DEBUG&2) printf("<+>"); n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1; n->p = p; add_layer(state, before, n); } } void swf_Process(RENDERBUF*dest, U32 clipdepth) { renderbuf_internal*i = (renderbuf_internal*)dest->internal; int y; if(i->ymax < i->ymin) { /* shape is empty. return. only, if it's a clipshape, remember the clipdepth */ if(clipdepth) { for(y=0;yheight2;y++) { if(clipdepth > i->lines[y].pending_clipdepth) i->lines[y].pending_clipdepth = clipdepth; } } return; //nothing (else) to do } if(clipdepth) { /* lines outside the clip shape are not filled immediately, only the highest clipdepth so far is stored there. They will be clipfilled once there's actually something about to happen in that line */ for(y=0;yymin;y++) { if(clipdepth > i->lines[y].pending_clipdepth) i->lines[y].pending_clipdepth = clipdepth; } for(y=i->ymax+1;yheight2;y++) { if(clipdepth > i->lines[y].pending_clipdepth) i->lines[y].pending_clipdepth = clipdepth; } } for(y=i->ymin;y<=i->ymax;y++) { int n; TAG*tag = i->lines[y].points; int num = i->lines[y].num; renderpoint_t*points = (renderpoint_t*)tag->data; RGBA*line = &i->img[i->width2*y]; int*zline = &i->zbuf[i->width2*y]; int lastx = 0; state_t fillstate; memset(&fillstate, 0, sizeof(state_t)); qsort(points, num, sizeof(renderpoint_t), compare_renderpoints); if(i->lines[y].pending_clipdepth && !clipdepth) { fill_clip(line, zline, y, 0, i->width2, i->lines[y].pending_clipdepth); i->lines[y].pending_clipdepth=0; } for(n=0;nx; int endx = next?next->x:i->width2; if(endx > i->width2) endx = i->width2; if(startx < 0) startx = 0; if(endx < 0) endx = 0; if(clipdepth) { /* for clipping, the inverse is filled TODO: lastx!=startx only at the start of the loop, so this might be moved up */ fill_clip(line, zline, y, lastx, startx, clipdepth); } change_state(y, &fillstate, p); fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth); /* if(y == 0 && startx == 232 && endx == 418) { printf("ymin=%d ymax=%d\n", i->ymin, i->ymax); for(n=0;nx, p->depth); } }*/ lastx = endx; if(endx == i->width2) break; } if(clipdepth) { /* TODO: is lastx *ever* != i->width2 here? */ fill_clip(line, zline, y, lastx, i->width2, clipdepth); } free_layers(&fillstate); i->lines[y].num = 0; swf_ClearTag(i->lines[y].points); } i->ymin = 0x7fffffff; i->ymax = -0x80000000; } 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; int antialize = i->antialize; if(antialize <= 1) /* no antializing */ { for(y=0;yheight2;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;yheight2;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;xwidth;x++) { int xpos = x*antialize; int yp; U32 r=0,g=0,b=0,a=0; for(yp=0;ypr; 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; font_t*font; } obj; } character_t; int compare_placements(const void *v1, const void *v2) { SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1; SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2; if(p1->depth != p2->depth) return (int)p1->depth - (int)p2->depth; else if(p2->clipdepth) return 1; // do the clip first else return -1; /* if(!p1->clipdepth) { if(!p2->clipdepth) { // !p1->clipdepth && !p2->clipdepth return (int)p1->depth - (int)p2->depth; } else { // !p1->clipdepth && p2->clipdepth if(p1->depth != p2->clipdepth) return (int)p1->depth - (int)p2->clipdepth; else return 1; // do the clip first } } else { if(!p2->clipdepth) { // p1->clipdepth && !p2->clipdepth if(p1->clipdepth != p2->depth) return (int)p1->clipdepth - (int)p2->depth; else return -1;// do the clip first } else { if(p1->clipdepth != p2->clipdepth) return (int)p1->clipdepth - (int)p2->clipdepth; else return (int)p1->depth - (int)p2->depth; } }*/ } 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].type != font_type) { fprintf(stderr, "ID %d is not a font\n", fontid); return; } else if(!info->idtable[fontid].obj.font) { fprintf(stderr, "Font %d unknown\n", fontid); return; } else { font = info->idtable[fontid].obj.font; } for(t=0;tm; 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 tag = swf->firstTag; numplacements = 0; while(tag) { if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) { numplacements++; } tag = tag->next; } placements = rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements); numplacements = 0; /* set background color */ color = swf_GetSWFBackgroundColor(swf); swf_Render_SetBackgroundColor(buf, color); /* parse definitions */ tag = swf->firstTag; while(tag) { if(swf_isDefiningTag(tag)) { int id = swf_GetDefineID(tag); idtable[id].tag = tag; idtable[id].bbox = rfx_alloc(sizeof(SRECT)); *idtable[id].bbox = swf_GetDefineBBox(tag); if(swf_isShapeTag(tag)) { SHAPE2* shape = rfx_calloc(sizeof(SHAPE2)); swf_ParseDefineShape(tag, shape); idtable[id].type = shape_type; idtable[id].obj.shape = shape; } else if(swf_isImageTag(tag)) { int width,height; RGBA*data = swf_ExtractImage(tag, &width, &height); idtable[id].type = image_type; swf_Render_AddImage(buf, id, data, width, height); free(data); } else if(tag->id == ST_DEFINEFONT || tag->id == ST_DEFINEFONT2) { 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;tnumchars;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; } else if(tag->id == ST_DEFINETEXT || tag->id == ST_DEFINETEXT2) { idtable[id].type = text_type; } } else if(tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) { SWFPLACEOBJECT p; swf_GetPlaceObject(tag, &p); /* TODO: add move and deletion */ placements[numplacements++] = p; swf_PlaceObjectFree(&p); //dirty! but it only frees fields we don't use } tag = tag->next; } qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements); for(t=0;tid; if(!idtable[id].tag) { fprintf(stderr, "rfxswf: Id %d is unknown\n", id); continue; } if(idtable[id].type == shape_type) { //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) { 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, &p->matrix, &m); /*printf("Text matrix:\n"); swf_DumpMatrix(stdout, &m); printf("Placement matrix:\n"); swf_DumpMatrix(stdout, &p->matrix); printf("Final matrix:\n"); swf_DumpMatrix(stdout, &info.m);*/ 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)); } } /* free id and depth tables again */ for(t=0;t<65536;t++) { if(idtable[t].bbox) { free(idtable[t].bbox); idtable[t].bbox=0; } if(idtable[t].type == shape_type) { SHAPE2* shape = idtable[t].obj.shape; if(shape) { 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;tnumchars;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); free(depthtable); }