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