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