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