added sprite support
[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     int 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.77887789
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]\n", 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, double 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
198     /* TODO: how does this interact with scaling? */
199     if(width * i->multiply < 20)
200         width = 20 / i->multiply;
201 #endif
202
203     sd = (double)dx*(double)dx+(double)dy*(double)dy;
204     d = sqrt(sd);
205
206     if(!dx && !dy) {
207         vx = 1;
208         vy = 0;
209     } else {
210         vx = ( dy/d);
211         vy = (-dx/d);
212     }
213
214     segments = width/2;
215     if(segments < 2)
216         segments = 2;
217
218     segments = 8;
219
220     vx=vx*width*0.5;
221     vy=vy*width*0.5;
222
223     xx = x2+vx;
224     yy = y2+vy;
225     add_line(buf, x1+vx, y1+vy, xx, yy, p);
226     lastx = xx;
227     lasty = yy;
228     for(t=1;t<segments;t++) {
229         double s = sin(t*PI/segments);
230         double c = cos(t*PI/segments);
231         xx = (x2 + vx*c - vy*s);
232         yy = (y2 + vx*s + vy*c);
233         add_line(buf, lastx, lasty, xx, yy, p);
234         lastx = xx;
235         lasty = yy;
236     }
237     
238     xx = (x2-vx);
239     yy = (y2-vy);
240     add_line(buf, lastx, lasty, xx, yy, p);
241     lastx = xx;
242     lasty = yy;
243     xx = (x1-vx);
244     yy = (y1-vy);
245     add_line(buf, lastx, lasty, xx, yy, p);
246     lastx = xx;
247     lasty = yy;
248     for(t=1;t<segments;t++) {
249         double s = sin(t*PI/segments);
250         double c = cos(t*PI/segments);
251         xx = (x1 - vx*c + vy*s);
252         yy = (y1 - vx*s - vy*c);
253         add_line(buf, lastx, lasty, xx, yy, p);
254         lastx = xx;
255         lasty = yy;
256     }
257     add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
258 }
259
260 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
261 {
262     SPOINT p,d;
263     p.x = x;
264     p.y = y;
265     d = swf_TurnPoint(p, m);
266     *dx = d.x;
267     *dy = d.y;
268 }
269
270 static int compare_renderpoints(const void * _a, const void * _b)
271 {
272     renderpoint_t*a = (renderpoint_t*)_a;
273     renderpoint_t*b = (renderpoint_t*)_b;
274     if(a->x < b->x) return -1;
275     if(a->x > b->x) return 1;
276     return 0;
277 }
278
279 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, int antialize, int multiply)
280 {
281     renderbuf_internal*i;
282     int y;
283     memset(buf, 0, sizeof(RENDERBUF));
284     buf->width = width*multiply;
285     buf->height = height*multiply;
286     buf->posx = posx;
287     buf->posy = posy;
288     buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
289     i = (renderbuf_internal*)buf->internal;
290     if(antialize < 1)
291         antialize = 1;
292     i->antialize = antialize;
293     i->multiply = multiply*antialize;
294     i->height2 = antialize*buf->height;
295     i->width2 = antialize*buf->width;
296     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
297     for(y=0;y<i->height2;y++) {
298         memset(&i->lines[y], 0, sizeof(renderline_t));
299         i->lines[y].points = swf_InsertTag(0, 0);
300         i->lines[y].num = 0;
301     }
302     i->zbuf = (int*)rfx_calloc(sizeof(int)*i->width2*i->height2);
303     i->img = (RGBA*)rfx_calloc(sizeof(RGBA)*i->width2*i->height2);
304     i->shapes = 0;
305     i->ymin = 0x7fffffff;
306     i->ymax = -0x80000000;
307 }
308 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
309 {
310     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
311     int x,xx,y,yy;
312     int xstep=width*65536/i->width2;
313     int ystep=height*65536/i->height2;
314     if(i->shapes) {
315         fprintf(stderr, "rfxswf: Warning: swf_Render_SetBackground() called after drawing shapes\n");
316     }
317     for(y=0,yy=0;y<i->height2;y++,yy+=ystep) {
318         RGBA*src = &img[(yy>>16) * width];
319         RGBA*line = &i->img[y * i->width2];
320         for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
321             line[x] = src[xx>>16];
322         }
323     }
324 }
325 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
326 {
327     swf_Render_SetBackground(buf, &color, 1, 1);
328 }
329 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
330 {
331     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
332
333     bitmap_t*bm = rfx_calloc(sizeof(bitmap_t));
334     bm->id = id;
335     bm->width = width;
336     bm->height = height;
337     bm->data = rfx_alloc(width*height*4);
338     memcpy(bm->data, img, width*height*4);
339
340     bm->next = i->bitmaps;
341     i->bitmaps = bm;
342 }
343 void swf_Render_ClearCanvas(RENDERBUF*dest)
344 {
345     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
346     int y;
347     for(y=0;y<i->height2;y++) {
348         swf_ClearTag(i->lines[y].points);
349     }
350     memset(i->zbuf, 0, sizeof(int)*i->width2*i->height2);
351     memset(i->img, 0, sizeof(RGBA)*i->width2*i->height2);
352 }
353 void swf_Render_Delete(RENDERBUF*dest)
354 {
355     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
356     int y;
357     bitmap_t*b = i->bitmaps;
358
359     /* delete canvas */
360     rfx_free(i->zbuf);
361     rfx_free(i->img);
362
363     /* delete line buffers */
364     for(y=0;y<i->height2;y++) {
365         swf_DeleteTag(i->lines[y].points);
366         i->lines[y].points = 0;
367     }
368
369     /* delete bitmaps */
370     while(b) {
371         bitmap_t*next = b->next;
372         free(b->data);b->data=0;
373         rfx_free(b);
374         b = next;
375     }
376
377     rfx_free(i->lines); i->lines = 0;
378     rfx_free(dest->internal); dest->internal = 0;
379 }
380
381 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
382 {
383     SHAPE2*s = rfx_calloc(sizeof(SHAPE2));
384     int t;
385     s->numfillstyles = shape->numlinestyles;
386     s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
387     s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
388     for(t=0;t<shape->numlinestyles;t++) {
389         s->lines[t].fillstyle0 = t+1;
390         s->fillstyles[t].type = FILL_SOLID;
391         s->fillstyles[t].color = shape->linestyles[t].color;
392     }
393     return s;
394 }
395
396 void swf_Process(RENDERBUF*dest, U32 clipdepth);
397
398 double matrixsize(MATRIX*m)
399 {
400     double l1 = sqrt((m->sx /65536.0) * (m->sx /65536.0) + (m->r0 /65536.0) * (m->r0/65536.0) );
401     double l2 = sqrt((m->r1 /65536.0) * (m->r1 /65536.0) + (m->sy /65536.0) * (m->sy/65536.0) );
402     return sqrt(l1*l2);
403 }
404
405 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
406 {
407     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
408     
409     SHAPELINE*line;
410     int x=0,y=0;
411     MATRIX mat = *m;
412     SHAPE2* s2 = 0;
413     SHAPE2* lshape = 0;
414     renderpoint_t p, lp;
415     U32 clipdepth;
416     double widthmultiply = matrixsize(m);
417
418     memset(&p, 0, sizeof(renderpoint_t));
419     memset(&lp, 0, sizeof(renderpoint_t));
420     
421     clipdepth = _clipdepth? _clipdepth << 16 | 0xffff : 0;
422     p.depth = _depth << 16;
423
424     mat.tx -= dest->posx*20;
425     mat.ty -= dest->posy*20;
426
427     s2 = swf_Shape2Clone(shape);
428     line = s2->lines;
429     if(shape->numfillstyles) {
430         int t;
431         p.s = s2;
432         /* multiply fillstyles matrices with placement matrix-
433            important for texture and gradient fill */
434         for(t=0;t<s2->numfillstyles;t++) {
435             MATRIX nm;
436             swf_MatrixJoin(&nm, &mat, &s2->fillstyles[t].m);
437             /*nm.sx *= i->multiply;
438             nm.sy *= i->multiply;
439             nm.r0 *= i->multiply;
440             nm.r1 *= i->multiply;
441             nm.tx *= i->multiply;
442             nm.ty *= i->multiply;*/
443             s2->fillstyles[t].m = nm;
444         }
445     }
446
447     if(shape->numlinestyles) {
448         lshape = linestyle2fillstyle(shape);
449         lp.s = lshape;
450         lp.depth = (_depth << 16)+1;
451     }
452
453
454     while(line)
455     {
456         int x1,y1,x2,y2,x3,y3;
457
458         if(line->type == moveTo) {
459         } else if(line->type == lineTo) {
460             transform_point(&mat, x, y, &x1, &y1);
461             transform_point(&mat, line->x, line->y, &x3, &y3);
462             
463             if(line->linestyle && ! clipdepth) {
464                 lp.shapeline = &lshape->lines[line->linestyle-1];
465                 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
466                 lp.depth++;
467             }
468             if(line->fillstyle0 || line->fillstyle1) {
469                 assert(shape->numfillstyles);
470                 p.shapeline = line;
471                 add_line(dest, x1, y1, x3, y3, &p);
472             }
473         } else if(line->type == splineTo) {
474             int c,t,parts,qparts;
475             double xx,yy;
476             
477             transform_point(&mat, x, y, &x1, &y1);
478             transform_point(&mat, line->sx, line->sy, &x2, &y2);
479             transform_point(&mat, line->x, line->y, &x3, &y3);
480             
481             c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
482             xx=x1;
483             yy=y1;
484
485             parts = (int)(sqrt(c)/3);
486             if(!parts) parts = 1;
487
488             for(t=1;t<=parts;t++) {
489                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
490                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
491                 
492                 if(line->linestyle && ! clipdepth) {
493                     lp.shapeline = &lshape->lines[line->linestyle-1];
494                     add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
495                     lp.depth++;
496                 }
497                 if(line->fillstyle0 || line->fillstyle1) {
498                     assert(shape->numfillstyles);
499                     p.shapeline = line;
500                     add_line(dest, xx, yy, nx, ny, &p);
501                 }
502
503                 xx = nx;
504                 yy = ny;
505             }
506         }
507         x = line->x;
508         y = line->y;
509         line = line->next;
510     }
511     
512     swf_Process(dest, clipdepth);
513     
514     if(s2) {
515         swf_Shape2Free(s2);rfx_free(s2);s2=0;
516     }
517     if(lshape) {
518         swf_Shape2Free(lshape);rfx_free(lshape);lshape=0;
519     }
520
521 }
522
523 static RGBA color_red = {255,255,0,0};
524 static RGBA color_white = {255,255,255,255};
525 static RGBA color_black = {255,0,0,0};
526
527 static void fill_clip(RGBA*line, int*z, int y, int x1, int x2, U32 depth)
528 {
529     int x = x1;
530     if(x1>=x2)
531         return;
532     do {
533         if(depth > z[x]) {
534             z[x] = depth;
535         }
536     } while(++x<x2);
537 }
538
539
540 static void fill_solid(RGBA*line, int*z, int y, int x1, int x2, RGBA col, U32 depth)
541 {
542     int x = x1;
543
544     if(col.a!=255) {
545         int ainv = 255-col.a;
546         col.r = (col.r*col.a)>>8;
547         col.g = (col.g*col.a)>>8;
548         col.b = (col.b*col.a)>>8;
549         col.a = 255;
550         do {
551             if(depth >= z[x]) {
552                 line[x].r = ((line[x].r*ainv)>>8)+col.r;
553                 line[x].g = ((line[x].g*ainv)>>8)+col.g;
554                 line[x].b = ((line[x].b*ainv)>>8)+col.b;
555                 line[x].a = 255;
556                 z[x] = depth;
557             }
558         } while(++x<x2);
559     } else {
560         do {
561             if(depth >= z[x]) {
562                 line[x] = col;
563                 z[x] = depth;
564             }
565         } while(++x<x2);
566     }
567 }
568
569 static void fill_bitmap(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, bitmap_t*b, int clipbitmap, U32 depth, double fmultiply)
570 {
571     int x = x1;
572     
573     double m11= m->sx*fmultiply/65536.0, m21= m->r1*fmultiply/65536.0;
574     double m12= m->r0*fmultiply/65536.0, m22= m->sy*fmultiply/65536.0;
575     double rx = m->tx*fmultiply/20.0;
576     double ry = m->ty*fmultiply/20.0;
577
578     double det = m11*m22 - m12*m21;
579     if(fabs(det) < 0.0005) { 
580         /* x direction equals y direction- the image is invisible */
581         return;
582     }
583     det = 20.0/det;
584
585     if(!b->width || !b->height) {
586         fill_solid(line, z, y, x1, x2, color_red, depth);
587         return;
588     }
589
590     do {
591         if(depth >= z[x]) {
592             RGBA col;
593             int xx = (int)((  (x - rx) * m22 - (y - ry) * m21)*det);
594             int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
595             int ainv;
596
597             if(clipbitmap) {
598                 if(xx<0) xx=0;
599                 if(xx>=b->width) xx = b->width-1;
600                 if(yy<0) yy=0;
601                 if(yy>=b->height) yy = b->height-1;
602             } else {
603                 xx %= b->width;
604                 yy %= b->height;
605                 if(xx<0) xx += b->width;
606                 if(yy<0) yy += b->height;
607             }
608
609             col = b->data[yy*b->width+xx];
610             ainv = 255-col.a;
611
612             line[x].r = ((line[x].r*ainv)>>8)+col.r;
613             line[x].g = ((line[x].g*ainv)>>8)+col.g;
614             line[x].b = ((line[x].b*ainv)>>8)+col.b;
615             line[x].a = 255;
616             
617             z[x] = depth;
618         }
619     } while(++x<x2);
620 }
621
622 typedef struct _layer {
623     int fillid;
624     renderpoint_t*p;
625     struct _layer*next;
626     struct _layer*prev;
627 } layer_t;
628
629 typedef struct {
630     layer_t*layers;
631 } state_t;
632
633
634 static void fill(RENDERBUF*dest, RGBA*line, int*zline, int y, int x1, int x2, state_t*fillstate, U32 clipdepth)
635 {
636     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
637     int clip=1;
638
639     layer_t*l = fillstate->layers;
640
641     if(x1>=x2) //zero width? nothing to do.
642         return;
643     
644     while(l) {
645         if(l->fillid == 0) {
646             /* not filled. TODO: we should never add those in the first place */
647             if(DEBUG&2)
648                 printf("(not filled)");
649         } else if(l->fillid > l->p->s->numfillstyles) {
650             fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->numlinestyles);
651         } else if(clipdepth) {
652             /* filled region- not used for clipping */
653             clip = 0;
654         } else {
655             FILLSTYLE*f;
656             if(DEBUG&2) 
657                 printf("(%d -> %d style %d)", x1, x2, l->fillid);
658
659             f = &l->p->s->fillstyles[l->fillid-1];
660
661             if(f->type == FILL_SOLID) {
662                 /* plain color fill */
663                 fill_solid(line, zline, y, x1, x2, f->color, l->p->depth);
664             } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED || f->type == (FILL_TILED|2) || f->type == (FILL_CLIPPED|2)) {
665                 /* TODO: optimize (do this in add_pixel()?) */
666                 bitmap_t* b = i->bitmaps;
667                 while(b && b->id != f->id_bitmap) {
668                     b = b->next;
669                 }
670                 if(!b) {
671                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
672                     fill_solid(line, zline, y, x1, x2, color_red, l->p->depth);
673                 } else {
674                     fill_bitmap(line, zline, y, x1, x2, &f->m, b, /*clipped?*/f->type&1, l->p->depth, i->multiply);
675                 }
676             } else {
677                 fprintf(stderr, "Undefined fillmode: %02x\n", f->type);
678             }
679         }
680         l = l->next;
681     }
682     if(clip && clipdepth) {
683         fill_clip(line, zline, y, x1, x2, clipdepth);
684     }
685 }
686
687 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
688 {
689     layer_t*last=0,*l = state->layers;
690     while(l && l->p->depth < depth) {
691         last = l;
692         l = l->next;
693     }
694     *before = last;
695     if(l && l->p->depth == depth)
696         *self = l;
697     else
698         *after = l;
699 }
700 static void delete_layer(state_t*state, layer_t*todel)
701 {
702     layer_t*before=todel->prev;
703     layer_t*next = todel->next;
704     rfx_free(todel);
705     if(!before) {
706         state->layers = next;
707         if(next)
708             next->prev = 0;
709     } else {
710         before->next = next;
711         if(before->next)
712             before->next->prev = before;
713     }
714 }
715 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
716 {
717     if(!before) {
718         toadd->next = state->layers;
719         toadd->prev = 0;
720         state->layers=toadd;
721     } else {
722         toadd->next = before->next;
723         toadd->prev = before;
724         before->next = toadd;
725     }
726     if(toadd->next)
727         toadd->next->prev = toadd;
728 }
729 static void free_layers(state_t* state)
730 {
731     layer_t*l = state->layers;
732     while(l) {
733         layer_t*next = l->next;
734         rfx_free(l);
735         l = next;
736     }
737 }
738
739 static void change_state(int y, state_t* state, renderpoint_t*p)
740 {
741     layer_t*before=0, *self=0, *after=0;
742
743     if(DEBUG&2) { 
744         printf("[(%d,%d)/%d/%d-%d]", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
745     }
746
747     search_layer(state, p->depth, &before, &self, &after);
748
749     if(self) {
750         /* shape update */
751         if(self->fillid<0/*??*/ || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
752             /* filling ends */
753             if(DEBUG&2) printf("<D>");
754             
755             delete_layer(state, self);
756         } else { 
757             /*both fill0 and fill1 are set- exchange the two, updating the layer */
758             if(self->fillid == p->shapeline->fillstyle0) {
759                 self->fillid = p->shapeline->fillstyle1;
760                 self->p = p;
761                 if(DEBUG&2) printf("<X>");
762             } else if(self->fillid == p->shapeline->fillstyle1) {
763                 self->fillid = p->shapeline->fillstyle0;
764                 self->p = p;
765                 if(DEBUG&2) printf("<X>");
766             } else {
767                 /* buggy shape. keep everything as-is. */
768                 if(DEBUG&2) printf("<!>");
769                 //fprintf(stderr, "<line %d: bad swap>\n", y);
770             }
771         }
772         return;
773     } else {
774         layer_t* n = 0;
775         if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
776             /* this is a hack- a better way would be to make sure that
777                we always get (0,32), (32, 33), (33, 0) in the right order if
778                they happen to fall on the same pixel.
779                (not: (0,32), (33, 0), (32, 33))
780                Notice: Weird fill styles appear if linestyles are involved, too.
781             */
782             fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
783             return;
784         }
785         
786         n = rfx_calloc(sizeof(layer_t));
787
788         if(DEBUG&2) printf("<+>");
789
790         n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
791         n->p = p;
792
793         add_layer(state, before, n);
794     }
795 }
796
797 void swf_Process(RENDERBUF*dest, U32 clipdepth)
798 {
799     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
800     int y;
801     
802     if(i->ymax < i->ymin) {
803         /* shape is empty. return. 
804            only, if it's a clipshape, remember the clipdepth */
805         if(clipdepth) {
806             for(y=0;y<i->height2;y++) {
807                 if(clipdepth > i->lines[y].pending_clipdepth)
808                     i->lines[y].pending_clipdepth = clipdepth;
809             }
810         }
811         return; //nothing (else) to do
812     }
813
814     if(clipdepth) {
815         /* lines outside the clip shape are not filled
816            immediately, only the highest clipdepth so far is
817            stored there. They will be clipfilled once there's
818            actually something about to happen in that line */
819         for(y=0;y<i->ymin;y++) {
820             if(clipdepth > i->lines[y].pending_clipdepth)
821                 i->lines[y].pending_clipdepth = clipdepth;
822         }
823         for(y=i->ymax+1;y<i->height2;y++) {
824             if(clipdepth > i->lines[y].pending_clipdepth)
825                 i->lines[y].pending_clipdepth = clipdepth;
826         }
827     }
828     
829     for(y=i->ymin;y<=i->ymax;y++) {
830         int n;
831         TAG*tag = i->lines[y].points;
832         int num = i->lines[y].num;
833         renderpoint_t*points = (renderpoint_t*)tag->data;
834         RGBA*line = &i->img[i->width2*y];
835         int*zline = &i->zbuf[i->width2*y];
836         int lastx = 0;
837         state_t fillstate;
838         memset(&fillstate, 0, sizeof(state_t));
839         qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
840         /* resort points */
841         /*if(y==884) {
842             for(n=0;n<num;n++) {
843                 printf("%f (%d/%d) %d\n", points[n].x, 
844                         points[n].shapeline->fillstyle0,
845                         points[n].shapeline->fillstyle1,
846                         points[n].shapeline->linestyle);
847             }
848         }*/
849
850         if(i->lines[y].pending_clipdepth && !clipdepth) {
851             fill_clip(line, zline, y, 0, i->width2, i->lines[y].pending_clipdepth);
852             i->lines[y].pending_clipdepth=0;
853         }
854
855         for(n=0;n<num;n++) {
856             renderpoint_t*p = &points[n];
857             renderpoint_t*next= n<num-1?&points[n+1]:0;
858             int startx = p->x;
859             int endx = next?next->x:i->width2;
860             if(endx > i->width2)
861                 endx = i->width2;
862             if(startx < 0)
863                 startx = 0;
864             if(endx < 0)
865                 endx = 0;
866
867             if(clipdepth) {
868                 /* for clipping, the inverse is filled 
869                    TODO: lastx!=startx only at the start of the loop, 
870                          so this might be moved up
871                  */
872                 fill_clip(line, zline, y, lastx, startx, clipdepth);
873             }
874             change_state(y, &fillstate, p);
875
876             fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth);
877 /*          if(y == 0 && startx == 232 && endx == 418) {
878                 printf("ymin=%d ymax=%d\n", i->ymin, i->ymax);
879                 for(n=0;n<num;n++) {
880                     renderpoint_t*p = &points[n];
881                     printf("x=%f depth=%08x\n", p->x, p->depth);
882                 }
883             }*/
884
885             lastx = endx;
886             if(endx == i->width2)
887                 break;
888         }
889         if(clipdepth) {
890             /* TODO: is lastx *ever* != i->width2 here? */
891             fill_clip(line, zline, y, lastx, i->width2, clipdepth);
892         }
893         free_layers(&fillstate);
894         
895         i->lines[y].num = 0;
896         swf_ClearTag(i->lines[y].points);
897     }
898     i->ymin = 0x7fffffff;
899     i->ymax = -0x80000000;
900 }
901
902 RGBA* swf_Render(RENDERBUF*dest)
903 {
904     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
905     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
906     int y;
907     int antialize = i->antialize;
908    
909     if(antialize <= 1) /* no antializing */ {
910         for(y=0;y<i->height2;y++) {
911             RGBA*line = &i->img[y*i->width2];
912             memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
913         }
914     } else {
915         RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*antialize);
916         int q = antialize*antialize;
917         int ypos = 0;
918         for(y=0;y<i->height2;y++) {
919             int n;
920             ypos = y % antialize;
921             lines[ypos] = &i->img[y*i->width2];
922             if(ypos == antialize-1) {
923                 RGBA*out = &img[(y / antialize)*dest->width];
924                 int x;
925                 int r,g,b,a;
926                 for(x=0;x<dest->width;x++) {
927                     int xpos = x*antialize;
928                     int yp;
929                     U32 r=0,g=0,b=0,a=0;
930                     for(yp=0;yp<antialize;yp++) {
931                         RGBA*lp = &lines[yp][xpos];
932                         int xp;
933                         for(xp=0;xp<antialize;xp++) {
934                             RGBA*p = &lp[xp];
935                             r += p->r;
936                             g += p->g;
937                             b += p->b;
938                             a += p->a;
939                         }
940                     }
941                     out[x].r = r / q;
942                     out[x].g = g / q;
943                     out[x].b = b / q;
944                     out[x].a = a / q;
945                 }
946             }
947         }
948         rfx_free(lines);
949     }
950     return img;
951 }
952
953 typedef struct
954 {
955     int numchars;
956     SHAPE2**glyphs;
957 } font_t;
958
959 typedef struct
960 {
961     TAG*tag;
962     SRECT*bbox;
963     enum {none_type, shape_type, image_type, text_type, font_type, sprite_type} type;
964     union {
965         SHAPE2*shape;
966         font_t*font;
967     } obj;
968 } character_t;
969
970 int compare_placements(const void *v1, const void *v2)
971 {
972     SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1;
973     SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2;
974     if(p1->depth != p2->depth)
975         return (int)p1->depth - (int)p2->depth;
976     else 
977         if(p2->clipdepth)
978             return 1; // do the clip first
979         else
980             return -1;
981
982 /*    if(!p1->clipdepth) {
983         if(!p2->clipdepth) {
984             // !p1->clipdepth && !p2->clipdepth
985             return (int)p1->depth - (int)p2->depth;
986         } else {
987             // !p1->clipdepth && p2->clipdepth
988             if(p1->depth != p2->clipdepth)
989                 return (int)p1->depth - (int)p2->clipdepth;
990             else
991                 return 1; // do the clip first
992         }
993     } else {
994         if(!p2->clipdepth) {
995             // p1->clipdepth && !p2->clipdepth
996             if(p1->clipdepth != p2->depth)
997                 return (int)p1->clipdepth - (int)p2->depth;
998             else
999                 return -1;// do the clip first
1000         } else {
1001             if(p1->clipdepth != p2->clipdepth)
1002                 return (int)p1->clipdepth - (int)p2->clipdepth;
1003             else
1004                 return (int)p1->depth - (int)p2->depth;
1005         }
1006     }*/
1007 }
1008
1009 typedef struct textcallbackblock
1010 {
1011     character_t*idtable;
1012     U16 depth;
1013     U16 clipdepth;
1014     CXFORM* cxform;
1015     MATRIX m;
1016     RENDERBUF*buf;
1017 } textcallbackblock_t;
1018
1019 static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize, 
1020                     int xstart, int ystart, RGBA* color)
1021 {
1022     textcallbackblock_t * info = (textcallbackblock_t*)self;
1023     font_t*font = 0;
1024     int t;
1025     if(info->idtable[fontid].type != font_type) {
1026         fprintf(stderr, "ID %d is not a font\n", fontid);
1027         return;
1028     } else if(!info->idtable[fontid].obj.font) {
1029         fprintf(stderr, "Font %d unknown\n", fontid);
1030         return;
1031     } else {
1032         font  = info->idtable[fontid].obj.font;
1033     }
1034     for(t=0;t<nr;t++) {
1035         int x = xstart + xpos[t];
1036         int y = ystart;
1037         MATRIX m = info->m;
1038         SPOINT p;
1039         
1040         p.x = x; p.y = y; 
1041         p = swf_TurnPoint(p, &m);
1042         
1043         m.sx = (m.sx * fontsize) / 1024;
1044         m.sy = (m.sy * fontsize) / 1024;
1045         m.r0 = (m.r0 * fontsize) / 1024;
1046         m.r1 = (m.r1 * fontsize) / 1024;
1047         m.tx = p.x;
1048         m.ty = p.y;
1049
1050         if(chars[t]<0 || chars[t]>= font->numchars) {
1051             fprintf(stderr, "Character out of range: %d\n", chars[t]);
1052         } else {
1053             SHAPE2*shape = font->glyphs[chars[t]];
1054             shape->fillstyles[0].color = *color; //q&d
1055             /*printf("Rendering char %d (size %d, x:%d, y:%d) color:%02x%02x%02x%02x\n", chars[t], fontsize, x, y,
1056                     color->a, color->r, color->g, color->b);
1057             swf_DumpMatrix(stdout, &m);
1058             swf_DumpShape(shape);*/
1059             swf_RenderShape(info->buf, shape, &m, info->cxform, info->depth, info->clipdepth);
1060         }
1061     }
1062 }
1063
1064 static void renderFromTag(RENDERBUF*buf, character_t*idtable, TAG*firstTag, MATRIX*m)
1065 {
1066     TAG*tag = 0;
1067     int numplacements = 0;
1068     SWFPLACEOBJECT* placements;
1069
1070     tag = firstTag;
1071     numplacements = 0;
1072     while(tag) {
1073         if(tag->id == ST_PLACEOBJECT || 
1074            tag->id == ST_PLACEOBJECT2) {
1075             numplacements++;
1076         }
1077         if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1078             break;
1079         tag = tag->next;
1080     }
1081     placements = rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
1082     numplacements = 0;
1083
1084     tag = firstTag;
1085     while(tag) {
1086         if(swf_isPlaceTag(tag)) {
1087             SWFPLACEOBJECT p;
1088             swf_GetPlaceObject(tag, &p);
1089             /* TODO: add move and deletion */
1090             placements[numplacements++] = p;
1091             swf_PlaceObjectFree(&p); //dirty! but it only frees fields we don't use
1092         }
1093         if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1094             break;
1095         tag = tag->next;
1096     }
1097
1098     qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements);
1099      
1100     int t;
1101     for(t=0;t<numplacements;t++) {
1102         SWFPLACEOBJECT*p = &placements[t];
1103         int id = p->id;
1104         MATRIX m2;
1105         swf_MatrixJoin(&m2, m, &p->matrix);
1106             
1107         if(!idtable[id].tag) { 
1108             fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1109             continue;
1110         }
1111
1112         if(idtable[id].type == shape_type) {
1113             //SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1114             swf_RenderShape(buf, idtable[id].obj.shape, &m2, &p->cxform, p->depth, p->clipdepth);
1115         } else if(idtable[id].type == sprite_type) {
1116             swf_UnFoldSprite(idtable[id].tag);
1117             renderFromTag(buf, idtable, idtable[id].tag->next, &m2);
1118             swf_FoldSprite(idtable[id].tag);
1119         } else if(idtable[id].type == text_type) {
1120             TAG* tag = idtable[id].tag;
1121             textcallbackblock_t info;
1122             MATRIX mt;
1123
1124             swf_SetTagPos(tag, 0);
1125             swf_GetU16(tag);
1126             swf_GetRect(tag,0);
1127             swf_GetMatrix(tag,&mt);
1128             swf_MatrixJoin(&info.m, &m2, &mt);
1129             /*printf("Text matrix:\n");
1130             swf_DumpMatrix(stdout, &m);
1131             printf("Placement matrix:\n");
1132             swf_DumpMatrix(stdout, &p->matrix);
1133             printf("Final matrix:\n");
1134             swf_DumpMatrix(stdout, &info.m);*/
1135
1136             info.idtable = idtable;
1137             info.depth = p->depth;
1138             info.cxform = &p->cxform;
1139             info.clipdepth = p->clipdepth;
1140             info.buf = buf;
1141             
1142             swf_ParseDefineText(tag, textcallback, &info);
1143         } else {
1144             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1145         }
1146     }
1147
1148     free(placements);
1149 }
1150
1151 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
1152 {
1153     TAG*tag;
1154     int t;
1155     int numplacements;
1156     RGBA color;
1157
1158     swf_FoldAll(swf);
1159     
1160     character_t* idtable = rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
1161     
1162     /* set background color */
1163     color = swf_GetSWFBackgroundColor(swf);
1164     swf_Render_SetBackgroundColor(buf, color);
1165
1166     /* parse definitions */
1167     tag = swf->firstTag;
1168     while(tag) {
1169         if(swf_isDefiningTag(tag)) {
1170             int id = swf_GetDefineID(tag);
1171             idtable[id].tag = tag;
1172             idtable[id].bbox = rfx_alloc(sizeof(SRECT));
1173             *idtable[id].bbox = swf_GetDefineBBox(tag);
1174
1175             if(swf_isShapeTag(tag)) {
1176                 SHAPE2* shape = rfx_calloc(sizeof(SHAPE2));
1177                 swf_ParseDefineShape(tag, shape);
1178                 idtable[id].type = shape_type;
1179                 idtable[id].obj.shape = shape;
1180             } else if(swf_isImageTag(tag)) {
1181                 int width,height;
1182                 RGBA*data = swf_ExtractImage(tag, &width, &height);
1183                 idtable[id].type = image_type;
1184                 swf_Render_AddImage(buf, id, data, width, height);
1185                 free(data);
1186             } else if(tag->id == ST_DEFINEFONT ||
1187                       tag->id == ST_DEFINEFONT2) {
1188                 int t;
1189                 SWFFONT*swffont;
1190                 font_t*font = rfx_calloc(sizeof(font_t));
1191                 idtable[id].obj.font = font;
1192                 swf_FontExtract(swf,id,&swffont);
1193                 font->numchars = swffont->numchars;
1194                 font->glyphs = rfx_calloc(sizeof(SHAPE2*)*font->numchars);
1195                 for(t=0;t<font->numchars;t++) {
1196                     if(!swffont->glyph[t].shape->fillstyle.n) {
1197                         /* the actual fill color will be overwritten while rendering */
1198                         swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
1199                     }
1200                     font->glyphs[t] = swf_ShapeToShape2(swffont->glyph[t].shape);
1201                 }
1202                 swf_FontFree(swffont);
1203                 idtable[id].type = font_type;
1204
1205             } else if(tag->id == ST_DEFINEFONTINFO ||
1206                       tag->id == ST_DEFINEFONTINFO2) {
1207                 idtable[id].type = font_type;
1208             } else if(tag->id == ST_DEFINETEXT ||
1209                       tag->id == ST_DEFINETEXT2) {
1210                 idtable[id].type = text_type;
1211             } else if(tag->id == ST_DEFINESPRITE) {
1212                 idtable[id].type = sprite_type;
1213             }
1214         }
1215         tag = tag->next;
1216     }
1217     MATRIX m;
1218     swf_GetMatrix(0, &m);
1219     renderFromTag(buf, idtable, swf->firstTag, &m);
1220     
1221     /* free id and depth tables again */
1222     for(t=0;t<65536;t++) {
1223         if(idtable[t].bbox) {
1224             free(idtable[t].bbox);
1225             idtable[t].bbox=0;
1226         }
1227         if(idtable[t].type == shape_type) {
1228             SHAPE2* shape = idtable[t].obj.shape;
1229             if(shape) {
1230                 swf_Shape2Free(shape); // FIXME
1231                 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
1232             }
1233         } else if(idtable[t].type == font_type) {
1234             font_t* font = idtable[t].obj.font;
1235             if(font) {
1236                 if(font->glyphs) {
1237                     int t;
1238                     for(t=0;t<font->numchars;t++) {
1239                         swf_Shape2Free(font->glyphs[t]);
1240                         free(font->glyphs[t]); font->glyphs[t] = 0;
1241                     }
1242                     free(font->glyphs);
1243                     font->glyphs = 0;
1244                 }
1245                 free(idtable[t].obj.font); idtable[t].obj.font = 0;
1246                 font = 0;
1247             }
1248         }
1249     }
1250     free(idtable);
1251 }
1252