cd57d6f06f0608b1f913e74155b3c1fe5f8fe119
[swftools.git] / lib / modules / swftools.c
1 /* swftools.c
2
3    Math and matrix functions, misc tools
4
5    Extension module for the rfxswf library.
6    Part of the swftools package.
7
8    Copyright (c) 2000, 2001 Rainer Böhme <rfxswf@reflex-studio.de>
9  
10    This program is free software; you can redistribute it and/or modify
11    it under the terms of the GNU General Public License as published by
12    the Free Software Foundation; either version 2 of the License, or
13    (at your option) any later version.
14
15    This program is distributed in the hope that it will be useful,
16    but WITHOUT ANY WARRANTY; without even the implied warranty of
17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18    GNU General Public License for more details.
19
20    You should have received a copy of the GNU General Public License
21    along with this program; if not, write to the Free Software
22    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
23
24 // Matrix & Math tools for SWF files
25
26 #include "../rfxswf.h"
27
28 #define S64 long long
29 SFIXED RFXSWF_SP(SFIXED a1,SFIXED a2,SFIXED b1,SFIXED b2)
30 { S64 a = ((S64)a1*(S64)b1+(S64)a2*(S64)b2)>>16;
31   SFIXED result = (SFIXED)(a);
32   if(a!=result) 
33       fprintf(stderr, "Warning: overflow in matrix multiplication");
34   return result;
35 }
36 SFIXED RFXSWF_QFIX(int zaehler,int nenner) // bildet Quotient von zwei INTs in SFIXED
37 { S64 z = zaehler<<16;
38   S64 a = z/(S64)nenner;
39   return (SFIXED)a;
40 }
41 #undef S64
42
43 MATRIX * swf_MatrixJoin(MATRIX * d,MATRIX * s1,MATRIX * s2)
44 {        
45   if (!d) return NULL;
46   if (!s1) return (s2)?(MATRIX *)memcpy(d,s2,sizeof(MATRIX)):NULL;
47   if (!s2) return (MATRIX *)memcpy(d,s1,sizeof(MATRIX));
48   
49   d->tx = s1->tx + RFXSWF_SP(s1->sx,s1->r1,s2->tx,s2->ty);
50   d->ty = s1->ty + RFXSWF_SP(s1->r0,s1->sy,s2->tx,s2->ty);
51   
52   d->sx = RFXSWF_SP(s1->sx,s1->r1,s2->sx,s2->r0);
53   d->r0 = RFXSWF_SP(s1->r0,s1->sy,s2->sx,s2->r0);
54
55   d->r1 = RFXSWF_SP(s1->sx,s1->r1,s2->r1,s2->sy);
56   d->sy = RFXSWF_SP(s1->r0,s1->sy,s2->r1,s2->sy);
57
58   //DumpMatrix(NULL,d);
59   
60   return d;
61 }
62
63 MATRIX * swf_MatrixMapTriangle(MATRIX * m,int dx,int dy,int x0,int y0,
64                                int x1,int y1,int x2,int y2)
65 { int dx1 = x1 - x0;
66   int dy1 = y1 - y0;
67   int dx2 = x2 - x0;
68   int dy2 = y2 - y0;
69   
70   if (!m) return NULL;
71   if ((!dx)||(!dy)) return NULL; // check DIV by zero
72
73   m->tx = x0;
74   m->ty = y0;
75   m->sx = RFXSWF_QFIX(dx1,dx);
76   m->sy = RFXSWF_QFIX(dy2,dy);
77   m->r0 = RFXSWF_QFIX(dy1,dx);
78   m->r1 = RFXSWF_QFIX(dx2,dy);
79   
80   return m;
81 }
82
83 void swf_SetDefineID(TAG * tag, U16 newid)
84 {
85   int oldlen = tag->len;
86   tag->len = 0;
87   swf_SetU16(tag, newid); /* set defining ID */
88   tag->len = oldlen;
89 }
90
91 U16 swf_GetDefineID(TAG * t)
92 // up to SWF 4.0
93 { U32 oldTagPos;
94   U16 id = 0;
95
96   oldTagPos = swf_GetTagPos(t);
97   swf_SetTagPos(t,0);
98
99   switch (swf_GetTagID(t))
100   { case ST_DEFINESHAPE:
101     case ST_DEFINESHAPE2:
102     case ST_DEFINESHAPE3:
103     case ST_DEFINESHAPE4:
104     case ST_DEFINEMORPHSHAPE:
105     case ST_DEFINEMORPHSHAPE2:
106     case ST_DEFINEEDITTEXT:
107     case ST_DEFINEBITS:
108     case ST_DEFINEBITSJPEG2:
109     case ST_DEFINEBITSJPEG3:
110     case ST_DEFINEBITSLOSSLESS:
111     case ST_DEFINEBITSLOSSLESS2:
112     case ST_DEFINESCALINGGRID: //pseudodefine
113     case ST_DEFINEBUTTON:
114     case ST_DEFINEBUTTON2:
115     case ST_DEFINEBUTTONCXFORM: //pseudodefine
116     case ST_DEFINEBUTTONSOUND: //pseudodefine
117     case ST_CSMTEXTSETTINGS: //pseudodefine
118     case ST_DEFINEFONT:
119     case ST_DEFINEFONT2:
120     case ST_DEFINEFONT3:
121     case ST_DEFINEFONTINFO: //pseudodefine
122     case ST_DEFINEFONTINFO2: //pseudodefine
123     case ST_DEFINEFONTALIGNZONES: //pseudodefine
124     case ST_DEFINEFONTNAME: //pseudodefine
125     case ST_DEFINETEXT:
126     case ST_DEFINEBINARY:
127     case ST_DEFINETEXT2:
128     case ST_DEFINESOUND:
129     case ST_DEFINESPRITE:
130     case ST_DEFINEMOVIE:
131     case ST_DEFINEVIDEOSTREAM:
132     case ST_GLYPHNAMES: //pseudodefine
133     case ST_VIDEOFRAME: //pseudodefine
134     case ST_NAMECHARACTER: //pseudodefine
135     case ST_DOINITACTION: //pseudodefine
136       id = swf_GetU16(t);
137       break;
138     default:
139       fprintf(stderr, "rfxswf: Error: tag %d (%s) has no id\n", t->id, swf_TagGetName(t));
140   }
141
142   swf_SetTagPos(t,oldTagPos);
143
144   return id;
145 }
146
147 SRECT swf_GetDefineBBox(TAG * t)
148 {
149   U32 oldTagPos;
150   U16 id = 0;
151   SRECT b1,b2;
152   memset(&b1, 0, sizeof(b1));
153
154   oldTagPos = swf_GetTagPos(t);
155   swf_SetTagPos(t,0);
156
157   swf_GetRect(0, &b1);
158
159   switch (swf_GetTagID(t))
160   { case ST_DEFINESHAPE:
161     case ST_DEFINESHAPE2:
162     case ST_DEFINESHAPE3:
163     case ST_DEFINESHAPE4:
164     case ST_DEFINEEDITTEXT:
165     case ST_DEFINETEXT:
166     case ST_DEFINETEXT2:
167     case ST_DEFINEVIDEOSTREAM:
168       id = swf_GetU16(t);
169       swf_GetRect(t, &b1);
170       break;
171     case ST_DEFINEMORPHSHAPE:
172       id = swf_GetU16(t);
173       swf_GetRect(t, &b1);
174       swf_GetRect(t, &b2);
175       swf_ExpandRect2(&b1, &b2);
176       break;
177     case ST_DEFINEBITSLOSSLESS:
178     case ST_DEFINEBITSLOSSLESS2:
179     case ST_DEFINEBITS:
180     case ST_DEFINEBITSJPEG2:
181     case ST_DEFINEBITSJPEG3:
182       // FIXME
183       break;
184   }
185
186   swf_SetTagPos(t,oldTagPos);
187
188   return b1;
189 }
190
191 U16 swf_GetPlaceID(TAG * t)
192 // up to SWF 4.0
193 { U32 oldTagPos;
194   U16 id = 0;
195
196   oldTagPos = swf_GetTagPos(t);
197   swf_SetTagPos(t,0);
198
199   switch (swf_GetTagID(t))
200   { case ST_PLACEOBJECT:
201     case ST_REMOVEOBJECT:
202     case ST_FREECHARACTER:
203     case ST_STARTSOUND:
204       id = swf_GetU16(t);
205       break;
206
207     case ST_PLACEOBJECT2:
208     { U8 flags = swf_GetU8(t);
209       U16 d = swf_GetU16(t);
210       id = (flags&PF_CHAR)?swf_GetU16(t):id;
211     } break;
212     case ST_PLACEOBJECT3:
213     { U8 flags = swf_GetU8(t);
214       U8 flags2 = swf_GetU8(t);
215       U16 d = swf_GetU16(t);
216       id = (flags&PF_CHAR)?swf_GetU16(t):id;
217     } break;
218
219   }
220
221   swf_SetTagPos(t,oldTagPos);
222
223   return id;
224 }
225
226 static int swf_definingtagids[] =
227 {ST_DEFINESHAPE,
228  ST_DEFINESHAPE2,
229  ST_DEFINESHAPE3,
230  ST_DEFINESHAPE4,
231  ST_DEFINEMORPHSHAPE,
232  ST_DEFINEMORPHSHAPE2,
233  ST_DEFINEFONT,
234  ST_DEFINEFONT2,
235  ST_DEFINEFONT3,
236  ST_DEFINETEXT,
237  ST_DEFINETEXT2,
238  ST_DEFINEEDITTEXT,
239  ST_DEFINEBITS,
240  ST_DEFINEBITSJPEG2,
241  ST_DEFINEBITSJPEG3,
242  ST_DEFINEBITSLOSSLESS,
243  ST_DEFINEBITSLOSSLESS2,
244  ST_DEFINEMOVIE,
245  ST_DEFINESPRITE,
246  ST_DEFINEBUTTON,
247  ST_DEFINEBUTTON2,
248  ST_DEFINESOUND,
249  ST_DEFINEVIDEOSTREAM,
250  ST_DEFINEBINARY,
251  -1
252 };
253
254 // tags which may be used inside a sprite definition
255 static int swf_spritetagids[] =
256 {ST_SHOWFRAME,
257  ST_PLACEOBJECT,
258  ST_PLACEOBJECT2,
259  ST_PLACEOBJECT3,
260  ST_REMOVEOBJECT,
261  ST_REMOVEOBJECT2,
262  ST_DOACTION,
263  ST_DOABC,
264  ST_STARTSOUND,
265  ST_FRAMELABEL,
266  ST_SOUNDSTREAMHEAD,
267  ST_SOUNDSTREAMHEAD2,
268  ST_SOUNDSTREAMBLOCK,
269  ST_END,
270  -1
271 };
272
273 /* tags which add content or information to a character with a given ID */
274 static int swf_pseudodefiningtagids[] = 
275 {
276  ST_DEFINEFONTINFO,
277  ST_DEFINEFONTINFO2,
278  ST_DEFINEFONTALIGNZONES,
279  ST_DEFINEFONTNAME,
280  ST_DEFINEBUTTONCXFORM,
281  ST_DEFINEBUTTONSOUND,
282  ST_DEFINESCALINGGRID,
283  ST_CSMTEXTSETTINGS,
284  ST_NAMECHARACTER,
285  ST_DOINITACTION,
286  ST_VIDEOFRAME,
287  ST_GLYPHNAMES,
288  -1
289 };
290
291 U8 swf_isAllowedSpriteTag(TAG * tag)
292 {
293     int id = tag->id;
294     int t=0;
295     while(swf_spritetagids[t]>=0)
296     {
297         if(swf_spritetagids[t] == id) 
298             return 1;
299         t++;
300     }
301     return 0; 
302 }
303
304 U8 swf_isDefiningTag(TAG * tag)
305 {
306     int id = tag->id;
307     int t=0;
308     while(swf_definingtagids[t]>=0)
309     {
310         if(swf_definingtagids[t] == id) 
311             return 1;
312         t++;
313     }
314     return 0; 
315 }
316
317 U8 swf_isPseudoDefiningTag(TAG * tag)
318 {
319     int id = tag->id;
320     int t=0;
321     while(swf_pseudodefiningtagids[t]>=0)
322     {
323         if(swf_pseudodefiningtagids[t] == id) 
324             return 1;
325         t++;
326     }
327     return 0; 
328 }
329
330 int swf_GetDepth(TAG * t)
331
332   int depth = -1;
333   U32 oldTagPos;
334   oldTagPos = swf_GetTagPos(t);
335   swf_SetTagPos(t,0);
336
337   switch (swf_GetTagID(t))
338   { case ST_PLACEOBJECT:
339     case ST_REMOVEOBJECT:
340       swf_GetU16(t); //id
341       depth = swf_GetU16(t);
342       break;
343     case ST_REMOVEOBJECT2:
344       depth = swf_GetU16(t);
345       break;
346     case ST_PLACEOBJECT2:
347     { U8 flags = swf_GetU8(t);
348       depth = swf_GetU16(t);
349     } break;
350     case ST_PLACEOBJECT3:
351     { U8 flags = swf_GetU8(t);
352       U8 flags2 = swf_GetU8(t);
353       depth = swf_GetU16(t);
354     } break;
355     case ST_SETTABINDEX:
356     {
357       depth = swf_GetU16(t);
358     }
359   }
360   swf_SetTagPos(t,oldTagPos);
361   return depth;
362 }
363
364 void swf_SetDepth(TAG * t, U16 depth)
365
366   switch (swf_GetTagID(t))
367   { case ST_PLACEOBJECT:
368     case ST_REMOVEOBJECT:
369       PUT16(t->data, depth);
370       break;
371     case ST_REMOVEOBJECT2:
372       PUT16(t->data, depth);
373       break;
374     case ST_PLACEOBJECT2:
375       PUT16(&t->data[1], depth);
376       break;
377     case ST_SETTABINDEX:
378       PUT16(t->data, depth);
379       break;
380     default:
381       fprintf(stderr, "rfxswf: Error: tag %d has no depth\n", t->id);
382   }
383 }
384
385 char* swf_GetName(TAG * t)
386 {
387     char* name = 0;
388     U32 oldTagPos;
389     MATRIX m;
390     CXFORM c;
391     oldTagPos = swf_GetTagPos(t);
392     swf_SetTagPos(t,0);
393     switch(swf_GetTagID(t))
394     {
395         case ST_FRAMELABEL:
396             name = (char*)&t->data[swf_GetTagPos(t)];
397         break;
398         case ST_PLACEOBJECT3:
399         case ST_PLACEOBJECT2: {   
400             U8 flags = swf_GetU8(t);
401             if(t->id == ST_PLACEOBJECT3)
402                 swf_GetU8(t);
403             swf_GetU16(t); //depth;
404             if(flags&PF_CHAR) 
405               swf_GetU16(t); //id
406             if(flags&PF_MATRIX)
407               swf_GetMatrix(t, &m);
408             if(flags&PF_CXFORM)
409               swf_GetCXForm(t, &c, 1);
410             if(flags&PF_RATIO)
411               swf_GetU16(t);
412             if(flags&PF_CLIPDEPTH)
413               swf_GetU16(t);
414             if(flags&PF_NAME) {
415               swf_ResetReadBits(t);
416               name = (char*)&t->data[swf_GetTagPos(t)];
417             }
418         }
419         break;
420     }
421     swf_SetTagPos(t,oldTagPos);
422     return name;
423 }
424
425 /* used in enumerateUsedIDs */
426 void swf_GetMorphGradient(TAG * tag, GRADIENT * gradient1, GRADIENT * gradient2)
427 {
428     int t;
429     int num = swf_GetU8(tag) & 15;
430     if(gradient1) gradient1->num = num;
431     if(gradient2) gradient2->num = num;
432     
433     if(gradient1) {
434         gradient1->num = num;
435         gradient1->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient1->num);
436         gradient1->ratios = (U8*)rfx_calloc(sizeof(gradient1->ratios[0])*gradient1->num);
437     }
438     if(gradient2) {
439         gradient2->num = num;
440         gradient2->rgba = (RGBA*)rfx_calloc(sizeof(RGBA)*gradient2->num);
441         gradient2->ratios = (U8*)rfx_calloc(sizeof(gradient2->ratios[0])*gradient2->num);
442     }
443     for(t=0;t<num;t++)
444     {
445         U8 ratio;
446         RGBA color;
447         
448         ratio = swf_GetU8(tag);
449         swf_GetRGBA(tag, &color);
450         if(gradient1) {
451             gradient1->ratios[t] = ratio;
452             gradient1->rgba[t] = color;
453         }
454
455         ratio = swf_GetU8(tag);
456         swf_GetRGBA(tag, &color);
457         if(gradient2) {
458             gradient2->ratios[t] = ratio;
459             gradient2->rgba[t] = color;
460         }
461     }
462 }
463
464 #define DEBUG_ENUMERATE if(0)
465 //#define DEBUG_ENUMERATE
466
467 void enumerateUsedIDs_fillstyle(TAG * tag, int t, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
468 {
469     int type;
470     type = swf_GetU8(tag); //type
471     DEBUG_ENUMERATE printf("fill style %d) type=%02x (tagpos=%d)\n", t, type, tag->pos);
472     if(type == 0) {
473         RGBA color;
474         if(num >= 3)
475             {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
476         else 
477             {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
478         DEBUG_ENUMERATE printf("               %02x%02x%02x%02x\n", color.r,color.g,color.b,color.a);
479     }
480     else if(type == 0x10 || type == 0x12 || type == 0x13)
481     {
482         swf_ResetReadBits(tag);
483         MATRIX m;
484         swf_GetMatrix(tag, &m);
485         DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
486         if(morph) {
487             swf_GetMatrix(tag, &m);
488             DEBUG_ENUMERATE swf_DumpMatrix(stdout, &m);
489         }
490         swf_ResetReadBits(tag);
491         if(morph) {
492             swf_GetMorphGradient(tag, NULL, NULL);
493             if(type == 0x13) {
494                 swf_GetU16(tag);
495                 swf_GetU16(tag);
496             }
497         } else {
498             GRADIENT g;
499             swf_GetGradient(tag, &g, /*alpha*/ num>=3?1:0);
500             DEBUG_ENUMERATE swf_DumpGradient(stdout, &g);
501             if(type == 0x13)
502                 swf_GetU16(tag);
503         }
504     }
505     else if(type == 0x40 || type == 0x41 || type == 0x42 || type == 0x43)
506     {
507         swf_ResetReadBits(tag);
508         if(tag->data[tag->pos] != 0xff ||
509            tag->data[tag->pos+1] != 0xff)
510         (callback)(tag, tag->pos, callback_data);
511
512         swf_GetU16(tag);
513         swf_ResetReadBits(tag);
514         swf_GetMatrix(tag, NULL);
515         if(morph)
516             swf_GetMatrix(tag, NULL);
517     }
518     else {
519         fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x in tag %02d\n",type, tag->id);
520     }
521 }
522
523 void enumerateUsedIDs_linestyle(TAG * tag, int t, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
524 {
525     U16  width;
526     RGBA color;
527     width = swf_GetU16(tag);
528     char fill=0;
529     if(morph)
530         swf_GetU16(tag);
531     if(num >= 4) {
532         U16 flags = swf_GetU16(tag);
533         DEBUG_ENUMERATE printf("line style %d) flags: %08x\n", t, flags);
534         if((flags & 0x30) == 0x20) {
535             U16 miter = swf_GetU16(tag); // miter limit
536             DEBUG_ENUMERATE printf("line style %d) miter join: %08x\n", t, miter);
537         }
538         if(flags & 0x08) {
539             fill = 1;
540         }
541     }
542     if(!fill) {
543         if(num >= 3)
544             {swf_GetRGBA(tag, &color);if(morph) swf_GetRGBA(tag, NULL);}
545         else
546             {swf_GetRGB(tag, &color);if(morph) swf_GetRGB(tag, NULL);}
547     } else {
548         enumerateUsedIDs_fillstyle(tag, t, callback, callback_data, num, morph);
549     }
550     DEBUG_ENUMERATE printf("line style %d) width=%.2f color=%02x%02x%02x%02x \n", t, width/20.0, color.r,color.g,color.b,color.a);
551 }
552
553 void enumerateUsedIDs_styles(TAG * tag, void (*callback)(TAG*, int, void*), void*callback_data, int num, int morph)
554 {
555     U16 count;
556     int t;
557     count = swf_GetU8(tag);
558     if(count == 0xff && num>1) // defineshape2,3,4 only
559         count = swf_GetU16(tag);
560
561     DEBUG_ENUMERATE printf("%d fill styles\n", count);
562     for(t=0;t<count;t++)
563     {
564         enumerateUsedIDs_fillstyle(tag, t, callback, callback_data, num, morph);
565     }
566     swf_ResetReadBits(tag);
567     count = swf_GetU8(tag); // line style array
568     if(count == 0xff)
569         count = swf_GetU16(tag);
570     DEBUG_ENUMERATE printf("%d line styles\n", count);
571     for(t=0;t<count;t++) 
572     {
573         enumerateUsedIDs_linestyle(tag, t, callback, callback_data, num, morph);
574     }
575 }
576
577 void enumerateUsedIDs(TAG * tag, int base, void (*callback)(TAG*, int, void*), void*callback_data)
578 {
579     int num = 1;
580     swf_ResetReadBits(tag);
581     tag->pos = 0;
582     switch(tag->id)
583     {
584         case ST_DEFINEBUTTONSOUND: {
585             int t;
586             callback(tag, tag->pos + base, callback_data);
587             for(t=0;t<4;t++) {
588                 int flags;
589                 callback(tag, tag->pos + base, callback_data);
590                 swf_GetU16(tag); //sound id
591                 flags = swf_GetU8(tag);
592                 if(flags&1)
593                     swf_GetU32(tag); // in point
594                 if(flags&2)
595                     swf_GetU32(tag); // out points
596                 if(flags&4)
597                     swf_GetU16(tag); // loop count
598                 if(flags&8)
599                 {
600                     int npoints = swf_GetU8(tag);
601                     int s;
602                     for(s=0;s<npoints;s++)
603                     {
604                         swf_GetU32(tag);
605                         swf_GetU16(tag);
606                         swf_GetU16(tag);
607                     }
608                 }
609             }
610         } break;
611         case ST_DEFINEBUTTONCXFORM:
612             callback(tag, tag->pos + base, callback_data); //button id
613         break;
614
615         case ST_EXPORTASSETS: {
616             int num =  swf_GetU16(tag);
617             int t;
618             for(t=0;t<num;t++) {
619                 callback(tag, tag->pos + base, callback_data); //button id
620                 swf_GetU16(tag); //id
621                 while(swf_GetU8(tag)); //name
622             }
623         } break;
624
625         case ST_IMPORTASSETS: 
626         case ST_IMPORTASSETS2: {
627             swf_GetString(tag); //count
628             swf_GetU8(tag); //reserved
629             swf_GetU8(tag); //reserved
630             int num =  swf_GetU16(tag); //url
631             int t;
632             for(t=0;t<num;t++) {
633                 callback(tag, tag->pos + base, callback_data); //button id
634                 swf_GetU16(tag); //id
635                 while(swf_GetU8(tag)); //name
636             }
637         } break;
638
639         case ST_DOABC:
640         case ST_RAWABC:
641         break;
642
643         case ST_FREECHARACTER: /* unusual tags, which all start with an ID */
644         case ST_NAMECHARACTER:
645         case ST_DEFINEBINARY:
646         case ST_DEFINEFONTNAME:
647         case ST_GENERATORTEXT:
648             callback(tag, tag->pos + base, callback_data);
649         break;
650         case ST_PLACEOBJECT:
651             callback(tag, tag->pos + base, callback_data);
652         break;
653         case ST_PLACEOBJECT2:
654             // only if placeflaghascharacter
655             if(!(tag->data[0]&2))
656                 break;
657             callback(tag, 3 + base, callback_data);
658         break;
659         case ST_PLACEOBJECT3:
660             // only if placeflaghascharacter
661             if(!(tag->data[0]&2))
662                 break;
663             callback(tag, 4 + base, callback_data);
664         break;
665         case ST_REMOVEOBJECT:
666             callback(tag, tag->pos + base, callback_data);
667         break;
668         case ST_STARTSOUND:
669             callback(tag, tag->pos + base, callback_data);
670         break;
671         case ST_DEFINESPRITE: {
672             if(tag->len <= 4)
673                 break; // sprite is expanded
674
675             swf_GetU16(tag); // id
676             swf_GetU16(tag); // framenum
677
678             while(1) {
679                 U16 flags = swf_GetU16(tag);
680                 U32 len;
681                 U16 id = flags>>6;
682                 TAG *tag2 = swf_InsertTag(NULL, id);
683                 len = flags&0x3f;
684                 if(len == 63)
685                     len = swf_GetU32(tag);
686                 if(id == ST_END)
687                     break;
688                 tag2->len = tag2->memsize = len;
689                 tag2->data = (U8*)rfx_alloc(len);
690                 memcpy(tag2->data, &tag->data[tag->pos], len);
691                 /* I never saw recursive sprites, but they are (theoretically) 
692                    possible, so better add base here again */
693                 enumerateUsedIDs(tag2, tag->pos + base, callback, callback_data);
694                 swf_DeleteTag(0, tag2);
695                 swf_GetBlock(tag, NULL, len);
696             }
697         } 
698         break;
699         case ST_DEFINEBUTTON2: // has some font ids in the button records
700             num++; 
701         //fallthrough
702         case ST_DEFINEBUTTON: {
703             swf_GetU16(tag); //button id
704             if(num>1)
705             { 
706                 int offset;
707                 swf_GetU8(tag); //flag
708                 offset = swf_GetU16(tag); //offset
709             }
710             while(1)
711             {
712                 U8 flags = swf_GetU8(tag);
713                 if(!flags) //flags
714                     break; 
715                 callback(tag, tag->pos + base, callback_data);
716                 swf_GetU16(tag); //char
717                 swf_GetU16(tag); //layer
718                 swf_ResetReadBits(tag);
719                 swf_GetMatrix(tag, NULL);
720                 if(num>1) {
721                   swf_ResetReadBits(tag);
722                   swf_GetCXForm(tag, NULL, 1);
723                 }
724                 if(flags&0x10) {
725                     U8 num = swf_GetU8(tag);
726                     int t;
727                     for(t=0;t<num;t++) {
728                         swf_DeleteFilter(swf_GetFilter(tag));
729                     }
730                 }
731                 if(flags&0x20) {
732                     U8 blendmode = swf_GetU8(tag);
733                 }
734             }
735             // ...
736         }
737         break;
738         case ST_DEFINEEDITTEXT:  {
739             U8 flags1,flags2;
740             swf_GetU16(tag); //id
741             swf_GetRect(tag, NULL); //bounding box
742             swf_ResetReadBits(tag);
743             flags1 = swf_GetU8(tag);
744             flags2 = swf_GetU8(tag);
745             if(flags1 & 1)
746                 callback(tag, tag->pos + base, callback_data);
747         }
748         break;
749         case ST_DEFINETEXT2:
750             num ++;
751         case ST_DEFINETEXT: { 
752             int glyphbits, advancebits;
753             int id;
754             id = swf_GetU16(tag); //id
755             swf_GetRect(tag, NULL); //bounding box
756             swf_ResetReadBits(tag);
757             swf_GetMatrix(tag, NULL); //matrix
758             swf_ResetReadBits(tag);
759             glyphbits = swf_GetU8(tag); //glyphbits
760             advancebits = swf_GetU8(tag); //advancebits
761             while(1) {
762                 U16 flags;
763                 int t;
764                 swf_ResetReadBits(tag);
765                 flags = swf_GetBits(tag, 8);
766                 if(!flags) break;
767                 
768                 swf_ResetReadBits(tag);
769                 if(flags & 8) { // hasfont
770                     callback(tag, tag->pos + base, callback_data);
771                     id = swf_GetU16(tag);
772                 }
773                 if(flags & 4) { // hascolor
774                     if(num==1) swf_GetRGB(tag, NULL);
775                     else       swf_GetRGBA(tag, NULL);
776                 }
777                 if(flags & 2) { //has x offset
778                     swf_ResetReadBits(tag);
779                     swf_GetU16(tag);
780                 }
781                 if(flags & 1) { //has y offset
782                     swf_ResetReadBits(tag);
783                     swf_GetU16(tag);
784                 }
785                 if(flags & 8) { //has height
786                     swf_ResetReadBits(tag);
787                     swf_GetU16(tag);
788                 }
789                 
790                 flags = swf_GetBits(tag, 8);
791                 if(!flags) break;
792                 swf_ResetReadBits(tag);
793                 for(t=0;t<flags;t++) {
794                     swf_GetBits(tag, glyphbits);
795                     swf_GetBits(tag, advancebits);
796                 }
797             }
798             break;
799         }
800         case ST_DEFINEFONTALIGNZONES:
801         case ST_DEFINESCALINGGRID:
802         case ST_GLYPHNAMES:
803         case ST_CSMTEXTSETTINGS:
804         case ST_DEFINEFONTINFO:
805         case ST_DEFINEFONTINFO2:
806         case ST_VIDEOFRAME:
807             callback(tag, tag->pos + base, callback_data);
808         break;
809         case ST_DEFINEVIDEOSTREAM:
810         break;
811
812         case ST_DOINITACTION:
813             callback(tag, tag->pos + base, callback_data);
814         break;
815
816         case ST_DEFINEMORPHSHAPE2:
817         case ST_DEFINESHAPE4:
818         num++;
819         case ST_DEFINEMORPHSHAPE:
820         case ST_DEFINESHAPE3:
821         num++; //fallthrough
822         case ST_DEFINESHAPE2:
823         num++; //fallthrough
824         case ST_DEFINESHAPE: {
825             int fillbits;
826             int linebits;
827             int id; 
828             int numshapes = 1;
829             int morph = 0;
830             if(tag->id == ST_DEFINEMORPHSHAPE || tag->id==ST_DEFINEMORPHSHAPE2) {
831                 numshapes = 2;
832                 morph = 1;
833             }
834
835             id = swf_GetU16(tag); // id;
836             SRECT r={0,0,0,0},r2={0,0,0,0};
837             swf_GetRect(tag, &r); // shape bounds
838             if(morph) {
839                 swf_ResetReadBits(tag);
840                 swf_GetRect(tag, NULL); // shape bounds2
841                 if(num>=4) {
842                     swf_ResetReadBits(tag);
843                     swf_GetRect(tag, NULL); // edge bounds1
844                 }
845             }
846             if(num>=4) {
847                 swf_ResetReadBits(tag);
848                 swf_GetRect(tag, &r2); // edge bounds
849                 U8 flags = swf_GetU8(tag); // flags, &1: contains scaling stroke, &2: contains non-scaling stroke
850                 DEBUG_ENUMERATE printf("flags: %02x (1=scaling strokes, 2=non-scaling strokes)\n", flags);
851             }
852             if(morph) {
853                 swf_GetU32(tag); //offset to endedges
854             }
855    
856             DEBUG_ENUMERATE printf("Tag:%d Name:%s ID:%d\n", tag->id, swf_TagGetName(tag), id);
857             DEBUG_ENUMERATE printf("BBox %.2f %.2f %.2f %.2f\n", r.xmin/20.0,r.ymin/20.0,r.xmax/20.0,r.ymax/20.0);
858             DEBUG_ENUMERATE printf("BBox %.2f %.2f %.2f %.2f\n", r2.xmin/20.0,r2.ymin/20.0,r2.xmax/20.0,r2.ymax/20.0);
859
860             DEBUG_ENUMERATE printf("style tag pos: %d\n", tag->pos);
861             enumerateUsedIDs_styles(tag, callback, callback_data, num, morph);
862             DEBUG_ENUMERATE printf("-------\n");
863             swf_ResetReadBits(tag);
864             while(--numshapes>=0) /* morph shapes define two shapes */
865             {
866                 DEBUG_ENUMERATE printf("shape:%d\n", numshapes);
867                 fillbits = swf_GetBits(tag, 4);
868                 linebits = swf_GetBits(tag, 4);
869                 DEBUG_ENUMERATE printf("fillbits=%d linebits=%d\n", fillbits, linebits);
870                 swf_ResetReadBits(tag);
871                 int x=0,y=0;
872                 while(1) {
873                     int flags;
874                     flags = swf_GetBits(tag, 1);
875                     if(!flags) { //style change
876                         flags = swf_GetBits(tag, 5);
877                         if(!flags)
878                             break;
879                         if(flags&1) { //move
880                             int n = swf_GetBits(tag, 5); 
881                             x = swf_GetBits(tag, n); //x
882                             y = swf_GetBits(tag, n); //y
883                             DEBUG_ENUMERATE printf("moveTo %.2f %.2f\n",x/20.0,y/20.0);
884                         }
885                         if(flags&2) { //fill0
886                             int fill0;
887                             fill0 = swf_GetBits(tag, fillbits); 
888                             DEBUG_ENUMERATE printf("fill0 %d\n", fill0);
889                         }
890                         if(flags&4) { //fill1
891                             int fill1;
892                             fill1 = swf_GetBits(tag, fillbits); 
893                             DEBUG_ENUMERATE printf("fill1 %d\n", fill1);
894                         }
895                         if(flags&8) { //linestyle
896                             int line;
897                             line = swf_GetBits(tag, linebits); 
898                             DEBUG_ENUMERATE printf("linestyle %d\n",line);
899                         }
900                         if(flags&16) {
901                             DEBUG_ENUMERATE printf("more fillstyles\n");
902                             enumerateUsedIDs_styles(tag, callback, callback_data, num, 0);
903                             fillbits = swf_GetBits(tag, 4);
904                             linebits = swf_GetBits(tag, 4);
905                         }
906                     } else {
907                         flags = swf_GetBits(tag, 1);
908                         if(flags) { //straight edge
909                             int n = swf_GetBits(tag, 4) + 2;
910                             if(swf_GetBits(tag, 1)) { //line flag
911                                 x += swf_GetSBits(tag, n); //delta x
912                                 y += swf_GetSBits(tag, n); //delta y
913                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f\n",x/20.0,y/20.0);
914                             } else {
915                                 int v=swf_GetBits(tag, 1);
916                                 int d;
917                                 d = swf_GetSBits(tag, n); //vert/horz
918                                 if(!v)
919                                     x += d;
920                                 else
921                                     y += d;
922                                 DEBUG_ENUMERATE printf("lineTo %.2f %.2f (%s)\n",x/20.0,y/20.0, v?"vertical":"horizontal");
923                             }
924                         } else { //curved edge
925                             int n = swf_GetBits(tag, 4) + 2;
926                             int x1,y1,x2,y2;
927                             x1 = swf_GetSBits(tag, n);
928                             y1 = swf_GetSBits(tag, n);
929                             x2 = swf_GetSBits(tag, n);
930                             y2 = swf_GetSBits(tag, n);
931                             DEBUG_ENUMERATE printf("splineTo %.2f %.2f %.2f %.2f\n", x1/20.0, y1/20.0, x2/20.0, y2/20.0);
932                         }
933                     }
934                 }
935             }
936         }
937         break;
938         default:
939         break;
940     }
941 }
942
943 void callbackCount(TAG * t,int pos, void*ptr)
944 {
945     (*(int*)ptr)++;
946     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
947 }
948
949 void callbackFillin(TAG * t,int pos, void*ptr)
950 {
951     **(int**)ptr = pos;
952     (*(int**)ptr)++;
953     DEBUG_ENUMERATE printf("callback(%d) %d\n", pos, *(U16*)&t->data[pos]);
954 }
955
956 int swf_GetNumUsedIDs(TAG * t)
957 {
958     int num = 0;
959     enumerateUsedIDs(t, 0, callbackCount, &num);
960     return num;
961 }
962
963 void swf_GetUsedIDs(TAG * t, int * positions)
964 {
965     int * ptr = positions;
966     enumerateUsedIDs(t, 0, callbackFillin, &ptr);
967 }
968
969 char swf_Relocate (SWF*swf, char*bitmap)
970 {
971     TAG*tag;
972     int slaveids[65536];
973     memset(slaveids, -1, sizeof(slaveids));
974     tag = swf->firstTag;
975     char ok = 1;
976     while(tag)
977     {
978         int num; 
979         int *ptr;
980         int t;
981
982         if(swf_isDefiningTag(tag))
983         {
984             int newid;
985             int id;
986             
987             id = swf_GetDefineID(tag); //own id
988
989             if(!bitmap[id]) { //free
990                 newid = id;
991             } else {
992                 newid = 0;
993                 for (t=1;t<65536;t++)
994                 {
995                     if(!bitmap[t])
996                     {
997                         newid = t;
998                         break;
999                     }
1000                 }
1001                 if(t==65536) {
1002                     fprintf(stderr, "swf_Relocate: Couldn't relocate: Out of IDs");
1003                     return 0;
1004                 }
1005             }
1006             bitmap[newid] = 1;
1007             slaveids[id] = newid;
1008
1009             swf_SetDefineID(tag, newid);
1010         } 
1011         
1012         num = swf_GetNumUsedIDs(tag);
1013         if(num) {
1014             ptr = (int*)rfx_alloc(sizeof(int)*num);
1015             swf_GetUsedIDs(tag, ptr);
1016
1017             for(t=0;t<num;t++) {
1018                 int id = GET16(&tag->data[ptr[t]]);
1019                 if(slaveids[id]<0) {
1020                     fprintf(stderr, "swf_Relocate: Mapping id (%d) never encountered before in %s\n", id,
1021                             swf_TagGetName(tag));
1022                     ok = 0;
1023                 } else {
1024                     id = slaveids[id];
1025                     PUT16(&tag->data[ptr[t]], id);
1026                 }
1027             }
1028         }
1029         tag=tag->next;
1030     }
1031     return ok;
1032 }
1033
1034 /* untested */
1035 void swf_Relocate2(SWF*swf, int*id2id)
1036 {
1037     TAG*tag;
1038     tag = swf->firstTag;
1039     while(tag) {
1040         if(swf_isDefiningTag(tag)) {
1041             int id = swf_GetDefineID(tag);
1042             id = id2id[id];
1043             if(id>=0) {
1044                 swf_SetDefineID(tag, id);
1045             }
1046         }
1047         int num = swf_GetNumUsedIDs(tag);
1048         if(num) {
1049             int *ptr;
1050             int t;
1051             ptr = (int*)rfx_alloc(sizeof(int)*num);
1052             swf_GetUsedIDs(tag, ptr);
1053             for(t=0;t<num;t++) {
1054                 int id = GET16(&tag->data[ptr[t]]);
1055                 id = id2id[id];
1056                 if(id>=0) {
1057                     PUT16(&tag->data[ptr[t]], id);
1058                 }
1059             }
1060         }
1061     }
1062 }
1063
1064 void swf_RelocateDepth(SWF*swf, char*bitmap)
1065 {
1066     TAG*tag;
1067     int nr;
1068     tag = swf->firstTag;
1069     for(nr=65535;nr>=0;nr--) {
1070         if(bitmap[nr] != 0) 
1071             break;
1072     }
1073     // now nr is the highest used depth. So we start
1074     // assigning depths at nr+1
1075     nr++;
1076
1077     while(tag)
1078     {
1079         int depth;
1080         /* TODO * clip depths 
1081                 * sprites
1082          */
1083         if(tag->id == ST_PLACEOBJECT2) {
1084             SWFPLACEOBJECT obj;
1085             swf_GetPlaceObject(tag, &obj);
1086             if(obj.clipdepth) {
1087                 int newdepth = obj.clipdepth+nr;
1088                 if(newdepth>65535) {
1089                     fprintf(stderr, "Couldn't relocate depths: too large values\n");
1090                     newdepth = 65535;
1091                 }
1092                 obj.clipdepth = newdepth;
1093                 swf_ResetTag(tag, ST_PLACEOBJECT2);
1094                 swf_SetPlaceObject(tag, &obj);
1095             }
1096             swf_PlaceObjectFree(&obj);
1097         }
1098
1099         depth = swf_GetDepth(tag);
1100         if(depth>=0) {
1101             int newdepth = depth+nr;
1102             if(newdepth>65535) {
1103                 fprintf(stderr, "Couldn't relocate depths: too large values\n");
1104                 newdepth = 65535;
1105             }
1106             swf_SetDepth(tag, newdepth);
1107         }
1108         tag=tag->next;
1109     }
1110 }
1111
1112 U8 swf_isShapeTag(TAG*tag)
1113 {
1114     if(tag->id == ST_DEFINESHAPE ||
1115        tag->id == ST_DEFINESHAPE2 ||
1116        tag->id == ST_DEFINESHAPE3 ||
1117        tag->id == ST_DEFINESHAPE4) 
1118         return 1;
1119     return 0;
1120 }
1121
1122 U8 swf_isPlaceTag(TAG*tag)
1123 {
1124     if(tag->id == ST_PLACEOBJECT ||
1125        tag->id == ST_PLACEOBJECT2 ||
1126        tag->id == ST_PLACEOBJECT3)
1127         return 1;
1128     return 0;
1129 }
1130 U8 swf_isTextTag(TAG*tag)
1131 {
1132     if(tag->id == ST_DEFINETEXT ||
1133        tag->id == ST_DEFINETEXT2)
1134         return 1;
1135     return 0;
1136 }
1137
1138 U8 swf_isFontTag(TAG*tag)
1139 {
1140     if(tag->id == ST_DEFINEFONT ||
1141        tag->id == ST_DEFINEFONT2 ||
1142        tag->id == ST_DEFINEFONTINFO)
1143         return 1;
1144     return 0;
1145 }
1146
1147 U8  swf_isImageTag(TAG*tag)
1148 {
1149     if(tag->id == ST_DEFINEBITSJPEG || 
1150        tag->id == ST_DEFINEBITSJPEG2 || 
1151        tag->id == ST_DEFINEBITSJPEG3 ||
1152        tag->id == ST_DEFINEBITSLOSSLESS || 
1153        tag->id == ST_DEFINEBITSLOSSLESS2)
1154         return 1;
1155     return 0;
1156 }
1157
1158 TAG* swf_Concatenate (TAG*list1,TAG*list2)
1159 {
1160     TAG*tag=0,*lasttag=0;
1161     char bitmap[65536];
1162     char depthmap[65536];
1163     SWF swf1,swf2;
1164     memset(bitmap, 0, sizeof(bitmap));
1165     memset(depthmap, 0, sizeof(depthmap));
1166     memset(&swf1, 0, sizeof(swf1));
1167     memset(&swf2, 0, sizeof(swf2));
1168
1169     swf1.firstTag = list1;
1170     swf_FoldAll(&swf1);
1171     swf2.firstTag = list2;
1172     swf_FoldAll(&swf2);
1173
1174     tag = list1;
1175     while(tag) {
1176         if(!swf_isDefiningTag(tag)) {
1177             int id = swf_GetDefineID(tag);
1178             bitmap[id] = 1;
1179         }
1180         if(tag->id == ST_PLACEOBJECT ||
1181            tag->id == ST_PLACEOBJECT2) {
1182             int depth = swf_GetDepth(tag);
1183             depthmap[depth] = 1;
1184         }
1185         if(tag->id == ST_REMOVEOBJECT ||
1186            tag->id == ST_REMOVEOBJECT2) {
1187             int depth = swf_GetDepth(tag);
1188             depthmap[depth] = 0;
1189         }
1190         tag = tag->next;
1191         lasttag = tag;
1192     }
1193     swf_Relocate(&swf2, bitmap);
1194     swf_RelocateDepth(&swf2, depthmap);
1195     lasttag->next = swf2.firstTag;
1196     swf2.firstTag->prev = lasttag;
1197
1198     return swf1.firstTag;
1199 }
1200
1201 static int tagHash(TAG*tag)
1202 {
1203     int t, h=0;
1204     unsigned int a = 0x6b973e5a;
1205     /* start at pos 2, as 0 and 1 are the id */
1206     for(t=2;t<tag->len;t++) {
1207         unsigned int b = a;
1208         a >>= 8;
1209         a += tag->data[t]*0xefbc35a5*b*(t+1);
1210     }
1211     return a&0x7fffffff; //always return positive number
1212 }
1213
1214 void swf_Optimize(SWF*swf)
1215 {
1216     const int hash_size = 131072;
1217     char* dontremap = (char*)rfx_calloc(sizeof(char)*65536);
1218     U16* remap = (U16*)rfx_alloc(sizeof(U16)*65536);
1219     TAG* id2tag = (TAG*)rfx_calloc(sizeof(TAG*)*65536);
1220     TAG** hashmap = (TAG**)rfx_calloc(sizeof(TAG*)*hash_size);
1221     TAG* tag;
1222     int t;
1223     for(t=0;t<65536;t++) {
1224         remap[t] = t;
1225     }
1226
1227     swf_FoldAll(swf);
1228
1229     tag = swf->firstTag;
1230     while(tag) {
1231         /* make sure we don't remap to this tag,
1232            as it might have different "helper tags" 
1233            FIXME: a better way would be to compare
1234                   the helper tags, too.
1235          */
1236         if(swf_isPseudoDefiningTag(tag) &&
1237            tag->id != ST_NAMECHARACTER) {
1238             dontremap[swf_GetDefineID(tag)] = 1;
1239         }
1240         tag=tag->next;
1241     }
1242     tag = swf->firstTag;
1243     while(tag) {
1244         TAG*next = tag->next;
1245
1246         /* remap the tag */
1247         int num = swf_GetNumUsedIDs(tag);
1248         int*positions = (int*)rfx_alloc(sizeof(int)*num);
1249         int t;
1250         swf_GetUsedIDs(tag, positions);
1251         for(t=0;t<num;t++) {
1252             int id = GET16(&tag->data[positions[t]]);
1253             id = remap[id];
1254             PUT16(&tag->data[positions[t]], id);
1255         }
1256         rfx_free(positions);
1257
1258         /* now look for previous tags with the same
1259            content */
1260         if(swf_isDefiningTag(tag)) {
1261             TAG*tag2;
1262             int id = swf_GetDefineID(tag);
1263             int hash = tagHash(tag);
1264             int match=0;
1265             if(!dontremap[id]) 
1266             while((tag2 = hashmap[hash%hash_size])) {
1267                 if(tag2 != (TAG*)0 && tag->len == tag2->len) {
1268                     if(memcmp(&tag->data[2],&tag2->data[2],tag->len-2) == 0) {
1269                         match = 1;
1270                         break;
1271                     }
1272                 }
1273                 hash++;
1274             }
1275             if(!match) {
1276                 while(hashmap[hash%hash_size]) hash++;
1277                 hashmap[hash%hash_size] = tag;
1278             } else {
1279                 /* we found two identical tags- remap one
1280                    of them */
1281                 remap[id] = swf_GetDefineID(tag2);
1282                 swf_DeleteTag(swf, tag);
1283             }
1284         } else if(swf_isPseudoDefiningTag(tag)) {
1285             int id = swf_GetDefineID(tag);
1286             if(remap[id]!=id) {
1287                 /* if this tag was remapped, we don't
1288                    need the helper tag anymore. Discard
1289                    it. */
1290                 swf_DeleteTag(swf, tag);
1291             }
1292         }
1293
1294         tag = next;
1295     }
1296     
1297     rfx_free(dontremap);
1298     rfx_free(remap);
1299     rfx_free(id2tag);
1300     rfx_free(hashmap);
1301 }
1302
1303 void swf_SetDefineBBox(TAG * tag, SRECT newbbox)
1304 {
1305     U16 id = 0;
1306     SRECT b1;
1307     swf_SetTagPos(tag,0);
1308
1309     switch (swf_GetTagID(tag))
1310     { 
1311         case ST_DEFINESHAPE:
1312         case ST_DEFINESHAPE2:
1313         case ST_DEFINESHAPE3:
1314         case ST_DEFINEEDITTEXT:
1315         case ST_DEFINETEXT:
1316         case ST_DEFINETEXT2:
1317         case ST_DEFINEVIDEOSTREAM: {
1318               U32 after_bbox_offset = 0, len;
1319               U8*data;
1320               id = swf_GetU16(tag);
1321               swf_GetRect(tag, &b1);
1322               swf_ResetReadBits(tag);
1323               after_bbox_offset = tag->pos;
1324               len = tag->len - after_bbox_offset;
1325               data = (U8*)malloc(len);
1326               memcpy(data, &tag->data[after_bbox_offset], len);
1327               tag->writeBit = 0;
1328               tag->len = 2;
1329               swf_SetRect(tag, &newbbox);
1330               swf_SetBlock(tag, data, len);
1331               free(data);
1332               tag->pos = tag->readBit = 0;
1333
1334         } break;
1335         default:
1336             fprintf(stderr, "rfxswf: Tag %d (%s) has no bbox\n", tag->id, swf_TagGetName(tag));
1337     }
1338 }
1339
1340 RGBA swf_GetSWFBackgroundColor(SWF*swf)
1341 {
1342     TAG*t=swf->firstTag;
1343     RGBA color;
1344     color.r = color.b = color.g = 0;
1345     color.a = 255;
1346     while(t) {
1347         if(t->id == ST_SETBACKGROUNDCOLOR) {
1348             swf_SetTagPos(t, 0);
1349             color.r = swf_GetU8(t);
1350             color.g = swf_GetU8(t);
1351             color.b = swf_GetU8(t);
1352             break;
1353         }
1354         t=t->next;
1355     }
1356     return color;
1357 }
1358