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