Hacking SWF – Everything You Never Wanted To Know About Shapes In Flash

I have been inbetween projects the last weeks and thought i could do something productive while i’m idle, so i created as3swf, a low level Actionscript 3 library to parse, create, modify and publish SWF files.

At the same time i experimented with iPhone development, as we at côdeazur have some commercial iPhone work lined up. Part of those experiments involved Core Graphics/Quartz programming, and the old grumpy Flash developer in me immediately thought it would be cool if i could reuse Flash vector art in my iPhone applications. More details on that here: Shape Export to Objective-C.

With as3swf (which parses SWF shapes down to the bone) i already had a powerful weapon in my hands. I’d “just” had to extend it to generate Objective-C source code from a SWF shape. SWF shape records translate almost directly to AS3 drawing API calls (so i thought), and thus also to the similar Quartz Core Graphics API calls. I couldn’t have been more wrong.

So, for the record, SWF shape records kinda translate to AS3 drawing API calls, but not really – there are quite a few nuts to crack along the way. It is not as easy as it initially sounds, and little of this is officially documented or explained in Adobe’s SWF10 specification (or anywhere else for that matter), so i thought i document my findings here.

One blog post was brought to my attention, that helped me understand how the Flash Player actually draws shapes: Mike Swanson’s “Converting Flash Shapes to WPF” (ironically, Mike is Technical Evangelist at Microsoft). A very good read, i learned much of the stuff i’m going to explain here through that post.

SWF Shapes

I don’t want to go into much detail about the SWF10 file format in general here, it is sufficiently well documented by Adobe. However, an introduction to Flash shapes and how they are stored in SWF seems appropriate.

Shapes are represented in SWF by DefineShape tags. Let’s jump right into practice and draw a simple shape on stage  in the Flash IDE.

Create a new FLA. Select the Rectangle tool, choose a 10px red stroke and a solid gray fill, and draw a rectangle:

Publish the SWF, load it into a ByteArray, have as3swf parse it, and dump the results to the console (example code here). The output should contain a DefineShape4 tag that looks something like this:

[83:DefineShape4] ID: 1
  FillStyles:
    [1] [SWFFillStyle] Type: 0 (solid), Color: 666666
  LineStyles:
    [1] [SWFLineStyle2] Width: 200, Color: ff0000
  ShapeRecords:
    [SWFShapeRecordStyleChange] MoveTo: 400,400, FillStyle1: 1, LineStyle: 1
    [SWFShapeRecordStraightEdge] Horizontal: 2000
    [SWFShapeRecordStraightEdge] Vertical: 2000
    [SWFShapeRecordStraightEdge] Horizontal: -2000
    [SWFShapeRecordStraightEdge] Vertical: -2000
    [SWFShapeRecordEnd]

DefineShape contains a set of initial style definitions (in this case that’s the dark gray solid fill  and the 10px red stroke), followed by a set of shape records describing the geometry of the shape. Note that all coordinates are measured in “Twips” (a 20th of a pixel) so you have to divide all of them by 20 to get to the pixel coordinates.

Shape records can come in four flavors:

StyleChange records can have more than one purpose. They move the drawing cursor to a new position (equivalent to moveTo in the drawing API), set line and fill styles for following edges (equivalent to beginFill and lineStyle in the drawing API), and define new line and fill styles where needed.

StraightEdge and CurvedEdge records define the actual edges of the shape (equivalent to lineTo and curveTo in the drawing API).

The End record is always the last shape record, it simply tells the Flash Player that the shape ends there.

Simple stuff you might think. I haven’t told you about FillStyle0 and FillStyle1 yet, though.

FillStyle0 and FillStyle1

This is where things get twisted. Edges in SWF shapes can have two different fills.

Create a new FLA, select the Rectangle tool, choose a 10px red stroke and no fill, and draw two rectangles, the second one completely enclosed by the first one. Then use the Paint Bucket tool to fill the donut part with solid dark gray:

The as3swf dump of the resulting SWF contains the following DefineShape tag:

[83:DefineShape4] ID: 1
  FillStyles:
    [1] [SWFFillStyle] Type: 0 (solid), Color: 666666
  LineStyles:
    [1] [SWFLineStyle2] Width: 200, Color: ff0000
  ShapeRecords:
    [SWFShapeRecordStyleChange] MoveTo: 1800,1800, FillStyle0: 1, LineStyle: 1
    [SWFShapeRecordStraightEdge] Horizontal: -800
    [SWFShapeRecordStraightEdge] Vertical: -800
    [SWFShapeRecordStraightEdge] Horizontal: 800
    [SWFShapeRecordStraightEdge] Vertical: 800
    [SWFShapeRecordStyleChange] MoveTo: 2400,400, FillStyle0: 0, FillStyle1: 1
    [SWFShapeRecordStraightEdge] Vertical: 2000
    [SWFShapeRecordStraightEdge] Horizontal: -2000
    [SWFShapeRecordStraightEdge] Vertical: -2000
    [SWFShapeRecordStraightEdge] Horizontal: 2000
    [SWFShapeRecordEnd]

The shape records define the inner rectangle first, then the outer rectangle.

It is important to note that in this example, both rectangles are “drawn” clockwise. The shape starts with the inner rectangle’s bottom right corner, then moves 40px left, 40px up, 40px right, and 40px down, back to the origin. It then moves to the right top corner of the outer rectangle, moves 100px down, 100px left, 100px up, and 100px right, back to the outer rectangle’s origin.

The interesting parts of this shape structure are the two StyleChange records and the FillStyle information they contain: FillStyle0 and FillStyle1 are 1-based indices into the array of FillStyles, defining the fills of the following (sub-)shapes. FillStyle0 defines the fill to the left side of an edge, FillStyle1 defines the fill to the right side of an edge.

Lets look at the first StyleChange record. It defines the style properties of the inner rectangle. FillStyle0 is set to 1 (our solid dark gray fill), FillStyle1 is missing and thus defaults to 0 (no fill). As the rectangle edges draw clockwise, FillStyle0 defines the fill on the outside of the rectangle. FillStyle1 defines the inside fill, which in our case is transparent.

Moving on to the second StyleChange record. It defines the style properties for the outer rectangle. FillStyle0 is reset to 0 (no fill), FillStyle1 is set to 1 (solid dark gray fill). The rectangle edges again draw clockwise, so FillStyle0 defines the outside fill (transparent), and FillStyle1 the inside fill (dark gray).

Now let’s add some more juice and look at a third, slightly more complex example.

Create a new FLA, select the Rectangle tool, choose a 10px red stroke and no fill, and draw two intersecting rectangles. Then use the Paint Bucket tool to fill each of the resulting three closed areas with a different color. I chose light gray, dark gray and black for this example:

Note how the intersecting edges are split in two. Fills and edges never overlap in individual SWF shapes. Ever.

The resulting SWF shape dump looks like this:

[83:DefineShape4] ID: 1
   FillStyles:
     [1] [SWFFillStyle] Type: 0 (solid), Color: 666666
     [2] [SWFFillStyle] Type: 0 (solid), Color: 999999
     [3] [SWFFillStyle] Type: 0 (solid), Color: 000000
   LineStyles:
     [1] [SWFLineStyle2] Width: 200, Color: ff0000
   ShapeRecords:
     [SWFShapeRecordStyleChange] MoveTo: 2400,1400, FillStyle1: 3, LineStyle: 1
     [SWFShapeRecordStraightEdge] Horizontal: 1000
     [SWFShapeRecordStraightEdge] Vertical: 2000
     [SWFShapeRecordStraightEdge] Horizontal: -2000
     [SWFShapeRecordStraightEdge] Vertical: -1000
     [SWFShapeRecordStyleChange] FillStyle1: 2
     [SWFShapeRecordStraightEdge] Horizontal: -1000
     [SWFShapeRecordStraightEdge] Vertical: -2000
     [SWFShapeRecordStraightEdge] Horizontal: 2000
     [SWFShapeRecordStraightEdge] Vertical: 1000
     [SWFShapeRecordStyleChange] FillStyle0: 3, FillStyle1: 1
     [SWFShapeRecordStraightEdge] Vertical: 1000
     [SWFShapeRecordStraightEdge] Horizontal: -1000
     [SWFShapeRecordStyleChange] FillStyle0: 2
     [SWFShapeRecordStraightEdge] Vertical: -1000
     [SWFShapeRecordStraightEdge] Horizontal: 1000
     [SWFShapeRecordEnd]

The first noteable difference to the previous two example are the FillStyle definitions. We use three different fills for this shape, so three FillStyles are defined in the header.

