Hacking SWF – PlaceObject and the Ratio Field

According to the SWF10 specification, PlaceObject2

.. can both add a character to the display list, and modify the attributes of a character that is already on the display list.

The placed character is usually defined earlier in the SWF, and can be anything supported by SWF, e.g., Shape, MorphShape, Sprite, Text, EditText etc. It stays on the display list until it is explicitly removed by the RemoveObject tag. PlaceObject2 might also tell the Flash Player that the placed object is to be treated as a mask (and what depth range will be masked), and might give the character an instance name (if it is a Sprite).

Additionally, PlaceObject2 might carry an optional ratio parameter. According to the SWF10 specification, it

.. specifies a morph ratio for the character being added or modified. This field applies only to characters defined with DefineMorphShape, and controls how far the morph has progressed. A ratio of zero displays the character at the start of the morph. A ratio of 65535 displays the character at the end of the morph. For values between zero and 65535 Flash Player interpolates between the start and end shapes, and displays an in- between shape.

For Flash users, this is better known as a “Shape Tween”.

However, the statement that  “this field applies only to characters defined with DefineMorphShape” is incorrect. It also applies to Sprite characters (“MovieClips”).

The Flash Player uses the value of the Ratio field to determine whether or not to reset the playhead in the placed Sprite to frame 1, when jumping to arbitrary frames in the parent timeline.

To illustrate that, let’s create a simple Flash movie using the Flash IDE. We create a one-frame MovieClip (called “square”) containing a simple shape. We create another MovieClip (called “animatedSquare”), which contains “square”, animated by a motion tween over 20 frames. We place “animatedSquare” on the main timeline (one frame only). When the Flash Player executes the resulting SWF, we see “animatedSquare” looping over all its 20 frames, as we would expect.

Here are the guts of the resulting SWF (simplified). Nothing surprising in there:

Now, let’s make the main timeline 10 frames long (each frame containing “animatedSquare”). The behavior doesn’t change, “animatedSquare” still loops over all of its 20 frames as expected. Also, the SWF tags still don’t reveal anything surprising, 9 more ShowFrame tags were added:

Finally, let’s remove “animatedSquare” from frame 5, leaving it only on frames 1-4 and 6-10:

This is where things get interesting. The character at depth 1 (our “animatedSquare” Sprite) is removed after frame 4, frame 5 is displayed without any content, and then “animatedSquare” is placed back on depth 1. Only now, the PlaceObject2 tag carries a value (5) in the Ratio field. Now why is that?

If we let Flash Player execute this SWF, you will notice the following behavior:

  1. In frames 1-4, the first 4 frames of “animatedSquare” are displayed
  2. In frame 5, a blank frame is displayed
  3. In frames 6-10, the first 5 frames of “animatedSquare” are displayed
  4. The main timeline then loops and jumps back to frame 1, displaying the first frame of “animatedSquare” again. Back to 1. Etc.

If we would put a gotoAndPlay(6) action on frame 10 of the main timeline, “animatedSquare” would reset to frame 1 once, and then loop over all 20 frames infinitely.

So what happens here is that once the Flash Player encounters a PlaceObject tag that attempts to place a Sprite on a depth that was previously occupied by the same Sprite, it looks at the ratio fields of the current and previous PlaceObject tags. If both carry the same value, the child Sprite keeps on playing normally. If not, the child Sprite’s playhead is reset to frame 1.

There is a little more to it yet, so if you are interested in digging deeper you should take a look at the Gnash Wiki, which lists many cases.

ActionScript 3 Bitmap Tracer (Vectorizer): as3potrace

Ever wanted to convert a bitmap to vector shapes, at runtime, in Flash? Look no further. Let me introduce you to as3potrace, an ActionScript 3 library to trace bitmaps.

As the name suggests, this is a port of the well known C library potrace by Peter Selinger. To be more exact, it is a AS3 port of Vectorization, a C# port of potrace 1.8 by Wolfgang Nagl.

Like potrace, as3potrace is released under GPL. The SWC and source code are available on GitHub:

https://github.com/PowerflasherBR/as3potrace

Demo

[SWF]http://wahlers.com.br/claus/blog/wp-content/uploads/POTrace.swf, 475, 475[/SWF]

Demo source code: https://gist.github.com/940219 (Warning: ugly)

Usage example

A minimal example of how to trace a bitmap with as3potrace, and draw the result into a Shape:

Note that you can write your own backends to ease handling/processing of generated shapes. The one backend that i already wrote, GraphicsDataBackend, produces GraphicsData structures that you can immediately draw using Graphics.drawGraphicsData().

Alternatives

And as always, after i finished porting this little beauty, i found out that this has been done before by the amazing folks at LibSpark. Check out nitoyon’s PotrAs (also GPL’ed).

