fixed a bug occuring when shapes are entirely outside of the viewport
[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             if(endx < 0)
836                 endx = 0;
837
838             if(clipdepth) {
839                 /* for clipping, the inverse is filled */
840                 fill_clip(line, zline, y, lastx, startx, clipdepth);
841             }
842             change_state(y, &fillstate, p);
843         
844             fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth);
845 /*          if(y == 0 && startx == 232 && endx == 418) {
846                 printf("ymin=%d ymax=%d\n", i->ymin, i->ymax);
847                 for(n=0;n<num;n++) {
848                     renderpoint_t*p = &points[n];
849                     printf("x=%f depth=%08x\n", p->x, p->depth);
850                 }
851             }*/
852
853             lastx = endx;
854             if(endx == i->width2)
855                 break;
856         }
857         if(clipdepth) {
858             fill_clip(line, zline, y, lastx, i->width2, clipdepth);
859         }
860         free_layers(&fillstate);
861         
862         i->lines[y].num = 0;
863         swf_ClearTag(i->lines[y].points);
864     }
865     i->ymin = 0x7fffffff;
866     i->ymax = -0x80000000;
867 }
868
869 RGBA* swf_Render(RENDERBUF*dest)
870 {
871     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
872     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
873     int y;
874     RGBA*line2=0;
875     
876     for(y=0;y<i->height2;y++) {
877         int n;
878         RGBA*line = &i->img[y*i->width2];
879
880         if(!i->antialize) {
881             memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
882         } else {
883             if(y&1) {
884                 int x;
885                 RGBA*line1=line;
886                 if(!line2)
887                     line2=line1;
888                 RGBA* p = &img[(y/2)*dest->width];
889                 for(x=0;x<dest->width;x++) {
890                     RGBA*p1 = &line1[x*2];
891                     RGBA*p2 = &line1[x*2+1];
892                     RGBA*p3 = &line2[x*2];
893                     RGBA*p4 = &line2[x*2+1];
894                     p[x].r = (p1->r + p2->r + p3->r + p4->r)/4;
895                     p[x].g = (p1->g + p2->g + p3->g + p4->g)/4;
896                     p[x].b = (p1->b + p2->b + p3->b + p4->b)/4;
897                     p[x].a = (p1->a + p2->a + p3->a + p4->a)/4;
898                 }
899             }
900         }
901         line2=line;
902     }
903
904     return img;
905 }
906
907 typedef struct
908 {
909     TAG*tag;
910     SRECT*bbox;
911     enum {none_type, shape_type, image_type, text_type, font_type} type;
912     union {
913         SHAPE2*shape;
914         SWFFONT*font;
915     } obj;
916 } character_t;
917
918 int compare_placements(const void *v1, const void *v2)
919 {
920     SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1;
921     SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2;
922     if(p1->depth != p2->depth)
923         return (int)p1->depth - (int)p2->depth;
924     else 
925         if(p2->clipdepth)
926             return 1; // do the clip first
927         else
928             return -1;
929
930 /*    if(!p1->clipdepth) {
931         if(!p2->clipdepth) {
932             // !p1->clipdepth && !p2->clipdepth
933             return (int)p1->depth - (int)p2->depth;
934         } else {
935             // !p1->clipdepth && p2->clipdepth
936             if(p1->depth != p2->clipdepth)
937                 return (int)p1->depth - (int)p2->clipdepth;
938             else
939                 return 1; // do the clip first
940         }
941     } else {
942         if(!p2->clipdepth) {
943             // p1->clipdepth && !p2->clipdepth
944             if(p1->clipdepth != p2->depth)
945                 return (int)p1->clipdepth - (int)p2->depth;
946             else
947                 return -1;// do the clip first
948         } else {
949             if(p1->clipdepth != p2->clipdepth)
950                 return (int)p1->clipdepth - (int)p2->clipdepth;
951             else
952                 return (int)p1->depth - (int)p2->depth;
953         }
954     }*/
955 }
956
957 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
958 {
959     TAG*tag;
960     int t;
961     int numplacements;
962     
963     character_t* idtable = rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
964     SWFPLACEOBJECT** depthtable = rfx_calloc(sizeof(SWFPLACEOBJECT*)*65536); // depth to placeobject mapping
965     
966     tag = swf->firstTag;
967     numplacements = 0;
968     while(tag) {
969         if(tag->id == ST_PLACEOBJECT || 
970            tag->id == ST_PLACEOBJECT2) {
971             numplacements++;
972         }
973         tag = tag->next;
974     }
975     SWFPLACEOBJECT* placements = rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
976     numplacements = 0;
977
978     /* set background color */
979     RGBA color = swf_GetSWFBackgroundColor(swf);
980     swf_Render_SetBackgroundColor(buf, color);
981
982     /* parse definitions */
983     tag = swf->firstTag;
984     while(tag) {
985         if(swf_isDefiningTag(tag)) {
986             int id = swf_GetDefineID(tag);
987             idtable[id].tag = tag;
988             idtable[id].bbox = rfx_alloc(sizeof(SRECT));
989             *idtable[id].bbox = swf_GetDefineBBox(tag);
990
991             if(swf_isShapeTag(tag)) {
992                 SHAPE2* shape = rfx_calloc(sizeof(SHAPE2));
993                 swf_ParseDefineShape(tag, shape);
994                 idtable[id].type = shape_type;
995                 idtable[id].obj.shape = shape;
996             } else if(swf_isImageTag(tag)) {
997                 int width,height;
998                 RGBA*data = swf_ExtractImage(tag, &width, &height);
999                 idtable[id].type = image_type;
1000                 swf_Render_AddImage(buf, id, data, width, height);
1001                 free(data);
1002             } else if(tag->id == ST_DEFINEFONT ||
1003                       tag->id == ST_DEFINEFONT2) {
1004                 //swf_FontExtract(swf,id,&idtable[id].font);
1005                 idtable[id].obj.font = 0;
1006             } else if(tag->id == ST_DEFINEFONTINFO ||
1007                       tag->id == ST_DEFINEFONTINFO2) {
1008                 idtable[id].type = font_type;
1009             } else if(tag->id == ST_DEFINETEXT ||
1010                       tag->id == ST_DEFINETEXT2) {
1011                 idtable[id].type = text_type;
1012             }
1013         } else if(tag->id == ST_PLACEOBJECT || 
1014                   tag->id == ST_PLACEOBJECT2) {
1015             SWFPLACEOBJECT p;
1016             swf_GetPlaceObject(tag, &p);
1017             /* TODO: add move and deletion */
1018             placements[numplacements++] = p;
1019             swf_PlaceObjectFree(&p); //dirty! but it only removes items we don't need
1020         }
1021         tag = tag->next;
1022     }
1023
1024     qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements);
1025       
1026     for(t=0;t<numplacements;t++) {
1027         SWFPLACEOBJECT*p = &placements[t];
1028         int id = p->id;
1029             
1030         if(!idtable[id].tag) { 
1031             fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1032             continue;
1033         }
1034
1035         if(idtable[id].type == shape_type) {
1036             SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1037             swf_RenderShape(buf, idtable[id].obj.shape, &p->matrix, &p->cxform, p->depth, p->clipdepth);
1038         } else if(idtable[id].type == text_type) {
1039             /* TODO */
1040         } else {
1041             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1042         }
1043     }
1044
1045     /* free id and depth tables again */
1046     for(t=0;t<65536;t++) {
1047         if(idtable[t].bbox) {
1048             free(idtable[t].bbox);
1049             idtable[t].bbox=0;
1050         }
1051         if(idtable[t].type == shape_type) {
1052             SHAPE2* shape = idtable[t].obj.shape;
1053             if(shape) {
1054                 swf_Shape2Free(shape); // FIXME
1055                 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
1056             }
1057         }
1058     }
1059     free(placements);
1060     free(idtable);
1061     free(depthtable);
1062 }
1063