added (long) GPL headers.
[swftools.git] / src / swfbbox.c
1 /* swfbbox.c
2    Tool for playing around with SWF bounding boxes.
3
4    Part of the swftools package.
5    
6    Copyright (c) 2003 Matthias Kramm <kramm@quiss.org>
7  
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
21
22 #include "../config.h"
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <assert.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include "../lib/rfxswf.h"
29 #include "../lib/args.h"
30
31 static char * filename = 0;
32 static char * outfilename = "output.swf";
33 static int optimize = 0;
34 static int swifty = 0;
35 static int verbose = 0;
36
37 struct options_t options[] =
38 {
39  {"V","version"},
40  {"O","optimize"},
41  {"o","output"},
42  {"S","swifty"},
43  {"v","verbose"},
44  {0,0}
45 };
46
47 int args_callback_option(char*name,char*val)
48 {
49     if(!strcmp(name, "V")) {
50         printf("swfbbox - part of %s %s\n", PACKAGE, VERSION);
51         exit(0);
52     } 
53     else if(!strcmp(name, "O")) {
54         optimize = 1;
55         return 0;
56     } 
57     else if(!strcmp(name, "S")) {
58         swifty = 1;
59         return 0;
60     } 
61     else if(!strcmp(name, "v")) {
62         verbose = 1;
63         return 0;
64     } 
65     else if(!strcmp(name, "o")) {
66         outfilename = val;
67         return 1;
68     } 
69     else {
70         printf("Unknown option: -%s\n", name);
71         exit(1);
72     }
73
74     return 0;
75 }
76 int args_callback_longoption(char*name,char*val)
77 {
78     return args_long2shortoption(options, name, val);
79 }
80 void args_callback_usage(char*name)
81 {    
82     printf("Usage: %s [-at] file.swf\n", name);
83     printf("\t-h , --help\t\t Print help and exit\n");
84     printf("\t-O , --optimize\t\t Recalculate bounding boxes\n");
85     printf("\t-S , --swifty\t\t Print out transformed bounding boxes\n");
86     printf("\t-o , --output\t\t Set output filename (for -O)\n");
87     printf("\t-V , --version\t\t Print program version and exit\n");
88 }
89 int args_callback_command(char*name,char*val)
90 {
91     if(filename) {
92         fprintf(stderr, "Only one file allowed. You supplied at least two. (%s and %s)\n",
93                  filename, name);
94     }
95     filename = name;
96     return 0;
97 }
98
99 #define swf_ResetReadBits(tag)   if (tag->readBit)  { tag->pos++; tag->readBit = 0; }
100
101 void parseFillStyleArray(TAG*tag, SHAPE2*shape)
102 {
103     U16 count;
104     int t;
105     int num=0;
106     if(tag->id == ST_DEFINESHAPE)
107         num = 1;
108     else if(tag->id == ST_DEFINESHAPE2)
109         num = 2;
110     else if(tag->id == ST_DEFINESHAPE3)
111         num = 3;
112
113     count = swf_GetU8(tag);
114     if(count == 0xff && num>1) // defineshape2,3 only
115         count = swf_GetU16(tag);
116
117     if(verbose) printf("num: %d\n", count);
118     shape->numfillstyles = count;
119     shape->fillstyles = malloc(sizeof(FILLSTYLE)*count);
120
121     for(t=0;t<count;t++)
122     {
123         int type;
124         U8*pos;
125         FILLSTYLE*dest = &shape->fillstyles[t];
126         swf_ResetReadBits(tag);
127         type = swf_GetU8(tag); //type
128         shape->fillstyles[t].type = type;
129         if(type == 0) {
130             /* plain color */
131             if(num == 3)
132                 swf_GetRGBA(tag, &dest->color);
133             else 
134                 swf_GetRGB(tag, &dest->color);
135         }
136         else if(type == 0x10 || type == 0x12)
137         {
138             /* linear/radial gradient fill */
139             swf_ResetReadBits(tag);
140             swf_GetMatrix(tag, &dest->m);
141             swf_ResetReadBits(tag);
142             dest->gradient = malloc(sizeof(GRADIENT)); // TODO: free this again
143             swf_GetGradient(tag, dest->gradient, num>=3?1:0);
144         }
145         else if(type == 0x40 || type == 0x41)
146         {
147             /* bitmap fill */
148             swf_ResetReadBits(tag);
149             dest->id_bitmap = swf_GetU16(tag); //id
150             swf_ResetReadBits(tag); //?
151             swf_GetMatrix(tag, &dest->m);
152         }
153         else {
154             fprintf(stderr, "rfxswf:swftools.c Unknown fillstyle:0x%02x\n",type);
155         }
156     }
157     swf_ResetReadBits(tag);
158     count = swf_GetU8(tag); // line style array
159     if(count == 0xff)
160         count = swf_GetU16(tag);
161
162     if(verbose) printf("lnum: %d\n", count);
163
164     shape->numlinestyles = count;
165     shape->linestyles = malloc(sizeof(LINESTYLE)*count);
166     /* TODO: should we start with 1 and insert a correct definition of the
167        "built in" linestyle 0? */
168     for(t=0;t<count;t++) 
169     {
170         shape->linestyles[t].width = swf_GetU16(tag);
171         if(num == 3)
172             swf_GetRGBA(tag, &shape->linestyles[t].color);
173         else
174             swf_GetRGB(tag, &shape->linestyles[t].color);
175     }
176     return;
177 }
178
179 void swf_ParseDefineShape(TAG*tag, SHAPE2*shape)
180 {
181     int num = 0, id;
182     U16 fill,line;
183     SRECT r;
184     SRECT r2;
185     SHAPELINE*l;
186     if(tag->id == ST_DEFINESHAPE)
187         num = 1;
188     else if(tag->id == ST_DEFINESHAPE2)
189         num = 2;
190     else if(tag->id == ST_DEFINESHAPE3)
191         num = 3;
192     else {
193         fprintf(stderr, "parseDefineShape must be called with a shape tag");
194     }
195
196     id = swf_GetU16(tag); //id
197     memset(shape, 0, sizeof(SHAPE2));
198     shape->bbox = malloc(sizeof(SRECT));
199     swf_GetRect(tag, &r);
200
201     memcpy(shape->bbox, &r, sizeof(SRECT));
202     parseFillStyleArray(tag, shape);
203
204     swf_ResetReadBits(tag); 
205     fill = (U16)swf_GetBits(tag,4);
206     line = (U16)swf_GetBits(tag,4);
207
208     shape->lines = swf_ParseShapeData(&tag->data[tag->pos], (tag->len - tag->pos)*8, fill, line);
209
210     l = shape->lines;
211 }
212
213 void swf_Shape2Optimize(SHAPE2*shape)
214 {
215     if(!shape->bbox)
216         shape->bbox = malloc(sizeof(SRECT));
217     *(shape->bbox) = swf_GetShapeBoundingBox(shape);
218 }
219
220 void swf_Shape2ToShape(SHAPE2*shape2, SHAPE*shape)
221 {
222     TAG*tag = swf_InsertTag(0,0);
223     SHAPELINE*l,*next;
224     int newx=0,newy=0,lastx=0,lasty=0,oldls=0,oldfs0=0,oldfs1=0;
225
226     memset(shape, 0, sizeof(SHAPE));
227
228     shape->linestyle.n = shape2->numlinestyles;
229     shape->linestyle.data = (LINESTYLE*)malloc(sizeof(LINESTYLE)*shape->linestyle.n);
230     memcpy(shape->linestyle.data, shape2->linestyles, sizeof(LINESTYLE)*shape->linestyle.n);
231     
232     shape->fillstyle.n =  shape2->numfillstyles;
233     shape->fillstyle.data = (FILLSTYLE*)malloc(sizeof(FILLSTYLE)*shape->fillstyle.n);
234     memcpy(shape->fillstyle.data, shape2->fillstyles, sizeof(FILLSTYLE)*shape->fillstyle.n);
235
236     swf_ShapeCountBits(shape,NULL,NULL);
237
238     l = shape2->lines;
239
240     while(l) {
241         int ls=0,fs0=0,fs1=0;
242
243         if(l->type != moveTo) {
244             if(oldls != l->linestyle) {oldls = ls = l->linestyle;if(!ls) ls=0x8000;}
245             if(oldfs0 != l->fillstyle0) {oldfs0 = fs0 = l->fillstyle0;if(!fs0) fs0=0x8000;}
246             if(oldfs1 != l->fillstyle1) {oldfs1 = fs1 = l->fillstyle1;if(!fs1) fs1=0x8000;}
247
248             if(ls || fs0 || fs1 || newx!=0x7fffffff || newy!=0x7fffffff) {
249                 swf_ShapeSetAll(tag,shape,newx,newy,ls,fs0,fs1);
250                 newx = 0x7fffffff;
251                 newy = 0x7fffffff;
252             }
253         }
254
255         if(l->type == lineTo) {
256             swf_ShapeSetLine(tag,shape,l->x-lastx,l->y-lasty);
257         } else if(l->type == splineTo) {
258             swf_ShapeSetCurve(tag,shape, l->sx-lastx,l->sy-lasty, l->x-l->sx,l->y-l->sy);
259         }
260         if(l->type == moveTo) {
261             newx = l->x;
262             newy = l->y;
263         }
264
265         lastx = l->x;
266         lasty = l->y;
267         l = l->next;
268     }
269     swf_ShapeSetEnd(tag);
270     shape->data = tag->data;
271     shape->bitlen = tag->len*8;
272 }
273
274 void swf_SetShape2(TAG*tag, SHAPE2*shape2)
275 {
276     SHAPE shape;
277     swf_Shape2ToShape(shape2, &shape);
278
279     swf_SetRect(tag,shape2->bbox);
280     swf_SetShapeStyles(tag, &shape);
281     swf_ShapeCountBits(&shape,NULL,NULL);
282     swf_SetShapeBits(tag,&shape);
283
284     swf_SetBlock(tag, shape.data, (shape.bitlen+7)/8);
285 }
286
287 /*
288    {char {x1 y1 x2 y2 x3 y3 x4 y4]]
289 */
290
291 SRECT bboxes[65536];
292 U16 depth2id[65536];
293 char*depth2name[65536];
294
295 int hasid(TAG*tag)
296 {
297     if(tag->id == ST_PLACEOBJECT)
298         return 1;
299     if(tag->id == ST_PLACEOBJECT2 && (tag->data[0] & 2))
300         return 1;
301     return 0;
302 }
303
304 int hasname(TAG*tag)
305 {
306     if(tag->id == ST_PLACEOBJECT)
307         return 0;
308     if(tag->id == ST_PLACEOBJECT2 && (tag->data[0] & 0x20))
309         return 1;
310     return 0;
311 }
312
313 char* getname(TAG*tag)
314 {
315     if(tag->id == ST_PLACEOBJECT)
316         return 0;
317     if(tag->id == ST_PLACEOBJECT2 && (tag->data[0] & 0x20)) {
318         SWFPLACEOBJECT o;
319         tag->pos = 0;tag->readBit = 0;
320         swf_GetPlaceObject(tag, &o);
321         return o.name;
322     }
323     return 0;
324 }
325
326 MATRIX getmatrix(TAG*tag)
327 {
328     SWFPLACEOBJECT o;
329     tag->pos = 0;tag->readBit = 0;
330     swf_GetPlaceObject(tag, &o);
331     return o.matrix;
332 }
333
334 int main (int argc,char ** argv)
335
336     TAG*tag;
337     SWF swf;
338     int fi;
339     int frame=0;
340     memset(bboxes, 0, sizeof(bboxes));
341     memset(depth2name, 0, sizeof(depth2name));
342
343     processargs(argc, argv);
344
345     if(!filename) {
346         fprintf(stderr, "You must supply a filename.\n");
347         return 1;
348     }
349
350     fi = open(filename,O_RDONLY|O_BINARY);
351
352     if (fi<0)
353     { 
354         perror("Couldn't open file: ");
355         exit(1);
356     }
357     if FAILED(swf_ReadSWF(fi,&swf))
358     { 
359         fprintf(stderr, "%s is not a valid SWF file or contains errors.\n",filename);
360         close(fi);
361         exit(1);
362     }
363     close(fi);
364
365     swf_FoldAll(&swf);
366     tag = swf.firstTag;
367
368     if(swifty) {
369         printf("{\n\t{frame %d}\n", frame++);
370     }
371
372     while (tag) {
373         if (tag->id == ST_DEFINESHAPE ||
374             tag->id == ST_DEFINESHAPE2 ||
375             tag->id == ST_DEFINESHAPE3) {
376             SHAPE2 s;
377             if(verbose) printf("%s\n", swf_TagGetName(tag));
378             swf_ParseDefineShape(tag, &s);
379             swf_Shape2Optimize(&s);
380             tag->len = 2;
381             swf_SetShape2(tag, &s);
382         }
383         if(swf_isDefiningTag(tag) && tag->id != ST_DEFINESPRITE) {
384             bboxes[swf_GetDefineID(tag)] = swf_GetDefineBBox(tag);
385         }
386         if(swifty) {
387             if (tag->id == ST_SHOWFRAME) {
388                 printf("}\n{\n\t{frame %d}\n", frame++);
389             }
390             if (tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
391                 if(hasid(tag)) {
392                     depth2id[swf_GetDepth(tag)] = swf_GetPlaceID(tag);
393                 }
394                 if(hasname(tag)) {
395                     depth2name[swf_GetDepth(tag)] = getname(tag);
396                 }
397             }
398             if (tag->id == ST_PLACEOBJECT || tag->id == ST_PLACEOBJECT2) {
399                 MATRIX m = getmatrix(tag);
400                 U16 id = depth2id[swf_GetDepth(tag)];
401                 char*name = depth2name[swf_GetDepth(tag)];
402                 char buf[40];
403                 SRECT bbox = bboxes[id];
404                 SPOINT p1,p2,p3,p4;
405                 p1.x = bbox.xmin; p1.y = bbox.ymin;
406                 p2.x = bbox.xmax; p2.y = bbox.ymin;
407                 p3.x = bbox.xmin; p3.y = bbox.ymax;
408                 p4.x = bbox.xmax; p4.y = bbox.ymax;
409                 p1 = swf_TurnPoint(p1, &m);
410                 p2 = swf_TurnPoint(p2, &m);
411                 p3 = swf_TurnPoint(p3, &m);
412                 p4 = swf_TurnPoint(p4, &m);
413                 if(!name) {
414                     sprintf(buf, "ID%d", id);name = buf;
415                 }
416                 //printf("\t#%.4f %.4f %.4f %.4f | %.4f %.4f\n", m.sx/65536.0, m.r1/65536.0, m.r0/65536.0, m.sy/65536.0, m.tx/20.0, m.ty/20.0);
417                 printf("\t{%s {%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f}}\n", name, 
418                         p1.x/20.0, p1.y/20.0, p2.x/20.0, p2.y/20.0,
419                         p3.x/20.0, p3.y/20.0, p4.x/20.0, p4.y/20.0);
420             }
421         }
422         tag = tag->next;
423     }
424     if(swifty) {
425         printf("}\n");
426     }
427
428     if(optimize) {
429         fi = open(outfilename, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, 0666);
430         if(swf_WriteSWF(fi, &swf) < 0) {
431             fprintf(stderr, "Error writing file %s", outfilename);
432             close(fi);
433             exit(1);
434         }
435         close(fi);
436     }
437
438     swf_FreeTags(&swf);
439     return 0;
440 }