The first two sets of shape records define the outer edges of our shape (all edges are again drawn clockwise in this example). The first four edge records define the outer edges of the black shape (FillStyle1 is set to 3, referencing black for the inner fill), the following four edge records define the outer edges of the light gray shape (FillStyle1 is set to 2, referencing light gray for the inner fill).

The last two sets of edges are the interesting ones. They define the inner edges of our shape (the intersection area), whose fill is dark gray. And sure enough, FillStyle1 is set to 1, referencing the dark gray FillStyle for the inner fill. What’s different here is that FillStyle0 is also set, first to 3 (black), then to 2 (light gray), defining the outer fills for those edges (remember, we’re drawing clockwise, and FillStyle0 defines the fill to the left of the edge).

To recap: fills and edges in SWF shapes never intersect, and edges can have two fill styles. With these little tricks, SWF shapes save some file size, as each edge is only defined once.

The drawback is that raw SWF shapes need some tweaking in order to be redrawn using Actionscript and the Drawing API, which doesn’t support those concepts. Luckily, i did all the heavy lifting for you already in as3swf. The TagDefineShape class features a export() method, which dissects the shape in question and outputs code that is compatible with the AS3 Drawing API and Apple’s Quartz framework.

Draw fills first

Fills and strokes need to be drawn separately (fills first, strokes last). We focus on fills for now.

Let’s look at the third example again. In order to redraw that shape using the AS3 Drawing API, we need to draw three closed paths with three different fills. Now how do we get from the rather compressed SWF shape format to shapes that can be drawn using AS3?

Mike Swanson explains that in his blog post “Converting Flash Shapes to WPF” quite well.

We look at the FillStyle indices of each StyleChange record. If FillStyle1 is set to anything other than 0, we add the following edge records as is to our new edge list, along with the FillStyle info referenced by FillStyle1. If FillStyle0 is set to anything other than 0, we add the following edge records to the list in reversed order, along with the FillStyle info referenced by FillStyle0.

This way, if both FillStyle0 and FillStyle1 are set in a StyleChange record, we end up adding the edges that are following twice, once for each of the two fills. In our example, we end up with 16 edges instead of the 12 that are defined in the SWF.

All that’s left is sorting the edges by FillStyle, i.e.first draw edges with black fill, then draw edges with light gray fill, and finally draw edges with dark gray fill. Voilá, we got fill!

Draw strokes last

Last but not least we have to draw the strokes. This really got me puzzled for a while. To draw strokes in the correct order, we need to know how the Flash Player draws them internally, otherwise we might end up getting artefacts at end points.

Of course this is not documented anywhere, so we need to investigate. A very good start is the Flash IDE (i am using CS4 btw. Your mileage may vary if you use CS3 or older versions of the IDE). Create a new FLA, and draw some lines:

Select the Line tool, choose a 10px red stroke, and draw a horizontal line.

Then, choose a 20px blue stroke, and draw a vertical line that intersects the red line. Note how the blue line is drawn on top of the red line, as we would expect.

Now choose the 10px red stroke again, and draw a horizontal line that intersects the blue line. At first everything seems right, the second red line appears on top of the blue line, as we would expect. Until you deselect the lines by clicking somewhere on stage, that is. The red line moves below the blue line. WTF?

So let’s publish the SWF and take a look inside:

[83:DefineShape4] ID: 1
   LineStyles:
     [1] [SWFLineStyle2] Width: 200, Color: ff0000
     [2] [SWFLineStyle2] Width: 400, Color: 0000ff
   Shapes:
     [SWFShapeRecordStyleChange] MoveTo: 1000,1000, LineStyle: 2
     [SWFShapeRecordStraightEdge] Vertical: 800
     [SWFShapeRecordStyleChange] LineStyle: 1
     [SWFShapeRecordStraightEdge] Horizontal: 1400
     [SWFShapeRecordStyleChange] MoveTo: 400,1800
     [SWFShapeRecordStraightEdge] Horizontal: 600
     [SWFShapeRecordStyleChange] LineStyle: 2
     [SWFShapeRecordStraightEdge] Vertical: 600
     [SWFShapeRecordStyleChange] MoveTo: 1000,400
     [SWFShapeRecordStraightEdge] Vertical: 600
     [SWFShapeRecordStyleChange] LineStyle: 1
     [SWFShapeRecordStraightEdge] Horizontal: 1400
     [SWFShapeRecordStyleChange] MoveTo: 400,1000
     [SWFShapeRecordStraightEdge] Horizontal: 600
     [SWFShapeRecordEnd]

