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