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
= 0;
38 static int showorigbbox
= 1;
39 static int expand
= 0;
41 static int checkclippings
= 0;
43 static struct options_t options
[] = {
57 int args_callback_option(char*name
,char*val
)
59 if(!strcmp(name
, "V")) {
60 printf("swfbbox - part of %s %s\n", PACKAGE
, VERSION
);
63 else if(!strcmp(name
, "b")) {
65 if(showbbox
== 1) showbbox
= 0;
68 else if(!strcmp(name
, "B")) {
72 else if(!strcmp(name
, "O")) {
74 if(showorigbbox
== 1) showorigbbox
= 0;
77 else if(!strcmp(name
, "S")) {
79 if(showorigbbox
== 1) showorigbbox
= 0;
82 else if(!strcmp(name
, "c")) {
83 if(showorigbbox
== 1) showorigbbox
= 0;
87 else if(!strcmp(name
, "v")) {
91 else if(!strcmp(name
, "q")) {
96 else if(!strcmp(name
, "Q")) {
97 /* DEPRECATED- was used for testing the bbox-clip feature
99 if(showorigbbox
== 1) showorigbbox
= 0;
103 else if(!strcmp(name
, "e")) {
107 else if(!strcmp(name
, "o")) {
112 printf("Unknown option: -%s\n", name
);
118 int args_callback_longoption(char*name
,char*val
)
120 return args_long2shortoption(options
, name
, val
);
122 void args_callback_usage(char *name
)
125 printf("Usage: %s [-OS] file.swf\n", name
);
127 printf("-h , --help Print help and exit\n");
128 printf("-b , --bbox Show movie bounding box (default)\n");
129 printf("-B , --newbbox Show recalculated (optimized/expanded) bounding box\n");
130 printf("-e , --expand Write out a new file using the recalculated header bounding box\n");
131 printf("-O , --optimize Recalculate all object bounding boxes (except for the header)\n");
132 printf("-S , --swifty Print out transformed bounding boxes\n");
133 printf("-c , --clip Clip bounding boxes to movie size\n");
134 printf("-o , --output <filename> Set output filename to <filename> (for -O)\n");
135 printf("-v , --verbose Be more verbose\n");
136 printf("-V , --version Print program version and exit\n");
139 int args_callback_command(char*name
,char*val
)
142 fprintf(stderr
, "Only one file allowed. You supplied at least two. (%s and %s)\n",
149 #define swf_ResetReadBits(tag) if (tag->readBit) { tag->pos++; tag->readBit = 0; }
151 void swf_Shape2Optimize(SHAPE2
*shape
)
154 shape
->bbox
= malloc(sizeof(SRECT
));
155 *(shape
->bbox
) = swf_GetShapeBoundingBox(shape
);
159 {char {x1 y1 x2 y2 x3 y3 x4 y4]]
164 char*depth2name
[65536];
168 if(tag
->id
== ST_PLACEOBJECT
)
170 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 2))
172 if(tag
->id
== ST_PLACEOBJECT3
&& (tag
->data
[0] & 2))
179 if(tag
->id
== ST_PLACEOBJECT
)
181 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 0x20))
183 if(tag
->id
== ST_PLACEOBJECT3
&& (tag
->data
[0] & 0x20))
188 char* getname(TAG
*tag
)
190 if(tag
->id
== ST_PLACEOBJECT
)
192 if(tag
->id
== ST_PLACEOBJECT2
&& (tag
->data
[0] & 0x20)) {
194 tag
->pos
= 0;tag
->readBit
= 0;
195 swf_GetPlaceObject(tag
, &o
);
198 if(tag
->id
== ST_PLACEOBJECT3
&& (tag
->data
[0] & 0x20)) {
200 tag
->pos
= 0;tag
->readBit
= 0;
201 swf_GetPlaceObject(tag
, &o
);
207 MATRIX
getmatrix(TAG
*tag
)
210 tag
->pos
= 0;tag
->readBit
= 0;
211 swf_GetPlaceObject(tag
, &o
);
216 static int fontnum
= -1;
217 static SWFFONT
**fonts
;
219 static void fontcallback1(void*self
, U16 id
,U8
* name
)
222 static void fontcallback2(void*self
, U16 id
,U8
* name
)
225 swf_FontExtract(c_swf
,id
,&fonts
[fontnum
]);
227 if(fonts
[fontnum
]) printf("Extracting font %d (%s)\n", id
, name
);
228 else printf("Extracting font %d (%s) failed\n", id
, name
);
233 typedef struct _textbounds
236 MATRIX m
; // character transform matrix
239 typedef struct _placement
245 static placement_t
* placements
;
247 static placement_t
* readPlacements(SWF
*swf
)
249 placement_t
* p
= (placement_t
*)rfx_calloc(sizeof(placement_t
)*65536);
250 TAG
*tag
= swf
->firstTag
;
252 if(swf_isPlaceTag(tag
)) {
253 SWFPLACEOBJECT
*po
= rfx_alloc(sizeof(SWFPLACEOBJECT
));
255 swf_GetPlaceObject(tag
, po
);
258 fprintf(stderr
, "MOVE tags not supported with -c");
269 static void freePlacements(placement_t
*p
)
272 for(t
=0;t
<65536;t
++) {
274 swf_PlaceObjectFree(p
[t
].po
); p
[t
].po
= 0;
280 static SRECT
clipBBox(TAG
*tag
, SRECT mbbox
, SRECT r
)
282 int id
= swf_GetDefineID(tag
);
284 if(!placements
[id
].po
) {
286 printf("Id %d is never set\n", id
);
289 if(placements
[id
].num
>1) {
291 printf("Id %d is set more than once\n", id
);
294 m
= placements
[id
].po
->matrix
;
296 fprintf(stderr
, "Rotating PLACEOBJECTS are not supported with -c\n");
301 printf("ID %d\n", id
);
302 swf_DumpMatrix(stdout
, &m
);
308 mbbox
.xmin
*= 65536.0/m
.sx
;
309 mbbox
.xmax
*= 65536.0/m
.sx
;
310 mbbox
.ymin
*= 65536.0/m
.sy
;
311 mbbox
.ymax
*= 65536.0/m
.sy
;
314 printf("border: %f/%f/%f/%f - rect: %f/%f/%f/%f\n",
327 if(r
.xmax
> mbbox
.xmax
) clip
+= r
.xmax
- mbbox
.xmax
;
328 if(r
.ymax
> mbbox
.ymax
) clip
+= r
.ymax
- mbbox
.ymax
;
329 if(r
.xmax
< mbbox
.xmin
) clip
+= -(r
.xmax
- mbbox
.xmin
);
330 if(r
.ymax
< mbbox
.ymin
) clip
+= -(r
.ymax
- mbbox
.ymin
);
332 if(r
.xmin
> mbbox
.xmax
) clip
+= r
.xmin
= mbbox
.xmax
;
333 if(r
.ymin
> mbbox
.ymax
) clip
+= r
.ymin
= mbbox
.ymax
;
334 if(r
.xmin
< mbbox
.xmin
) clip
+= -(r
.xmin
= mbbox
.xmin
);
335 if(r
.ymin
< mbbox
.ymin
) clip
+= -(r
.ymin
= mbbox
.ymin
);
337 printf("needs clipping: [%.2f %.2f %2.f %2.f] is outside [%.2f %2.f %2.f %2.f]\n",
338 r
.xmin
/ 20.0, r
.ymin
/ 20.0, r
.xmax
/ 20.0, r
.ymax
/ 20.0,
339 mbbox
.xmin
/ 20.0, mbbox
.ymin
/ 20.0, mbbox
.xmax
/ 20.0, mbbox
.ymax
/ 20.0
344 r
= swf_ClipRect(mbbox
, r
);
347 printf("new rect: %f/%f/%f/%f\n",
358 static void textcallback(void*self
, int*chars
, int*xpos
, int nr
, int fontid
, int fontsize
,
359 int xstart
, int ystart
, RGBA
* color
)
361 textbounds_t
* bounds
= (textbounds_t
*)self
;
364 for(t
=0;t
<fontnum
;t
++) {
365 if(fonts
[t
]->id
== fontid
) {
371 fprintf(stderr
, "Font %d unknown\n", fontid
);
375 /* This is an expensive operation- but what should we do, we
376 need the glyph's bounding boxes */
377 swf_FontCreateLayout(font
);
381 printf("%d chars, font %d, size %d, at (%d,%d)\n", nr
, fontid
, fontsize
, xstart
, ystart
);
384 /* not tested yet- the matrix/fontsize calculation is probably all wrong */
385 int x
= xstart
+ xpos
[t
];
388 SRECT newglyphbbox
, glyphbbox
= font
->layout
->bounds
[chars
[t
]];
389 MATRIX m
= bounds
->m
;
392 if(chars
[t
] < font
->numchars
&& font
->glyph2ascii
) {
393 ch
= font
->glyph2ascii
[chars
[t
]];
397 p
= swf_TurnPoint(p
, &m
);
399 m
.sx
= (m
.sx
* fontsize
) / 1024;
400 m
.sy
= (m
.sy
* fontsize
) / 1024;
401 m
.r0
= (m
.r0
* fontsize
) / 1024;
402 m
.r1
= (m
.r1
* fontsize
) / 1024;
406 newglyphbbox
= swf_TurnRect(glyphbbox
, &m
);
410 swf_ExpandRect2(&(bounds
->r
), &newglyphbbox
);
412 printf("%5d %c, %d %d %d %d (%d %d %d %d) -> %d %d %d %d\n",
414 glyphbbox
.xmin
, glyphbbox
.ymin
, glyphbbox
.xmax
, glyphbbox
.ymax
,
415 newglyphbbox
.xmin
, newglyphbbox
.ymin
, newglyphbbox
.xmax
, newglyphbbox
.ymax
,
416 bounds
->r
.xmin
, bounds
->r
.ymin
, bounds
->r
.xmax
, bounds
->r
.ymax
);
422 static void swf_OptimizeBoundingBoxes(SWF
*swf
)
424 TAG
* tag
= swf
->firstTag
;
427 if (tag
->id
== ST_DEFINESHAPE
||
428 tag
->id
== ST_DEFINESHAPE2
||
429 tag
->id
== ST_DEFINESHAPE3
||
430 tag
->id
== ST_DEFINESHAPE4
) {
432 if(verbose
) printf("%s\n", swf_TagGetName(tag
));
433 swf_ParseDefineShape(tag
, &s
);
435 swf_Shape2Optimize(&s
);
439 fprintf(stderr
, "Internal error (5)\n");
442 if(clip
|| checkclippings
) {
443 *s
.bbox
= clipBBox(tag
, swf
->movieSize
, *s
.bbox
);
445 swf_SetShape2(tag
, &s
);
447 if (tag
->id
== ST_DEFINETEXT
|| tag
->id
== ST_DEFINETEXT2
) {
453 if(verbose
) printf("%s\n", swf_TagGetName(tag
));
455 if(verbose
) printf("Extracting fonts...\n");
458 swf_FontEnumerate(swf
,&fontcallback1
,0);
459 fonts
= (SWFFONT
**)malloc(fontnum
*sizeof(SWFFONT
*));
460 memset(fonts
, 0, fontnum
*sizeof(SWFFONT
*));
462 swf_FontEnumerate(swf
,&fontcallback2
,0);
465 memset(&bounds
, 0, sizeof(bounds
));
467 swf_SetTagPos(tag
, 0);
469 swf_GetRect(tag
,&oldbox
);
470 swf_ResetReadBits(tag
);
471 matrix_offset
= tag
->pos
;
472 swf_GetMatrix(tag
,&bounds
.m
);
473 swf_ParseDefineText(tag
, textcallback
, &bounds
);
476 swf_DumpMatrix(stdout
, &bounds
.m
);
477 printf("old: %d %d %d %d\n", oldbox
.xmin
, oldbox
.ymin
, oldbox
.xmax
, oldbox
.ymax
);
478 printf("new: %d %d %d %d\n", bounds
.r
.xmin
, bounds
.r
.ymin
, bounds
.r
.xmax
, bounds
.r
.ymax
);
481 bounds
.r
= oldbox
; //set to old bounds from the tag header
482 if(clip
|| checkclippings
) {
483 bounds
.r
= clipBBox(tag
, swf
->movieSize
, bounds
.r
);
486 /* now comes the tricky part:
487 we have to fiddle the data back in
488 thank heavens that the bbox is follow by a matrix
489 struct, which always starts on a byte boundary.
491 len
= tag
->len
- matrix_offset
;
493 memcpy(data
, &tag
->data
[matrix_offset
], len
);
496 swf_SetRect(tag
, &bounds
.r
);
497 swf_SetBlock(tag
, data
, len
);
499 tag
->pos
= tag
->readBit
= 0;
505 static void showSwiftyOutput(SWF
*swf
)
507 TAG
*tag
= swf
->firstTag
;
509 printf("{\n\t{frame %d}\n", frame
++);
512 if (tag
->id
== ST_SHOWFRAME
) {
513 printf("}\n{\n\t{frame %d}\n", frame
++);
515 if (swf_isPlaceTag(tag
)) {
517 depth2id
[swf_GetDepth(tag
)] = swf_GetPlaceID(tag
);
520 depth2name
[swf_GetDepth(tag
)] = getname(tag
);
523 if (swf_isPlaceTag(tag
)) {
524 MATRIX m
= getmatrix(tag
);
525 U16 id
= depth2id
[swf_GetDepth(tag
)];
526 char*name
= depth2name
[swf_GetDepth(tag
)];
528 SRECT bbox
= bboxes
[id
];
530 p1
.x
= bbox
.xmin
; p1
.y
= bbox
.ymin
;
531 p2
.x
= bbox
.xmax
; p2
.y
= bbox
.ymin
;
532 p3
.x
= bbox
.xmin
; p3
.y
= bbox
.ymax
;
533 p4
.x
= bbox
.xmax
; p4
.y
= bbox
.ymax
;
534 p1
= swf_TurnPoint(p1
, &m
);
535 p2
= swf_TurnPoint(p2
, &m
);
536 p3
= swf_TurnPoint(p3
, &m
);
537 p4
= swf_TurnPoint(p4
, &m
);
539 sprintf(buf
, "ID%d", id
);name
= buf
;
541 //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);
542 printf("\t{%s {%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f}}\n", name
,
543 p1
.x
/20.0, p1
.y
/20.0, p2
.x
/20.0, p2
.y
/20.0,
544 p3
.x
/20.0, p3
.y
/20.0, p4
.x
/20.0, p4
.y
/20.0);
550 static SRECT
getMovieClipBBox(TAG
*tag
)
552 //TAG*tag = swf->firstTag;
556 memset(depth2id
, 0, sizeof(depth2id
));
558 memset(&movieSize
,0,sizeof(SRECT
));
560 while (tag
&& tag
->id
!= ST_END
) {
561 if (swf_isPlaceTag(tag
)) {
563 depth2id
[swf_GetDepth(tag
)] = swf_GetPlaceID(tag
);
566 if (swf_isPlaceTag(tag
)) {
567 MATRIX m
= getmatrix(tag
);
568 U16 id
= depth2id
[swf_GetDepth(tag
)];
569 SRECT bbox
= bboxes
[id
];
571 SRECT tbbox
= swf_TurnRect(bbox
, &m
);
572 swf_ExpandRect2(&movieSize
, &tbbox
);
579 static SRECT
getSWFBBox(SWF
*swf
)
581 SRECT movieSize
= getMovieClipBBox(swf
->firstTag
);
586 int main (int argc
,char ** argv
)
593 memset(bboxes
, 0, sizeof(bboxes
));
594 memset(depth2name
, 0, sizeof(depth2name
));
596 processargs(argc
, argv
);
597 initLog(0,0,0,0,0,verbose
?LOGLEVEL_DEBUG
:LOGLEVEL_WARNING
);
600 fprintf(stderr
, "You must supply a filename.\n");
604 fi
= open(filename
,O_RDONLY
|O_BINARY
);
608 perror("Couldn't open file: ");
611 if FAILED(swf_ReadSWF(fi
,&swf
))
613 fprintf(stderr
, "%s is not a valid SWF file or contains errors.\n",filename
);
619 swf_OptimizeTagOrder(&swf
);
621 if(clip
|| checkclippings
) {
622 placements
= readPlacements(&swf
);
627 /* Optimize bounding boxes in case -O flag was set */
628 if(optimize
|| checkclippings
|| clip
) {
629 swf_OptimizeBoundingBoxes(&swf
);
632 /* Create an ID to Bounding Box table */
635 if(swf_isDefiningTag(tag
)) {
636 int id
= swf_GetDefineID(tag
);
637 if(tag
->id
!= ST_DEFINESPRITE
) {
638 bboxes
[id
] = swf_GetDefineBBox(tag
);
640 swf_UnFoldSprite(tag
);
641 bboxes
[id
] = getMovieClipBBox(tag
);
644 printf("sprite %d is %.2fx%.2f\n", id
,
645 (bboxes
[id
].xmax
- bboxes
[id
].xmin
)/20.0,
646 (bboxes
[id
].ymax
- bboxes
[id
].ymin
)/20.0);
653 /* Create an ID->Bounding Box table for all bounding boxes */
655 showSwiftyOutput(&swf
);
658 oldMovieSize
= swf
.movieSize
;
659 newMovieSize
= getSWFBBox(&swf
);
661 if(optimize
|| expand
) {
664 swf
.movieSize
= newMovieSize
;
666 fi
= open(outfilename
, O_BINARY
| O_RDWR
| O_CREAT
| O_TRUNC
, 0666);
667 if(swf_WriteSWF(fi
, &swf
) < 0) {
668 fprintf(stderr
, "Error writing file %s", outfilename
);
677 printf("Real Movie Size (size of visible objects): ");
678 printf("%.2f x %.2f :%.2f :%.2f\n",
679 (newMovieSize
.xmax
-newMovieSize
.xmin
)/20.0,
680 (newMovieSize
.ymax
-newMovieSize
.ymin
)/20.0,
681 (newMovieSize
.xmin
)/20.0,
682 (newMovieSize
.ymin
)/20.0
687 printf("Movie Size accordings to file header: ");
688 printf("%.2f x %.2f :%.2f :%.2f\n",
689 (oldMovieSize
.xmax
-oldMovieSize
.xmin
)/20.0,
690 (oldMovieSize
.ymax
-oldMovieSize
.ymin
)/20.0,
691 (oldMovieSize
.xmin
)/20.0,
692 (oldMovieSize
.ymin
)/20.0
699 freePlacements(placements
);