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