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