Examining the shape records, we find that the order of edges is totally messed up. There is no way to find out what is drawn first from the order of the edges in a SWF. Apparently, the Flash Player uses some other way of sorting stroked edges before it draws them.

The Flash IDE gave us a clue though. Lines with the same LineStyle seem to be drawn on the same layer. This is confirmed by the order of LineStyles in the SWF: the first LineStyle is the 10px red stroke (drawn first, thus appearing below everything else), the second LineStyle is the 20px blue stroke (drawn last, thus appearing in front). This suggests that stroked edges are sorted by LineStyle index.

Sure enough, this seems to be the case. We thus need to sort edges by LineStyle index and draw them in that order.

BAZINGA!

We now have knowledge about all the theory to redraw SWF shapes in AS3, Objective-C or any other language/platform, and as3swf provides us with the implementation.

Go create!

I’ll leave you with the Drawing API code for the fancy côdeazur logo, generated directly from SWF:

// Fills:
graphics.beginFill(0x000000);
graphics.moveTo(132.200000, 38.200000);
graphics.curveTo(146.150000, 44.950000, 156.050000, 56.950000);
graphics.curveTo(146.450000, 70.000000, 143.150000, 85.600000);
graphics.curveTo(138.200000, 73.000000, 127.100000, 65.200000);
graphics.curveTo(115.550000, 57.250000, 101.600000, 57.250000);
graphics.curveTo(83.150000, 57.250000, 70.100000, 70.300000);
graphics.curveTo(57.050000, 83.350000, 57.050000, 101.800000);
graphics.curveTo(57.050000, 120.250000, 70.100000, 133.300000);
graphics.curveTo(83.150000, 146.350000, 101.600000, 146.350000);
graphics.curveTo(115.550000, 146.350000, 127.100000, 138.400000);
graphics.curveTo(138.200000, 130.600000, 143.150000, 118.000000);
graphics.curveTo(146.450000, 133.600000, 156.050000, 146.650000);
graphics.curveTo(146.150000, 158.650000, 132.200000, 165.400000);
graphics.curveTo(117.800000, 172.300000, 101.600000, 172.300000);
graphics.curveTo(72.350000, 172.300000, 51.800000, 151.750000);
graphics.curveTo(31.100000, 131.050000, 31.100000, 101.800000);
graphics.curveTo(31.100000, 72.550000, 51.800000, 52.000000);
graphics.curveTo(72.350000, 31.300000, 101.600000, 31.300000);
graphics.curveTo(117.800000, 31.300000, 132.200000, 38.200000);
graphics.beginFill(0x86b9e1);
graphics.moveTo(128.750000, 74.650000);
graphics.curveTo(140.000000, 85.900000, 140.000000, 101.800000);
graphics.curveTo(140.000000, 117.850000, 128.750000, 128.950000);
graphics.curveTo(117.650000, 140.200000, 101.600000, 140.200000);
graphics.curveTo(85.550000, 140.200000, 74.300000, 128.950000);
graphics.curveTo(63.050000, 117.700000, 63.050000, 101.800000);
graphics.curveTo(63.050000, 85.900000, 74.300000, 74.650000);
graphics.curveTo(85.550000, 63.400000, 101.600000, 63.400000);
graphics.curveTo(117.650000, 63.400000, 128.750000, 74.650000);
graphics.moveTo(118.700000, 84.700000);
graphics.curveTo(111.650000, 77.500000, 101.600000, 77.500000);
graphics.curveTo(91.550000, 77.500000, 84.500000, 84.700000);
graphics.curveTo(77.300000, 91.750000, 77.300000, 101.800000);
graphics.curveTo(77.300000, 111.850000, 84.500000, 118.900000);
graphics.curveTo(91.550000, 126.100000, 101.600000, 126.100000);
graphics.curveTo(111.650000, 126.100000, 118.700000, 118.900000);
graphics.curveTo(125.900000, 111.850000, 125.900000, 101.800000);
graphics.curveTo(125.900000, 91.750000, 118.700000, 84.700000);
graphics.beginFill(0x000000);
graphics.moveTo(288.800000, 101.500000);
graphics.lineTo(288.800000, 172.600000);
graphics.lineTo(262.550000, 172.600000);
graphics.lineTo(262.700000, 101.650000);
graphics.curveTo(262.550000, 83.200000, 249.500000, 70.150000);
graphics.curveTo(236.450000, 57.100000, 218.000000, 57.100000);
graphics.curveTo(199.550000, 57.100000, 186.500000, 70.300000);
graphics.curveTo(173.300000, 83.350000, 173.300000, 101.800000);
graphics.curveTo(173.300000, 120.250000, 186.350000, 133.300000);
graphics.curveTo(199.400000, 146.350000, 217.850000, 146.500000);
graphics.lineTo(256.850000, 146.500000);
graphics.lineTo(256.850000, 172.600000);
graphics.lineTo(217.700000, 172.600000);
graphics.curveTo(188.450000, 172.450000, 167.900000, 151.750000);
graphics.curveTo(147.200000, 131.050000, 147.200000, 101.800000);
graphics.curveTo(147.200000, 72.400000, 167.900000, 51.700000);
graphics.curveTo(188.600000, 31.000000, 218.000000, 31.000000);
graphics.curveTo(247.250000, 31.000000, 267.950000, 51.700000);
graphics.curveTo(288.650000, 72.400000, 288.800000, 101.500000);
graphics.beginFill(0x86b9e1);
graphics.moveTo(242.600000, 101.950000);
graphics.lineTo(242.600000, 101.650000);
graphics.curveTo(242.450000, 91.450000, 235.250000, 84.400000);
graphics.curveTo(228.050000, 77.200000, 218.000000, 77.200000);
graphics.curveTo(207.950000, 77.200000, 200.600000, 84.400000);
graphics.curveTo(193.400000, 91.750000, 193.400000, 101.800000);
graphics.curveTo(193.400000, 111.850000, 200.600000, 119.050000);
graphics.curveTo(207.650000, 126.250000, 217.850000, 126.400000);
graphics.lineTo(242.450000, 126.400000);
graphics.lineTo(242.450000, 102.550000);
graphics.lineTo(242.600000, 101.950000);
graphics.moveTo(218.000000, 62.950000);
graphics.curveTo(234.050000, 62.950000, 245.450000, 74.200000);
graphics.curveTo(256.850000, 85.600000, 256.850000, 101.650000);
graphics.lineTo(256.850000, 140.650000);
graphics.lineTo(217.850000, 140.650000);
graphics.curveTo(201.800000, 140.650000, 190.400000, 129.250000);
graphics.curveTo(179.150000, 117.850000, 179.150000, 101.800000);
graphics.curveTo(179.150000, 85.750000, 190.550000, 74.350000);
graphics.curveTo(201.950000, 62.950000, 218.000000, 62.950000);
graphics.endFill();

