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