1 /* swfrender.c
2 
3    functions for rendering swf content
4 
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7 
8    Copyright (c) 2004 Mederra Oy <http://www.mederra.fi>
9    Copyright (c) 2004 Matthias Kramm
10 
11    This program is free software; you can redistribute it and/or modify
12    it under the terms of the GNU General Public License as published by
13    the Free Software Foundation; either version 2 of the License, or
14    (at your option) any later version.
15 
16    This program is distributed in the hope that it will be useful,
17    but WITHOUT ANY WARRANTY; without even the implied warranty of
18    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19    GNU General Public License for more details.
20 
21    You should have received a copy of the GNU General Public License
22    along with this program; if not, write to the Free Software
23    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
24 
25 #include <assert.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include "../rfxswf.h"
29 
30 /* one bit flag: */
31 #define clip_type 0
32 #define fill_type 1
33 
34 typedef struct _renderpoint
35 {
36     float x;
37     U32 depth;
38 
39     SHAPELINE*shapeline;
40     SHAPE2*s;
41 
42 } renderpoint_t;
43 
44 /*
45     enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
46     float fx;
47     int x;
48     U32 depth;
49     U32 clipdepth;
50 
51     // solidfill;
52     RGBA color;
53 
54     // texturefill
55     bitmap_t* bitmap;
56 
57     // gradientfill
58     gradient_t* gradient;
59 
60     // texture- & gradientfill;
61     U32 x,y;
62     U32 dx,dy;
63 
64 */
65 
66 typedef struct _renderline
67 {
68     TAG*points; //incremented in 128 byte steps
69     int num;
70     U32 pending_clipdepth;
71 } renderline_t;
72 
73 typedef struct _bitmap {
74     int width;
75     int height;
76     RGBA*data;
77     int id;
78     struct _bitmap*next;
79 } bitmap_t;
80 
81 typedef struct _renderbuf_internal
82 {
83     renderline_t*lines;
84     bitmap_t*bitmaps;
85     int antialize;
86     int multiply;
87     int width2,height2;
88     int shapes;
89     int ymin, ymax;
90 
91     RGBA* img;
92     int* zbuf;
93 } renderbuf_internal;
94 
95 #define DEBUG 0
96 
add_pixel(RENDERBUF * dest,float x,int y,renderpoint_t * p)97 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
98 {
99     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
100     if(x >= i->width2 || y >= i->height2 || y<0) return;
101     p->x = x;
102     if(y<i->ymin) i->ymin = y;
103     if(y>i->ymax) i->ymax = y;
104 
105     i->lines[y].num++;
106     swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
107 }
108 
109 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
110    problem appears to often */
111 #define CUT 0.77887789
112 
113 #define INT(x) ((int)((x)+16)-16)
114 
add_line(RENDERBUF * buf,double x1,double y1,double x2,double y2,renderpoint_t * p)115 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
116 {
117     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
118     double diffx, diffy;
119     double ny1, ny2, stepx;
120 /*    if(DEBUG&4) {
121         int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
122         printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]\n", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
123     }*/
124     assert(p->shapeline);
125 
126     y1=y1*i->multiply;
127     y2=y2*i->multiply;
128     x1=x1*i->multiply;
129     x2=x2*i->multiply;
130 
131     y1 = y1/20.0;
132     y2 = y2/20.0;
133     x1 = x1/20.0;
134     x2 = x2/20.0;
135 
136     if(y2 < y1) {
137         double x;
138         double y;
139 	x = x1;x1 = x2;x2=x;
140 	y = y1;y1 = y2;y2=y;
141     }
142 
143     diffx = x2 - x1;
144     diffy = y2 - y1;
145 
146     ny1 = INT(y1)+CUT;
147     ny2 = INT(y2)+CUT;
148 
149     if(ny1 < y1) {
150         ny1 = INT(y1) + 1.0 + CUT;
151     }
152     if(ny2 >= y2) {
153         ny2 = INT(y2) - 1.0 + CUT;
154     }
155 
156     if(ny1 > ny2)
157         return;
158 
159     stepx = diffx/diffy;
160     x1 = x1 + (ny1-y1)*stepx;
161     x2 = x2 + (ny2-y2)*stepx;
162 
163     {
164 	int posy=INT(ny1);
165 	int endy=INT(ny2);
166 	double posx=0;
167 	double startx = x1;
168 
169 	while(posy<=endy) {
170 	    float xx = (float)(startx + posx);
171 	    add_pixel(buf, xx ,posy, p);
172 	    posx+=stepx;
173 	    posy++;
174 	}
175     }
176 }
177 #define PI 3.14159265358979
add_solidline(RENDERBUF * buf,double x1,double y1,double x2,double y2,double width,renderpoint_t * p)178 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, double width, renderpoint_t*p)
179 {
180     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
181 
182     double dx = x2-x1;
183     double dy = y2-y1;
184     double sd;
185     double d;
186 
187     int t;
188     int segments;
189     double lastx,lasty;
190     double vx,vy;
191     double xx,yy;
192 
193     /* Make sure the line is always at least one pixel wide */
194 #ifdef LINEMODE1
195     /* That's what Macromedia's Player does at least at zoom level >= 1.  */
196     width += 20;
197 #else
198     /* That's what Macromedia's Player seems to do at zoom level 0.  */
199     /* TODO: needs testing */
200 
201     /* TODO: how does this interact with scaling? */
202     if(width * i->multiply < 20)
203 	width = 20 / i->multiply;
204 #endif
205 
206     sd = (double)dx*(double)dx+(double)dy*(double)dy;
207     d = sqrt(sd);
208 
209     if(!dx && !dy) {
210         vx = 1;
211         vy = 0;
212     } else {
213         vx = ( dy/d);
214         vy = (-dx/d);
215     }
216 
217     segments = (int)(width/2);
218     if(segments < 2)
219         segments = 2;
220 
221     segments = 8;
222 
223     vx=vx*width*0.5;
224     vy=vy*width*0.5;
225 
226     xx = x2+vx;
227     yy = y2+vy;
228     add_line(buf, x1+vx, y1+vy, xx, yy, p);
229     lastx = xx;
230     lasty = yy;
231     for(t=1;t<segments;t++) {
232         double s = sin(t*PI/segments);
233         double c = cos(t*PI/segments);
234         xx = (x2 + vx*c - vy*s);
235         yy = (y2 + vx*s + vy*c);
236         add_line(buf, lastx, lasty, xx, yy, p);
237         lastx = xx;
238         lasty = yy;
239     }
240 
241     xx = (x2-vx);
242     yy = (y2-vy);
243     add_line(buf, lastx, lasty, xx, yy, p);
244     lastx = xx;
245     lasty = yy;
246     xx = (x1-vx);
247     yy = (y1-vy);
248     add_line(buf, lastx, lasty, xx, yy, p);
249     lastx = xx;
250     lasty = yy;
251     for(t=1;t<segments;t++) {
252         double s = sin(t*PI/segments);
253         double c = cos(t*PI/segments);
254         xx = (x1 - vx*c + vy*s);
255         yy = (y1 - vx*s - vy*c);
256         add_line(buf, lastx, lasty, xx, yy, p);
257         lastx = xx;
258         lasty = yy;
259     }
260     add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
261 }
262 
transform_point(MATRIX * m,int x,int y,int * dx,int * dy)263 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
264 {
265     SPOINT p,d;
266     p.x = x;
267     p.y = y;
268     d = swf_TurnPoint(p, m);
269     *dx = d.x;
270     *dy = d.y;
271 }
272 
compare_renderpoints(const void * _a,const void * _b)273 static int compare_renderpoints(const void * _a, const void * _b)
274 {
275     renderpoint_t*a = (renderpoint_t*)_a;
276     renderpoint_t*b = (renderpoint_t*)_b;
277     if(a->x < b->x) return -1;
278     if(a->x > b->x) return 1;
279     return 0;
280 }
281 
swf_Render_Init(RENDERBUF * buf,int posx,int posy,int width,int height,int antialize,int multiply)282 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, int antialize, int multiply)
283 {
284     renderbuf_internal*i;
285     int y;
286     memset(buf, 0, sizeof(RENDERBUF));
287     buf->width = width*multiply;
288     buf->height = height*multiply;
289     buf->posx = posx;
290     buf->posy = posy;
291     buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
292     i = (renderbuf_internal*)buf->internal;
293     if(antialize < 1)
294 	antialize = 1;
295     i->antialize = antialize;
296     i->multiply = multiply*antialize;
297     i->height2 = antialize*buf->height;
298     i->width2 = antialize*buf->width;
299     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
300     for(y=0;y<i->height2;y++) {
301 	memset(&i->lines[y], 0, sizeof(renderline_t));
302         i->lines[y].points = swf_InsertTag(0, 0);
303         i->lines[y].num = 0;
304     }
305     i->zbuf = (int*)rfx_calloc(sizeof(int)*i->width2*i->height2);
306     i->img = (RGBA*)rfx_calloc(sizeof(RGBA)*i->width2*i->height2);
307     i->shapes = 0;
308     i->ymin = 0x7fffffff;
309     i->ymax = -0x80000000;
310 }
swf_Render_SetBackground(RENDERBUF * buf,RGBA * img,int width,int height)311 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
312 {
313     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
314     int x,xx,y,yy;
315     int xstep=width*65536/i->width2;
316     int ystep=height*65536/i->height2;
317     if(i->shapes) {
318 	fprintf(stderr, "rfxswf: Warning: swf_Render_SetBackground() called after drawing shapes\n");
319     }
320     for(y=0,yy=0;y<i->height2;y++,yy+=ystep) {
321 	RGBA*src = &img[(yy>>16) * width];
322 	RGBA*line = &i->img[y * i->width2];
323 	for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
324 	    line[x] = src[xx>>16];
325 	}
326     }
327 }
swf_Render_SetBackgroundColor(RENDERBUF * buf,RGBA color)328 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
329 {
330     swf_Render_SetBackground(buf, &color, 1, 1);
331 }
swf_Render_AddImage(RENDERBUF * buf,U16 id,RGBA * img,int width,int height)332 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
333 {
334     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
335 
336     bitmap_t*bm = (bitmap_t*)rfx_calloc(sizeof(bitmap_t));
337     bm->id = id;
338     bm->width = width;
339     bm->height = height;
340     bm->data = (RGBA*)rfx_alloc(width*height*4);
341     memcpy(bm->data, img, width*height*4);
342 
343     bm->next = i->bitmaps;
344     i->bitmaps = bm;
345 }
swf_Render_ClearCanvas(RENDERBUF * dest)346 void swf_Render_ClearCanvas(RENDERBUF*dest)
347 {
348     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
349     int y;
350     for(y=0;y<i->height2;y++) {
351         swf_ClearTag(i->lines[y].points);
352     }
353     memset(i->zbuf, 0, sizeof(int)*i->width2*i->height2);
354     memset(i->img, 0, sizeof(RGBA)*i->width2*i->height2);
355 }
swf_Render_Delete(RENDERBUF * dest)356 void swf_Render_Delete(RENDERBUF*dest)
357 {
358     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
359     int y;
360     bitmap_t*b = i->bitmaps;
361 
362     /* delete canvas */
363     rfx_free(i->zbuf);
364     rfx_free(i->img);
365 
366     /* delete line buffers */
367     for(y=0;y<i->height2;y++) {
368         swf_DeleteTag(0, i->lines[y].points);
369         i->lines[y].points = 0;
370     }
371 
372     /* delete bitmaps */
373     while(b) {
374         bitmap_t*next = b->next;
375         free(b->data);b->data=0;
376         rfx_free(b);
377         b = next;
378     }
379 
380     rfx_free(i->lines); i->lines = 0;
381     rfx_free(dest->internal); dest->internal = 0;
382 }
383 
linestyle2fillstyle(SHAPE2 * shape)384 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
385 {
386     SHAPE2*s = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
387     int t;
388     s->numfillstyles = shape->numlinestyles;
389     s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
390     s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
391     for(t=0;t<shape->numlinestyles;t++) {
392         s->lines[t].fillstyle0 = t+1;
393         s->fillstyles[t].type = FILL_SOLID;
394         s->fillstyles[t].color = shape->linestyles[t].color;
395     }
396     return s;
397 }
398 
399 void swf_Process(RENDERBUF*dest, U32 clipdepth);
400 
matrixsize(MATRIX * m)401 double matrixsize(MATRIX*m)
402 {
403     double l1 = sqrt((m->sx /65536.0) * (m->sx /65536.0) + (m->r0 /65536.0) * (m->r0/65536.0) );
404     double l2 = sqrt((m->r1 /65536.0) * (m->r1 /65536.0) + (m->sy /65536.0) * (m->sy/65536.0) );
405     return sqrt(l1*l2);
406 }
407 
swf_RenderShape(RENDERBUF * dest,SHAPE2 * shape,MATRIX * m,CXFORM * c,U16 _depth,U16 _clipdepth)408 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
409 {
410     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
411 
412     SHAPELINE*line;
413     int x=0,y=0;
414     MATRIX mat = *m;
415     SHAPE2* s2 = 0;
416     SHAPE2* lshape = 0;
417     renderpoint_t p, lp;
418     U32 clipdepth;
419     double widthmultiply = matrixsize(m);
420 
421     memset(&p, 0, sizeof(renderpoint_t));
422     memset(&lp, 0, sizeof(renderpoint_t));
423 
424     clipdepth = _clipdepth? _clipdepth << 16 | 0xffff : 0;
425     p.depth = _depth << 16;
426 
427     mat.tx -= dest->posx*20;
428     mat.ty -= dest->posy*20;
429 
430     s2 = swf_Shape2Clone(shape);
431     line = s2->lines;
432     if(shape->numfillstyles) {
433         int t;
434         p.s = s2;
435         /* multiply fillstyles matrices with placement matrix-
436            important for texture and gradient fill */
437         for(t=0;t<s2->numfillstyles;t++) {
438             MATRIX nm;
439             swf_MatrixJoin(&nm, &mat, &s2->fillstyles[t].m);
440             /*nm.sx *= i->multiply;
441             nm.sy *= i->multiply;
442             nm.r0 *= i->multiply;
443             nm.r1 *= i->multiply;
444             nm.tx *= i->multiply;
445             nm.ty *= i->multiply;*/
446             s2->fillstyles[t].m = nm;
447         }
448     }
449 
450     if(shape->numlinestyles) {
451         lshape = linestyle2fillstyle(shape);
452         lp.s = lshape;
453         lp.depth = (_depth << 16)+1;
454     }
455 
456 
457     while(line)
458     {
459         int x1,y1,x2,y2,x3,y3;
460 
461         if(line->type == moveTo) {
462         } else if(line->type == lineTo) {
463             transform_point(&mat, x, y, &x1, &y1);
464             transform_point(&mat, line->x, line->y, &x3, &y3);
465 
466             if(line->linestyle && ! clipdepth) {
467                 lp.shapeline = &lshape->lines[line->linestyle-1];
468                 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
469                 lp.depth++;
470             }
471             if(line->fillstyle0 || line->fillstyle1) {
472                 assert(shape->numfillstyles);
473 		p.shapeline = line;
474                 add_line(dest, x1, y1, x3, y3, &p);
475             }
476         } else if(line->type == splineTo) {
477 	    int c,t,parts,qparts;
478 	    double xx,yy;
479 
480             transform_point(&mat, x, y, &x1, &y1);
481             transform_point(&mat, line->sx, line->sy, &x2, &y2);
482             transform_point(&mat, line->x, line->y, &x3, &y3);
483 
484             c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
485             xx=x1;
486 	    yy=y1;
487 
488             parts = (int)(sqrt((float)c)/3);
489             if(!parts) parts = 1;
490 
491             for(t=1;t<=parts;t++) {
492                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
493                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
494 
495                 if(line->linestyle && ! clipdepth) {
496                     lp.shapeline = &lshape->lines[line->linestyle-1];
497                     add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
498                     lp.depth++;
499                 }
500                 if(line->fillstyle0 || line->fillstyle1) {
501                     assert(shape->numfillstyles);
502 		    p.shapeline = line;
503                     add_line(dest, xx, yy, nx, ny, &p);
504                 }
505 
506                 xx = nx;
507                 yy = ny;
508             }
509         }
510         x = line->x;
511         y = line->y;
512         line = line->next;
513     }
514 
515     swf_Process(dest, clipdepth);
516 
517     if(s2) {
518 	swf_Shape2Free(s2);rfx_free(s2);s2=0;
519     }
520     if(lshape) {
521 	swf_Shape2Free(lshape);rfx_free(lshape);lshape=0;
522     }
523 
524 }
525 
526 static RGBA color_red = {255,255,0,0};
527 static RGBA color_white = {255,255,255,255};
528 static RGBA color_black = {255,0,0,0};
529 
fill_clip(RGBA * line,int * z,int y,int x1,int x2,U32 depth)530 static void fill_clip(RGBA*line, int*z, int y, int x1, int x2, U32 depth)
531 {
532     int x = x1;
533     if(x1>=x2)
534 	return;
535     do {
536 	if(depth > z[x]) {
537 	    z[x] = depth;
538 	}
539     } while(++x<x2);
540 }
541 
542 
fill_solid(RGBA * line,int * z,int y,int x1,int x2,RGBA col,U32 depth)543 static void fill_solid(RGBA*line, int*z, int y, int x1, int x2, RGBA col, U32 depth)
544 {
545     int x = x1;
546 
547     if(col.a!=255) {
548         int ainv = 255-col.a;
549         col.r = (col.r*col.a)>>8;
550         col.g = (col.g*col.a)>>8;
551         col.b = (col.b*col.a)>>8;
552         col.a = 255;
553         do {
554 	    if(depth >= z[x]) {
555 		line[x].r = ((line[x].r*ainv)>>8)+col.r;
556 		line[x].g = ((line[x].g*ainv)>>8)+col.g;
557 		line[x].b = ((line[x].b*ainv)>>8)+col.b;
558 		line[x].a = 255;
559 		z[x] = depth;
560 	    }
561         } while(++x<x2);
562     } else {
563         do {
564 	    if(depth >= z[x]) {
565 		line[x] = col;
566 		z[x] = depth;
567 	    }
568         } while(++x<x2);
569     }
570 }
571 
clamp(int v)572 static int inline clamp(int v)
573 {
574     if(v>255) return 255;
575     else return v;
576 }
577 
fill_bitmap(RGBA * line,int * z,int y,int x1,int x2,MATRIX * m,bitmap_t * b,int clipbitmap,U32 depth,double fmultiply)578 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)
579 {
580     int x = x1;
581 
582     double m11= m->sx*fmultiply/65536.0, m21= m->r1*fmultiply/65536.0;
583     double m12= m->r0*fmultiply/65536.0, m22= m->sy*fmultiply/65536.0;
584     double rx = m->tx*fmultiply/20.0;
585     double ry = m->ty*fmultiply/20.0;
586 
587     double det = m11*m22 - m12*m21;
588     if(fabs(det) < 0.0005) {
589 	/* x direction equals y direction- the image is invisible */
590 	return;
591     }
592     det = 20.0/det;
593 
594     if(!b->width || !b->height) {
595         fill_solid(line, z, y, x1, x2, color_red, depth);
596         return;
597     }
598 
599     do {
600 	if(depth >= z[x]) {
601 	    RGBA col;
602 	    int xx = (int)((  (x - rx) * m22 - (y - ry) * m21)*det);
603 	    int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
604 	    int ainv;
605 
606 	    if(clipbitmap) {
607 		if(xx<0) xx=0;
608 		if(xx>=b->width) xx = b->width-1;
609 		if(yy<0) yy=0;
610 		if(yy>=b->height) yy = b->height-1;
611 	    } else {
612 		xx %= b->width;
613 		yy %= b->height;
614 		if(xx<0) xx += b->width;
615 		if(yy<0) yy += b->height;
616 	    }
617 
618 	    col = b->data[yy*b->width+xx];
619 	    ainv = 255-col.a;
620 
621 	    line[x].r = clamp(((line[x].r*ainv)>>8)+col.r);
622 	    line[x].g = clamp(((line[x].g*ainv)>>8)+col.g);
623 	    line[x].b = clamp(((line[x].b*ainv)>>8)+col.b);
624 	    line[x].a = 255;
625 
626 	    z[x] = depth;
627 	}
628     } while(++x<x2);
629 }
630 
fill_gradient(RGBA * line,int * z,int y,int x1,int x2,MATRIX * m,GRADIENT * g,int type,U32 depth,double fmultiply)631 static void fill_gradient(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, GRADIENT*g, int type, U32 depth, double fmultiply)
632 {
633     int x = x1;
634 
635     double m11= m->sx*fmultiply/80, m21= m->r1*fmultiply/80;
636     double m12= m->r0*fmultiply/80, m22= m->sy*fmultiply/80;
637     double rx = m->tx*fmultiply/20.0;
638     double ry = m->ty*fmultiply/20.0;
639 
640     double det = m11*m22 - m12*m21;
641     if(fabs(det) < 0.0005) {
642 	/* x direction equals y direction- the image is invisible */
643 	return;
644     }
645     det = 1.0/det;
646 
647     RGBA palette[512];
648     RGBA oldcol = g->rgba[0];
649     int r0 = g->ratios[0]*2;
650     int t;
651     for(t=0;t<r0;t++)
652 	palette[t] = oldcol;
653     for(t=1;t<g->num;t++) {
654 	int r1 = g->ratios[t]*2;
655 	RGBA newcol = g->rgba[t];
656 	if(r0 == r1)
657 	    continue;
658 	//printf("%d %d->%d %02x%02x%02x%02x->%02x%02x%02x%02x\n",
659 	//	t, r0, r1, oldcol.r,oldcol.g,oldcol.b,oldcol.a,
660 	//	newcol.r,newcol.g,newcol.b,newcol.a);
661 	double f = 1.0 / (r1-r0);
662 	double p0 = 1;
663 	double p1 = 0;
664 	int s;
665 	for(;r0<=r1;r0++) {
666 	    palette[r0].r = oldcol.r*p0 + newcol.r*p1;
667 	    palette[r0].g = oldcol.g*p0 + newcol.g*p1;
668 	    palette[r0].b = oldcol.b*p0 + newcol.b*p1;
669 	    palette[r0].a = oldcol.a*p0 + newcol.a*p1;
670 	    p0 -= f;
671 	    p1 += f;
672 	}
673 	oldcol = newcol;
674     }
675     for(t=r0;t<512;t++)
676 	palette[t] = oldcol;
677 
678     do {
679 	if(depth >= z[x]) {
680 	    RGBA col;
681 	    double xx = (  (x - rx) * m22 - (y - ry) * m21)*det;
682 	    double yy = (- (x - rx) * m12 + (y - ry) * m11)*det;
683 
684 	    if(type == FILL_LINEAR) {
685 		int xr = xx*256;
686 		if(xr<-256)
687 		    xr = -256;
688 		if(xr>255)
689 		    xr = 255;
690 		col = palette[xr+256];
691 	    } else {
692 		int xr = sqrt(xx*xx+yy*yy)*511;
693 		if(xr<0)
694 		    xr = 0;
695 		if(xr>511)
696 		    xr = 511;
697 		col = palette[xr];
698 	    }
699 	    int ainv;
700 	    ainv = 255-col.a;
701 	    line[x].r = clamp(((line[x].r*ainv)>>8)+col.r);
702 	    line[x].g = clamp(((line[x].g*ainv)>>8)+col.g);
703 	    line[x].b = clamp(((line[x].b*ainv)>>8)+col.b);
704 	    line[x].a = 255;
705 
706 	    z[x] = depth;
707 	}
708     } while(++x<x2);
709 }
710 
711 typedef struct _layer {
712     int fillid;
713     renderpoint_t*p;
714     struct _layer*next;
715     struct _layer*prev;
716 } layer_t;
717 
718 typedef struct {
719     layer_t*layers;
720 } state_t;
721 
722 
fill(RENDERBUF * dest,RGBA * line,int * zline,int y,int x1,int x2,state_t * fillstate,U32 clipdepth)723 static void fill(RENDERBUF*dest, RGBA*line, int*zline, int y, int x1, int x2, state_t*fillstate, U32 clipdepth)
724 {
725     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
726     int clip=1;
727 
728     layer_t*l = fillstate->layers;
729 
730     if(x1>=x2) //zero width? nothing to do.
731         return;
732 
733     while(l) {
734         if(l->fillid == 0) {
735             /* not filled. TODO: we should never add those in the first place */
736             if(DEBUG&2)
737                 printf("(not filled)");
738         } else if(l->fillid > l->p->s->numfillstyles) {
739             fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->numlinestyles);
740         } else if(clipdepth) {
741 	    /* filled region- not used for clipping */
742 	    clip = 0;
743 	} else {
744             FILLSTYLE*f;
745             if(DEBUG&2)
746                 printf("(%d -> %d style %d)", x1, x2, l->fillid);
747 
748             f = &l->p->s->fillstyles[l->fillid-1];
749 
750 	    if(f->type == FILL_SOLID) {
751                 /* plain color fill */
752                 fill_solid(line, zline, y, x1, x2, f->color, l->p->depth);
753             } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED || f->type == (FILL_TILED|2) || f->type == (FILL_CLIPPED|2)) {
754                 /* TODO: optimize (do this in add_pixel()?) */
755                 bitmap_t* b = i->bitmaps;
756                 while(b && b->id != f->id_bitmap) {
757                     b = b->next;
758                 }
759                 if(!b) {
760                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
761                     fill_solid(line, zline, y, x1, x2, color_red, l->p->depth);
762                 } else {
763                     fill_bitmap(line, zline, y, x1, x2, &f->m, b, /*clipped?*/f->type&1, l->p->depth, i->multiply);
764                 }
765             } else if(f->type == FILL_LINEAR || f->type == FILL_RADIAL) {
766 		fill_gradient(line, zline, y, x1, x2, &f->m, &f->gradient, f->type, l->p->depth, i->multiply);
767             } else {
768                 fprintf(stderr, "Undefined fillmode: %02x\n", f->type);
769 	    }
770         }
771 	l = l->next;
772     }
773     if(clip && clipdepth) {
774 	fill_clip(line, zline, y, x1, x2, clipdepth);
775     }
776 }
777 
search_layer(state_t * state,int depth,layer_t ** before,layer_t ** self,layer_t ** after)778 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
779 {
780     layer_t*last=0,*l = state->layers;
781     while(l && l->p->depth < depth) {
782         last = l;
783         l = l->next;
784     }
785     *before = last;
786     if(l && l->p->depth == depth)
787         *self = l;
788     else
789         *after = l;
790 }
delete_layer(state_t * state,layer_t * todel)791 static void delete_layer(state_t*state, layer_t*todel)
792 {
793     layer_t*before=todel->prev;
794     layer_t*next = todel->next;
795     rfx_free(todel);
796     if(!before) {
797         state->layers = next;
798         if(next)
799             next->prev = 0;
800     } else {
801         before->next = next;
802         if(before->next)
803             before->next->prev = before;
804     }
805 }
add_layer(state_t * state,layer_t * before,layer_t * toadd)806 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
807 {
808     if(!before) {
809         toadd->next = state->layers;
810         toadd->prev = 0;
811         state->layers=toadd;
812     } else {
813         toadd->next = before->next;
814         toadd->prev = before;
815         before->next = toadd;
816     }
817     if(toadd->next)
818         toadd->next->prev = toadd;
819 }
free_layers(state_t * state)820 static void free_layers(state_t* state)
821 {
822     layer_t*l = state->layers;
823     while(l) {
824         layer_t*next = l->next;
825         rfx_free(l);
826         l = next;
827     }
828 }
829 
change_state(int y,state_t * state,renderpoint_t * p)830 static void change_state(int y, state_t* state, renderpoint_t*p)
831 {
832     layer_t*before=0, *self=0, *after=0;
833 
834     if(DEBUG&2) {
835         printf("[(%f,%d)/%d/%d-%d]", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
836     }
837 
838     search_layer(state, p->depth, &before, &self, &after);
839 
840     if(self) {
841         /* shape update */
842         if(self->fillid<0/*??*/ || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
843             /* filling ends */
844             if(DEBUG&2) printf("<D>");
845 
846             delete_layer(state, self);
847         } else {
848             /*both fill0 and fill1 are set- exchange the two, updating the layer */
849             if(self->fillid == p->shapeline->fillstyle0) {
850                 self->fillid = p->shapeline->fillstyle1;
851                 self->p = p;
852                 if(DEBUG&2) printf("<X>");
853             } else if(self->fillid == p->shapeline->fillstyle1) {
854                 self->fillid = p->shapeline->fillstyle0;
855                 self->p = p;
856                 if(DEBUG&2) printf("<X>");
857             } else {
858                 /* buggy shape. keep everything as-is. */
859                 if(DEBUG&2) printf("<!>");
860                 //fprintf(stderr, "<line %d: bad swap>\n", y);
861             }
862         }
863         return;
864     } else {
865         layer_t* n = 0;
866         if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
867             /* this is a hack- a better way would be to make sure that
868                we always get (0,32), (32, 33), (33, 0) in the right order if
869                they happen to fall on the same pixel.
870                (not: (0,32), (33, 0), (32, 33))
871 	       Notice: Weird fill styles appear if linestyles are involved, too.
872             */
873             fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
874             return;
875         }
876 
877         n = (layer_t*)rfx_calloc(sizeof(layer_t));
878 
879         if(DEBUG&2) printf("<+>");
880 
881 	n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
882 	n->p = p;
883 
884         add_layer(state, before, n);
885     }
886 }
887 
swf_Process(RENDERBUF * dest,U32 clipdepth)888 void swf_Process(RENDERBUF*dest, U32 clipdepth)
889 {
890     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
891     int y;
892 
893     if(i->ymax < i->ymin) {
894 	/* shape is empty. return.
895 	   only, if it's a clipshape, remember the clipdepth */
896 	if(clipdepth) {
897 	    for(y=0;y<i->height2;y++) {
898 		if(clipdepth > i->lines[y].pending_clipdepth)
899 		    i->lines[y].pending_clipdepth = clipdepth;
900 	    }
901 	}
902 	return; //nothing (else) to do
903     }
904 
905     if(clipdepth) {
906 	/* lines outside the clip shape are not filled
907 	   immediately, only the highest clipdepth so far is
908 	   stored there. They will be clipfilled once there's
909 	   actually something about to happen in that line */
910 	for(y=0;y<i->ymin;y++) {
911 	    if(clipdepth > i->lines[y].pending_clipdepth)
912 		i->lines[y].pending_clipdepth = clipdepth;
913 	}
914 	for(y=i->ymax+1;y<i->height2;y++) {
915 	    if(clipdepth > i->lines[y].pending_clipdepth)
916 		i->lines[y].pending_clipdepth = clipdepth;
917 	}
918     }
919 
920     for(y=i->ymin;y<=i->ymax;y++) {
921         int n;
922         TAG*tag = i->lines[y].points;
923         int num = i->lines[y].num;
924 	renderpoint_t*points = (renderpoint_t*)tag->data;
925         RGBA*line = &i->img[i->width2*y];
926         int*zline = &i->zbuf[i->width2*y];
927 	int lastx = 0;
928 	state_t fillstate;
929         memset(&fillstate, 0, sizeof(state_t));
930         qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
931 	/* resort points */
932 	/*if(y==884) {
933 	    for(n=0;n<num;n++) {
934 		printf("%f (%d/%d) %d\n", points[n].x,
935 			points[n].shapeline->fillstyle0,
936 			points[n].shapeline->fillstyle1,
937 			points[n].shapeline->linestyle);
938 	    }
939 	}*/
940 
941 	if(i->lines[y].pending_clipdepth && !clipdepth) {
942 	    fill_clip(line, zline, y, 0, i->width2, i->lines[y].pending_clipdepth);
943 	    i->lines[y].pending_clipdepth=0;
944 	}
945 
946         for(n=0;n<num;n++) {
947             renderpoint_t*p = &points[n];
948             renderpoint_t*next= n<num-1?&points[n+1]:0;
949             int startx = (int)p->x;
950             int endx = (int)(next?next->x:i->width2);
951             if(endx > i->width2)
952                 endx = i->width2;
953             if(startx < 0)
954                 startx = 0;
955             if(endx < 0)
956                 endx = 0;
957 
958 	    if(clipdepth) {
959 		/* for clipping, the inverse is filled
960 		   TODO: lastx!=startx only at the start of the loop,
961 		         so this might be moved up
962 		 */
963 		fill_clip(line, zline, y, lastx, startx, clipdepth);
964 	    }
965 	    change_state(y, &fillstate, p);
966 
967 	    fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth);
968 /*	    if(y == 0 && startx == 232 && endx == 418) {
969 		printf("ymin=%d ymax=%d\n", i->ymin, i->ymax);
970 		for(n=0;n<num;n++) {
971 		    renderpoint_t*p = &points[n];
972 		    printf("x=%f depth=%08x\n", p->x, p->depth);
973 		}
974 	    }*/
975 
976 	    lastx = endx;
977             if(endx == i->width2)
978                 break;
979         }
980 	if(clipdepth) {
981 	    /* TODO: is lastx *ever* != i->width2 here? */
982 	    fill_clip(line, zline, y, lastx, i->width2, clipdepth);
983 	}
984         free_layers(&fillstate);
985 
986 	i->lines[y].num = 0;
987 	swf_ClearTag(i->lines[y].points);
988     }
989     i->ymin = 0x7fffffff;
990     i->ymax = -0x80000000;
991 }
992 
swf_Render(RENDERBUF * dest)993 RGBA* swf_Render(RENDERBUF*dest)
994 {
995     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
996     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
997     int y;
998     int antialize = i->antialize;
999 
1000     if(antialize <= 1) /* no antializing */ {
1001 	for(y=0;y<i->height2;y++) {
1002 	    RGBA*line = &i->img[y*i->width2];
1003 	    memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
1004 	}
1005     } else {
1006 	RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*antialize);
1007 	int q = antialize*antialize;
1008 	int ypos = 0;
1009 	for(y=0;y<i->height2;y++) {
1010 	    int n;
1011 	    ypos = y % antialize;
1012 	    lines[ypos] = &i->img[y*i->width2];
1013 	    if(ypos == antialize-1) {
1014 		RGBA*out = &img[(y / antialize)*dest->width];
1015 		int x;
1016 		int r,g,b,a;
1017 		for(x=0;x<dest->width;x++) {
1018 		    int xpos = x*antialize;
1019 		    int yp;
1020 		    U32 r=0,g=0,b=0,a=0;
1021 		    for(yp=0;yp<antialize;yp++) {
1022 			RGBA*lp = &lines[yp][xpos];
1023 			int xp;
1024 			for(xp=0;xp<antialize;xp++) {
1025 			    RGBA*p = &lp[xp];
1026 			    r += p->r;
1027 			    g += p->g;
1028 			    b += p->b;
1029 			    a += p->a;
1030 			}
1031 		    }
1032 		    out[x].r = r / q;
1033 		    out[x].g = g / q;
1034 		    out[x].b = b / q;
1035 		    out[x].a = a / q;
1036 		}
1037 	    }
1038 	}
1039 	rfx_free(lines);
1040     }
1041     return img;
1042 }
1043 
1044 typedef struct
1045 {
1046     int numchars;
1047     SHAPE2**glyphs;
1048 } font_t;
1049 
1050 enum CHARACTER_TYPE {none_type, shape_type, image_type, text_type, edittext_type, font_type, sprite_type};
1051 typedef struct
1052 {
1053     TAG*tag;
1054     SRECT*bbox;
1055     enum CHARACTER_TYPE type;
1056     union {
1057         SHAPE2*shape;
1058         font_t*font;
1059     } obj;
1060 } character_t;
1061 
compare_placements(const void * v1,const void * v2)1062 int compare_placements(const void *v1, const void *v2)
1063 {
1064     SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1;
1065     SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2;
1066     if(p1->depth != p2->depth)
1067 	return (int)p1->depth - (int)p2->depth;
1068     else
1069 	if(p2->clipdepth)
1070 	    return 1; // do the clip first
1071 	else
1072 	    return -1;
1073 
1074 /*    if(!p1->clipdepth) {
1075 	if(!p2->clipdepth) {
1076 	    // !p1->clipdepth && !p2->clipdepth
1077 	    return (int)p1->depth - (int)p2->depth;
1078 	} else {
1079 	    // !p1->clipdepth && p2->clipdepth
1080 	    if(p1->depth != p2->clipdepth)
1081 		return (int)p1->depth - (int)p2->clipdepth;
1082 	    else
1083 		return 1; // do the clip first
1084 	}
1085     } else {
1086 	if(!p2->clipdepth) {
1087 	    // p1->clipdepth && !p2->clipdepth
1088 	    if(p1->clipdepth != p2->depth)
1089 		return (int)p1->clipdepth - (int)p2->depth;
1090 	    else
1091 		return -1;// do the clip first
1092 	} else {
1093 	    if(p1->clipdepth != p2->clipdepth)
1094 		return (int)p1->clipdepth - (int)p2->clipdepth;
1095 	    else
1096 		return (int)p1->depth - (int)p2->depth;
1097 	}
1098     }*/
1099 }
1100 
1101 typedef struct textcallbackblock
1102 {
1103     character_t*idtable;
1104     U16 depth;
1105     U16 clipdepth;
1106     CXFORM* cxform;
1107     MATRIX m;
1108     RENDERBUF*buf;
1109 } textcallbackblock_t;
1110 
textcallback(void * self,int * chars,int * xpos,int nr,int fontid,int fontsize,int xstart,int ystart,RGBA * color)1111 static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize,
1112 		    int xstart, int ystart, RGBA* color)
1113 {
1114     textcallbackblock_t * info = (textcallbackblock_t*)self;
1115     font_t*font = 0;
1116     int t;
1117     if(info->idtable[fontid].type != font_type) {
1118 	fprintf(stderr, "ID %d is not a font\n", fontid);
1119 	return;
1120     } else if(!info->idtable[fontid].obj.font) {
1121 	fprintf(stderr, "Font %d unknown\n", fontid);
1122 	return;
1123     } else {
1124 	font  = info->idtable[fontid].obj.font;
1125     }
1126     for(t=0;t<nr;t++) {
1127 	int x = xstart + xpos[t];
1128 	int y = ystart;
1129 	MATRIX m = info->m;
1130 	SPOINT p;
1131 
1132 	p.x = x; p.y = y;
1133 	p = swf_TurnPoint(p, &m);
1134 
1135 	m.sx = (m.sx * fontsize) / 1024;
1136 	m.sy = (m.sy * fontsize) / 1024;
1137 	m.r0 = (m.r0 * fontsize) / 1024;
1138 	m.r1 = (m.r1 * fontsize) / 1024;
1139 	m.tx = p.x;
1140 	m.ty = p.y;
1141 
1142 	if(chars[t]<0 || chars[t]>= font->numchars) {
1143 	    fprintf(stderr, "Character out of range: %d\n", chars[t]);
1144 	} else {
1145 	    SHAPE2*shape = font->glyphs[chars[t]];
1146 	    shape->fillstyles[0].color = *color; //q&d
1147 	    /*printf("Rendering char %d (size %d, x:%d, y:%d) color:%02x%02x%02x%02x\n", chars[t], fontsize, x, y,
1148 		    color->a, color->r, color->g, color->b);
1149 	    swf_DumpMatrix(stdout, &m);
1150 	    swf_DumpShape(shape);*/
1151 	    swf_RenderShape(info->buf, shape, &m, info->cxform, info->depth, info->clipdepth);
1152 	}
1153     }
1154 }
1155 
renderFromTag(RENDERBUF * buf,character_t * idtable,TAG * firstTag,MATRIX * m)1156 static void renderFromTag(RENDERBUF*buf, character_t*idtable, TAG*firstTag, MATRIX*m)
1157 {
1158     TAG*tag = 0;
1159     int numplacements = 0;
1160     SWFPLACEOBJECT* placements;
1161 
1162     tag = firstTag;
1163     numplacements = 0;
1164     while(tag) {
1165         if(tag->id == ST_PLACEOBJECT ||
1166            tag->id == ST_PLACEOBJECT2) {
1167 	    numplacements++;
1168 	}
1169 	if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1170 	    break;
1171 	tag = tag->next;
1172     }
1173     placements = (SWFPLACEOBJECT*)rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
1174     numplacements = 0;
1175 
1176     tag = firstTag;
1177     while(tag) {
1178 	if(swf_isPlaceTag(tag)) {
1179 	    SWFPLACEOBJECT p;
1180 	    swf_GetPlaceObject(tag, &p);
1181 	    /* TODO: add move and deletion */
1182 	    placements[numplacements++] = p;
1183 	    swf_PlaceObjectFree(&p); //dirty! but it only frees fields we don't use
1184 	}
1185 	if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1186 	    break;
1187         tag = tag->next;
1188     }
1189 
1190     qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements);
1191 
1192     int t;
1193     for(t=0;t<numplacements;t++) {
1194         SWFPLACEOBJECT*p = &placements[t];
1195         int id = p->id;
1196 	MATRIX m2;
1197 	swf_MatrixJoin(&m2, m, &p->matrix);
1198 
1199         if(!idtable[id].tag) {
1200             fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1201             continue;
1202         }
1203 
1204         if(idtable[id].type == shape_type) {
1205             //SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1206             swf_RenderShape(buf, idtable[id].obj.shape, &m2, &p->cxform, p->depth, p->clipdepth);
1207 	} else if(idtable[id].type == sprite_type) {
1208 	    swf_UnFoldSprite(idtable[id].tag);
1209 	    renderFromTag(buf, idtable, idtable[id].tag->next, &m2);
1210 	    swf_FoldSprite(idtable[id].tag);
1211         } else if(idtable[id].type == text_type) {
1212 	    TAG* tag = idtable[id].tag;
1213 	    textcallbackblock_t info;
1214 	    MATRIX mt;
1215 
1216 	    swf_SetTagPos(tag, 0);
1217 	    swf_GetU16(tag);
1218 	    swf_GetRect(tag,0);
1219   	    swf_GetMatrix(tag,&mt);
1220 	    swf_MatrixJoin(&info.m, &m2, &mt);
1221 	    /*printf("Text matrix:\n");
1222 	    swf_DumpMatrix(stdout, &m);
1223 	    printf("Placement matrix:\n");
1224 	    swf_DumpMatrix(stdout, &p->matrix);
1225 	    printf("Final matrix:\n");
1226 	    swf_DumpMatrix(stdout, &info.m);*/
1227 
1228 	    info.idtable = idtable;
1229 	    info.depth = p->depth;
1230 	    info.cxform = &p->cxform;
1231 	    info.clipdepth = p->clipdepth;
1232 	    info.buf = buf;
1233 
1234 	    swf_ParseDefineText(tag, textcallback, &info);
1235         } else if(idtable[id].type == edittext_type) {
1236 	    TAG* tag = idtable[id].tag;
1237             U16 flags = swf_GetBits(tag, 16);
1238 	    if(flags & ET_HASTEXT) {
1239                 fprintf(stderr, "edittext not supported yet (id %d)\n", id);
1240             }
1241         } else {
1242             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1243         }
1244     }
1245 
1246     free(placements);
1247 }
1248 
swf_RenderSWF(RENDERBUF * buf,SWF * swf)1249 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
1250 {
1251     TAG*tag;
1252     int t;
1253     RGBA color;
1254 
1255     swf_OptimizeTagOrder(swf);
1256     swf_FoldAll(swf);
1257 
1258     character_t* idtable = (character_t*)rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
1259 
1260     /* set background color */
1261     color = swf_GetSWFBackgroundColor(swf);
1262     swf_Render_SetBackgroundColor(buf, color);
1263 
1264     /* parse definitions */
1265     tag = swf->firstTag;
1266     while(tag) {
1267         if(swf_isDefiningTag(tag)) {
1268             int id = swf_GetDefineID(tag);
1269             idtable[id].tag = tag;
1270             idtable[id].bbox = (SRECT*)rfx_alloc(sizeof(SRECT));
1271             *idtable[id].bbox = swf_GetDefineBBox(tag);
1272 
1273             if(swf_isShapeTag(tag)) {
1274                 SHAPE2* shape = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
1275                 swf_ParseDefineShape(tag, shape);
1276                 idtable[id].type = shape_type;
1277                 idtable[id].obj.shape = shape;
1278             } else if(swf_isImageTag(tag)) {
1279 		int width,height;
1280                 RGBA*data = swf_ExtractImage(tag, &width, &height);
1281                 idtable[id].type = image_type;
1282                 swf_Render_AddImage(buf, id, data, width, height);
1283 		free(data);
1284             } else if(tag->id == ST_DEFINEFONT ||
1285                       tag->id == ST_DEFINEFONT2) {
1286 		int t;
1287 		SWFFONT*swffont;
1288 		font_t*font = (font_t*)rfx_calloc(sizeof(font_t));
1289 		idtable[id].obj.font = font;
1290                 swf_FontExtract(swf,id,&swffont);
1291 		font->numchars = swffont->numchars;
1292 		font->glyphs = (SHAPE2**)rfx_calloc(sizeof(SHAPE2*)*font->numchars);
1293 		for(t=0;t<font->numchars;t++) {
1294 		    if(!swffont->glyph[t].shape->fillstyle.n) {
1295 			/* the actual fill color will be overwritten while rendering */
1296 			swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
1297 		    }
1298 		    font->glyphs[t] = swf_ShapeToShape2(swffont->glyph[t].shape);
1299 		}
1300 		swf_FontFree(swffont);
1301                 idtable[id].type = font_type;
1302 
1303             } else if(tag->id == ST_DEFINEFONTINFO ||
1304                       tag->id == ST_DEFINEFONTINFO2) {
1305                 idtable[id].type = font_type;
1306             } else if(tag->id == ST_DEFINETEXT ||
1307                       tag->id == ST_DEFINETEXT2) {
1308                 idtable[id].type = text_type;
1309             } else if(tag->id == ST_DEFINESPRITE) {
1310 		idtable[id].type = sprite_type;
1311             } else if(tag->id == ST_DEFINEEDITTEXT) {
1312 		idtable[id].type = edittext_type;
1313 	    }
1314         }
1315 	tag = tag->next;
1316     }
1317     MATRIX m;
1318     swf_GetMatrix(0, &m);
1319     renderFromTag(buf, idtable, swf->firstTag, &m);
1320 
1321     /* free id and depth tables again */
1322     for(t=0;t<65536;t++) {
1323         if(idtable[t].bbox) {
1324             free(idtable[t].bbox);
1325             idtable[t].bbox=0;
1326         }
1327         if(idtable[t].type == shape_type) {
1328             SHAPE2* shape = idtable[t].obj.shape;
1329             if(shape) {
1330                 swf_Shape2Free(shape); // FIXME
1331                 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
1332             }
1333         } else if(idtable[t].type == font_type) {
1334 	    font_t* font = idtable[t].obj.font;
1335 	    if(font) {
1336 		if(font->glyphs) {
1337 		    int t;
1338 		    for(t=0;t<font->numchars;t++) {
1339 			swf_Shape2Free(font->glyphs[t]);
1340 			free(font->glyphs[t]); font->glyphs[t] = 0;
1341 		    }
1342 		    free(font->glyphs);
1343 		    font->glyphs = 0;
1344 		}
1345 		free(idtable[t].obj.font); idtable[t].obj.font = 0;
1346 		font = 0;
1347 	    }
1348 	}
1349     }
1350     free(idtable);
1351 }
1352 
1353