44 thoughts on “Hacking SWF – Everything You Never Wanted To Know About Shapes In Flash

  1. Now that was a good read. Thanks for doing the work there Claus. Documentation should definitely be better on this subject. But with the FXP format some of it seems to be definitely more open, especially the bridge between IDE and raw data.

    That being said, the documentation on the swf format is still somewhat lacking and you certainly uncovered my eyes on the graphic part of it here.

  2. why do people still claim flash was an open format? the “documentation” adobe provides is a meager joke and pure alibi on their part. no wonder people long for real open standards.

  3. @ron: Who claims that? Did i? Where? Anyways, SWF is open as in “there is a spec”. And SWF is not open because the general public has no say in how it should evolve, and it is published after or at the same time the only significant implementation is published. Not the point of my post though, really.

    @mario, joa, philip, ralph: thanks!

  4. A couple of things:
    One thing are the SWF specifications and one thing is the SWF exported by the Flash IDE. The fact that in SWF shapes never have any overlapping edge is true ONLY for SWF generated by the Flash IDE. For example, a shape containing a stroked and filled circle overlapped by a smaller circle only stroked, will display a donut in the player (it applies the odd-even rule to overlapping edges), so presuming that it will never happen is wrong. It is always the IDE that forces same styles for strokes to be on the same level, but it is perfectly ok to have two or more stroke styles with same characteristics displayed at different levels. Priority between various styles (both fills and strokes) it determined by definition order; edges are not sorted, simply assigned to the index of the defined style. I guess a certain way of optimizing shapes is dated back when saving even a few bytes was significant. Regarding the comments about the format being closed, that’s not true. The format is documented, some areas better than others, but definitively with a little background you can understand what an SWF is made of; specs like this are not designed to be understood by general public, if somebody can’t read it should question his/her own base knowledge, not the spec itself. A couple of resources from AntiGrain can also help to decode SWf directly in C++ (and thus linking it to Obj-C) http://www.antigrain.com/demo/flash_rasterizer.cpp.html and http://www.antigrain.com/demo/flash_rasterizer2.cpp.html

  5. @emanuele: Displaying same stroke styles on different levels is of course possible by redefining the LineStyle in question further down the shape structure with a StyleChange record. That’s what happens when you layer your shape manually in the IDE.

    The fact that in SWF shapes never have any overlapping edge is always true. Even when you layer them in the IDE, edges never overlap in the resulting SWF structures.

    I couldn’t reproduce your circle example. Care to post an example somewhere?

  6. Pingback: *drawlogic » AS3SWF Tool and Flash Shapes Under the Hood - interactive and game development technologies for the web - flash, flex, unity3d, silverlight, javascript [ draw.logic ] » AS3SWF Tool and Flash Shapes Under the Hood » AS3SWF To

  7. Great post Claus, very well written and both cool and useful! I have been using cocos2d alot in iPhone but it would be great if there was some way to better translate and port graphics to the iphone (obj-c or c++ using a obj-c wrapper). Looks like this is a great start on that.

  8. Nice Post! I wish i found this earlier. Im working on Swiff JS, a swf parser and movie player written in pure JavaScript, based on dojo and dojox.gfx. So i had the same challenge and troubles found some useful information or documentation about the way flash defines and draw shapes. Mike Swanson’s Post was very useful, also Steve Probets’ “SWF and SVG” (http://www.eprg.org/research/SVG/flash2svg/).

  9. Claus, I have specified that only the swf coming out from the Flash IDE are always non-intersecting. But if you use any other system (like the Ming library for example) to synthesize the swf, it is perfectly ok to output shapes with overlapping edges and Flash Player renders them without any problem.

  10. @emanuele: ah that’s interesting, i have to investigate that. do you happen to have a SWF created by Ming handy that has intersecting shapes?

  11. Claus, nice post!
    Well, my brain is a mess right now.
    I just can’t see how flash developers got there. Don’t know if it’s sort of “evolutive” confusion, since first versions of flash player, simply a structural limitation, or if this can improve performance in any way.

    @emanuele: I guess visual overlapping of edges was always possible, from any generator, but, if flash player really behaves like Mike Swanson said, it’s possible that Ming separates those shapes, or does it’s magic to generate non-overlapping structure in the final SWF.

  12. @vFragosop: The SWF spec certainly doesn’t forbid intersecting edges. It is well possible that the Flash IDE behavior is historically motivated. I guess only a Flash Player Engineer can answer this question for sure though.

  13. Pingback: Even More Accessibility 4 Flash… | Cool websites that do everything.

  14. Pingback: “Сырой” флеш – шейпы изнутри « Flash Team

  15. Thomas, i am not sure but here’s what i think is happening: At least in SWF, shape coordinates are stored as integers with a precision of a 1/20 pixel. Thus if you make your shape really really small, you likely run into rounding errors.

  16. Hi Claus, great post and great wok on as3swf. I’m now using it and it really helps me. I have one question not related to shapes by SymbolClass. I want to create SWF on the fly which add images into swf with proper SymbolClass. I’m using swfAssist for generating it and your as3swf for comparing my generated SWF and swf from FLASH IDE to check for differences. Now i’m able to add SymbloClass to swf, but i’m not able to get that image with loaderInfo.applicationDomain.getDefinition. Only difference is line [82:DoABC] Lazy: true, Length: 185 between [35:DefineBitsJPEG3] and [76:SymbolClass], which is probably generating class for that image. Do you know if there is way to create such proper symbolClass to be able to load that asset later from swf? I hope I was clear what I want to achieve.

    franto

  17. Franto, yes, in order to export an image, you need a BitmapData subclass in your SWF. SymbolClass makes the connection between that class and the image character. I have been working on exactly that (as3abc to the rescue), but i’m not there yet.

    I discuss similar things (just with sound) here.

  18. Pingback: Opening The Blackbox « blog.joa-ebert.com – Blog of Joa Ebert

  19. Pingback: Alexander Zats » Blog Archive » Opening The Blackbox

  20. I looked into this myself, and what you described helped a lot, but im having problems, when i view an swf exported from flash in a decompiler, it has a shape bounds tags.
    Now all the values in the bounds tag are negative which is baffling, can anyone tell me why/how a shape defined on stage has negative values?

    Is what i view when my swf is decompiled. The shape is a rectangle 200* 200 placed at X20, Y20. And these bounds tags are just crazyily unreadable.

  21. oops, values got cut out for being xml,
    left=”-4129″ right=”-1509″ top=”-1650″ bottom=”150″
    is what is displayed :)

  22. trishtren, those values are most likely “twips”, not pixels. One twip is 1/20th pixel, so you have to divide them by 20 to get to the pixel values. In your case that would be a shape of size 131×90, positioned at -206.45/-75.45. Negative values are of course possible.

  23. hmmm hugely helpful thanks, but even in twips, i do not understand why the flash ide is using negative values at all. Im placing a shape defined in the library onto the stage directly. Surely its coordinates and bounds are positive values and not negative. Your tutorial and advice is helping a lot but its just the methodology of the flash exporter thats losing me. Id expect negative values only if placed outside of the movies width and height coordinates?

  24. The shape symbol you are placing on stage is a container that contains the actual shape. But you are looking at the shape, and not the container. So even though the container (shape symbol) is placed within the movie’s width/height, the contained shape may have negative coordinates.

  25. now that makes sense, its a shame adobe didn’t seem to document this fact very well in their specifications. I spent days scrounging around trying to piece bits together to solve this enigma. Dam you adobe and your poor poor representational document skills.

  26. Cool library. I just got it and started to play around.. and I got a question. Is it possible to apply scaling, change x,y, rotation of a movieclip at runtime and then at a certain point export this particular movieclip with all changes applied? Would it be like making a snapshot of whats on stage and generating swf from that?
    Thanks :)

  27. @fjoceklis – Yes that would be possible. You could write a “controller” SWF that loads in the SWF in question and manipulates contained MovieClips. You then read the transformation properties of those MovieClips and apply them to the respective PlaceObject Tags in the child SWF (parsed by as3swf).

  28. The IDE will actually output some self-intersecting edges in cases. Take a look at this SWF:

    SWF: http://www.newgrounds.com/dump/item/a0549599ce4428a86027a3980a68131e
    FLA (CS6): http://www.newgrounds.com/dump/item/4b0c465d9d850415aa9fec866ca1f633

    If you zoom way in on the SWF, you can see the artifact in between the shades of gray. Introspecting the shape data, you can see the self-intersecting edge: (purple and blue lines in the diagram)

    http://www.newgrounds.com/dump/item/4f4f9265db4bb829d59fb30fb495ccf4

    This causes the fill-style data to be incorrect — that is, left = FillStyle0 and right = FillStyle1 is no longer always true. This can send some SWF rasterizing/triangulation code into fits!

    Interestingly, fiddling with the shape (even moving it a bit) causes Flash to export the vectors differently and removes the self-intersection.

  29. @Mike – Wow that’s interesting. How did you create that shape? Did you just draw on stage, or did you import it into the IDE from somewhere?

  30. It’s from some art that an artist drew directly in the IDE — I had chopped it down to the problematic area. Here’s full artwork for two cases:

    http://www.newgrounds.com/dump/item/bc3817a2103fd7eaab04c7043d11d293

    Intersecting edges appear in the left thumbstick and in the knight’s green belt.

    This seems to be an occasional bug/precision issue in the way Flash compiles art. It is generally smart enough to avoid intersecting edges, but every once in a while, it will screw up. You’ll definitely see it occur with some regularity in complex art!

    Flash seems to handle these cases gracefully, so I’m wondering: When the SWF loads, does it do a pre-pass over the art to “fix” these intersections? How does Flash decide which FillStyle to use in these cases?

    Take care,
    Mike

  31. Hi Claus Wahlers,

    Great job, this is very useful work; I appreciate you….!
    Sorry for my poor English. I am using this nice library now, I have a question that; How to convert the shape object to “TagDefineShape” on the fly? I create an object of shape, draw something on it and want to export this shape to svg. Any help will be appreciated…!

    Regard: Rana Yasir

Comments are closed.