multiply overflow fixes
[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 #include <stdio.h>
27 #include <stdlib.h>
28 #include "../rfxswf.h"
29
30 /* one bit flag: */
31 #define clip_type 0
32 #define fill_type 1
33
34 typedef struct _renderpoint
35 {
36     float x;
37     U32 depth;
38
39     SHAPELINE*shapeline;
40     SHAPE2*s;
41     
42 } renderpoint_t;
43
44 /* 
45     enum {clip_type, solidfill_type, texturefill_type, gradientfill_type} type;
46     float fx;
47     int x;
48     U32 depth;
49     U32 clipdepth;
50
51     // solidfill;
52     RGBA color; 
53     
54     // texturefill
55     bitmap_t* bitmap;
56
57     // gradientfill
58     gradient_t* gradient;
59
60     // texture- & gradientfill;
61     U32 x,y;
62     U32 dx,dy;
63
64 */
65
66 typedef struct _renderline
67 {
68     TAG*points; //incremented in 128 byte steps
69     int num;
70     U32 pending_clipdepth;
71 } renderline_t;
72
73 typedef struct _bitmap {
74     int width;
75     int height;
76     RGBA*data;
77     int id;
78     struct _bitmap*next;
79 } bitmap_t;
80
81 typedef struct _renderbuf_internal
82 {
83     renderline_t*lines;
84     bitmap_t*bitmaps;
85     int antialize;
86     int multiply;
87     int width2,height2;
88     int shapes;
89     int ymin, ymax;
90     
91     RGBA* img;
92     int* zbuf; 
93 } renderbuf_internal;
94
95 #define DEBUG 0
96
97 static inline void add_pixel(RENDERBUF*dest, float x, int y, renderpoint_t*p)
98 {
99     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
100     if(x >= i->width2 || y >= i->height2 || y<0) return;
101     p->x = x;
102     if(y<i->ymin) i->ymin = y;
103     if(y>i->ymax) i->ymax = y;
104
105     i->lines[y].num++;
106     swf_SetBlock(i->lines[y].points, (U8*)p, sizeof(renderpoint_t));
107 }
108
109 /* set this to 0.777777 or something if the "both fillstyles set while not inside shape"
110    problem appears to often */
111 #define CUT 0.77887789
112
113 #define INT(x) ((int)((x)+16)-16)
114
115 static void add_line(RENDERBUF*buf, double x1, double y1, double x2, double y2, renderpoint_t*p)
116 {
117     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
118     double diffx, diffy;
119     double ny1, ny2, stepx;
120 /*    if(DEBUG&4) {
121         int l = sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
122         printf(" l[%d - %.2f/%.2f -> %.2f/%.2f]\n", l, x1/20.0, y1/20.0, x2/20.0, y2/20.0);
123     }*/
124     assert(p->shapeline);
125
126     y1=y1*i->multiply;
127     y2=y2*i->multiply;
128     x1=x1*i->multiply;
129     x2=x2*i->multiply;
130     
131     y1 = y1/20.0;
132     y2 = y2/20.0;
133     x1 = x1/20.0;
134     x2 = x2/20.0;
135
136     if(y2 < y1) {
137         double x;
138         double y;
139         x = x1;x1 = x2;x2=x;
140         y = y1;y1 = y2;y2=y;
141     }
142     
143     diffx = x2 - x1;
144     diffy = y2 - y1;
145     
146     ny1 = INT(y1)+CUT;
147     ny2 = INT(y2)+CUT;
148
149     if(ny1 < y1) {
150         ny1 = INT(y1) + 1.0 + CUT;
151     }
152     if(ny2 >= y2) {
153         ny2 = INT(y2) - 1.0 + CUT;
154     }
155
156     if(ny1 > ny2)
157         return;
158
159     stepx = diffx/diffy;
160     x1 = x1 + (ny1-y1)*stepx;
161     x2 = x2 + (ny2-y2)*stepx;
162
163     {
164         int posy=INT(ny1);
165         int endy=INT(ny2);
166         double posx=0;
167         double startx = x1;
168
169         while(posy<=endy) {
170             float xx = (float)(startx + posx);
171             add_pixel(buf, xx ,posy, p);
172             posx+=stepx;
173             posy++;
174         }
175     }
176 }
177 #define PI 3.14159265358979
178 static void add_solidline(RENDERBUF*buf, double x1, double y1, double x2, double y2, double width, renderpoint_t*p)
179 {
180     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
181
182     double dx = x2-x1;
183     double dy = y2-y1;
184     double sd;
185     double d;
186
187     int t;
188     int segments;
189     double lastx,lasty;
190     double vx,vy;
191     double xx,yy;
192   
193     /* Make sure the line is always at least one pixel wide */
194 #ifdef LINEMODE1
195     /* That's what Macromedia's Player does at least at zoom level >= 1.  */
196     width += 20;
197 #else
198     /* That's what Macromedia's Player seems to do at zoom level 0.  */
199     /* TODO: needs testing */
200
201     /* TODO: how does this interact with scaling? */
202     if(width * i->multiply < 20)
203         width = 20 / i->multiply;
204 #endif
205
206     sd = (double)dx*(double)dx+(double)dy*(double)dy;
207     d = sqrt(sd);
208
209     if(!dx && !dy) {
210         vx = 1;
211         vy = 0;
212     } else {
213         vx = ( dy/d);
214         vy = (-dx/d);
215     }
216
217     segments = (int)(width/2);
218     if(segments < 2)
219         segments = 2;
220
221     segments = 8;
222
223     vx=vx*width*0.5;
224     vy=vy*width*0.5;
225
226     xx = x2+vx;
227     yy = y2+vy;
228     add_line(buf, x1+vx, y1+vy, xx, yy, p);
229     lastx = xx;
230     lasty = yy;
231     for(t=1;t<segments;t++) {
232         double s = sin(t*PI/segments);
233         double c = cos(t*PI/segments);
234         xx = (x2 + vx*c - vy*s);
235         yy = (y2 + vx*s + vy*c);
236         add_line(buf, lastx, lasty, xx, yy, p);
237         lastx = xx;
238         lasty = yy;
239     }
240     
241     xx = (x2-vx);
242     yy = (y2-vy);
243     add_line(buf, lastx, lasty, xx, yy, p);
244     lastx = xx;
245     lasty = yy;
246     xx = (x1-vx);
247     yy = (y1-vy);
248     add_line(buf, lastx, lasty, xx, yy, p);
249     lastx = xx;
250     lasty = yy;
251     for(t=1;t<segments;t++) {
252         double s = sin(t*PI/segments);
253         double c = cos(t*PI/segments);
254         xx = (x1 - vx*c + vy*s);
255         yy = (y1 - vx*s - vy*c);
256         add_line(buf, lastx, lasty, xx, yy, p);
257         lastx = xx;
258         lasty = yy;
259     }
260     add_line(buf, lastx, lasty, (x1+vx), (y1+vy), p);
261 }
262
263 static inline void transform_point(MATRIX*m, int x, int y, int*dx, int*dy)
264 {
265     SPOINT p,d;
266     p.x = x;
267     p.y = y;
268     d = swf_TurnPoint(p, m);
269     *dx = d.x;
270     *dy = d.y;
271 }
272
273 static int compare_renderpoints(const void * _a, const void * _b)
274 {
275     renderpoint_t*a = (renderpoint_t*)_a;
276     renderpoint_t*b = (renderpoint_t*)_b;
277     if(a->x < b->x) return -1;
278     if(a->x > b->x) return 1;
279     return 0;
280 }
281
282 void swf_Render_Init(RENDERBUF*buf, int posx, int posy, int width, int height, int antialize, int multiply)
283 {
284     renderbuf_internal*i;
285     int y;
286     memset(buf, 0, sizeof(RENDERBUF));
287     buf->width = width*multiply;
288     buf->height = height*multiply;
289     buf->posx = posx;
290     buf->posy = posy;
291     buf->internal = (renderbuf_internal*)rfx_calloc(sizeof(renderbuf_internal));
292     i = (renderbuf_internal*)buf->internal;
293     if(antialize < 1)
294         antialize = 1;
295     i->antialize = antialize;
296     i->multiply = multiply*antialize;
297     i->height2 = antialize*buf->height;
298     i->width2 = antialize*buf->width;
299     i->lines = (renderline_t*)rfx_alloc(i->height2*sizeof(renderline_t));
300     for(y=0;y<i->height2;y++) {
301         memset(&i->lines[y], 0, sizeof(renderline_t));
302         i->lines[y].points = swf_InsertTag(0, 0);
303         i->lines[y].num = 0;
304     }
305     i->zbuf = (int*)rfx_calloc(sizeof(int)*i->width2*i->height2);
306     i->img = (RGBA*)rfx_calloc(sizeof(RGBA)*i->width2*i->height2);
307     i->shapes = 0;
308     i->ymin = 0x7fffffff;
309     i->ymax = -0x80000000;
310 }
311 void swf_Render_SetBackground(RENDERBUF*buf, RGBA*img, int width, int height)
312 {
313     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
314     int x,xx,y,yy;
315     int xstep=width*65536/i->width2;
316     int ystep=height*65536/i->height2;
317     if(i->shapes) {
318         fprintf(stderr, "rfxswf: Warning: swf_Render_SetBackground() called after drawing shapes\n");
319     }
320     for(y=0,yy=0;y<i->height2;y++,yy+=ystep) {
321         RGBA*src = &img[(yy>>16) * width];
322         RGBA*line = &i->img[y * i->width2];
323         for(x=0,xx=0;x<i->width2;x++,xx+=xstep) {
324             line[x] = src[xx>>16];
325         }
326     }
327 }
328 void swf_Render_SetBackgroundColor(RENDERBUF*buf, RGBA color)
329 {
330     swf_Render_SetBackground(buf, &color, 1, 1);
331 }
332 void swf_Render_AddImage(RENDERBUF*buf, U16 id, RGBA*img, int width, int height)
333 {
334     renderbuf_internal*i = (renderbuf_internal*)buf->internal;
335
336     bitmap_t*bm = (bitmap_t*)rfx_calloc(sizeof(bitmap_t));
337     bm->id = id;
338     bm->width = width;
339     bm->height = height;
340     bm->data = (RGBA*)rfx_alloc(width*height*4);
341     memcpy(bm->data, img, width*height*4);
342
343     bm->next = i->bitmaps;
344     i->bitmaps = bm;
345 }
346 void swf_Render_ClearCanvas(RENDERBUF*dest)
347 {
348     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
349     int y;
350     for(y=0;y<i->height2;y++) {
351         swf_ClearTag(i->lines[y].points);
352     }
353     memset(i->zbuf, 0, sizeof(int)*i->width2*i->height2);
354     memset(i->img, 0, sizeof(RGBA)*i->width2*i->height2);
355 }
356 void swf_Render_Delete(RENDERBUF*dest)
357 {
358     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
359     int y;
360     bitmap_t*b = i->bitmaps;
361
362     /* delete canvas */
363     rfx_free(i->zbuf);
364     rfx_free(i->img);
365
366     /* delete line buffers */
367     for(y=0;y<i->height2;y++) {
368         swf_DeleteTag(0, i->lines[y].points);
369         i->lines[y].points = 0;
370     }
371
372     /* delete bitmaps */
373     while(b) {
374         bitmap_t*next = b->next;
375         free(b->data);b->data=0;
376         rfx_free(b);
377         b = next;
378     }
379
380     rfx_free(i->lines); i->lines = 0;
381     rfx_free(dest->internal); dest->internal = 0;
382 }
383
384 static SHAPE2* linestyle2fillstyle(SHAPE2*shape)
385 {
386     SHAPE2*s = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
387     int t;
388     s->numfillstyles = shape->numlinestyles;
389     s->fillstyles = (FILLSTYLE*)rfx_calloc(sizeof(FILLSTYLE)*shape->numlinestyles);
390     s->lines = (SHAPELINE*)rfx_calloc(sizeof(SHAPELINE)*shape->numlinestyles);
391     for(t=0;t<shape->numlinestyles;t++) {
392         s->lines[t].fillstyle0 = t+1;
393         s->fillstyles[t].type = FILL_SOLID;
394         s->fillstyles[t].color = shape->linestyles[t].color;
395     }
396     return s;
397 }
398
399 void swf_Process(RENDERBUF*dest, U32 clipdepth);
400
401 double matrixsize(MATRIX*m)
402 {
403     double l1 = sqrt((m->sx /65536.0) * (m->sx /65536.0) + (m->r0 /65536.0) * (m->r0/65536.0) );
404     double l2 = sqrt((m->r1 /65536.0) * (m->r1 /65536.0) + (m->sy /65536.0) * (m->sy/65536.0) );
405     return sqrt(l1*l2);
406 }
407
408 void swf_RenderShape(RENDERBUF*dest, SHAPE2*shape, MATRIX*m, CXFORM*c, U16 _depth,U16 _clipdepth)
409 {
410     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
411     
412     SHAPELINE*line;
413     int x=0,y=0;
414     MATRIX mat = *m;
415     SHAPE2* s2 = 0;
416     SHAPE2* lshape = 0;
417     renderpoint_t p, lp;
418     U32 clipdepth;
419     double widthmultiply = matrixsize(m);
420
421     memset(&p, 0, sizeof(renderpoint_t));
422     memset(&lp, 0, sizeof(renderpoint_t));
423     
424     clipdepth = _clipdepth? _clipdepth << 16 | 0xffff : 0;
425     p.depth = _depth << 16;
426
427     mat.tx -= dest->posx*20;
428     mat.ty -= dest->posy*20;
429
430     s2 = swf_Shape2Clone(shape);
431     line = s2->lines;
432     if(shape->numfillstyles) {
433         int t;
434         p.s = s2;
435         /* multiply fillstyles matrices with placement matrix-
436            important for texture and gradient fill */
437         for(t=0;t<s2->numfillstyles;t++) {
438             MATRIX nm;
439             swf_MatrixJoin(&nm, &mat, &s2->fillstyles[t].m);
440             /*nm.sx *= i->multiply;
441             nm.sy *= i->multiply;
442             nm.r0 *= i->multiply;
443             nm.r1 *= i->multiply;
444             nm.tx *= i->multiply;
445             nm.ty *= i->multiply;*/
446             s2->fillstyles[t].m = nm;
447         }
448     }
449
450     if(shape->numlinestyles) {
451         lshape = linestyle2fillstyle(shape);
452         lp.s = lshape;
453         lp.depth = (_depth << 16)+1;
454     }
455
456
457     while(line)
458     {
459         int x1,y1,x2,y2,x3,y3;
460
461         if(line->type == moveTo) {
462         } else if(line->type == lineTo) {
463             transform_point(&mat, x, y, &x1, &y1);
464             transform_point(&mat, line->x, line->y, &x3, &y3);
465             
466             if(line->linestyle && ! clipdepth) {
467                 lp.shapeline = &lshape->lines[line->linestyle-1];
468                 add_solidline(dest, x1, y1, x3, y3, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
469                 lp.depth++;
470             }
471             if(line->fillstyle0 || line->fillstyle1) {
472                 assert(shape->numfillstyles);
473                 p.shapeline = line;
474                 add_line(dest, x1, y1, x3, y3, &p);
475             }
476         } else if(line->type == splineTo) {
477             int c,t,parts,qparts;
478             double xx,yy;
479             
480             transform_point(&mat, x, y, &x1, &y1);
481             transform_point(&mat, line->sx, line->sy, &x2, &y2);
482             transform_point(&mat, line->x, line->y, &x3, &y3);
483             
484             c = abs(x3-2*x2+x1) + abs(y3-2*y2+y1);
485             xx=x1;
486             yy=y1;
487
488             parts = (int)(sqrt((float)c)/3);
489             if(!parts) parts = 1;
490
491             for(t=1;t<=parts;t++) {
492                 double nx = (double)(t*t*x3 + 2*t*(parts-t)*x2 + (parts-t)*(parts-t)*x1)/(double)(parts*parts);
493                 double ny = (double)(t*t*y3 + 2*t*(parts-t)*y2 + (parts-t)*(parts-t)*y1)/(double)(parts*parts);
494                 
495                 if(line->linestyle && ! clipdepth) {
496                     lp.shapeline = &lshape->lines[line->linestyle-1];
497                     add_solidline(dest, xx, yy, nx, ny, shape->linestyles[line->linestyle-1].width * widthmultiply, &lp);
498                     lp.depth++;
499                 }
500                 if(line->fillstyle0 || line->fillstyle1) {
501                     assert(shape->numfillstyles);
502                     p.shapeline = line;
503                     add_line(dest, xx, yy, nx, ny, &p);
504                 }
505
506                 xx = nx;
507                 yy = ny;
508             }
509         }
510         x = line->x;
511         y = line->y;
512         line = line->next;
513     }
514     
515     swf_Process(dest, clipdepth);
516     
517     if(s2) {
518         swf_Shape2Free(s2);rfx_free(s2);s2=0;
519     }
520     if(lshape) {
521         swf_Shape2Free(lshape);rfx_free(lshape);lshape=0;
522     }
523
524 }
525
526 static RGBA color_red = {255,255,0,0};
527 static RGBA color_white = {255,255,255,255};
528 static RGBA color_black = {255,0,0,0};
529
530 static void fill_clip(RGBA*line, int*z, int y, int x1, int x2, U32 depth)
531 {
532     int x = x1;
533     if(x1>=x2)
534         return;
535     do {
536         if(depth > z[x]) {
537             z[x] = depth;
538         }
539     } while(++x<x2);
540 }
541
542
543 static void fill_solid(RGBA*line, int*z, int y, int x1, int x2, RGBA col, U32 depth)
544 {
545     int x = x1;
546
547     if(col.a!=255) {
548         int ainv = 255-col.a;
549         col.r = (col.r*col.a)>>8;
550         col.g = (col.g*col.a)>>8;
551         col.b = (col.b*col.a)>>8;
552         col.a = 255;
553         do {
554             if(depth >= z[x]) {
555                 line[x].r = ((line[x].r*ainv)>>8)+col.r;
556                 line[x].g = ((line[x].g*ainv)>>8)+col.g;
557                 line[x].b = ((line[x].b*ainv)>>8)+col.b;
558                 line[x].a = 255;
559                 z[x] = depth;
560             }
561         } while(++x<x2);
562     } else {
563         do {
564             if(depth >= z[x]) {
565                 line[x] = col;
566                 z[x] = depth;
567             }
568         } while(++x<x2);
569     }
570 }
571
572 static int inline clamp(int v)
573 {
574     if(v>255) return 255;
575     else return v;
576 }
577
578 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)
579 {
580     int x = x1;
581     
582     double m11= m->sx*fmultiply/65536.0, m21= m->r1*fmultiply/65536.0;
583     double m12= m->r0*fmultiply/65536.0, m22= m->sy*fmultiply/65536.0;
584     double rx = m->tx*fmultiply/20.0;
585     double ry = m->ty*fmultiply/20.0;
586
587     double det = m11*m22 - m12*m21;
588     if(fabs(det) < 0.0005) { 
589         /* x direction equals y direction- the image is invisible */
590         return;
591     }
592     det = 20.0/det;
593
594     if(!b->width || !b->height) {
595         fill_solid(line, z, y, x1, x2, color_red, depth);
596         return;
597     }
598
599     do {
600         if(depth >= z[x]) {
601             RGBA col;
602             int xx = (int)((  (x - rx) * m22 - (y - ry) * m21)*det);
603             int yy = (int)((- (x - rx) * m12 + (y - ry) * m11)*det);
604             int ainv;
605
606             if(clipbitmap) {
607                 if(xx<0) xx=0;
608                 if(xx>=b->width) xx = b->width-1;
609                 if(yy<0) yy=0;
610                 if(yy>=b->height) yy = b->height-1;
611             } else {
612                 xx %= b->width;
613                 yy %= b->height;
614                 if(xx<0) xx += b->width;
615                 if(yy<0) yy += b->height;
616             }
617
618             col = b->data[yy*b->width+xx];
619             ainv = 255-col.a;
620
621             line[x].r = clamp(((line[x].r*ainv)>>8)+col.r);
622             line[x].g = clamp(((line[x].g*ainv)>>8)+col.g);
623             line[x].b = clamp(((line[x].b*ainv)>>8)+col.b);
624             line[x].a = 255;
625             
626             z[x] = depth;
627         }
628     } while(++x<x2);
629 }
630
631 static void fill_gradient(RGBA*line, int*z, int y, int x1, int x2, MATRIX*m, GRADIENT*g, int type, U32 depth, double fmultiply)
632 {
633     int x = x1;
634     
635     double m11= m->sx*fmultiply/80, m21= m->r1*fmultiply/80;
636     double m12= m->r0*fmultiply/80, m22= m->sy*fmultiply/80;
637     double rx = m->tx*fmultiply/20.0;
638     double ry = m->ty*fmultiply/20.0;
639
640     double det = m11*m22 - m12*m21;
641     if(fabs(det) < 0.0005) { 
642         /* x direction equals y direction- the image is invisible */
643         return;
644     }
645     det = 1.0/det;
646
647     RGBA palette[512];
648     RGBA oldcol = g->rgba[0];
649     int r0 = g->ratios[0]*2;
650     int t;
651     for(t=0;t<r0;t++) 
652         palette[t] = oldcol;
653     for(t=1;t<g->num;t++) {
654         int r1 = g->ratios[t]*2;
655         RGBA newcol = g->rgba[t];
656         if(r0 == r1)
657             continue;
658         //printf("%d %d->%d %02x%02x%02x%02x->%02x%02x%02x%02x\n", 
659         //      t, r0, r1, oldcol.r,oldcol.g,oldcol.b,oldcol.a,
660         //      newcol.r,newcol.g,newcol.b,newcol.a);
661         double f = 1.0 / (r1-r0);
662         double p0 = 1;
663         double p1 = 0;
664         int s;
665         for(;r0<=r1;r0++) {
666             palette[r0].r = oldcol.r*p0 + newcol.r*p1;
667             palette[r0].g = oldcol.g*p0 + newcol.g*p1;
668             palette[r0].b = oldcol.b*p0 + newcol.b*p1;
669             palette[r0].a = oldcol.a*p0 + newcol.a*p1;
670             p0 -= f;
671             p1 += f;
672         }
673         oldcol = newcol;
674     }
675     for(t=r0;t<512;t++) 
676         palette[t] = oldcol;
677
678     do {
679         if(depth >= z[x]) {
680             RGBA col;
681             double xx = (  (x - rx) * m22 - (y - ry) * m21)*det;
682             double yy = (- (x - rx) * m12 + (y - ry) * m11)*det;
683
684             if(type == FILL_LINEAR) {
685                 int xr = xx*256;
686                 if(xr<-256)
687                     xr = -256;
688                 if(xr>255)
689                     xr = 255;
690                 col = palette[xr+256];
691             } else {
692                 int xr = sqrt(xx*xx+yy*yy)*511;
693                 if(xr<0)
694                     xr = 0;
695                 if(xr>511)
696                     xr = 511;
697                 col = palette[xr];
698             }
699             int ainv;
700             ainv = 255-col.a;
701             line[x].r = clamp(((line[x].r*ainv)>>8)+col.r);
702             line[x].g = clamp(((line[x].g*ainv)>>8)+col.g);
703             line[x].b = clamp(((line[x].b*ainv)>>8)+col.b);
704             line[x].a = 255;
705             
706             z[x] = depth;
707         }
708     } while(++x<x2);
709 }
710
711 typedef struct _layer {
712     int fillid;
713     renderpoint_t*p;
714     struct _layer*next;
715     struct _layer*prev;
716 } layer_t;
717
718 typedef struct {
719     layer_t*layers;
720 } state_t;
721
722
723 static void fill(RENDERBUF*dest, RGBA*line, int*zline, int y, int x1, int x2, state_t*fillstate, U32 clipdepth)
724 {
725     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
726     int clip=1;
727
728     layer_t*l = fillstate->layers;
729
730     if(x1>=x2) //zero width? nothing to do.
731         return;
732     
733     while(l) {
734         if(l->fillid == 0) {
735             /* not filled. TODO: we should never add those in the first place */
736             if(DEBUG&2)
737                 printf("(not filled)");
738         } else if(l->fillid > l->p->s->numfillstyles) {
739             fprintf(stderr, "Fill style out of bounds (%d>%d)", l->fillid, l->p->s->numlinestyles);
740         } else if(clipdepth) {
741             /* filled region- not used for clipping */
742             clip = 0;
743         } else {
744             FILLSTYLE*f;
745             if(DEBUG&2) 
746                 printf("(%d -> %d style %d)", x1, x2, l->fillid);
747
748             f = &l->p->s->fillstyles[l->fillid-1];
749
750             if(f->type == FILL_SOLID) {
751                 /* plain color fill */
752                 fill_solid(line, zline, y, x1, x2, f->color, l->p->depth);
753             } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED || f->type == (FILL_TILED|2) || f->type == (FILL_CLIPPED|2)) {
754                 /* TODO: optimize (do this in add_pixel()?) */
755                 bitmap_t* b = i->bitmaps;
756                 while(b && b->id != f->id_bitmap) {
757                     b = b->next;
758                 }
759                 if(!b) {
760                     fprintf(stderr, "Shape references unknown bitmap %d\n", f->id_bitmap);
761                     fill_solid(line, zline, y, x1, x2, color_red, l->p->depth);
762                 } else {
763                     fill_bitmap(line, zline, y, x1, x2, &f->m, b, /*clipped?*/f->type&1, l->p->depth, i->multiply);
764                 }
765             } else if(f->type == FILL_LINEAR || f->type == FILL_RADIAL) {
766                 fill_gradient(line, zline, y, x1, x2, &f->m, &f->gradient, f->type, l->p->depth, i->multiply);
767             } else {
768                 fprintf(stderr, "Undefined fillmode: %02x\n", f->type);
769             }
770         }
771         l = l->next;
772     }
773     if(clip && clipdepth) {
774         fill_clip(line, zline, y, x1, x2, clipdepth);
775     }
776 }
777
778 static void search_layer(state_t*state, int depth, layer_t**before, layer_t**self, layer_t**after)
779 {
780     layer_t*last=0,*l = state->layers;
781     while(l && l->p->depth < depth) {
782         last = l;
783         l = l->next;
784     }
785     *before = last;
786     if(l && l->p->depth == depth)
787         *self = l;
788     else
789         *after = l;
790 }
791 static void delete_layer(state_t*state, layer_t*todel)
792 {
793     layer_t*before=todel->prev;
794     layer_t*next = todel->next;
795     rfx_free(todel);
796     if(!before) {
797         state->layers = next;
798         if(next)
799             next->prev = 0;
800     } else {
801         before->next = next;
802         if(before->next)
803             before->next->prev = before;
804     }
805 }
806 static void add_layer(state_t*state, layer_t*before, layer_t*toadd)
807 {
808     if(!before) {
809         toadd->next = state->layers;
810         toadd->prev = 0;
811         state->layers=toadd;
812     } else {
813         toadd->next = before->next;
814         toadd->prev = before;
815         before->next = toadd;
816     }
817     if(toadd->next)
818         toadd->next->prev = toadd;
819 }
820 static void free_layers(state_t* state)
821 {
822     layer_t*l = state->layers;
823     while(l) {
824         layer_t*next = l->next;
825         rfx_free(l);
826         l = next;
827     }
828 }
829
830 static void change_state(int y, state_t* state, renderpoint_t*p)
831 {
832     layer_t*before=0, *self=0, *after=0;
833
834     if(DEBUG&2) { 
835         printf("[(%f,%d)/%d/%d-%d]", p->x, y, p->depth, p->shapeline->fillstyle0, p->shapeline->fillstyle1);
836     }
837
838     search_layer(state, p->depth, &before, &self, &after);
839
840     if(self) {
841         /* shape update */
842         if(self->fillid<0/*??*/ || !p->shapeline->fillstyle0 || !p->shapeline->fillstyle1) {
843             /* filling ends */
844             if(DEBUG&2) printf("<D>");
845             
846             delete_layer(state, self);
847         } else { 
848             /*both fill0 and fill1 are set- exchange the two, updating the layer */
849             if(self->fillid == p->shapeline->fillstyle0) {
850                 self->fillid = p->shapeline->fillstyle1;
851                 self->p = p;
852                 if(DEBUG&2) printf("<X>");
853             } else if(self->fillid == p->shapeline->fillstyle1) {
854                 self->fillid = p->shapeline->fillstyle0;
855                 self->p = p;
856                 if(DEBUG&2) printf("<X>");
857             } else {
858                 /* buggy shape. keep everything as-is. */
859                 if(DEBUG&2) printf("<!>");
860                 //fprintf(stderr, "<line %d: bad swap>\n", y);
861             }
862         }
863         return;
864     } else {
865         layer_t* n = 0;
866         if(p->shapeline && p->shapeline->fillstyle0 && p->shapeline->fillstyle1) {
867             /* this is a hack- a better way would be to make sure that
868                we always get (0,32), (32, 33), (33, 0) in the right order if
869                they happen to fall on the same pixel.
870                (not: (0,32), (33, 0), (32, 33))
871                Notice: Weird fill styles appear if linestyles are involved, too.
872             */
873             fprintf(stderr, "<line %d: both fillstyles set while not inside shape>\n", y);
874             return;
875         }
876         
877         n = (layer_t*)rfx_calloc(sizeof(layer_t));
878
879         if(DEBUG&2) printf("<+>");
880
881         n->fillid = p->shapeline->fillstyle0 ? p->shapeline->fillstyle0 : p->shapeline->fillstyle1;
882         n->p = p;
883
884         add_layer(state, before, n);
885     }
886 }
887
888 void swf_Process(RENDERBUF*dest, U32 clipdepth)
889 {
890     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
891     int y;
892     
893     if(i->ymax < i->ymin) {
894         /* shape is empty. return. 
895            only, if it's a clipshape, remember the clipdepth */
896         if(clipdepth) {
897             for(y=0;y<i->height2;y++) {
898                 if(clipdepth > i->lines[y].pending_clipdepth)
899                     i->lines[y].pending_clipdepth = clipdepth;
900             }
901         }
902         return; //nothing (else) to do
903     }
904
905     if(clipdepth) {
906         /* lines outside the clip shape are not filled
907            immediately, only the highest clipdepth so far is
908            stored there. They will be clipfilled once there's
909            actually something about to happen in that line */
910         for(y=0;y<i->ymin;y++) {
911             if(clipdepth > i->lines[y].pending_clipdepth)
912                 i->lines[y].pending_clipdepth = clipdepth;
913         }
914         for(y=i->ymax+1;y<i->height2;y++) {
915             if(clipdepth > i->lines[y].pending_clipdepth)
916                 i->lines[y].pending_clipdepth = clipdepth;
917         }
918     }
919     
920     for(y=i->ymin;y<=i->ymax;y++) {
921         int n;
922         TAG*tag = i->lines[y].points;
923         int num = i->lines[y].num;
924         renderpoint_t*points = (renderpoint_t*)tag->data;
925         RGBA*line = &i->img[i->width2*y];
926         int*zline = &i->zbuf[i->width2*y];
927         int lastx = 0;
928         state_t fillstate;
929         memset(&fillstate, 0, sizeof(state_t));
930         qsort(points, num, sizeof(renderpoint_t), compare_renderpoints);
931         /* resort points */
932         /*if(y==884) {
933             for(n=0;n<num;n++) {
934                 printf("%f (%d/%d) %d\n", points[n].x, 
935                         points[n].shapeline->fillstyle0,
936                         points[n].shapeline->fillstyle1,
937                         points[n].shapeline->linestyle);
938             }
939         }*/
940
941         if(i->lines[y].pending_clipdepth && !clipdepth) {
942             fill_clip(line, zline, y, 0, i->width2, i->lines[y].pending_clipdepth);
943             i->lines[y].pending_clipdepth=0;
944         }
945
946         for(n=0;n<num;n++) {
947             renderpoint_t*p = &points[n];
948             renderpoint_t*next= n<num-1?&points[n+1]:0;
949             int startx = (int)p->x;
950             int endx = (int)(next?next->x:i->width2);
951             if(endx > i->width2)
952                 endx = i->width2;
953             if(startx < 0)
954                 startx = 0;
955             if(endx < 0)
956                 endx = 0;
957
958             if(clipdepth) {
959                 /* for clipping, the inverse is filled 
960                    TODO: lastx!=startx only at the start of the loop, 
961                          so this might be moved up
962                  */
963                 fill_clip(line, zline, y, lastx, startx, clipdepth);
964             }
965             change_state(y, &fillstate, p);
966
967             fill(dest, line, zline, y, startx, endx, &fillstate, clipdepth);
968 /*          if(y == 0 && startx == 232 && endx == 418) {
969                 printf("ymin=%d ymax=%d\n", i->ymin, i->ymax);
970                 for(n=0;n<num;n++) {
971                     renderpoint_t*p = &points[n];
972                     printf("x=%f depth=%08x\n", p->x, p->depth);
973                 }
974             }*/
975
976             lastx = endx;
977             if(endx == i->width2)
978                 break;
979         }
980         if(clipdepth) {
981             /* TODO: is lastx *ever* != i->width2 here? */
982             fill_clip(line, zline, y, lastx, i->width2, clipdepth);
983         }
984         free_layers(&fillstate);
985         
986         i->lines[y].num = 0;
987         swf_ClearTag(i->lines[y].points);
988     }
989     i->ymin = 0x7fffffff;
990     i->ymax = -0x80000000;
991 }
992
993 RGBA* swf_Render(RENDERBUF*dest)
994 {
995     renderbuf_internal*i = (renderbuf_internal*)dest->internal;
996     RGBA* img = (RGBA*)rfx_alloc(sizeof(RGBA)*dest->width*dest->height);
997     int y;
998     int antialize = i->antialize;
999    
1000     if(antialize <= 1) /* no antializing */ {
1001         for(y=0;y<i->height2;y++) {
1002             RGBA*line = &i->img[y*i->width2];
1003             memcpy(&img[y*dest->width], line, sizeof(RGBA)*dest->width);
1004         }
1005     } else {
1006         RGBA**lines = (RGBA**)rfx_calloc(sizeof(RGBA*)*antialize);
1007         int q = antialize*antialize;
1008         int ypos = 0;
1009         for(y=0;y<i->height2;y++) {
1010             int n;
1011             ypos = y % antialize;
1012             lines[ypos] = &i->img[y*i->width2];
1013             if(ypos == antialize-1) {
1014                 RGBA*out = &img[(y / antialize)*dest->width];
1015                 int x;
1016                 int r,g,b,a;
1017                 for(x=0;x<dest->width;x++) {
1018                     int xpos = x*antialize;
1019                     int yp;
1020                     U32 r=0,g=0,b=0,a=0;
1021                     for(yp=0;yp<antialize;yp++) {
1022                         RGBA*lp = &lines[yp][xpos];
1023                         int xp;
1024                         for(xp=0;xp<antialize;xp++) {
1025                             RGBA*p = &lp[xp];
1026                             r += p->r;
1027                             g += p->g;
1028                             b += p->b;
1029                             a += p->a;
1030                         }
1031                     }
1032                     out[x].r = r / q;
1033                     out[x].g = g / q;
1034                     out[x].b = b / q;
1035                     out[x].a = a / q;
1036                 }
1037             }
1038         }
1039         rfx_free(lines);
1040     }
1041     return img;
1042 }
1043
1044 typedef struct
1045 {
1046     int numchars;
1047     SHAPE2**glyphs;
1048 } font_t;
1049
1050 enum CHARACTER_TYPE {none_type, shape_type, image_type, text_type, edittext_type, font_type, sprite_type};
1051 typedef struct
1052 {
1053     TAG*tag;
1054     SRECT*bbox;
1055     enum CHARACTER_TYPE type;
1056     union {
1057         SHAPE2*shape;
1058         font_t*font;
1059     } obj;
1060 } character_t;
1061
1062 int compare_placements(const void *v1, const void *v2)
1063 {
1064     SWFPLACEOBJECT*p1 = (SWFPLACEOBJECT*)v1;
1065     SWFPLACEOBJECT*p2 = (SWFPLACEOBJECT*)v2;
1066     if(p1->depth != p2->depth)
1067         return (int)p1->depth - (int)p2->depth;
1068     else 
1069         if(p2->clipdepth)
1070             return 1; // do the clip first
1071         else
1072             return -1;
1073
1074 /*    if(!p1->clipdepth) {
1075         if(!p2->clipdepth) {
1076             // !p1->clipdepth && !p2->clipdepth
1077             return (int)p1->depth - (int)p2->depth;
1078         } else {
1079             // !p1->clipdepth && p2->clipdepth
1080             if(p1->depth != p2->clipdepth)
1081                 return (int)p1->depth - (int)p2->clipdepth;
1082             else
1083                 return 1; // do the clip first
1084         }
1085     } else {
1086         if(!p2->clipdepth) {
1087             // p1->clipdepth && !p2->clipdepth
1088             if(p1->clipdepth != p2->depth)
1089                 return (int)p1->clipdepth - (int)p2->depth;
1090             else
1091                 return -1;// do the clip first
1092         } else {
1093             if(p1->clipdepth != p2->clipdepth)
1094                 return (int)p1->clipdepth - (int)p2->clipdepth;
1095             else
1096                 return (int)p1->depth - (int)p2->depth;
1097         }
1098     }*/
1099 }
1100
1101 typedef struct textcallbackblock
1102 {
1103     character_t*idtable;
1104     U16 depth;
1105     U16 clipdepth;
1106     CXFORM* cxform;
1107     MATRIX m;
1108     RENDERBUF*buf;
1109 } textcallbackblock_t;
1110
1111 static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize, 
1112                     int xstart, int ystart, RGBA* color)
1113 {
1114     textcallbackblock_t * info = (textcallbackblock_t*)self;
1115     font_t*font = 0;
1116     int t;
1117     if(info->idtable[fontid].type != font_type) {
1118         fprintf(stderr, "ID %d is not a font\n", fontid);
1119         return;
1120     } else if(!info->idtable[fontid].obj.font) {
1121         fprintf(stderr, "Font %d unknown\n", fontid);
1122         return;
1123     } else {
1124         font  = info->idtable[fontid].obj.font;
1125     }
1126     for(t=0;t<nr;t++) {
1127         int x = xstart + xpos[t];
1128         int y = ystart;
1129         MATRIX m = info->m;
1130         SPOINT p;
1131         
1132         p.x = x; p.y = y; 
1133         p = swf_TurnPoint(p, &m);
1134         
1135         m.sx = (m.sx * fontsize) / 1024;
1136         m.sy = (m.sy * fontsize) / 1024;
1137         m.r0 = (m.r0 * fontsize) / 1024;
1138         m.r1 = (m.r1 * fontsize) / 1024;
1139         m.tx = p.x;
1140         m.ty = p.y;
1141
1142         if(chars[t]<0 || chars[t]>= font->numchars) {
1143             fprintf(stderr, "Character out of range: %d\n", chars[t]);
1144         } else {
1145             SHAPE2*shape = font->glyphs[chars[t]];
1146             shape->fillstyles[0].color = *color; //q&d
1147             /*printf("Rendering char %d (size %d, x:%d, y:%d) color:%02x%02x%02x%02x\n", chars[t], fontsize, x, y,
1148                     color->a, color->r, color->g, color->b);
1149             swf_DumpMatrix(stdout, &m);
1150             swf_DumpShape(shape);*/
1151             swf_RenderShape(info->buf, shape, &m, info->cxform, info->depth, info->clipdepth);
1152         }
1153     }
1154 }
1155
1156 static void renderFromTag(RENDERBUF*buf, character_t*idtable, TAG*firstTag, MATRIX*m)
1157 {
1158     TAG*tag = 0;
1159     int numplacements = 0;
1160     SWFPLACEOBJECT* placements;
1161
1162     tag = firstTag;
1163     numplacements = 0;
1164     while(tag) {
1165         if(tag->id == ST_PLACEOBJECT || 
1166            tag->id == ST_PLACEOBJECT2) {
1167             numplacements++;
1168         }
1169         if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1170             break;
1171         tag = tag->next;
1172     }
1173     placements = (SWFPLACEOBJECT*)rfx_calloc(sizeof(SWFPLACEOBJECT)*numplacements);
1174     numplacements = 0;
1175
1176     tag = firstTag;
1177     while(tag) {
1178         if(swf_isPlaceTag(tag)) {
1179             SWFPLACEOBJECT p;
1180             swf_GetPlaceObject(tag, &p);
1181             /* TODO: add move and deletion */
1182             placements[numplacements++] = p;
1183             swf_PlaceObjectFree(&p); //dirty! but it only frees fields we don't use
1184         }
1185         if(tag->id == ST_SHOWFRAME || tag->id == ST_END)
1186             break;
1187         tag = tag->next;
1188     }
1189
1190     qsort(placements, numplacements, sizeof(SWFPLACEOBJECT), compare_placements);
1191      
1192     int t;
1193     for(t=0;t<numplacements;t++) {
1194         SWFPLACEOBJECT*p = &placements[t];
1195         int id = p->id;
1196         MATRIX m2;
1197         swf_MatrixJoin(&m2, m, &p->matrix);
1198             
1199         if(!idtable[id].tag) { 
1200             fprintf(stderr, "rfxswf: Id %d is unknown\n", id);
1201             continue;
1202         }
1203
1204         if(idtable[id].type == shape_type) {
1205             //SRECT sbbox = swf_TurnRect(*idtable[id].bbox, &p->matrix);
1206             swf_RenderShape(buf, idtable[id].obj.shape, &m2, &p->cxform, p->depth, p->clipdepth);
1207         } else if(idtable[id].type == sprite_type) {
1208             swf_UnFoldSprite(idtable[id].tag);
1209             renderFromTag(buf, idtable, idtable[id].tag->next, &m2);
1210             swf_FoldSprite(idtable[id].tag);
1211         } else if(idtable[id].type == text_type) {
1212             TAG* tag = idtable[id].tag;
1213             textcallbackblock_t info;
1214             MATRIX mt;
1215
1216             swf_SetTagPos(tag, 0);
1217             swf_GetU16(tag);
1218             swf_GetRect(tag,0);
1219             swf_GetMatrix(tag,&mt);
1220             swf_MatrixJoin(&info.m, &m2, &mt);
1221             /*printf("Text matrix:\n");
1222             swf_DumpMatrix(stdout, &m);
1223             printf("Placement matrix:\n");
1224             swf_DumpMatrix(stdout, &p->matrix);
1225             printf("Final matrix:\n");
1226             swf_DumpMatrix(stdout, &info.m);*/
1227
1228             info.idtable = idtable;
1229             info.depth = p->depth;
1230             info.cxform = &p->cxform;
1231             info.clipdepth = p->clipdepth;
1232             info.buf = buf;
1233             
1234             swf_ParseDefineText(tag, textcallback, &info);
1235         } else if(idtable[id].type == edittext_type) {
1236             TAG* tag = idtable[id].tag;
1237             U16 flags = swf_GetBits(tag, 16);
1238             if(flags & ET_HASTEXT) {
1239                 fprintf(stderr, "edittext not supported yet (id %d)\n", id);
1240             }
1241         } else {
1242             fprintf(stderr, "Unknown/Unsupported Object Type for id %d: %s\n", id, swf_TagGetName(idtable[id].tag));
1243         }
1244     }
1245
1246     free(placements);
1247 }
1248
1249 void swf_RenderSWF(RENDERBUF*buf, SWF*swf)
1250 {
1251     TAG*tag;
1252     int t;
1253     RGBA color;
1254
1255     swf_OptimizeTagOrder(swf);
1256     swf_FoldAll(swf);
1257     
1258     character_t* idtable = (character_t*)rfx_calloc(sizeof(character_t)*65536);            // id to character mapping
1259     
1260     /* set background color */
1261     color = swf_GetSWFBackgroundColor(swf);
1262     swf_Render_SetBackgroundColor(buf, color);
1263
1264     /* parse definitions */
1265     tag = swf->firstTag;
1266     while(tag) {
1267         if(swf_isDefiningTag(tag)) {
1268             int id = swf_GetDefineID(tag);
1269             idtable[id].tag = tag;
1270             idtable[id].bbox = (SRECT*)rfx_alloc(sizeof(SRECT));
1271             *idtable[id].bbox = swf_GetDefineBBox(tag);
1272
1273             if(swf_isShapeTag(tag)) {
1274                 SHAPE2* shape = (SHAPE2*)rfx_calloc(sizeof(SHAPE2));
1275                 swf_ParseDefineShape(tag, shape);
1276                 idtable[id].type = shape_type;
1277                 idtable[id].obj.shape = shape;
1278             } else if(swf_isImageTag(tag)) {
1279                 int width,height;
1280                 RGBA*data = swf_ExtractImage(tag, &width, &height);
1281                 idtable[id].type = image_type;
1282                 swf_Render_AddImage(buf, id, data, width, height);
1283                 free(data);
1284             } else if(tag->id == ST_DEFINEFONT ||
1285                       tag->id == ST_DEFINEFONT2) {
1286                 int t;
1287                 SWFFONT*swffont;
1288                 font_t*font = (font_t*)rfx_calloc(sizeof(font_t));
1289                 idtable[id].obj.font = font;
1290                 swf_FontExtract(swf,id,&swffont);
1291                 font->numchars = swffont->numchars;
1292                 font->glyphs = (SHAPE2**)rfx_calloc(sizeof(SHAPE2*)*font->numchars);
1293                 for(t=0;t<font->numchars;t++) {
1294                     if(!swffont->glyph[t].shape->fillstyle.n) {
1295                         /* the actual fill color will be overwritten while rendering */
1296                         swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
1297                     }
1298                     font->glyphs[t] = swf_ShapeToShape2(swffont->glyph[t].shape);
1299                 }
1300                 swf_FontFree(swffont);
1301                 idtable[id].type = font_type;
1302
1303             } else if(tag->id == ST_DEFINEFONTINFO ||
1304                       tag->id == ST_DEFINEFONTINFO2) {
1305                 idtable[id].type = font_type;
1306             } else if(tag->id == ST_DEFINETEXT ||
1307                       tag->id == ST_DEFINETEXT2) {
1308                 idtable[id].type = text_type;
1309             } else if(tag->id == ST_DEFINESPRITE) {
1310                 idtable[id].type = sprite_type;
1311             } else if(tag->id == ST_DEFINEEDITTEXT) {
1312                 idtable[id].type = edittext_type;
1313             }
1314         }
1315         tag = tag->next;
1316     }
1317     MATRIX m;
1318     swf_GetMatrix(0, &m);
1319     renderFromTag(buf, idtable, swf->firstTag, &m);
1320     
1321     /* free id and depth tables again */
1322     for(t=0;t<65536;t++) {
1323         if(idtable[t].bbox) {
1324             free(idtable[t].bbox);
1325             idtable[t].bbox=0;
1326         }
1327         if(idtable[t].type == shape_type) {
1328             SHAPE2* shape = idtable[t].obj.shape;
1329             if(shape) {
1330                 swf_Shape2Free(shape); // FIXME
1331                 free(idtable[t].obj.shape);idtable[t].obj.shape = 0;
1332             }
1333         } else if(idtable[t].type == font_type) {
1334             font_t* font = idtable[t].obj.font;
1335             if(font) {
1336                 if(font->glyphs) {
1337                     int t;
1338                     for(t=0;t<font->numchars;t++) {
1339                         swf_Shape2Free(font->glyphs[t]);
1340                         free(font->glyphs[t]); font->glyphs[t] = 0;
1341                     }
1342                     free(font->glyphs);
1343                     font->glyphs = 0;
1344                 }
1345                 free(idtable[t].obj.font); idtable[t].obj.font = 0;
1346                 font = 0;
1347             }
1348         }
1349     }
1350     free(idtable);
1351 }
1352