fixed line width handling
[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.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]\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, &s2->fillstyles[t].m, &mat); //TODO: is this the right order?
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 static void fill_solid(RGBA*line, int*z, int y, int x1, int x2, RGBA col, U32 depth)
540 {
541     int x = x1;
542
543     if(col.a!=255) {
544         int ainv = 255-col.a;
545         col.r = (col.r*col.a)>>8;
546         col.g = (col.g*col.a)>>8;
547         col.b = (col.b*col.a)>>8;
548         col.a = 255;
549         do {
550             if(depth >= z[x]) {
551                 line[x].r = ((line[x].r*ainv)>>8)+col.r;
552                 line[x].g = ((line[x].g*ainv)>>8)+col.g;
553                 line[x].b = ((line[x].b*ainv)>>8)+col.b;
554                 line[x].a = 255;
555                 z[x] = depth;
556             }
557         } while(++x<x2);
558     } else {
559         do {
560             if(depth >= z[x]) {
561                 line[x] = col;
562                 z[x] = depth;
563             }
564         } while(++x<x2);
565     }
566 }
567
568 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)
569 {
570     int x = x1;
571     
572     double m11= m->sx*fmultiply/65536.0, m21= m->r1*fmultiply/65536.0;
573     double m12= m->r0*fmultiply/65536.0, m22= m->sy*fmultiply/65536.0;
574     double rx = m->tx*fmultiply/20.0;
575     double ry = m->ty*fmultiply/20.0;
576
577     double det = m11*m22 - m12*m21;
578     if(fabs(det) < 0.0005) { 
579         /* x direction equals y direction- the image is invisible */
580         return;
581     }
582     det = 20.0/det;
583
584     if(!b->width || !b->height) {
585         fill_solid(line, z, y, x1, x2, color_red, depth);
586         return;
587     }
588
589     do {
590         if(depth >= z[x]) {
591             RGBA col;
592             int xx = (int)((  (x - rx) * m22 - (y - ry) * m21)*det);
593             int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
594             int ainv;
595
596             if(clipbitmap) {
597                 if(xx<0) xx=0;
598                 if(xx>=b->width) xx = b->width-1;
599                 if(yy<0) yy=0;
600                 if(yy>=b->height) yy = b->height-1;
601             } else {
602                 xx %= b->width;
603                 yy %= b->height;
604                 if(xx<0) xx += b->width;
605                 if(yy<0) yy += b->height;
606             }
607
608             col = b->data[yy*b->width+xx];
609             ainv = 255-col.a;
610
611             line[x].r = ((line[x].r*ainv)>>8)+col.r;
612             line[x].g = ((line[x].g*ainv)>>8)+col.g;
613             line[x].b = ((line[x].b*ainv)>>8)+col.b;
614             line[x].a = 255;
615             
616             z[x] = depth;
617         }
618     } while(++x<x2);
619 }
620
621 typedef struct _layer {
622     int fillid;
623     renderpoint_t*p;
624     struct _layer*next;
625     struct _layer*prev;
626 } layer_t;
627
628 typedef struct {
629     layer_t*layers;
630 } state_t;
631
632
633 static void fill(RENDERBUF*dest, RGBA*line, int*zline, int y, int x1, int x2, state_t*fillstate, U32 clipdepth)
634 {
635     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
636     int clip=1;
637
638     layer_t*l = fillstate->layers;
639
640     if(x1>=x2) //zero width? nothing to do.
641         return;
642     
643     while(l) {
644         if(l->fillid == 0) {
645             /* not filled. TODO: we should never add those in the first place */
646             if(DEBUG&2)
647                 printf("(not filled)");
648         } else if(l->fillid > l->p->s->numfillstyles) {
649             fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->numlinestyles);
650         } else if(clipdepth) {
651             /* filled region- not used for clipping */
652             clip = 0;
653         } else {
654             FILLSTYLE*f;
655             if(DEBUG&2) 
656                 printf("(%d -> %d style %d)", x1, x2, l->fillid);
657
658             f = &l->p->s->fillstyles[l->fillid-1];
659
660             if(f->type == FILL_SOLID) {
661                 /* plain color fill */
662                 fill_solid(line, zline, y, x1, x2, f->color, l->p->depth);
663             } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
664                 /* TODO: optimize (do this in add_pixel()?) */
665                 bitmap_t* b = i->bitmaps;
666                 while(b && b->id != f->id_bitmap) {
667                     b = b->next;
668                 }
669                 if(!b) {
670                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
671                     fill_solid(line, zline, y, x1, x2, color_red, l->p->depth);
672                 } else {
673                     fill_bitmap(line, zline, y, x1, x2, &f->m, b, FILL_CLIPPED?1:0, l->p->depth, i->multiply);
674                 }
675             } else {
676                 fprintf(stderr, "Undefined fillmode: %02x\n", f->type);
677             }
678         }
679         l = l->next;
680     }
681     if(clip && clipdepth) {
682         fill_clip(line, zline, y, x1, x2, clipdepth);
683     }
684 }
685
686 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
687 {
688     layer_t*last=0,*l = state->layers;
689     while(l && l->p->depth < depth) {
690         last = l;
691         l = l->next;
692     }
693     *before = last;
694     if(l && l->p->depth == depth)
695         *self = l;
696     else
697         *after = l;
698 }
699 static void delete_layer(state_t*state, layer_t*todel)
700 {
701     layer_t*before=todel->prev;
702     layer_t*next = todel->next;
703     rfx_free(todel);
704     if(!before) {
705         state->layers = next;
706         if(next)
707             next->prev = 0;
708     } else {
709         before->next = next;
710         if(before->next)
711             before->next->prev = before;
712     }
713 }
714 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
715 {
716     if(!before) {
717         toadd->next = state->layers;
718         toadd->prev = 0;
719         state->layers=toadd;
720     } else {
721         toadd->next = before->next;
722         toadd->prev = before;
723         before->next = toadd;
724     }
725     if(toadd->next)
726         toadd->next->prev = toadd;
727 }
728 static void free_layers(state_t* state)
729 {
730     layer_t*l = state->layers;
731     while(l) {
732         layer_t*next = l->next;
733         rfx_free(l);
734         l = next;
735     }
736 }
737
738 static void change_state(int y, state_t* state, renderpoint_t*p)
739 {
740     layer_t*before=0, *self=0, *after=0;
741
742     if(DEBUG&2) { 
743         printf("[(%d,%d)/%d/%d-%d]", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
744     }
745
746     search_layer(state, p->depth, &before, &self, &after);
747
748     if(self) {
749         /* shape update */
750         if(self->fillid<0/*??*/ || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
751             /* filling ends */
752             if(DEBUG&2) printf("<D>");
753             
754             delete_layer(state, self);
755         } else { 
756             /*both fill0 and fill1 are set- exchange the two, updating the layer */
757             if(self->fillid == p->shapeline->fillstyle0) {
758                 self->fillid = p->shapeline->fillstyle1;
759                 self->p = p;
760                 if(DEBUG&2) printf("<X>");
761             } else if(self->fillid == p->shapeline->fillstyle1) {
762                 self->fillid = p->shapeline->fillstyle0;
763                 self->p = p;
764                 if(DEBUG&2) printf("<X>");
765             } else {
766                 /* buggy shape. keep everything as-is. */
767                 if(DEBUG&2) printf("<!>");
768                 //fprintf(stderr, "<line %d: bad swap>\n", y);
769             }
770         }
771         return;
772     } else {
773         layer_t* n = 0;
774         if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
775             /* this is a hack- a better way would be to make sure that
776                we always get (0,32), (32, 33), (33, 0) in the right order if
777                they happen to fall on the same pixel.
778                (not: (0,32), (33, 0), (32, 33))
779             */
780             fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
781             return;
782         }
783         
784         n = rfx_calloc(sizeof(layer_t));
785
786         if(DEBUG&2) printf("<+>");
787
788         n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
789         n->p = p;
790
791         add_layer(state, before, n);
792     }
793 }
794
795 void swf_Process(RENDERBUF*dest, U32 clipdepth)
796 {
797     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
798     int y;
799     
800     if(i->ymax < i->ymin) {
801         /* shape is empty. return. 
802            only, if it's a clipshape, remember the clipdepth */
803         if(clipdepth) {
804             for(y=0;y<i->height2;y++) {
805                 if(clipdepth > i->lines[y].pending_clipdepth)
806                     i->lines[y].pending_clipdepth = clipdepth;
807             }
808         }
809         return; //nothing (else) to do
810     }
811
812     if(clipdepth) {
813         /* lines outside the clip shape are not filled
814            immediately, only the highest clipdepth so far is
815            stored there. They will be clipfilled once there's
816            actually something about to happen in that line */
817         for(y=0;y<i->ymin;y++) {
818             if(clipdepth > i->lines[y].pending_clipdepth)
819                 i->lines[y].pending_clipdepth = clipdepth;
820         }
821         for(y=i->ymax+1;y<i->height2;y++) {
822             if(clipdepth > i->lines[y].pending_clipdepth)
823                 i->lines[y].pending_clipdepth = clipdepth;
824         }
825     }
826     
827     for(y=i->ymin;y<=i->ymax;y++) {
828         int n;
829         TAG*tag = i->lines[y].points;
830         int num = i->lines[y].num;
831         renderpoint_t*points = (renderpoint_t*)tag->data;
832         RGBA*line = &i->img[i->width2*y];
833         int*zline = &i->zbuf[i->width2*y];
834         int lastx = 0;
835         state_t fillstate;
836         memset(&fillstate, 0, sizeof(state_t));
837         qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
838
839         if(i->lines[y].pending_clipdepth && !clipdepth) {
840             fill_clip(line, zline, y, 0, i->width2, i->lines[y].pending_clipdepth);
841             i->lines[y].pending_clipdepth=0;
842         }
843
844         for(n=0;n<num;n++) {
845             renderpoint_t*p = &points[n];
846             renderpoint_t*next= n<num-1?&points[n+1]:0;
847             int startx = p->x;
848             int endx = next?next->x:i->width2;
849             if(endx > i->width2)
850                 endx = i->width2;
851             if(startx < 0)
852                 startx = 0;
853             if(endx < 0)
854                 endx = 0;
855
856             if(clipdepth) {
857                 /* for clipping, the inverse is filled */
858                 fill_clip(line, zline, y, lastx, startx, clipdepth);
859             }
860             change_state(y, &fillstate, p);
861         
862             fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth);
863 /*          if(y == 0 && startx == 232 && endx == 418) {
864                 printf("ymin=%d ymax=%d\n", i->ymin, i->ymax);
865                 for(n=0;n<num;n++) {
866                     renderpoint_t*p = &points[n];
867                     printf("x=%f depth=%08x\n", p->x, p->depth);
868                 }
869             }*/
870
871             lastx = endx;
872             if(endx == i->width2)
873                 break;
874         }
875         if(clipdepth) {
876             fill_clip(line, zline, y, lastx, i->width2, clipdepth);
877         }
878         free_layers(&fillstate);
879         
880         i->lines[y].num = 0;
881         swf_ClearTag(i->lines[y].points);
882     }
883     i->ymin = 0x7fffffff;
884     i->ymax = -0x80000000;
885 }
886
887 RGBA* swf_Render(RENDERBUF*dest)
888 {
889     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
890     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
891     int y;
892     int antialize = i->antialize;
893    
894     if(antialize <= 1) /* no antializing */ {
895         for(y=0;y<i->height2;y++) {
896             RGBA*line = &i->img[y*i->width2];
897             memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
898         }
899     } else {
900         RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*antialize);
901         int q = antialize*antialize;
902         int ypos = 0;
903         for(y=0;y<i->height2;y++) {
904             int n;
905             ypos = y % antialize;
906             lines[ypos] = &i->img[y*i->width2];
907             if(ypos == antialize-1) {
908                 RGBA*out = &img[(y / antialize)*dest->width];
909                 int x;
910                 int r,g,b,a;
911                 for(x=0;x<dest->width;x++) {
912                     int xpos = x*antialize;
913                     int yp;
914                     U32 r=0,g=0,b=0,a=0;
915                     for(yp=0;yp<antialize;yp++) {
916                         RGBA*lp = &lines[yp][xpos];
917                         int xp;
918                         for(xp=0;xp<antialize;xp++) {
919                             RGBA*p = &lp[xp];
920                             r += p->r;
921                             g += p->g;
922                             b += p->b;
923                             a += p->a;
924                         }
925                     }
926                     out[x].r = r / q;
927                     out[x].g = g / q;
928                     out[x].b = b / q;
929                     out[x].a = a / q;
930                 }
931             }
932         }
933         rfx_free(lines);
934     }
935     return img;
936 }
937
938 typedef struct
939 {
940     int numchars;
941     SHAPE2**glyphs;
942 } font_t;
943
944 typedef struct
945 {
946     TAG*tag;
947     SRECT*bbox;
948     enum {none_type, shape_type, image_type, text_type, font_type} type;
949     union {
950         SHAPE2*shape;
951         font_t*font;
952     } obj;
953 } character_t;
954
955 int compare_placements(const void *v1, const void *v2)
956 {
957     SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1;
958     SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2;
959     if(p1->depth != p2->depth)
960         return (int)p1->depth - (int)p2->depth;
961     else 
962         if(p2->clipdepth)
963             return 1; // do the clip first
964         else
965             return -1;
966
967 /*    if(!p1->clipdepth) {
968         if(!p2->clipdepth) {
969             // !p1->clipdepth && !p2->clipdepth
970             return (int)p1->depth - (int)p2->depth;
971         } else {
972             // !p1->clipdepth && p2->clipdepth
973             if(p1->depth != p2->clipdepth)
974                 return (int)p1->depth - (int)p2->clipdepth;
975             else
976                 return 1; // do the clip first
977         }
978     } else {
979         if(!p2->clipdepth) {
980             // p1->clipdepth && !p2->clipdepth
981             if(p1->clipdepth != p2->depth)
982                 return (int)p1->clipdepth - (int)p2->depth;
983             else
984                 return -1;// do the clip first
985         } else {
986             if(p1->clipdepth != p2->clipdepth)
987                 return (int)p1->clipdepth - (int)p2->clipdepth;
988             else
989                 return (int)p1->depth - (int)p2->depth;
990         }
991     }*/
992 }
993
994 typedef struct textcallbackblock
995 {
996     character_t*idtable;
997     U16 depth;
998     U16 clipdepth;
999     CXFORM* cxform;
1000     MATRIX m;
1001     RENDERBUF*buf;
1002 } textcallbackblock_t;
1003
1004 static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize, 
1005                     int xstart, int ystart, RGBA* color)
1006 {
1007     textcallbackblock_t * info = (textcallbackblock_t*)self;
1008     font_t*font = 0;
1009     int t;
1010     if(!info->idtable[fontid].obj.font) {
1011         fprintf(stderr, "Font %d unknown\n", fontid);
1012         return;
1013     } else {
1014         font  = info->idtable[fontid].obj.font;
1015     }
1016     for(t=0;t<nr;t++) {
1017         int x = xstart + xpos[t];
1018         int y = ystart;
1019         MATRIX m = info->m;
1020         SPOINT p;
1021         
1022         p.x = x; p.y = y; 
1023         p = swf_TurnPoint(p, &m);
1024         
1025         m.sx = (m.sx * fontsize) / 1024;
1026         m.sy = (m.sy * fontsize) / 1024;
1027         m.r0 = (m.r0 * fontsize) / 1024;
1028         m.r1 = (m.r1 * fontsize) / 1024;
1029         m.tx += p.x;
1030         m.ty += p.y;
1031
1032         if(chars[t]<0 || chars[t]>= font->numchars) {
1033             fprintf(stderr, "Character out of range: %d\n", chars[t]);
1034         } else {
1035             SHAPE2*shape = font->glyphs[chars[t]];
1036             shape->fillstyles[0].color = *color; //q&d
1037             /*printf("Rendering char %d (size %d, x:%d, y:%d) color:%02x%02x%02x%02x\n", chars[t], fontsize, x, y,
1038                     color->a, color->r, color->g, color->b);
1039             swf_DumpMatrix(stdout, &m);
1040             swf_DumpShape(shape);*/
1041             swf_RenderShape(info->buf, shape, &m, info->cxform, info->depth, info->clipdepth);
1042         }
1043     }
1044 }
1045
1046 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
1047 {
1048     TAG*tag;
1049     int t;
1050     int numplacements;
1051     RGBA color;
1052     SWFPLACEOBJECT* placements;
1053     
1054     character_t* idtable = rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
1055     SWFPLACEOBJECT** depthtable = rfx_calloc(sizeof(SWFPLACEOBJECT*)*65536); // depth to placeobject mapping
1056     
1057     tag = swf->firstTag;
1058     numplacements = 0;
1059     while(tag) {
1060         if(tag->id == ST_PLACEOBJECT || 
1061            tag->id == ST_PLACEOBJECT2) {
1062             numplacements++;
1063         }
1064         tag = tag->next;
1065     }
1066     placements = rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
1067     numplacements = 0;
1068
1069     /* set background color */
1070     color = swf_GetSWFBackgroundColor(swf);
1071     swf_Render_SetBackgroundColor(buf, color);
1072
1073     /* parse definitions */
1074     tag = swf->firstTag;
1075     while(tag) {
1076         if(swf_isDefiningTag(tag)) {
1077             int id = swf_GetDefineID(tag);
1078             idtable[id].tag = tag;
1079             idtable[id].bbox = rfx_alloc(sizeof(SRECT));
1080             *idtable[id].bbox = swf_GetDefineBBox(tag);
1081
1082             if(swf_isShapeTag(tag)) {
1083                 SHAPE2* shape = rfx_calloc(sizeof(SHAPE2));
1084                 swf_ParseDefineShape(tag, shape);
1085                 idtable[id].type = shape_type;
1086                 idtable[id].obj.shape = shape;
1087             } else if(swf_isImageTag(tag)) {
1088                 int width,height;
1089                 RGBA*data = swf_ExtractImage(tag, &width, &height);
1090                 idtable[id].type = image_type;
1091                 swf_Render_AddImage(buf, id, data, width, height);
1092                 free(data);
1093             } else if(tag->id == ST_DEFINEFONT ||
1094                       tag->id == ST_DEFINEFONT2) {
1095                 int t;
1096                 SWFFONT*swffont;
1097                 font_t*font = rfx_calloc(sizeof(font_t));
1098                 idtable[id].obj.font = font;
1099                 swf_FontExtract(swf,id,&swffont);
1100                 font->numchars = swffont->numchars;
1101                 font->glyphs = rfx_calloc(sizeof(SHAPE2*)*font->numchars);
1102                 for(t=0;t<font->numchars;t++) {
1103                     if(!swffont->glyph[t].shape->fillstyle.n) {
1104                         /* the actual fill color will be overwritten while rendering */
1105                         swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
1106                     }
1107                     font->glyphs[t] = swf_ShapeToShape2(swffont->glyph[t].shape);
1108                 }
1109                 swf_FontFree(swffont);
1110                 idtable[id].type = font_type;
1111
1112             } else if(tag->id == ST_DEFINEFONTINFO ||
1113                       tag->id == ST_DEFINEFONTINFO2) {
1114                 idtable[id].type = font_type;
1115             } else if(tag->id == ST_DEFINETEXT ||
1116                       tag->id == ST_DEFINETEXT2) {
1117                 idtable[id].type = text_type;
1118             }
1119         } else if(tag->id == ST_PLACEOBJECT || 
1120                   tag->id == ST_PLACEOBJECT2) {
1121             SWFPLACEOBJECT p;
1122             swf_GetPlaceObject(tag, &p);
1123             /* TODO: add move and deletion */
1124             placements[numplacements++] = p;
1125             swf_PlaceObjectFree(&p); //dirty! but it only removes items we don't need
1126         }
1127         tag = tag->next;
1128     }
1129
1130     qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements);
1131       
1132     for(t=0;t<numplacements;t++) {
1133         SWFPLACEOBJECT*p = &placements[t];
1134         int id = p->id;
1135             
1136         if(!idtable[id].tag) { 
1137             fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1138             continue;
1139         }
1140
1141         if(idtable[id].type == shape_type) {
1142             //SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1143             swf_RenderShape(buf, idtable[id].obj.shape, &p->matrix, &p->cxform, p->depth, p->clipdepth);
1144         } else if(idtable[id].type == text_type) {
1145             TAG* tag = idtable[id].tag;
1146             textcallbackblock_t info;
1147             MATRIX m;
1148
1149             swf_SetTagPos(tag, 0);
1150             swf_GetU16(tag);
1151             swf_GetRect(tag,0);
1152             swf_GetMatrix(tag,&m);
1153             swf_MatrixJoin(&info.m, &m, &p->matrix);
1154             /*printf("Text matrix:\n");
1155             swf_DumpMatrix(stdout, &m);
1156             printf("Placement matrix:\n");
1157             swf_DumpMatrix(stdout, &p->matrix);*/
1158
1159             info.idtable = idtable;
1160             info.depth = p->depth;
1161             info.cxform = &p->cxform;
1162             info.clipdepth = p->clipdepth;
1163             info.buf = buf;
1164             
1165             swf_ParseDefineText(tag, textcallback, &info);
1166         } else {
1167             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1168         }
1169     }
1170
1171     /* free id and depth tables again */
1172     for(t=0;t<65536;t++) {
1173         if(idtable[t].bbox) {
1174             free(idtable[t].bbox);
1175             idtable[t].bbox=0;
1176         }
1177         if(idtable[t].type == shape_type) {
1178             SHAPE2* shape = idtable[t].obj.shape;
1179             if(shape) {
1180                 swf_Shape2Free(shape); // FIXME
1181                 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
1182             }
1183         } else if(idtable[t].type == font_type) {
1184             font_t* font = idtable[t].obj.font;
1185             if(font) {
1186                 if(font->glyphs) {
1187                     int t;
1188                     for(t=0;t<font->numchars;t++) {
1189                         swf_Shape2Free(font->glyphs[t]);
1190                         free(font->glyphs[t]); font->glyphs[t] = 0;
1191                     }
1192                     free(font->glyphs);
1193                     font->glyphs = 0;
1194                 }
1195                 free(idtable[t].obj.font); idtable[t].obj.font = 0;
1196                 font = 0;
1197             }
1198         }
1199     }
1200     free(placements);
1201     free(idtable);
1202     free(depthtable);
1203 }
1204