Scaling Nightmares in AIR

Scaling Nightmares in AIR

I spent way too long on this, hopefully someone else can benefit from my trials.

I’m currently developing a system for playback and control of live visuals using Ableton Live and Flash – more on the details of this project soon. One of the core needs is to have controls in one window and playback  in another, which is piped out to a projector.

So upon starting my Storyteller AIR application, I load controls into my main window, and spawn a secondary Utility window which holds all the movies for playback. These are all added to a single view object I call the PlayBox. The problem arises when the window is resized or full-screened. AIR doesn’t like using anything besides StageScaleMode.NO_SCALE, and Adobe’s documentation discourages using any other scaling mode.

Fighting the System

What I really want is StageScaleMode.SHOW_ALL, so that the whole of each movie will be shown, no matter the aspect ratio of the window, with black bars at the top or sides as necessary. Unfortunately, using this scale mode resulted in HUGE movies, of which I could see only a corner. I thought I’d hit the jackpot when I found this Adobe documentation which spelled out what was going on and how to fix it:

“…When you create a NativeWindow object, AIR chooses an arbitrary relationship between the window size and the scale factor of 72:1. Thus, if your window is 72×72 pixels, a 10×10 rectangle added to the window is drawn the correct size of 10×10 pixels. However, if the window is 144×144 pixels, then a 10×10 pixel rectangle is scaled to 20×20 pixels. If you insist on using a 
scaleMode

other than 
noScale

for a window stage, you can compensate by setting the scale factor of any display objects in the window to the ratio of 72 pixels to the current width and height of the stage. For example, the following code calculates the required scale factor for a display object named 
client

:

if(newWindow.stage.scaleMode != StageScaleMode.NO_SCALE){
client.scaleX = 72/newWindow.stage.stageWidth;
client.scaleY = 72/newWindow.stage.stageHeight;
}

Unfortunately, this never worked quite right. I applied this formula to my main PlayBox view object before adding any loaded movies to it, but it was never quite tall or wide enough.

Giving In

So I scrapped that idea (after, of course, hours of trying to get this work way too late at night when my brain was fried and wouldn’t give it). The new plan was to use NO_SCALE, and instead listen for window resizing and adjust the PlayBox size and position to match.

It was still not as straightforward as I thought. The idea was simple enough – listen for NativeWindowBoundsEvent.RESIZE, take a look at the new afterBounds or stageWidth, scale to match, and center. The problem was in centering the PlayBox. The formula should be:

view.x = (rect.width * 0.5) - (view.width * 0.5);

Where view is the PlayBox, and rect are the new bounds. I swear this was working earlier in development. The issue is that the view’s width does not directly correlate to anything I could see. If I were to take the bottom edge of the window and pull it straight down (making it taller), the view’s width would fluctuate in a Sine-like pattern. This means that I couldn’t do any calculations involving the width of the view.

The Answer

Ultimately, I devised a solution that works, though its reliance on constants isn’t ideal. I store two numbers for the original width and height of the window and stage – 640 and 480, in this case. These go in a Preferences model I’ll call prefs. On resize, I generate a newScale variable like this:

var newScale : Number = window.stage.stageHeight / prefs.playHeight;

Then I can apply it to the PlayBox:

view.scaleX = newScale;
view.scaleY = newScale;
view.x = (view.stage.stageWidth - (prefs.playWidth *  newScale)) * 0.5;

By taking the original playWidth variable and multiplying it by the new scale, I get a reliable width for the PlayBox. Then I subtract the width of the PlayBox from the width of the stage, which gives me the total number of ‘extra pixels’ I have. Divide this by two (aka multiply by 0.5) and we’ve got the number of pixels that should be to the left of the PlayBox in order to center it. Phew!

1 comment

  1. Posted by Mikronator, at Reply

    AHHHHHHHHHHH My brain was fried as well…. I just couldn't believe Adobe is that stupid……. Why ?!? Why would you change the stageWidth to the screen's but keep on acting by the original when we set the position ?! ARE YOU STUPID ADOBE ? AHHHHHH! at least you can save the original number for us in the stage object..naaah.

    Thanks for the post… although I got to it too late, after I've used your solution.. but still.. there isn't a lot of docs about this stupidity around the net. cheers.

Post your comment