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 expand
= 1;
40 static struct options_t options
[] = {
51 int args_callback_option(char*name
,char*val
)
53 if(!strcmp(name
, "V")) {
54 printf("swfbbox - part of %s %s\n", PACKAGE
, VERSION
);
57 else if(!strcmp(name
, "O")) {
62 else if(!strcmp(name
, "S")) {
67 else if(!strcmp(name
, "v")) {
71 else if(!strcmp(name
, "e")) {
75 else if(!strcmp(name
, "o")) {
80 printf("Unknown option: -%s\n", name
);
86 int args_callback_longoption(char*name
,char*val
)
88 return args_long2shortoption(options
, name
, val
);
90 void args_callback_usage(char *name
)
93 printf("Usage: %s [-OSe] file.swf\n", name
);
95 printf("-h , --help Print help and exit\n");
96 printf("-S , --swifty Print out transformed bounding boxes\n");
97 printf("-O , --optimize Recalculate bounding boxes and write new file\n");
98 printf("-e , --expand Recalculate main bounding box and write new file\n");
99 printf("-o , --output <filename> Set output filename to <filename> (for -O/-e)\n");
100 printf("-v , --verbose Be more verbose\n");
101 printf("-V , --version Print program version and exit\n");
104 int args_callback_command(char*name
,char*val
)
107 fprintf(stderr
, "Only one file allowed. You supplied at least two. (%s and %s)\n",
114 #define swf_ResetReadBits(tag) if (tag->readBit) { tag->pos++; tag->readBit = 0; }
116 void swf_Shape2Optimize(SHAPE2
*shape
)
119 shape
->bbox
= malloc(sizeof(SRECT
));
120 *(shape
->bbox
) = swf_GetShapeBoundingBox(shape
);
124 {char {x1 y1 x2 y2 x3 y3 x4 y4]]
129 char*depth2name
[65536];
133 if(tag
->id
== ST_PLACEOBJECT
)
135 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 2))
142 if(tag
->id
== ST_PLACEOBJECT
)
144 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 0x20))
149 char* getname(TAG
*tag
)
151 if(tag
->id
== ST_PLACEOBJECT
)
153 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 0x20)) {
155 tag
->pos
= 0;tag
->readBit
= 0;
156 swf_GetPlaceObject(tag
, &o
);
162 MATRIX
getmatrix(TAG
*tag
)
165 tag
->pos
= 0;tag
->readBit
= 0;
166 swf_GetPlaceObject(tag
, &o
);
171 static int fontnum
= -1;
172 static SWFFONT
**fonts
;
174 static void fontcallback1(U16 id
,U8
* name
)
177 static void fontcallback2(U16 id
,U8
* name
)
180 swf_FontExtract(c_swf
,id
,&fonts
[fontnum
]);
182 if(fonts
[fontnum
]) printf("Extracting font %d (%s)\n", id
, name
);
183 else printf("Extracting font %d (%s) failed\n", id
, name
);
188 typedef struct _textbounds
191 MATRIX m
; // character transform matrix
194 static void textcallback(void*self
, int*chars
, int*xpos
, int nr
, int fontid
, int fontsize
,
195 int xstart
, int ystart
, RGBA
* color
)
197 textbounds_t
* bounds
= (textbounds_t
*)self
;
200 for(t
=0;t
<fontnum
;t
++) {
201 if(fonts
[t
]->id
== fontid
) {
207 fprintf(stderr
, "Font %d unknown\n", fontid
);
211 /* This is an expensive operation- but what should we do, we
212 need the glyph's bounding boxes */
213 swf_FontCreateLayout(font
);
217 printf("%d chars, font %d, size %d, at (%d,%d)\n", nr
, fontid
, fontsize
, xstart
, ystart
);
220 /* not tested yet- the matrix/fontsize calculation is probably all wrong */
221 int x
= xstart
+ xpos
[t
];
224 SRECT newglyphbbox
, glyphbbox
= font
->layout
->bounds
[chars
[t
]];
225 MATRIX m
= bounds
->m
;
227 if(ch
< font
->numchars
&& font
->glyph2ascii
) {
228 ch
= font
->glyph2ascii
[ch
];
231 m
.sx
= (m
.sx
* fontsize
) / 1024;
232 m
.sy
= (m
.sy
* fontsize
) / 1024;
233 m
.r0
= (m
.r0
* fontsize
) / 1024;
234 m
.r1
= (m
.r1
* fontsize
) / 1024;
238 newglyphbbox
= swf_TurnRect(glyphbbox
, &m
);
242 swf_ExpandRect2(&(bounds
->r
), &newglyphbbox
);
244 printf("%5d %c, %d %d %d %d (%d %d %d %d) -> %d %d %d %d\n",
246 glyphbbox
.xmin
, glyphbbox
.ymin
, glyphbbox
.xmax
, glyphbbox
.ymax
,
247 newglyphbbox
.xmin
, newglyphbbox
.ymin
, newglyphbbox
.xmax
, newglyphbbox
.ymax
,
248 bounds
->r
.xmin
, bounds
->r
.ymin
, bounds
->r
.xmax
, bounds
->r
.ymax
);
254 static void swf_OptimizeBoundingBoxes(SWF
*swf
)
256 TAG
* tag
= swf
->firstTag
;
259 if (tag
->id
== ST_DEFINESHAPE
||
260 tag
->id
== ST_DEFINESHAPE2
||
261 tag
->id
== ST_DEFINESHAPE3
) {
263 if(verbose
) printf("%s\n", swf_TagGetName(tag
));
264 swf_ParseDefineShape(tag
, &s
);
265 swf_Shape2Optimize(&s
);
268 swf_SetShape2(tag
, &s
);
270 if (tag
->id
== ST_DEFINETEXT
|| tag
->id
== ST_DEFINETEXT2
) {
276 if(verbose
) printf("%s\n", swf_TagGetName(tag
));
278 if(verbose
) printf("Extracting fonts...\n");
281 swf_FontEnumerate(swf
,&fontcallback1
);
282 fonts
= (SWFFONT
**)malloc(fontnum
*sizeof(SWFFONT
*));
283 memset(fonts
, 0, fontnum
*sizeof(SWFFONT
*));
285 swf_FontEnumerate(swf
,&fontcallback2
);
288 memset(&bounds
, 0, sizeof(bounds
));
290 swf_SetTagPos(tag
, 0);
292 swf_GetRect(tag
,&oldbox
);
293 swf_ResetReadBits(tag
);
294 matrix_offset
= tag
->pos
;
295 swf_GetMatrix(tag
,&bounds
.m
);
296 swf_ParseDefineText(tag
, textcallback
, &bounds
);
299 swf_DumpMatrix(stdout
, &bounds
.m
);
300 printf("old: %d %d %d %d\n", oldbox
.xmin
, oldbox
.ymin
, oldbox
.xmax
, oldbox
.ymax
);
301 printf("new: %d %d %d %d\n", bounds
.r
.xmin
, bounds
.r
.ymin
, bounds
.r
.xmax
, bounds
.r
.ymax
);
304 /* now comes the tricky part:
305 we have to fiddle the data back in
306 thank heavens that the bbox is follow by a matrix
307 struct, which always starts on a byte boundary.
309 len
= tag
->len
- matrix_offset
;
311 memcpy(data
, &tag
->data
[matrix_offset
], len
);
314 swf_SetRect(tag
, &bounds
.r
);
315 swf_SetBlock(tag
, data
, len
);
317 tag
->pos
= tag
->readBit
= 0;
323 static void showSwiftyOutput(SWF
*swf
)
325 TAG
*tag
= swf
->firstTag
;
327 printf("{\n\t{frame %d}\n", frame
++);
330 if (tag
->id
== ST_SHOWFRAME
) {
331 printf("}\n{\n\t{frame %d}\n", frame
++);
333 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
335 depth2id
[swf_GetDepth(tag
)] = swf_GetPlaceID(tag
);
338 depth2name
[swf_GetDepth(tag
)] = getname(tag
);
341 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
342 MATRIX m
= getmatrix(tag
);
343 U16 id
= depth2id
[swf_GetDepth(tag
)];
344 char*name
= depth2name
[swf_GetDepth(tag
)];
346 SRECT bbox
= bboxes
[id
];
348 p1
.x
= bbox
.xmin
; p1
.y
= bbox
.ymin
;
349 p2
.x
= bbox
.xmax
; p2
.y
= bbox
.ymin
;
350 p3
.x
= bbox
.xmin
; p3
.y
= bbox
.ymax
;
351 p4
.x
= bbox
.xmax
; p4
.y
= bbox
.ymax
;
352 p1
= swf_TurnPoint(p1
, &m
);
353 p2
= swf_TurnPoint(p2
, &m
);
354 p3
= swf_TurnPoint(p3
, &m
);
355 p4
= swf_TurnPoint(p4
, &m
);
357 sprintf(buf
, "ID%d", id
);name
= buf
;
359 //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);
360 printf("\t{%s {%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f}}\n", name
,
361 p1
.x
/20.0, p1
.y
/20.0, p2
.x
/20.0, p2
.y
/20.0,
362 p3
.x
/20.0, p3
.y
/20.0, p4
.x
/20.0, p4
.y
/20.0);
368 static SRECT
getMovieClipBBox(TAG
*tag
)
370 //TAG*tag = swf->firstTag;
374 memset(depth2id
, 0, sizeof(depth2id
));
376 memset(&movieSize
,0,sizeof(SRECT
));
378 while (tag
->id
!= ST_END
) {
379 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
381 depth2id
[swf_GetDepth(tag
)] = swf_GetPlaceID(tag
);
384 if (tag
->id
== ST_PLACEOBJECT
|| tag
->id
== ST_PLACEOBJECT2
) {
385 MATRIX m
= getmatrix(tag
);
386 U16 id
= depth2id
[swf_GetDepth(tag
)];
387 SRECT bbox
= bboxes
[id
];
389 SRECT tbbox
= swf_TurnRect(bbox
, &m
);
390 swf_ExpandRect2(&movieSize
, &tbbox
);
397 static SRECT
getSWFBBox(SWF
*swf
)
399 SRECT movieSize
= getMovieClipBBox(swf
->firstTag
);
404 int main (int argc
,char ** argv
)
410 memset(bboxes
, 0, sizeof(bboxes
));
411 memset(depth2name
, 0, sizeof(depth2name
));
413 processargs(argc
, argv
);
414 initLog(0,0,0,0,0,verbose
?LOGLEVEL_DEBUG
:LOGLEVEL_WARNING
);
417 fprintf(stderr
, "You must supply a filename.\n");
421 fi
= open(filename
,O_RDONLY
|O_BINARY
);
425 perror("Couldn't open file: ");
428 if FAILED(swf_ReadSWF(fi
,&swf
))
430 fprintf(stderr
, "%s is not a valid SWF file or contains errors.\n",filename
);
436 swf_OptimizeTagOrder(&swf
);
439 /* Optimize bounding boxes in case -O flag was set */
441 swf_OptimizeBoundingBoxes(&swf
);
444 /* Create an ID to Bounding Box table */
447 if(swf_isDefiningTag(tag
)) {
448 int id
= swf_GetDefineID(tag
);
449 if(tag
->id
!= ST_DEFINESPRITE
) {
450 bboxes
[id
] = swf_GetDefineBBox(tag
);
452 swf_UnFoldSprite(tag
);
453 bboxes
[id
] = getMovieClipBBox(tag
);
456 printf("sprite %d is %.2fx%.2f\n", id
,
457 (bboxes
[id
].xmax
- bboxes
[id
].xmin
)/20.0,
458 (bboxes
[id
].ymax
- bboxes
[id
].ymin
)/20.0);
465 /* Create an ID->Bounding Box table for all bounding boxes */
467 showSwiftyOutput(&swf
);
470 newMovieSize
= getSWFBBox(&swf
);
472 if(optimize
|| expand
) {
475 swf
.movieSize
= newMovieSize
;
477 fi
= open(outfilename
, O_BINARY
| O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
478 if(swf_WriteSWF(fi
, &swf
) < 0) {
479 fprintf(stderr
, "Error writing file %s", outfilename
);
487 printf("Real Movie Size: %.2fx%.2f (:%.2f:%.2f)\n",
488 (newMovieSize
.xmax
-newMovieSize
.xmin
)/20.0,
489 (newMovieSize
.ymax
-newMovieSize
.ymin
)/20.0,
490 (newMovieSize
.xmin
)/20.0,
491 (newMovieSize
.ymin
)/20.0