* some bugfixes
[swftools.git] / lib / modules / swfrender.c
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
27 typedef struct _dummyshape
28 {
29     SHAPE2*shape;
30     //CXFORM //TODO
31     struct _dummyshape*next;
32 } dummyshape_t;
33
34 /* one bit flag: */
35 #define clip_type 0
36 #define fill_type 1
37
38 typedef struct _renderpoint
39 {
40     char type;
41     float x;
42     U32 depth;
43
44     SHAPELINE*shapeline;
45     
46     U32 clipdepth;
47     dummyshape_t*s;
48     
49 } renderpoint_t;
50
51 /* 
52     enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
53     float fx;
54     int x;
55     U32 depth;
56     U32 clipdepth;
57
58     // solidfill;
59     RGBA color; 
60     
61     // texturefill
62     bitmap_t* bitmap;
63
64     // gradientfill
65     gradient_t* gradient;
66
67     // texture- & gradientfill;
68     U32 x,y;
69     U32 dx,dy;
70
71 */
72
73 typedef struct _renderline
74 {
75     TAG*points; //incremented in 128 byte steps
76 } renderline_t;
77
78 typedef struct _bitmap {
79     int width;
80     int height;
81     RGBA*data;
82     int id;
83     struct _bitmap*next;
84 } bitmap_t;
85
86 typedef struct _renderbuf_internal
87 {
88     renderline_t*lines;
89     bitmap_t*bitmaps;
90     char antialize;
91     int multiply;
92     int width2,height2;
93     dummyshape_t*dshapes;
94     dummyshape_t*dshapes_next;
95     RGBA*background;
96     int background_width, background_height;
97 } renderbuf_internal;
98
99 #define DEBUG 0
100
101 static void renderpoint_write(TAG*tag, renderpoint_t*p)
102 {
103     if(tag->len == 0) {
104         swf_SetU32(tag, 1);
105     } else {
106         int num = GET32(tag->data);
107         PUT32(tag->data, num+1);
108     }
109
110     swf_SetBits(tag, p->type, 1);
111     swf_SetBits(tag, *(U32*)&p->x, 32);
112     if(p->depth & 0xffff) {
113         swf_SetBits(tag, 1, 1);
114         swf_SetBits(tag, p->depth, 32);
115     } else {
116         swf_SetBits(tag, 0, 1);
117         swf_SetBits(tag, p->depth >> 16, 16);
118     }
119     if(p->shapeline) {
120         swf_SetBits(tag, 1, 1);
121         swf_SetBits(tag, *(U32*)&p->shapeline, 32);
122     } else {
123         swf_SetBits(tag, 0, 1);
124     }
125
126     if(p->type == clip_type) {
127         //printf("type=%d x=%f, depth=%08x, shapeline=%08x, clipdepth=%08x\n", p->type, p->x, p->depth, p->shapeline, p->clipdepth);
128         assert((p->clipdepth & 0xffff) == 0xffff);
129         swf_SetBits(tag, p->clipdepth >> 16, 16);
130         /* don't set s */
131     } else {
132         //printf("type=%d x=%f, depth=%08x, shapeline=%08x, s=%08x\n", p->type, p->x, p->depth, p->shapeline, p->s);
133         swf_SetBits(tag, *(U32*)&p->s, 32);
134         /* don't set clipdepth */
135     }
136 }
137 static renderpoint_t renderpoint_read(TAG*tag, int num)
138 {
139     renderpoint_t p;
140     U8 flag = 0;
141     U32 dummy = 0;
142
143     p.type = swf_GetBits(tag, 1);
144     
145     dummy = swf_GetBits(tag, 32);p.x = *(float*)&dummy;
146     flag = swf_GetBits(tag, 1);
147     if(flag) {
148         p.depth = swf_GetBits(tag, 32);
149     } else {
150         p.depth = swf_GetBits(tag, 16) << 16;
151     }
152     flag = swf_GetBits(tag, 1);
153     if(flag) {
154         dummy = swf_GetBits(tag, 32);p.shapeline = *(SHAPELINE**)&dummy;
155     } else {
156         p.shapeline = 0;
157     }
158
159     if(p.type == clip_type) {
160         p.clipdepth = swf_GetBits(tag, 16) << 16 | 0xffff;
161         p.s = 0;
162     } else {
163         dummy = swf_GetBits(tag, 32);p.s = *(dummyshape_t**)&dummy;
164         p.clipdepth = 0;
165     }
166
167     return p;
168 }
169
170 static int renderpoint_num(TAG*tag)
171 {
172     if(tag->len == 0)
173         return 0;
174     return GET32(tag->data);
175 }
176
177 static renderpoint_t* renderpoint_readall(TAG*tag)
178 {
179     int num;
180     int t;
181     renderpoint_t*p;
182     swf_SetTagPos(tag, 0);
183     if(tag->len == 0)
184         num = 0;
185     else
186         num = swf_GetU32(tag);
187     p = (renderpoint_t*)rfx_alloc(num*sizeof(renderpoint_t));
188     for(t=0;t<num;t++)
189         p[t] = renderpoint_read(tag,t);
190     return p;
191 }
192
193 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
194 {
195     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
196     if(x >= i->width2 || y >= i->height2 || y<0) return;
197     p->x = x;
198     if(y<10)
199         renderpoint_write(i->lines[y].points, p);
200     else
201         renderpoint_write(i->lines[y].points, p);
202 }
203
204 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
205    problem appears to often */
206 #define CUT 0.5
207
208 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
209 {
210     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
211     double diffx, diffy;
212     double ny1, ny2, stepx;
213 /*    if(DEBUG&4) {
214         int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
215         printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
216     }*/
217
218     y1=y1*i->multiply;
219     y2=y2*i->multiply;
220     x1=x1*i->multiply;
221     x2=x2*i->multiply;
222     
223     y1 = y1/20.0;
224     y2 = y2/20.0;
225     x1 = x1/20.0;
226     x2 = x2/20.0;
227
228     if(y2 < y1) {
229         double x;
230         double y;
231         x = x1;x1 = x2;x2=x;
232         y = y1;y1 = y2;y2=y;
233     }
234     
235     diffx = x2 - x1;
236     diffy = y2 - y1;
237     
238     ny1 = (int)(y1)+CUT;
239     ny2 = (int)(y2)+CUT;
240
241     if(ny1 < y1) {
242         ny1 = (int)(y1) + 1.0 + CUT;
243     }
244     if(ny2 >= y2) {
245         ny2 = (int)(y2) - 1.0 + CUT;
246     }
247
248     if(ny1 > ny2)
249         return;
250
251     stepx = diffx/diffy;
252     x1 = x1 + (ny1-y1)*stepx;
253     x2 = x2 + (ny2-y2)*stepx;
254
255     {
256         int posy=(int)ny1;
257         int endy=(int)ny2;
258         double posx=0;
259         double startx = x1;
260
261         while(posy<=endy) {
262             float xx = (float)(startx + posx);
263             add_pixel(buf, xx ,posy, p);
264             posx+=stepx;
265             posy++;
266         }
267     }
268 }
269 #define PI 3.14159265358979
270 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, int width, renderpoint_t*p)
271 {
272     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
273
274     double dx = x2-x1;
275     double dy = y2-y1;
276     double sd;
277     double d;
278
279     int t;
280     int segments;
281     double lastx,lasty;
282     double vx,vy;
283     double xx,yy;
284   
285     /* Make sure the line is always at least one pixel wide */
286 #ifdef LINEMODE1
287     /* That's what Macromedia's Player does at least at zoom level >= 1.  */
288     width += 20;
289 #else
290     /* That's what Macromedia's Player seems to do at zoom level 0.  */
291     /* TODO: needs testing */
292     if(width<20)
293         width = 20;
294 #endif
295
296     sd = (double)dx*(double)dx+(double)dy*(double)dy;
297     d = sqrt(sd);
298
299     if(!dx && !dy) {
300         vx = 1;
301         vy = 0;
302     } else {
303         vx = ( dy/d);
304         vy = (-dx/d);
305     }
306
307     segments = width/2;
308     if(segments < 2)
309         segments = 2;
310
311     segments = 8;
312
313     vx=vx*width*0.5;
314     vy=vy*width*0.5;
315
316     xx = x2+vx;
317     yy = y2+vy;
318     add_line(buf, x1+vx, y1+vy, xx, yy, p);
319     lastx = xx;
320     lasty = yy;
321     for(t=1;t<segments;t++) {
322         double s = sin(t*PI/segments);
323         double c = cos(t*PI/segments);
324         xx = (x2 + vx*c - vy*s);
325         yy = (y2 + vx*s + vy*c);
326         add_line(buf, lastx, lasty, xx, yy, p);
327         lastx = xx;
328         lasty = yy;
329     }
330     
331     xx = (x2-vx);
332     yy = (y2-vy);
333     add_line(buf, lastx, lasty, xx, yy, p);
334     lastx = xx;
335     lasty = yy;
336     xx = (x1-vx);
337     yy = (y1-vy);
338     add_line(buf, lastx, lasty, xx, yy, p);
339     lastx = xx;
340     lasty = yy;
341     for(t=1;t<segments;t++) {
342         double s = sin(t*PI/segments);
343         double c = cos(t*PI/segments);
344         xx = (x1 - vx*c + vy*s);
345         yy = (y1 - vx*s - vy*c);
346         add_line(buf, lastx, lasty, xx, yy, p);
347         lastx = xx;
348         lasty = yy;
349     }
350     add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
351 }
352
353 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
354 {
355     SPOINT p,d;
356     p.x = x;
357     p.y = y;
358     d = swf_TurnPoint(p, m);
359     *dx = d.x;
360     *dy = d.y;
361 }
362
363 static int compare_renderpoints(const void * _a, const void * _b)
364 {
365     renderpoint_t*a = (renderpoint_t*)_a;
366     renderpoint_t*b = (renderpoint_t*)_b;
367     if(a->x < b->x) return -1;
368     if(a->x > b->x) return 1;
369     return 0;
370 }
371
372 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, char antialize, int multiply)
373 {
374     renderbuf_internal*i;
375     int y;
376     memset(buf, 0, sizeof(RENDERBUF));
377     buf->width = width*multiply;
378     buf->height = height*multiply;
379     buf->posx = posx;
380     buf->posy = posy;
381     buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
382     i = (renderbuf_internal*)buf->internal;
383     i->antialize = !!antialize;
384     i->multiply = antialize?multiply*2:multiply;
385     i->height2 = antialize?2*buf->height:buf->height;
386     i->width2 = antialize?2*buf->width:buf->width;
387     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
388     for(y=0;y<i->height2;y++) {
389         i->lines[y].points = swf_InsertTag(0, 0);
390     }
391 }
392 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
393 {
394     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
395     RGBA*bck = (RGBA*)rfx_alloc(sizeof(RGBA)*width*height);
396     memcpy(bck, img, sizeof(RGBA)*width*height);
397     i->background = bck;
398     i->background_width = width;
399     i->background_height = height;
400 }
401 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
402 {
403     swf_Render_SetBackground(buf, &color, 1, 1);
404 }
405 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
406 {
407     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
408
409     bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
410     bm->id = id;
411     bm->width = width;
412     bm->height = height;
413     bm->data = rfx_alloc(width*height*4);
414     memcpy(bm->data, img, width*height*4);
415
416     bm->next = i->bitmaps;
417     i->bitmaps = bm;
418 }
419 void swf_Render_ClearCanvas(RENDERBUF*dest)
420 {
421     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
422     int y;
423     for(y=0;y<i->height2;y++) {
424         swf_ClearTag(i->lines[y].points);
425     }
426 }
427 void swf_Render_Delete(RENDERBUF*dest)
428 {
429     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
430     int y;
431     bitmap_t*b = i->bitmaps;
432     dummyshape_t*d = i->dshapes;
433
434     if(i->background) {
435         free(i->background);i->background=0;
436     }
437
438     /* delete line buffers */
439     for(y=0;y<i->height2;y++) {
440         swf_DeleteTag(i->lines[y].points);
441         i->lines[y].points = 0;
442     }
443
444     while(d) {
445         dummyshape_t*next = d->next;
446         swf_Shape2Free(d->shape);
447         free(d->shape);d->shape=0;
448         free(d);
449         d=next;
450     }
451     i->dshapes = 0;
452     
453     /* delete bitmaps */
454     while(b) {
455         bitmap_t*next = b->next;
456         free(b->data);b->data=0;
457         rfx_free(b);
458         b = next;
459     }
460
461     rfx_free(i->lines); i->lines = 0;
462     rfx_free(dest->internal); dest->internal = 0;
463 }
464
465 static void swf_Render_AddShape(RENDERBUF*dest,dummyshape_t*s)
466 {
467     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
468
469     s->next = 0;
470     if(i->dshapes_next)
471         i->dshapes_next->next = s;
472     i->dshapes_next = s;
473     if(!i->dshapes) {
474         i->dshapes = s;
475     }
476 }
477
478 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
479 {
480     SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
481     int t;
482     s->numfillstyles = shape->numlinestyles;
483     s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
484     s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
485     for(t=0;t<shape->numlinestyles;t++) {
486         s->lines[t].fillstyle0 = t+1;
487         s->fillstyles[t].type = FILL_SOLID;
488         s->fillstyles[t].color = shape->linestyles[t].color;
489     }
490     return s;
491 }
492
493 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
494 {
495     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
496     
497     SHAPELINE*line;
498     int x=0,y=0;
499     MATRIX mat = *m;
500     SHAPE2* lshape = 0;
501
502     SHAPE2* s2 = swf_Shape2Clone(shape);
503     /* add this shape to the global shape list, for deallocing */
504     dummyshape_t*fshape = rfx_calloc(sizeof(dummyshape_t));
505     fshape->shape = s2;
506     swf_Render_AddShape(dest, fshape);
507
508     line = s2->lines;
509
510     renderpoint_t p, lp;
511     memset(&p, 0, sizeof(renderpoint_t));
512     memset(&lp, 0, sizeof(renderpoint_t));
513     
514     p.type = _clipdepth?clip_type:fill_type;
515     p.depth = _depth << 16;
516     p.clipdepth = _clipdepth? _clipdepth << 16 | 0xffff : 0;
517
518     mat.tx -= dest->posx*20;
519     mat.ty -= dest->posy*20;
520         
521
522     if(shape->numfillstyles) {
523         int t;
524         p.s = fshape;
525         /* multiply fillstyles matrices with placement matrix-
526            important for texture and gradient fill */
527         for(t=0;t<s2->numfillstyles;t++) {
528             MATRIX nm;
529             swf_MatrixJoin(&nm, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
530             nm.sx *= i->multiply;
531             nm.sy *= i->multiply;
532             nm.r0 *= i->multiply;
533             nm.r1 *= i->multiply;
534             nm.tx *= i->multiply;
535             nm.ty *= i->multiply;
536             s2->fillstyles[t].m = nm;
537         }
538
539     }
540
541     if(shape->numlinestyles) {
542         dummyshape_t*dshape = rfx_calloc(sizeof(dummyshape_t));
543         
544         lshape = linestyle2fillstyle(shape);
545             
546         lp.type = fill_type;
547         lp.s = dshape;
548         lp.depth = (_depth << 16)+1;
549
550         dshape->shape = lshape;
551
552         /* add this shape to the global shape list, for deallocing */
553         swf_Render_AddShape(dest, dshape);
554     }
555
556     if(p.clipdepth) {
557         /* invert shape */
558         p.shapeline = 0;
559         add_line(dest, -20, 0, -20, i->height2*20, &p);
560     }
561
562     while(line)
563     {
564         int x1,y1,x2,y2,x3,y3;
565
566         p.shapeline = line;
567
568         if(line->type == moveTo) {
569         } else if(line->type == lineTo) {
570             if(DEBUG&4) {
571                 int l;
572                 x1 = x;
573                 y1 = y;
574                 x2 = line->x;
575                 y2 = line->y;
576                 l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
577                 printf("%d - %.2f/%.2f -> %.2f/%.2f ", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
578             }
579
580             transform_point(&mat, x, y, &x1, &y1);
581             transform_point(&mat, line->x, line->y, &x3, &y3);
582             
583             if(line->linestyle && ! p.clipdepth) {
584                 lp.shapeline = &lshape->lines[line->linestyle-1];
585                 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width, &lp);
586                 lp.depth++;
587             }
588             if(line->fillstyle0 || line->fillstyle1) {
589                 assert(shape->numfillstyles);
590                 add_line(dest, x1, y1, x3, y3, &p);
591             }
592             
593             if(DEBUG&4) printf("\n");
594         } else if(line->type == splineTo) {
595             int c,t,parts,qparts;
596             double xx,yy;
597             
598             transform_point(&mat, x, y, &x1, &y1);
599             transform_point(&mat, line->sx, line->sy, &x2, &y2);
600             transform_point(&mat, line->x, line->y, &x3, &y3);
601             
602             c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
603             xx=x1;
604             yy=y1;
605
606             parts = (int)(sqrt(c)/3);
607             if(!parts) parts = 1;
608
609             if(DEBUG&4)
610             {
611                 printf("spline %.2f/%.2f -(%.2f/%.2f)-> %.2f/%.2f (c=%d, %d parts)", 
612                         x1/20.0, y1/20.0, 
613                         x2/20.0, y2/20.0, 
614                         x3/20.0, y3/20.0, c, parts);
615             }
616
617             for(t=1;t<=parts;t++) {
618                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
619                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
620                 
621                 if(line->linestyle && ! p.clipdepth) {
622                     lp.shapeline = &lshape->lines[line->linestyle-1];
623                     add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width, &lp);
624                     lp.depth++;
625                 }
626                 if(line->fillstyle0 || line->fillstyle1) {
627                     assert(shape->numfillstyles);
628                     add_line(dest, (int)xx, (int)yy, (int)nx, (int)ny, &p);
629                 }
630
631                 xx = nx;
632                 yy = ny;
633             }
634             if(DEBUG&4) 
635                 printf("\n");
636         }
637         x = line->x;
638         y = line->y;
639         line = line->next;
640     }
641 }
642
643 typedef struct _layer {
644     int fillid;
645     U32 clipdepth;
646     renderpoint_t*p;
647     struct _layer*next;
648     struct _layer*prev;
649 } layer_t;
650
651 typedef struct {
652     layer_t*layers;
653 } state_t;
654
655 static RGBA color_red = {255,255,0,0};
656 static RGBA color_white = {255,255,255,255};
657
658 static void fill_plain(RGBA*line, int x1, int x2, RGBA col)
659 {
660     int x = x1;
661     if(col.a!=255) {
662         int ainv = 255-col.a;
663         col.r = (col.r*col.a)>>8;
664         col.g = (col.g*col.a)>>8;
665         col.b = (col.b*col.a)>>8;
666         col.a = 255;
667         do {
668             line[x].r = ((line[x].r*ainv)>>8)+col.r;
669             line[x].g = ((line[x].g*ainv)>>8)+col.g;
670             line[x].b = ((line[x].b*ainv)>>8)+col.b;
671             line[x].a = 255;
672         } while(++x<x2);
673     } else {
674         do {
675             line[x] = col;
676         } while(++x<x2);
677     }
678 }
679
680 static void fill_bitmap(RGBA*line, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clip)
681 {
682     int x = x1;
683     double m11=m->sx/65536.0, m21=m->r1/65536.0;
684     double m12=m->r0/65536.0, m22=m->sy/65536.0;
685     double rx = m->tx/20.0;
686     double ry = m->ty/20.0;
687     double det = m11*m22 - m12*m21;
688     if(fabs(det) < 0.0005) { 
689         /* x direction equals y direction- the image is invisible */
690         return;
691     }
692     det = 20.0/det;
693  
694     if(!b->width || !b->height) {
695         fill_plain(line, x1, x2, color_red);
696         return;
697     }
698
699     do {
700         RGBA col;
701         int xx = (int)((  (x - rx) * m22 - (y - ry) * m21)*det);
702         int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
703         int ainv;
704         
705         if(clip) {
706             if(xx<0) xx=0;
707             if(xx>=b->width) xx = b->width-1;
708             if(yy<0) yy=0;
709             if(yy>=b->height) yy = b->height-1;
710         } else {
711             xx %= b->width;
712             yy %= b->height;
713         }
714
715         col = b->data[yy*b->width+xx];
716         ainv = 255-col.a;
717
718         line[x].r = ((line[x].r*ainv)>>8)+col.r;
719         line[x].g = ((line[x].g*ainv)>>8)+col.g;
720         line[x].b = ((line[x].b*ainv)>>8)+col.b;
721         line[x].a = 255;
722     } while(++x<x2);
723 }
724
725 static void fill(RENDERBUF*dest, RGBA*line, int y, int x1, int x2, state_t*clipstate, state_t*fillstate)
726 {
727     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
728     U32 clipdepth;
729
730     layer_t*lc = clipstate->layers;
731     layer_t*lf = fillstate->layers;
732     layer_t*l = 0;
733
734     if(x1>=x2) //zero width? nothing to do.
735         return;
736     
737     clipdepth = 0;
738     while(lf) {
739         if(lc && (!lf || lc->p->depth < lf->p->depth)) {
740             l = lc;
741             lc = lc->next;
742         } else if(lf && (!lc || lf->p->depth < lc->p->depth)) {
743             l = lf;
744             lf = lf->next;
745         } else if(lf && lc && lf->p->depth == lc->p->depth) {
746             /* A clipshape and a fillshape at the same depth. Yuck.
747                Bug in the SWF file */
748             fprintf(stderr, "Error: Multiple use of depth %d in SWF\n", lf->p->depth);
749             l = lc;
750             lc = lc->next;
751         } else {
752             fprintf(stderr, "Internal error: %08x %08x\n", lc, lf);
753             if(lc) fprintf(stderr, "                lc->depth = %08x\n", lc->p->depth);
754             if(lf) fprintf(stderr, "                lf->depth = %08x\n", lf->p->depth);
755         }
756
757         if(l->p->depth <= clipdepth) {
758             if(DEBUG&2) printf("(clipped)");
759             continue;
760         }
761         if(l->fillid < 0 /*clip*/) {
762             if(DEBUG&2) printf("(add clip %d)", l->clipdepth);
763             if(l->clipdepth > clipdepth)
764                 clipdepth = l->clipdepth;
765         } else if(l->fillid == 0) {
766             /* not filled. TODO: we should never add those in the first place */
767             if(DEBUG&2)
768                 printf("(not filled)");
769         } else if(l->fillid > l->p->s->shape->numfillstyles) {
770             fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->shape->numlinestyles);
771         } else {
772             FILLSTYLE*f;
773             if(DEBUG&2) 
774                 printf("(%d -> %d style %d)", x1, x2, l->fillid);
775
776             f = &l->p->s->shape->fillstyles[l->fillid-1];
777
778             if(f->type == FILL_SOLID) {
779                 /* plain color fill */
780                 fill_plain(line, x1, x2, f->color);
781             } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
782                 /* TODO: optimize (do this in add_pixel()?) */
783                 bitmap_t* b = i->bitmaps;
784                 while(b && b->id != f->id_bitmap) {
785                     b = b->next;
786                 }
787                 if(!b) {
788                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
789                     fill_plain(line, x1, x2, color_red);
790                 } else {
791                     //done in swf_RenderShape now
792                     //MATRIX m = f->m;
793                     //m.tx -= dest->posx*20;
794                     //m.ty -= dest->posy*20;
795                     //m.sx *= i->multiply;
796                     //m.sy *= i->multiply;
797                     //m.r0 *= i->multiply;
798                     //m.r1 *= i->multiply;
799                     //m.tx *= i->multiply;
800                     //m.ty *= i->multiply;
801                     fill_bitmap(line, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0);
802                 }
803             }
804         }
805     }
806 }
807
808 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
809 {
810     layer_t*last=0,*l = state->layers;
811     while(l && l->p->depth < depth) {
812         last = l;
813         l = l->next;
814     }
815     *before = last;
816     if(l && l->p->depth == depth)
817         *self = l;
818     else
819         *after = l;
820 }
821 static void delete_layer(state_t*state, layer_t*todel)
822 {
823     layer_t*before=todel->prev;
824     layer_t*next = todel->next;
825     rfx_free(todel);
826     if(!before) {
827         state->layers = next;
828         if(next)
829             next->prev = 0;
830     } else {
831         before->next = next;
832         if(before->next)
833             before->next->prev = before;
834     }
835 }
836 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
837 {
838     if(!before) {
839         toadd->next = state->layers;
840         toadd->prev = 0;
841         state->layers=toadd;
842     } else {
843         toadd->next = before->next;
844         toadd->prev = before;
845         before->next = toadd;
846     }
847     if(toadd->next)
848         toadd->next->prev = toadd;
849 }
850 static void free_layers(state_t* state)
851 {
852     layer_t*l = state->layers;
853     while(l) {
854         layer_t*next = l->next;
855         rfx_free(l);
856         l = next;
857     }
858 }
859
860 static void change_state(int y, state_t* state, renderpoint_t*p)
861 {
862     layer_t*before=0, *self=0, *after=0;
863
864     if(DEBUG&2) { 
865         printf("[%s(%d,%d)/%d/%d-%d]", p->type==clip_type?"C":"F", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
866     }
867
868     search_layer(state, p->depth, &before, &self, &after);
869
870     if(self) {
871         /* shape update */
872         if(self->fillid<0 || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
873             /* filling/clipping ends */
874             if(DEBUG&2) printf("<D>");
875             
876             delete_layer(state, self);
877         } else { 
878             /*both fill0 and fill1 are set- exchange the two, updating the layer */
879             if(self->fillid == p->shapeline->fillstyle0) {
880                 self->fillid = p->shapeline->fillstyle1;
881                 self->clipdepth = 0;
882                 self->p = p;
883                 if(DEBUG&2) printf("<X>");
884             } else if(self->fillid == p->shapeline->fillstyle1) {
885                 self->fillid = p->shapeline->fillstyle0;
886                 self->clipdepth = 0;
887                 self->p = p;
888                 if(DEBUG&2) printf("<X>");
889             } else {
890                 /* buggy shape. keep everything as-is. */
891                 if(DEBUG&2) printf("<!>");
892                 //fprintf(stderr, "<line %d: bad swap>\n", y);
893             }
894         }
895         return;
896     } else {
897         layer_t* n = 0;
898         if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
899             /* this is a hack- a better way would be to make sure that
900                we always get (0,32), (32, 33), (33, 0) in the right order if
901                they happen to fall on the same pixel.
902                (not: (0,32), (33, 0), (32, 33))
903             */
904             fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
905             return;
906         }
907         
908         n = rfx_calloc(sizeof(layer_t));
909
910         if(DEBUG&2) printf("<+>");
911
912         if(p->type == clip_type) {
913             /* add clipping */
914             n->fillid = -1;
915             n->clipdepth = p->clipdepth;
916             n->p = p;
917         } else {
918             n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
919             n->clipdepth = 0;
920             n->p = p;
921         }
922
923         add_layer(state, before, n);
924     }
925 }
926
927 RGBA* swf_Render(RENDERBUF*dest)
928 {
929     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
930     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
931     int y;
932     long memory = 0;
933     RGBA * line1 = rfx_alloc(sizeof(RGBA)*i->width2);
934     RGBA * line2 = rfx_alloc(sizeof(RGBA)*i->width2);
935
936     for(y=0;y<i->height2;y++) {
937         TAG*tag = i->lines[y].points;
938         int n;
939         int num = renderpoint_num(tag);
940         renderpoint_t*points = renderpoint_readall(tag);
941         RGBA*line = line1;
942         state_t clipstate;
943         state_t fillstate;
944         memset(&clipstate, 0, sizeof(state_t));
945         memset(&fillstate, 0, sizeof(state_t));
946
947         if((y&1) && i->antialize)
948             line = line2;
949
950         if(!i->background) {
951             memset(line, 0, sizeof(RGBA)*i->width2);
952         } else {
953             int x,xx;
954             int xstep=i->background_width*65536/i->width2;
955             RGBA*src = &i->background[(i->background_height*y/i->height2)*i->background_width];
956             for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
957                 line[x] = src[xx>>16];
958             }
959         }
960         memory += tag->memsize;
961         qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
962         for(n=0;n<num;n++) {
963             renderpoint_t*p = &points[n];
964             renderpoint_t*next= n<num-1?&points[n+1]:0;
965             int startx = p->x;
966             int endx = next?next->x:i->width2;
967             if(endx > i->width2)
968                 endx = i->width2;
969             if(startx < 0)
970                 startx = 0;
971
972             if(p->type == clip_type)
973                 change_state(y, &clipstate, p);
974             else
975                 change_state(y, &fillstate, p);
976
977             fill(dest, line, y, startx, endx, &clipstate, &fillstate);
978             if(endx == i->width2)
979                 break;
980         }
981         free_layers(&clipstate);
982         free_layers(&fillstate);
983         if(DEBUG&2) printf("\n");
984
985         if(!i->antialize) {
986             memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
987         } else {
988             if(y&1) {
989                 int x;
990                 RGBA* p = &img[(y/2)*dest->width];
991                 for(x=0;x<dest->width;x++) {
992                     RGBA*p1 = &line1[x*2];
993                     RGBA*p2 = &line1[x*2+1];
994                     RGBA*p3 = &line2[x*2];
995                     RGBA*p4 = &line2[x*2+1];
996                     p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
997                     p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
998                     p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
999                     p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
1000                 }
1001             }
1002         }
1003         free(points);
1004     }
1005     free(line1);
1006     free(line2);
1007
1008 #define MEMORY
1009     
1010     if(DEBUG) printf("\nMemory used: %d\n", memory);
1011 #ifdef STATISTICS
1012     if(DEBUG) printf("Statistics:\n");
1013     if(DEBUG) printf("Average layer depth: %f\n", (double)layers/layernum);
1014 #endif
1015
1016
1017     return img;
1018 }
1019
1020 typedef struct
1021 {
1022     TAG*tag;
1023     SRECT*bbox;
1024     enum {none_type, shape_type, image_type, text_type, font_type} type;
1025     union {
1026         SHAPE2*shape;
1027         SWFFONT*font;
1028     };
1029 } character_t;
1030
1031 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
1032 {
1033     TAG*tag;
1034     int t;
1035
1036     character_t* idtable = rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
1037     SWFPLACEOBJECT** depthtable = rfx_calloc(sizeof(SWFPLACEOBJECT*)*65536); // depth to placeobject mapping
1038
1039     /* set background color */
1040     RGBA color = swf_GetSWFBackgroundColor(swf);
1041     swf_Render_SetBackgroundColor(buf, color);
1042
1043     /* parse definitions */
1044     tag = swf->firstTag;
1045     while(tag) {
1046         if(swf_isDefiningTag(tag)) {
1047             int id = swf_GetDefineID(tag);
1048             idtable[id].tag = tag;
1049             idtable[id].bbox = rfx_alloc(sizeof(SRECT));
1050             *idtable[id].bbox = swf_GetDefineBBox(tag);
1051
1052             if(swf_isShapeTag(tag)) {
1053                 SHAPE2* shape = rfx_calloc(sizeof(SHAPE2));
1054                 swf_ParseDefineShape(tag, shape);
1055                 idtable[id].type = shape_type;
1056                 idtable[id].shape = shape;
1057             } else if(swf_isImageTag(tag)) {
1058                 int width,height;
1059                 RGBA*data = swf_ExtractImage(tag, &width, &height);
1060                 idtable[id].type = image_type;
1061                 swf_Render_AddImage(buf, id, data, width, height);
1062                 free(data);
1063             } else if(tag->id == ST_DEFINEFONT ||
1064                       tag->id == ST_DEFINEFONT2) {
1065                 //swf_FontExtract(swf,id,&idtable[id].font);
1066                 idtable[id].font = 0;
1067             } else if(tag->id == ST_DEFINEFONTINFO ||
1068                       tag->id == ST_DEFINEFONTINFO2) {
1069                 idtable[id].type = font_type;
1070             } else if(tag->id == ST_DEFINETEXT ||
1071                       tag->id == ST_DEFINETEXT2) {
1072                 idtable[id].type = text_type;
1073             }
1074         } else if(tag->id == ST_PLACEOBJECT || 
1075                   tag->id == ST_PLACEOBJECT2) {
1076             SWFPLACEOBJECT* p = rfx_calloc(sizeof(SWFPLACEOBJECT));
1077             swf_GetPlaceObject(tag, p);
1078             /* TODO: add move and deletion */
1079             depthtable[p->depth] = p;
1080         }
1081         tag = tag->next;
1082     }
1083       
1084     for(t=65535;t>=0;t--) if(depthtable[t]) {
1085         SWFPLACEOBJECT*p = depthtable[t];
1086         int id = p->id;
1087             
1088         if(!idtable[id].tag) { 
1089             fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1090             continue;
1091         }
1092
1093         if(idtable[id].type == shape_type) {
1094             SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1095             swf_RenderShape(buf, idtable[id].shape, &p->matrix, &p->cxform, p->depth, p->clipdepth);
1096         } else if(idtable[id].type == text_type) {
1097             /* TODO */
1098         } else {
1099             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1100         }
1101     }
1102
1103     /* free id and depth tables again */
1104     for(t=0;t<65536;t++) {
1105         if(idtable[t].bbox) {
1106             free(idtable[t].bbox);
1107             idtable[t].bbox=0;
1108         }
1109         if(idtable[t].type == shape_type) {
1110             SHAPE2* shape = idtable[t].shape;
1111             if(shape) {
1112                 swf_Shape2Free(shape); // FIXME
1113                 free(idtable[t].shape);idtable[t].shape = 0;
1114             }
1115         }
1116         if(depthtable[t]) {
1117             swf_PlaceObjectFree(depthtable[t]);
1118             free(depthtable[t]);depthtable[t] = 0;
1119         }
1120     }
1121     free(idtable);
1122     free(depthtable);
1123 }
1124