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