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