Mythbusting “HTML 5 Did Not Kill Flash”

So, i was thinking.. the Flash Player version number in the “HTML 5 Did Not Kill Flash” comic strip (258.1) … PLAUSIBLE?

It is obvious that we deal with Jean-Luc Picard, captain of the Starship USS Enterprise NCC-1701-D, which places the scene in the Star Trek TNG series. According to Wikipedia, Star Trek TNG is set in the 24th century from the year 2364 through 2370.

Today: 1285956121684 (Fri Oct 1 15:02:01 GMT-0300 2010)
Star Trek TNG start: 12433402800000 (Wed Jan 1 00:00:00 GMT-0300 2364)
Star Trek TNG end: 12654327600000 (Fri Jan 1 00:00:00 GMT-0300 2371)
Assumed 18 months release cycle, in ms: 47304000000
Release cycles until Star Trek start:
   12433402800000 - 1285956121684 = 11147446678316
   11147446678316 / 47304000000 = 236
   236 + 10 = 246
Release cycles until Star Trek end:
   12654327600000 - 1285956121684 = 11368371478316
   11368371478316 / 47304000000 = 240
   240 + 10 = 250

So assuming a strict 18 month release cycle, this places version 258.1 just about a decade or so after Star Trek TNG (versions 246-250) ends. If we further assume that new technologies will emerge in the coming 350 years that speed up release cycles a bit, i think we can safely say that Flash Player version 258.1 is indeed plausible.

Hypothetically speaking…

Flash IDE Inspired Flex 4 Timeline Component

Here’s a little demo of a Flex 4 component i wrote that displays a Flash IDE-like timeline for any SWF you load into it. The timeline data is reconstructed by as3swf (i discuss how that works in a previous blog post, SWF Timeline Reconstruction with as3swf). The timeline is not interactive in this demo, and only the root timeline is shown.

Continue reading

My Silly Gist Collection

I’ve been quite busy lately on both client and personal projects (can’t talk about the former, other than it being a big ass Flex 4 enterprise application i’ve been working on for the fine folks at Powerflasher; and it’s too early to talk about the latter.. stay tuned).

In the last few weeks i silently released random little experiments on Gist – mostly fallout from personal projects of mine. Nothing super impressive, all pretty rough, but i thought some of you might be interested.

Continue reading

SWF Timeline Reconstruction with as3swf

In case you havent heard of it yet, as3swf is an ActionScript 3 library to parse and publish SWF files. It does that rather well by now, providing full roundtrip publishing, plus some neat extra features like shape export to AS3 Drawing API, AS3 GraphicsObjects, FXG, and Objective-C.

Whenever i find some free time i’m working on adding new, useful features. One feature of as3swf, that i haven’t talked about much yet but is implemented for quite a while already, is the reconstruction of timelines as you know them from Adobe’s Flash IDEs.

In the first place, timelines in as3swf help you to export and render layered animations. Having a long list of parsed SWF tags won’t help much if you want to export or render frame X, as the Flash Player would display it.

Continue reading

“Flash is as open as HTML5” – No, it isn’t.

Lately, Adobe representatives and Flash fan boys alike became more vocal than usual about the alleged openness of Flash. This is probably spurred by the proposed feature set of HTML5, as well as the decisions of a certain vendor to ban Flash from some of their products, both potentially being threats to Adobe and the Flash Platform.

I originally posted the following article as comment to an article by Serge Jespers, “Flash is as open as HTML5“. Serge is an Adobe Platform Evangelist. I thought this comment deserves its own post, so here we go.

No, it isn’t.

And it is beyond me why so many independent Flash Platform developers fail to see it. I completely understand of course why Adobe evangelists downplay it.

It is irrelevant for the so called “open web” whether the Flash Player is going to get open sourced or not. That’s not the point. Microsoft won’t open source their browser, Opera won’t open source their browser, etc.

Relevant is who decides about the development of the data format that a runtime consumes, and the APIs a runtime provides to access that data. In the Flash world that’s SWF and the Flash Player APIs, both controlled by a single vendor: Adobe. In the HTML world that’s HTML and DOM, controlled by many vendors, including you and me, via standards bodies.

Adobe neither provides formal means for other companies and individuals to participate in the development of SWF and Flash Player APIs, nor does it provide detailed work-in-progress specs to the general public for discussion. This effectively rules out the possibility for third parties to provide alternative runtimes. The runtime and its specs are released to the public at the same time.

This is the exact opposite of “open”.

To make matters worse, the specs released by Adobe are incomplete and buggy (e.g. the SWF spec fails to explain how exactly shapes are supposed to be rendered and leaves out information on codecs, the ABC spec is plain wrong on some things) and generally infested with patented technologies.

