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