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