Of course i understand why it is how it is (and likely always will be). If Adobe were to become truly open and put the development of SWF etc in the hands of standards bodies, the 1.5 year release cycle would become a 10+ years release cycle. Innovation would slow down significantly. I as a Flash Platform developer wouldn’t want that to happen.

However, sorry to say that, but to tout “Flash is as open as HTML5″ is pure FUD.

SWFtrospection

Open Flash CS4, create new AS3 FLA, add as3swf.swc and paste this on frame 1:

import com.codeazur.as3swf.SWF;
var swf:SWF = new SWF(root.loaderInfo.bytes);
trace(swf);

Trace output:

[SWF]
  Header:
    Version: 10
    FileLength: 149405
    FileLengthCompressed: 149405
    FrameSize: (550,400)
    FrameRate: 24
    FrameCount: 1
  Tags:
    [69:FileAttributes] AS3: true, HasMetadata: false, 
      UseDirectBlit: false, UseGPU: false, UseNetwork: false
    [09:SetBackgroundColor] Color: #FFFFFF
    [86:DefineSceneAndFrameLabelData] 
      Scenes:
        [0] Frame: 0, Name: Scene 1
    [82:DoABC] Lazy: true, Length: 149219
    [76:SymbolClass] 
      Symbols:
        [0] TagID: 0, Name: Untitled_fla.MainTimeline
    [01:ShowFrame] 
    [00:End]

Excercise: Draw something on stage, and run the code again.

Want more? Drop by my session Hacking SWF at FITC Amsterdam (Feb 22th, 16:00).

[Edit] Jim Cheng deserves credits. He was the one who whispered “root.loaderInfo.bytes” into my virtual ear on IM.

Custom Installer For Adobe AIR Applications

I need a custom installer for an AIR application i’m currently developing. That’s because my AIR app needs additional functionality that the AIR runtime doesn’t provide (specifically: detecting USB storage devices, act as a TCP socket server, talk to Last.fm scrobbler plugins). For that purpose i wrote a local RPC socket server gateway in C (one for Mac OS X and one for Windows) which always runs once the user logs in to her OS. The AIR application can then call methods on that local gateway, or receive events.

The problem is that the user needs to install the RPC server before she installs the actual AIR application. The install process should be seamless (one installer installs RPC server, AIR runtime if needed, and the application itself in one go) and the installer should be as small as possible. Currently there is no info available from Adobe on how to write custom installers that automatically download/install the AIR runtime if needed (is there?).

Artemis is another project aiming at extending AIR using a local socket server, but it seems that the project has been shut down because of the reasons stated above.

So i have been pulling out my hair lately on how to solve that problem.

I think i found a feasible solution. I am not sure because i haven’t tested all this, but i wanted to throw it online for discussion. The drawback is that the user needs to install your application with a OS native custom installer.

First you write standard installers for both Mac OS X and Windows, that install the local socket server either as a service/daemon or as an agent so that the server always starts at system launch or user login. Nothing special here yet.

The trick would be to write a simple SWHX application that basically implements the code included with the AIR Installer badge. That helper application can then be included with the installer, which executes it after the local socket server has been installed.

As i said i haven’t tested this yet (will do soonish), but this should work, no?

The question remains why i don’t just use SWHX for the main app and screw AIR alltogether.

FZip Alpha Release: Create And Modify ZIP Archives

FZip has been around for some time now, and people seem to like it. However one feature has been asked for repeatedly: In addition to reading ZIP archive, people want to be able to create new (and modify existing) archives.

So i finally sat down this weekend and added that.

The code is not tested very well (it works for me but may not work for you) and has no ASDocs yet, so i release it as an alpha version, with the hope of massive bug feedback.. :)

Download: fzip_1_0_52_alpha.zip fzip.zip

New methods in class FZip:

  • addFile(name:String, date:Date, content:ByteArray)
  • addFileAt(index:uint, name:String, date:Date, content:ByteArray)
  • removeFileAt(index:uint)
  • serialize(stream:IDataOutput)

Sample code:

// Create file contents
var ba:ByteArray = new ByteArray();
ba.writeUTFBytes("Hello World!");
// Create ZIP archive and add file
var zip:FZip = new FZip();
zip.addFile("hello.txt", null, ba);
// Serialize ZIP into a new file
// (we use the Adobe AIR specific class FileStream here,
// but you can as well use ByteArray or anything that
// implements IDataOutput)
var file:File = File.applicationStorageDirectory;
file = file.resolvePath("hello.zip");
var stream:FileStream = new FileStream();
stream.open(file, FileMode.WRITE);
zip.serialize(stream);
stream.close();