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