2 Tool for playing around with SWF bounding boxes.
4 Part of the swftools package.
6 Copyright (c) 2003 Matthias Kramm <kramm@quiss.org>
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.
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.
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 */
22 #include "../config.h"
28 #include "../lib/rfxswf.h"
29 #include "../lib/args.h"
30 #include "../lib/log.h"
32 static char * filename
= 0;
33 static char * outfilename
= "output.swf";
34 static int optimize
= 0;
35 static int swifty
= 0;
36 static int verbose
= 0;
37 static int showbbox
= 1;
38 static int showorigbbox
= 0;
39 static int expand
= 1;
41 static struct options_t options
[] = {
54 int args_callback_option(char*name
,char*val
)
56 if(!strcmp(name
, "V")) {
57 printf("swfbbox - part of %s %s\n", PACKAGE
, VERSION
);
60 else if(!strcmp(name
, "b")) {
62 if(showbbox
== 1) showbbox
= 0;
65 else if(!strcmp(name
, "B")) {
69 else if(!strcmp(name
, "O")) {
71 if(showbbox
== 1) showbbox
= 0;
74 else if(!strcmp(name
, "S")) {
76 if(showbbox
== 1) showbbox
= 0;
79 else if(!strcmp(name
, "v")) {
83 else if(!strcmp(name
, "q")) {
87 else if(!strcmp(name
, "e")) {
91 else if(!strcmp(name
, "o")) {
96 printf("Unknown option: -%s\n", name
);
102 int args_callback_longoption(char*name
,char*val
)
104 return args_long2shortoption(options
, name
, val
);
106 void args_callback_usage(char *name
)
109 printf("Usage: %s [-OSe] file.swf\n", name
);
111 printf("-h , --help Print help and exit\n");
112 printf("-S , --swifty Print out transformed bounding boxes\n");
113 printf("-O , --optimize Recalculate bounding boxes and write new file\n");
114 printf("-e , --expand Recalculate main bounding box and write new file\n");
115 printf("-o , --output <filename> Set output filename to <filename> (for -O/-e)\n");
116 printf("-v , --verbose Be more verbose\n");
117 printf("-V , --version Print program version and exit\n");
120 int args_callback_command(char*name
,char*val
)
123 fprintf(stderr
, "Only one file allowed. You supplied at least two. (%s and %s)\n",
130 #define swf_ResetReadBits(tag) if (tag->readBit) { tag->pos++; tag->readBit = 0; }
132 void swf_Shape2Optimize(SHAPE2
*shape
)
135 shape
->bbox
= malloc(sizeof(SRECT
));
136 *(shape
->bbox
) = swf_GetShapeBoundingBox(shape
);
140 {char {x1 y1 x2 y2 x3 y3 x4 y4]]
145 char*depth2name
[65536];
149 if(tag
->id
== ST_PLACEOBJECT
)
151 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 2))
158 if(tag
->id
== ST_PLACEOBJECT
)
160 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 0x20))
165 char* getname(TAG
*tag
)
167 if(tag
->id
== ST_PLACEOBJECT
)
169 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 0x20)) {
171 tag
->pos
= 0;tag
->readBit
= 0;
172 swf_GetPlaceObject(tag
, &o
);
178 MATRIX
getmatrix(TAG
*tag
)
181 tag
->pos
= 0;tag
->readBit
= 0;
182 swf_GetPlaceObject(tag
, &o
);
187 static int fontnum
= -1;
188 static SWFFONT
**fonts
;
190 static void fontcallback1(U16 id
,U8
* name
)
193 static void fontcallback2(U16 id
,U8
* name
)
196 swf_FontExtract(c_swf
,id
,&fonts
[fontnum
]);
198 if(fonts
[fontnum
]) printf("Extracting font %d (%s)\n", id
, name
);
199 else printf("Extracting font %d (%s) failed\n", id
, name
);
204 typedef struct _textbounds
207 MATRIX m
; // character transform matrix
210 static void textcallback(void*self
, int*chars
, int*xpos
, int nr
, int fontid
, int fontsize
,
211 int xstart
, int ystart
, RGBA
* color
)
213 textbounds_t
* bounds
= (textbounds_t
*)self
;
216 for(t
=0;t
<fontnum
;t
++) {
217 if(fonts
[t
]->id
== fontid
) {
223 fprintf(stderr
, "Font %d unknown\n", fontid
);
227 /* This is an expensive operation- but what should we do, we
228 need the glyph's bounding boxes */
229 swf_FontCreateLayout(font
);
233 printf("%d chars, font %d, size %d, at (%d,%d)\n", nr
, fontid
, fontsize
, xstart
, ystart
);
236 /* not tested yet- the matrix/fontsize calculation is probably all wrong */
237 int x
= xstart
+ xpos
[t
];
240 SRECT newglyphbbox
, glyphbbox
= font
->layout
->bounds
[chars
[t
]];
241 MATRIX m
= bounds
->m
;
243 if(ch
< font
->numchars
&& font
->glyph2ascii
) {
244 ch
= font
->glyph2ascii
[ch
];
247 m
.sx
= (m
.sx
* fontsize
) / 1024;
248 m
.sy
= (m
.sy
* fontsize
) / 1024;
249 m
.r0
= (m
.r0
* fontsize
) / 1024;
250 m
.r1
= (m
.r1
* fontsize
) / 1024;
254 newglyphbbox
= swf_TurnRect(glyphbbox
, &m
);
258 swf_ExpandRect2(&(bounds
->r
), &newglyphbbox
);
260 printf("%5d %c, %d %d %d %d (%d %d %d %d) -> %d %d %d %d\n",
262 glyphbbox
.xmin
, glyphbbox
.ymin
, glyphbbox
.xmax
, glyphbbox
.ymax
,
263 newglyphbbox
.xmin
, newglyphbbox
.ymin
, newglyphbbox
.xmax
, newglyphbbox
.ymax
,
264 bounds
->r
.xmin
, bounds
->r
.ymin
, bounds
->r
.xmax
, bounds
->r
.ymax
);
270 static void swf_OptimizeBoundingBoxes(SWF
*swf
)
272 TAG
* tag
= swf
->firstTag
;
275 if (tag
->id
== ST_DEFINESHAPE
||
276 tag
->id
== ST_DEFINESHAPE2
||
277 tag
->id
== ST_DEFINESHAPE3
) {
279 if(verbose
) printf("%s\n", swf_TagGetName(tag
));
280 swf_ParseDefineShape(tag
, &s
);
281 swf_Shape2Optimize(&s
);
284 swf_SetShape2(tag
, &s
);
286 if (tag
->id
== ST_DEFINETEXT
|| tag
->id
== ST_DEFINETEXT2
) {
292 if(verbose
) printf("%s\n", swf_TagGetName(tag
));
294 if(verbose
) printf("Extracting fonts...\n");
297 swf_FontEnumerate(swf
,&fontcallback1
);
298 fonts
= (SWFFONT
**)malloc(fontnum
*sizeof(SWFFONT
*));
299 memset(fonts
, 0, fontnum
*sizeof(SWFFONT
*));
301 swf_FontEnumerate(swf
,&fontcallback2
);
304 memset(&bounds
, 0, sizeof(bounds
));
306 swf_SetTagPos(tag
, 0);
308 swf_GetRect(tag
,&oldbox
);
309 swf_ResetReadBits(tag
);
310 matrix_offset
= tag
->pos
;
311 swf_GetMatrix(tag
,&bounds
.m
);
312 swf_ParseDefineText(tag
, textcallback
, &bounds
);
315 swf_DumpMatrix(stdout
, &bounds
.m
);
316 printf("old: %d %d %d %d\n", oldbox
.xmin
, oldbox
.ymin
, oldbox
.xmax
, oldbox
.ymax
);
317 printf("new: %d %d %d %d\n", bounds
.r
.xmin
, bounds
.r
.ymin
, bounds
.r
.xmax
, bounds
.r
.ymax
);
320 /* now comes the tricky part:
321 we have to fiddle the data back in
322 thank heavens that the bbox is follow by a matrix
323 struct, which always starts on a byte boundary.
325 len
= tag
->len
- matrix_offset
;
327 memcpy(data
, &tag
->data
[matrix_offset
], len
);
330 swf_SetRect(tag
, &bounds
.r
);
331 swf_SetBlock(tag
, data
, len
);
333 tag
->pos
= tag
->readBit
= 0;
339 static void showSwiftyOutput(SWF
*swf
)
341 TAG
*tag
= swf
->firstTag
;
343 printf("{\n\t{frame %d}\n", frame
++);
346 if (tag
->id
== ST_SHOWFRAME
) {
347 printf("}\n{\n\t{frame %d}\n", frame
++);
349 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
351 depth2id
[swf_GetDepth(tag
)] = swf_GetPlaceID(tag
);
354 depth2name
[swf_GetDepth(tag
)] = getname(tag
);
357 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
358 MATRIX m
= getmatrix(tag
);
359 U16 id
= depth2id
[swf_GetDepth(tag
)];
360 char*name
= depth2name
[swf_GetDepth(tag
)];
362 SRECT bbox
= bboxes
[id
];
364 p1
.x
= bbox
.xmin
; p1
.y
= bbox
.ymin
;
365 p2
.x
= bbox
.xmax
; p2
.y
= bbox
.ymin
;
366 p3
.x
= bbox
.xmin
; p3
.y
= bbox
.ymax
;
367 p4
.x
= bbox
.xmax
; p4
.y
= bbox
.ymax
;
368 p1
= swf_TurnPoint(p1
, &m
);
369 p2
= swf_TurnPoint(p2
, &m
);
370 p3
= swf_TurnPoint(p3
, &m
);
371 p4
= swf_TurnPoint(p4
, &m
);
373 sprintf(buf
, "ID%d", id
);name
= buf
;
375 //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);
376 printf("\t{%s {%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f}}\n", name
,
377 p1
.x
/20.0, p1
.y
/20.0, p2
.x
/20.0, p2
.y
/20.0,
378 p3
.x
/20.0, p3
.y
/20.0, p4
.x
/20.0, p4
.y
/20.0);
384 static SRECT
getMovieClipBBox(TAG
*tag
)
386 //TAG*tag = swf->firstTag;
390 memset(depth2id
, 0, sizeof(depth2id
));
392 memset(&movieSize
,0,sizeof(SRECT
));
394 while (tag
->id
!= ST_END
) {
395 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
397 depth2id
[swf_GetDepth(tag
)] = swf_GetPlaceID(tag
);
400 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
401 MATRIX m
= getmatrix(tag
);
402 U16 id
= depth2id
[swf_GetDepth(tag
)];
403 SRECT bbox
= bboxes
[id
];
405 SRECT tbbox
= swf_TurnRect(bbox
, &m
);
406 swf_ExpandRect2(&movieSize
, &tbbox
);
413 static SRECT
getSWFBBox(SWF
*swf
)
415 SRECT movieSize
= getMovieClipBBox(swf
->firstTag
);
420 int main (int argc
,char ** argv
)
427 memset(bboxes
, 0, sizeof(bboxes
));
428 memset(depth2name
, 0, sizeof(depth2name
));
430 processargs(argc
, argv
);
431 initLog(0,0,0,0,0,verbose
?LOGLEVEL_DEBUG
:LOGLEVEL_WARNING
);
434 fprintf(stderr
, "You must supply a filename.\n");
438 fi
= open(filename
,O_RDONLY
|O_BINARY
);
442 perror("Couldn't open file: ");
445 if FAILED(swf_ReadSWF(fi
,&swf
))
447 fprintf(stderr
, "%s is not a valid SWF file or contains errors.\n",filename
);
453 swf_OptimizeTagOrder(&swf
);
456 /* Optimize bounding boxes in case -O flag was set */
458 swf_OptimizeBoundingBoxes(&swf
);
461 /* Create an ID to Bounding Box table */
464 if(swf_isDefiningTag(tag
)) {
465 int id
= swf_GetDefineID(tag
);
466 if(tag
->id
!= ST_DEFINESPRITE
) {
467 bboxes
[id
] = swf_GetDefineBBox(tag
);
469 swf_UnFoldSprite(tag
);
470 bboxes
[id
] = getMovieClipBBox(tag
);
473 printf("sprite %d is %.2fx%.2f\n", id
,
474 (bboxes
[id
].xmax
- bboxes
[id
].xmin
)/20.0,
475 (bboxes
[id
].ymax
- bboxes
[id
].ymin
)/20.0);
482 /* Create an ID->Bounding Box table for all bounding boxes */
484 showSwiftyOutput(&swf
);
487 oldMovieSize
= swf
.movieSize
;
488 newMovieSize
= getSWFBBox(&swf
);
490 if(optimize
|| expand
) {
493 swf
.movieSize
= newMovieSize
;
495 fi
= open(outfilename
, O_BINARY
| O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
496 if(swf_WriteSWF(fi
, &swf
) < 0) {
497 fprintf(stderr
, "Error writing file %s", outfilename
);
506 printf("Real Movie Size: ");
507 printf("%.2f x %.2f :%.2f :%.2f\n",
508 (newMovieSize
.xmax
-newMovieSize
.xmin
)/20.0,
509 (newMovieSize
.ymax
-newMovieSize
.ymin
)/20.0,
510 (newMovieSize
.xmin
)/20.0,
511 (newMovieSize
.ymin
)/20.0
516 printf("Original Movie Size: ");
517 printf("%.2f x %.2f :%.2f :%.2f\n",
518 (oldMovieSize
.xmax
-oldMovieSize
.xmin
)/20.0,
519 (oldMovieSize
.ymax
-oldMovieSize
.ymin
)/20.0,
520 (oldMovieSize
.xmin
)/20.0,
521 (oldMovieSize
.ymin
)/20.0