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