fixed clipping inside sprite objects
[swftools.git] / lib / readers / swf.c
1 #include <assert.h>
2 #include "../gfxdevice.h"
3 #include "../gfxsource.h"
4 #include "../gfxtools.h"
5 #include "../log.h"
6 #include "../mem.h"
7 #include "../png.h"
8 #include "../rfxswf.h"
9 #include "swf.h"
10
11 typedef struct _map16_t
12 {
13     void** ids;
14 } map16_t;
15
16 typedef struct _swf_page_internal
17 {
18     int frame;
19 } swf_page_internal_t;
20
21 typedef struct _swf_doc_internal
22 {
23     map16_t*id2char;
24     SWF swf;
25     int width,height;
26     MATRIX m;
27 } swf_doc_internal_t;
28
29 #define TYPE_SHAPE 1
30 #define TYPE_BITMAP 2
31 #define TYPE_SPRITE 3
32 #define TYPE_FONT 4
33 #define TYPE_TEXT 5
34
35 typedef struct _character
36 {
37     U16 id;
38     TAG*tag;
39     char type;
40     void*data;
41 } character_t;
42
43 typedef struct _placement
44 {
45     SWFPLACEOBJECT po;
46     int age;
47     int startFrame;
48 } placement_t;
49
50 typedef struct _sprite
51 {
52     int frameCount;
53 } sprite_t;
54
55 struct _render
56 {
57     map16_t*id2char;
58     gfxdevice_t*device;
59     MATRIX m;
60     int clips;
61     int*clips_waiting;
62 };
63 typedef struct _render render_t;
64
65
66 static void placement_free(placement_t*p)
67 {
68     swf_PlaceObjectFree(&p->po);
69     free(p);
70 }
71
72 //---- object/depth handling ----
73
74 map16_t* map16_new()
75 {
76     map16_t*map = rfx_calloc(sizeof(map16_t));
77     /* TODO: replace this by a more sophisticated data structure */
78     map->ids = (void**)rfx_calloc(sizeof(character_t)*65536);
79     return map;
80 }
81 character_t*map16_get_id(map16_t*map, int id)
82 {
83     if(id<0 || id>=65536)
84         return 0;
85     return map->ids[id];
86 }
87 void map16_free(map16_t*map)
88 {
89     free(map->ids);
90 }
91 void map16_add_id(map16_t*map, int nr, void*id)
92 {
93     if(map->ids[nr])
94         fprintf(stderr, "Warning: ID %d defined more than once\n");
95     map->ids[nr] = id;
96 }
97 void map16_remove_id(map16_t*map, int nr)
98 {
99     map->ids[nr] = 0;
100 }
101 void map16_enumerate(map16_t*map, void (*f)(void*self, int id, void*data), void*self)
102 {
103     int t;
104     for(t=0;t<65536;t++) {
105         if(map->ids[t]) {
106             f(self, t, map->ids[t]);
107         }
108     }
109 }
110 //---- conversion stuff ----
111
112 static void convertMatrix(MATRIX*from, gfxmatrix_t*to)
113 {
114     to->m00 = from->sx / 65536.0; to->m10 = from->r1 / 65536.0;
115     to->m01 = from->r0 / 65536.0; to->m11 = from->sy / 65536.0;
116     to->tx = from->tx/20.0;
117     to->ty = from->ty/20.0;
118 }
119
120 static void convertCXForm(CXFORM*from, gfxcxform_t*to)
121 {
122     memset(to, 0, sizeof(gfxcxform_t));
123     to->aa = from->a0 / 256.0;
124     to->rr = from->r0 / 256.0;
125     to->gg = from->g0 / 256.0;
126     to->bb = from->b0 / 256.0;
127     to->ta = from->a1;
128     to->tr = from->r1;
129     to->tg = from->g1;
130     to->tb = from->b1;
131 }
132
133 static gfxgradient_t* convertGradient(GRADIENT*from)
134 {
135     gfxgradient_t*g = rfx_calloc(from->num * sizeof(gfxgradient_t));
136     int t;
137     for(t=0;t<from->num;t++) {
138         g[t].pos = from->ratios[t] / 255.0;
139         g[t].color = *(gfxcolor_t*)&from->rgba[t];
140         if(t<from->num-1)
141             g[t].next = &g[t+1];
142         else
143             g[t].next = 0;
144     }
145     return g;
146 }
147
148 gfxline_t* swfline_to_gfxline(SHAPELINE*line, int linestyle, int fillstyle0)
149 {
150     gfxdrawer_t d;
151     SCOORD x=0,y=0;
152     gfxline_t*l;
153     gfxdrawer_target_gfxline(&d);
154     if(line && line->type != moveTo) {
155         fprintf(stderr, "Warning: Shape doesn't start with a moveTo\n");
156     }
157     while(line) {
158         if(line->fillstyle0 == fillstyle0 || line->fillstyle1 == fillstyle0 || 
159            line->linestyle == linestyle) {
160             if(line->type == lineTo) {
161                 d.moveTo(&d, x/20.0,y/20.0);
162                 d.lineTo(&d, line->x/20.0,line->y/20.0);
163             } else if(line->type == splineTo) {
164                 d.moveTo(&d, x/20.0,y/20.0);
165                 d.splineTo(&d, line->sx/20.0, line->sy/20.0, line->x/20.0,line->y/20.0);
166             }
167         }
168         x = line->x;
169         y = line->y;
170         line = line->next;
171     }
172     l = d.result(&d);    
173     return l;
174 }
175
176
177 //---- bitmap handling ----
178
179 gfximage_t* gfximage_new(RGBA*data, int width, int height)
180 {
181     gfximage_t* b = (gfximage_t*)rfx_calloc(sizeof(gfximage_t));
182     b->data = (gfxcolor_t*)data;
183     b->width = width;
184     b->height = height;
185     return b;
186 }
187
188 void gfximage_free(gfximage_t*b)
189 {
190     free(b->data); //!
191     b->data = 0;
192     free(b);
193 }
194
195 static gfximage_t* findimage(render_t*r, U16 id)
196 {
197     character_t*c = (character_t*)map16_get_id(r->id2char, id);
198     assert(c && c->type == TYPE_BITMAP);
199     gfximage_t*img = (gfximage_t*)c->data;
200
201     /*char filename[80];
202     sprintf(filename, "bitmap%d.png", id);
203     writePNG(filename, (unsigned char*)img->data, img->width, img->height);
204     printf("saving bitmap %d to %s\n", id, filename);*/
205
206     return c->data;
207 }
208 //---- shape handling ----
209
210 static void renderFilled(render_t*r, gfxline_t*line, FILLSTYLE*f, CXFORM*cx, MATRIX*po_m)
211 {
212     if(f->type == FILL_SOLID) {
213         gfxcolor_t c = *(gfxcolor_t*)&f->color;
214         r->device->fill(r->device, line, &c);
215     } else if(f->type == FILL_TILED || f->type == FILL_CLIPPED) {
216         gfximage_t* img = findimage(r, f->id_bitmap);
217         gfxmatrix_t m;
218         gfxcxform_t gfxcx;
219         convertCXForm(cx, &gfxcx);
220         MATRIX m2;
221         swf_MatrixJoin(&m2, po_m, &f->m);
222         convertMatrix(&m2, &m);
223         m.m00/=20.0; m.m10/=20.0;
224         m.m01/=20.0; m.m11/=20.0;
225         /* TODO: handle clipped */
226         r->device->fillbitmap(r->device, line, img, &m, &gfxcx);
227     } else if(f->type == FILL_LINEAR || f->type == FILL_RADIAL) {
228         gfxmatrix_t m;
229         gfxgradient_t* g;
230         convertMatrix(&f->m, &m);
231         g = convertGradient(&f->gradient);
232         r->device->fillgradient(r->device, line, g, f->type == FILL_LINEAR ? gfxgradient_linear : gfxgradient_radial, &m);
233         free(g);
234     }
235 }
236
237 //---- font handling ----
238
239 typedef struct
240 {
241     int numchars;
242     gfxline_t**glyphs;
243 } font_t;
244         
245 typedef struct textcallbackblock
246 {
247     render_t*r;
248     MATRIX m;
249 } textcallbackblock_t;
250
251 static void textcallback(void*self, int*chars, int*xpos, int nr, int fontid, int fontsize, 
252                     int xstart, int ystart, RGBA* color)
253 {
254     textcallbackblock_t * info = (textcallbackblock_t*)self;
255     font_t*font = 0;
256     int t;
257     character_t*cfont = map16_get_id(info->r->id2char, fontid);
258     if(!cfont) {
259         fprintf(stderr, "Font %d unknown\n", fontid);
260         return;
261     }
262     if(cfont->type != TYPE_FONT) {
263         fprintf(stderr, "ID %d is not a font\n", fontid);
264         return;
265     }
266     font = cfont->data;
267
268     for(t=0;t<nr;t++) {
269         int x = xstart + xpos[t];
270         int y = ystart;
271         MATRIX m = info->m;
272         SPOINT p;
273         
274         p.x = x; p.y = y; 
275         p = swf_TurnPoint(p, &m);
276         
277         m.sx = (m.sx * fontsize) / 1024;
278         m.sy = (m.sy * fontsize) / 1024;
279         m.r0 = (m.r0 * fontsize) / 1024;
280         m.r1 = (m.r1 * fontsize) / 1024;
281         m.tx = p.x;
282         m.ty = p.y;
283
284         gfxmatrix_t gm;
285         convertMatrix(&m, &gm);
286
287         if(chars[t]<0 || chars[t]>= font->numchars) {
288             fprintf(stderr, "Character out of range: %d\n", chars[t]);
289         } else {
290             gfxline_t*line = gfxline_clone(font->glyphs[chars[t]]);
291             gfxline_transform(line, &gm);
292             FILLSTYLE f;
293             f.type = FILL_SOLID;
294             f.color = *color;
295             renderFilled(info->r, line, &f, 0, 0);
296             gfxline_free(line);
297         }
298     }
299 }
300
301
302 //---- tag handling ----
303
304 static map16_t* extractDefinitions(SWF*swf)
305 {
306     map16_t*map = map16_new();
307     TAG*tag = swf->firstTag;
308     while(tag)
309     {
310         int id = 0;
311         if(swf_isDefiningTag(tag)) {
312             id = swf_GetDefineID(tag);
313         }
314
315         if(tag->id == ST_DEFINESPRITE) {
316             character_t*c = rfx_calloc(sizeof(character_t));
317             sprite_t*s = rfx_calloc(sizeof(sprite_t));
318             swf_SetTagPos(tag, 0);
319             swf_GetU16(tag); //id
320             s->frameCount = swf_GetU16(tag); //frameno
321             c->tag = tag;
322             c->type = TYPE_SPRITE;
323             c->data = s;
324             map16_add_id(map, id, c);
325         }
326         else if(tag->id == ST_DEFINESHAPE ||
327                 tag->id == ST_DEFINESHAPE2 ||
328                 tag->id == ST_DEFINESHAPE3) {
329             character_t*c = rfx_calloc(sizeof(character_t));
330             c->tag = tag;
331             c->type = TYPE_SHAPE;
332             map16_add_id(map, id, c);
333         }
334         else if(tag->id == ST_DEFINEFONT ||
335                 tag->id == ST_DEFINEFONT2) {
336             character_t*c = rfx_calloc(sizeof(character_t));
337             SWFFONT*swffont = 0;
338             font_t*font = (font_t*)rfx_calloc(sizeof(font_t));
339             swf_FontExtract(swf, id, &swffont);
340             font->numchars = swffont->numchars;
341             font->glyphs = (gfxline_t**)rfx_calloc(sizeof(gfxline_t*)*font->numchars);
342             int t;
343             RGBA color_white = {255,255,255,255};
344             for(t=0;t<font->numchars;t++) {
345                 if(!swffont->glyph[t].shape->fillstyle.n) {
346                     swf_ShapeAddSolidFillStyle(swffont->glyph[t].shape, &color_white);
347                 }
348                 SHAPE2*s2 = swf_ShapeToShape2(swffont->glyph[t].shape);
349                 font->glyphs[t] = swfline_to_gfxline(s2->lines, 0, 1);
350                 swf_Shape2Free(s2);
351             }
352             swf_FontFree(swffont);
353
354             c->tag = tag;
355             c->type = TYPE_FONT;
356             c->data = font;
357             map16_add_id(map, id, c);
358         }
359         else if(tag->id == ST_DEFINETEXT ||
360                 tag->id == ST_DEFINETEXT2) {
361             character_t*c = rfx_calloc(sizeof(character_t));
362             c->tag = tag;
363             c->type = TYPE_TEXT;
364             c->data = 0;
365             map16_add_id(map, id, c);
366         }
367         else if(tag->id == ST_DEFINEBITSJPEG || 
368                 tag->id == ST_DEFINEBITSJPEG2 || 
369                 tag->id == ST_DEFINEBITSJPEG3 ||
370                 tag->id == ST_DEFINEBITSLOSSLESS || 
371                 tag->id == ST_DEFINEBITSLOSSLESS2) {
372             character_t*c = rfx_calloc(sizeof(character_t));
373             int width, height;
374             void*data = swf_ExtractImage(tag, &width, &height);
375             gfximage_t*b = gfximage_new(data, width, height);
376             c->tag = tag;
377             c->type = TYPE_BITMAP;
378             c->data = b;
379             map16_add_id(map, id, c);
380         }
381
382         tag = tag->next;
383     }
384     return map;
385 }
386
387 void swf_FreeTaglist(TAG*tag)
388
389     while(tag)
390     { 
391         TAG * tnew = tag->next;
392         if (tag->data) 
393             rfx_free(tag->data);
394         rfx_free(tag);
395         tag = tnew;
396     }
397 }
398
399 static void increaseAge(void*self, int id, void*data)
400 {
401     placement_t*p = (placement_t*)data;
402     p->age++;
403 }
404
405 static map16_t* extractFrame(TAG*startTag, int frame_to_extract)
406 {
407     map16_t*depthmap = map16_new();
408     TAG*tag = startTag;
409     int frame = 1;
410     int insprite = 0;
411
412     SWF*swf = rfx_calloc(sizeof(SWF));
413     swf->firstTag = startTag;
414
415     for(;tag;tag = tag->next) {
416         if(tag->id == ST_DEFINESPRITE) {
417             while(tag->id != ST_END)
418                 tag = tag->next;
419             continue;
420         }
421         if(tag->id == ST_PLACEOBJECT ||
422            tag->id == ST_PLACEOBJECT2) {
423             placement_t* p = rfx_calloc(sizeof(placement_t));
424             p->age = 1;
425             p->startFrame = frame;
426             swf_GetPlaceObject(tag, &p->po);
427             if(p->po.move) {
428                 placement_t*old = (placement_t*)map16_get_id(depthmap, p->po.depth);
429                 p->po.id = old->po.id;
430                 map16_remove_id(depthmap, p->po.depth);
431                 placement_free(p);
432             } else {
433                 map16_add_id(depthmap, p->po.depth, p);
434             }
435         }
436         if(tag->id == ST_REMOVEOBJECT ||
437            tag->id == ST_REMOVEOBJECT2) {
438             U16 depth = swf_GetDepth(tag);
439             map16_remove_id(depthmap, depth);
440         }
441         if(tag->id == ST_SHOWFRAME || tag->id == ST_END || !tag->next) {
442             if(frame == frame_to_extract) {
443                 return depthmap;
444             }
445             if(tag->id == ST_SHOWFRAME) {
446                 frame++;
447                 map16_enumerate(depthmap, increaseAge, 0);
448             }
449         }
450         if(tag->id == ST_END) 
451             break;
452     }
453     return depthmap;
454 }
455
456 // ---- rendering ----
457
458 void swf_ShapeApplyMatrix(SHAPE2*shape, MATRIX*m)
459 {
460 }
461
462 RGBA swf_ColorTransform(RGBA*color, CXFORM*cx)
463 {
464     RGBA dest;
465     dest.r = (cx->r0*color->r + cx->r1*256) >> 8;
466     dest.g = (cx->g0*color->g + cx->g1*256) >> 8;
467     dest.b = (cx->b0*color->b + cx->b1*256) >> 8;
468     dest.a = (cx->a0*color->a + cx->a1*256) >> 8;
469     return dest;
470 }
471
472 void renderOutline(render_t*r, gfxline_t*line, LINESTYLE*l, CXFORM*cx)
473 {
474     RGBA c = swf_ColorTransform(&l->color, cx);
475     gfxcoord_t width = l->width/20.0;
476     r->device->stroke(r->device, line, width, (gfxcolor_t*)&c, gfx_capRound, gfx_joinRound, 0.0);
477 }
478
479 void swf_ApplyMatrixToShape(SHAPE2*shape, MATRIX*m)
480 {
481     SHAPELINE*line = shape->lines;
482     while(line) {
483         SPOINT p;
484         p.x = line->x; p.y = line->y;
485         p = swf_TurnPoint(p, m);
486         line->x = p.x; line->y = p.y;
487         line = line->next;
488     }
489 }
490
491 static void renderCharacter(render_t*r, placement_t*p, character_t*c)
492 {
493     if(c->type == TYPE_SHAPE) {
494         SHAPE2 shape;
495         swf_ParseDefineShape(c->tag, &shape);
496         MATRIX m;
497         swf_MatrixJoin(&m, &r->m, &p->po.matrix);
498         swf_ApplyMatrixToShape(&shape, &m);
499         SHAPELINE*line = shape.lines;
500         int t;
501         for(t=1;t<=shape.numfillstyles;t++) {
502            gfxline_t*line;
503            line = swfline_to_gfxline(shape.lines, -1, t);
504            if(line) {
505                if(!p->po.clipdepth) {
506                    renderFilled(r, line, &shape.fillstyles[t-1], &p->po.cxform, &p->po.matrix);
507                } else { 
508                    r->device->startclip(r->device, line);
509                    r->clips_waiting[p->po.clipdepth]++;
510                }
511            }
512            gfxline_free(line);
513            /*line = swfline_to_gfxline(shape.lines, -1, -1, t);
514            if(line) renderFilled(r, line, &shape.fillstyles[t-1], &p->po.cxform);
515            gfxline_free(line);*/
516         }
517         for(t=1;t<=shape.numlinestyles;t++) {
518            gfxline_t*line = swfline_to_gfxline(shape.lines, t, -1);
519            if(line) renderOutline(r, line, &shape.linestyles[t-1], &p->po.cxform);
520            gfxline_free(line);
521         }
522     } else if(c->type == TYPE_TEXT) {
523         TAG* tag = c->tag;
524         textcallbackblock_t info;
525         MATRIX mt,mt2;
526         swf_SetTagPos(tag, 0);
527         swf_GetU16(tag);
528         swf_GetRect(tag,0);
529         swf_GetMatrix(tag,&mt);
530
531         swf_MatrixJoin(&mt2, &r->m, &mt);
532         swf_MatrixJoin(&info.m, &mt2, &p->po.matrix);
533         info.r = r;
534         swf_ParseDefineText(tag, textcallback, &info);
535     }
536 }
537
538 // ---- main ----
539
540 static void placeObject(void*self, int id, void*data)
541 {
542     render_t*r = (render_t*)self;
543     placement_t*p = (placement_t*)data;
544     character_t*c = map16_get_id(r->id2char, p->po.id);
545     if(!c)  {
546         fprintf(stderr, "Error: ID %d unknown\n", p->po.id);
547         return;
548     }
549     if(c->type == TYPE_SPRITE) {
550         int*old_clips_waiting = r->clips_waiting;
551         r->clips_waiting = rfx_calloc(sizeof(r->clips_waiting[0])*65536);
552
553         sprite_t* s = (sprite_t*)c->data;
554
555         map16_t* depths = extractFrame(c->tag->next, p->age % s->frameCount);
556         map16_enumerate(depths, placeObject, r);
557        
558         int t;
559         for(t=0;t<65536;t++) {
560             int i;
561             for(i=0; i<r->clips_waiting[t]; i++) {
562                 r->device->endclip(r->device);
563             }
564         }
565         free(r->clips_waiting);
566         r->clips_waiting = old_clips_waiting;
567         return;
568     }
569     renderCharacter(r, p, c);
570 }
571
572 void swfpage_destroy(gfxpage_t*swf_page)
573 {
574     swf_page_internal_t*i= (swf_page_internal_t*)swf_page->internal;
575     free(swf_page->internal);swf_page->internal = 0;
576     free(swf_page);swf_page=0;
577 }
578
579 void swfpage_render(gfxpage_t*page, gfxdevice_t*output)
580 {
581     swf_page_internal_t*i = (swf_page_internal_t*)page->internal;
582     swf_doc_internal_t*pi = (swf_doc_internal_t*)page->parent->internal;
583     map16_t* depths = extractFrame(pi->swf.firstTag, i->frame);
584     render_t r;
585     r.id2char = pi->id2char;
586     r.clips = 0;
587     r.device = output;
588     r.m = pi->m;
589     r.clips_waiting = malloc(sizeof(r.clips_waiting[0])*65536);
590     memset(r.clips_waiting, 0, sizeof(r.clips_waiting[0])*65536);
591
592     int t;
593     for(t=0;t<65536;t++) {
594         int i;
595
596         for(i=0; i<r.clips_waiting[t]; i++) {
597             output->endclip(output);
598         }
599
600         if(depths->ids[t]) {
601             placeObject(&r, t, depths->ids[t]);
602         }
603     }
604     free(r.clips_waiting);
605 }
606
607 void swfpage_rendersection(gfxpage_t*page, gfxdevice_t*output, gfxcoord_t x, gfxcoord_t y, gfxcoord_t _x1, gfxcoord_t _y1, gfxcoord_t _x2, gfxcoord_t _y2)
608 {
609     swf_doc_internal_t*pi = (swf_doc_internal_t*)page->parent->internal;
610     /* FIXME */
611     swfpage_render(page,output);
612 }
613
614 void swf_doc_destroy(gfxdocument_t*gfx)
615 {
616     swf_doc_internal_t*i= (swf_doc_internal_t*)gfx->internal;
617     swf_FreeTags(&i->swf);
618     free(gfx->internal);gfx->internal=0;
619     free(gfx);gfx=0;
620 }
621
622 void swf_doc_set_parameter(gfxdocument_t*gfx, const char*name, const char*value)
623 {
624     swf_doc_internal_t*i= (swf_doc_internal_t*)gfx->internal;
625 }
626
627 gfxpage_t* swf_doc_getpage(gfxdocument_t*doc, int page)
628 {
629     swf_doc_internal_t*di= (swf_doc_internal_t*)doc->internal;
630     if(page < 1 || page > doc->num_pages)
631         return 0;
632     
633     gfxpage_t* swf_page = (gfxpage_t*)malloc(sizeof(gfxpage_t));
634     swf_page_internal_t*pi= (swf_page_internal_t*)malloc(sizeof(swf_page_internal_t));
635     memset(pi, 0, sizeof(swf_page_internal_t));
636
637     pi->frame = page;
638
639     swf_page->internal = pi;
640     swf_page->destroy = swfpage_destroy;
641     swf_page->render = swfpage_render;
642     swf_page->rendersection = swfpage_rendersection;
643     swf_page->width = di->width;
644     swf_page->height = di->height;
645     swf_page->parent = doc;
646     swf_page->nr = page;
647     return swf_page;
648 }
649
650 void swf_set_parameter(gfxsource_t*src, const char*name, const char*value)
651 {
652     msg("<verbose> setting parameter %s to \"%s\"", name, value);
653 }
654
655 gfxdocument_t*swf_open(gfxsource_t*src, const char*filename)
656 {
657     gfxdocument_t*swf_doc = (gfxdocument_t*)malloc(sizeof(gfxdocument_t));
658     memset(swf_doc, 0, sizeof(gfxdocument_t));
659     swf_doc_internal_t*i= (swf_doc_internal_t*)malloc(sizeof(swf_doc_internal_t));
660     memset(i, 0, sizeof(swf_doc_internal_t));
661
662     TAG*tag = 0;
663     int f;
664     int frame;
665     render_t r;
666     gfxdevice_t d;
667     
668     if(!filename) {
669         return 0;
670     }
671     f = open(filename,O_RDONLY|O_BINARY);
672     if (f<0) { 
673         perror("Couldn't open file: ");
674         return 0;
675     }
676     if FAILED(swf_ReadSWF(f,&i->swf)) { 
677         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
678         close(f);
679         return 0;
680     }
681     swf_UnFoldAll(&i->swf);
682     
683     i->id2char = extractDefinitions(&i->swf);
684     i->width = (i->swf.movieSize.xmax - i->swf.movieSize.xmin) / 20;
685     i->height = (i->swf.movieSize.ymax - i->swf.movieSize.ymin) / 20;
686     
687     swf_GetMatrix(0, &i->m);
688     i->m.tx = -i->swf.movieSize.xmin;
689     i->m.ty = -i->swf.movieSize.ymin;
690
691     swf_doc->num_pages = i->swf.frameCount;
692     swf_doc->internal = i;
693     swf_doc->get = 0;
694     swf_doc->destroy = swf_doc_destroy;
695     swf_doc->set_parameter = swf_doc_set_parameter;
696     swf_doc->getpage = swf_doc_getpage;
697
698     return swf_doc;
699 }
700
701 gfxsource_t*gfxsource_swf_create()
702 {
703     gfxsource_t*src = (gfxsource_t*)malloc(sizeof(gfxsource_t));
704     memset(src, 0, sizeof(gfxsource_t));
705     src->set_parameter = swf_set_parameter;
706     src->open = swf_open;
707     return src;
708 }
709