Tag Archives: ActionScript

Notes around Starling and the Display List

I learned a couple of neat things while working on TelemetryEASY that have to do with some limitations of Starling and also communication between Starling and the traditional display list. You might wonder… why bother using Stage3D frameworks at all for a simple utility app? Mostly to increase my familiarity with Starling and especially to discover some of the little trouble points I’m writing about here, along with some solutions.

Problem #1: NativeDragManager

TelemtryEASY allows for a user to drag and drop files onto the utility app from the desktop or file system. Using AIR, this is pretty easy using flash.desktop.NativeDragManager. The problem comes into play when you are using a Stage3D rendering view like Starling though; because when using the acceptDragDrop() method, you must pass in an object which extends flash.display.InteractiveObject… and there seems to be no equivalent for Starling.

So what I ended up doing was mixing the display list and Starling and using a normal Flash Sprite to accept the drag and drop procedure. Now, you are not supposed to mix Stage3D and the display list for performance reasons… but nothing much is really going on here – so it is  suitable concession.

private function addedToStage(e:Event):void
{
	this.removeEventListener(Event.ADDED_TO_STAGE, addedToStage);
	
	_dragTarget = new Sprite();
	_dragTarget.graphics.beginFill(0x00000, 0);
	_dragTarget.graphics.drawRect(0,0,stage.stageWidth,stage.stageWidth);
	_dragTarget.graphics.endFill();
	this.addChild(_dragTarget);
	_dragTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_ENTER, onDragEnter);
	_dragTarget.addEventListener(NativeDragEvent.NATIVE_DRAG_DROP, onDragDrop);
			
	if(this.stage)
	{
		this.stage.align = StageAlign.TOP_LEFT;
		this.stage.scaleMode = StageScaleMode.NO_SCALE;
	}
			
	DeviceCapabilities.dpi = 200;
	DeviceCapabilities.screenPixelWidth = stage.stageWidth;
	DeviceCapabilities.screenPixelHeight = stage.stageHeight;
			
	this.loaderInfo.addEventListener(Event.COMPLETE, loaderInfo_completeHandler);
}

So we have a basic Sprite serving as a drop target overlaying the Starling render view. Problem solved.

Problem #2: Stage3D Communication

The second problem was also a bit tricky and I’m sure there is probably more than one way to handle this. I need to be able to pass a modified string variable from my document class (where all of the processing is occurring) down through my Starling classes in order to update a status message view (Feathers component) being rendered deep within the application.

To accomplish this, I created a small method within the document class which simply accepts string values to pass on through to the primary Starling class defined when we instantiate Starling itself. This was probably the most tricky bit to the entire process; figuring out how to access a member of that main Starling class, com.fracturedvisionmedia.telemetryEASY.Main, from the document class root.

private function updateMessage(s:String):void
{
	(_starling.stage.getChildAt(1) as Main).message = s;
}

We can then invoke this updateMessage() method from wherever we want in the process. In this instance, I’m invoking it whenever the native process (Python) exits in order to let the user know what’s going on.

private function onExit(e:NativeProcessExitEvent):void {
	if(e.exitCode == 0){
		updateMessage("Telemetry Tag Added!\n\nHooray :D");
	}else if(e.exitCode == 1){
		updateMessage("Looks like Telemetry is already enabled.\n\nGood for you!");
	}else{
		updateMessage("I AM ERROR");
	}
}

Within the main Starling class, com.fracturedvisionmedia.telemetryEASY.Main, we create a small public variable (or method, if you prefer that instead) to retain any changes fed in through the document class. This variable will hold the new string values to be drawn from by other Starling classes.

public var message:String;

The final piece (in this case) is to have a listener which runs every frame to update the text view by polling the root Starling class, com.fracturedvisionmedia.telemetryEASY.screens.MainMenuScreen, for changes fed in through the document class.

override protected function initialize():void
{
	status = new ScrollText();
	status.text = "Drag a SWF here to enable advanced telemetry.";
	status.addEventListener(Event.ENTER_FRAME, checkMessage);
	this.addChild(this._status);
	status.textFormat = new TextFormat("Arial", 14, 0xffb400, true, false, false, null, null, "center");
}

private function checkMessage():void
{
	status.text = (this.root as Main).message;
}

This enables any updates from the traditional Flash side of things to be pickup up on by the Starling classes and have appropriate modifications rendered on screen!

AS3 Quickie – drawWithQuality()

Since Flash Player 9, we’ve been able to use the BitmapData.draw() method in order to capture visual data from a display object. The major limitation of using this method, is that it will render the visual at the stage quality with which the swf has been embedded/compiled. One trick to get around this is to switch the stage quality on the fly, via ActionScript – yet this is not a supported workflow. With Flash Player 11.3 we have a new method with which to render drawn visuals at any desited stage quality: BitmapData.drawWithQuality().

In the example below, the swf is embedded with a stage quality of “medium”. Clicking on the top square will invoke the regular draw() command which uses the default stage quality to render the data. The second square will employ drawWithQuality() and render with the stage quality set to “low”, while the bottom square will do the same – but use stage quality “best” to render the image. Essentially; medium, low, and best – from top to bottom.


Requires Flash Player 11.3 or above!

Go ahead and toggle between each quality setting to see the differences. I’ve rendered the resulting BitmapData scaled nearly 2x the original size and with a white background to further accentuate the quality differences in the image. Also, a variety of display elements exist within the MovieClip symbol itself for further illustration: shapes, images, filters, et cetera.

To accomplish this, we must first perform the necessary imports:

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.StageQuality;

We need to declare a Bitmap instance to display the BitmapData gathered from the stage using either the draw() or drawWithQuality() methods of the BitmapData class:

private var savaBitmap:Bitmap;

..and add it to the display list:

savaBitmap = new Bitmap();
addChild(savaBitmap);

With the Bitmap instance in place and a MovieClip instance already on stage- we simply need to craete a new BitmapData instance to hold our data, invoke drawWithQuality() using the desired stage quality, and then apply that data to our existing Bitmap on stage:

var qualityBitmapData:BitmapData = new BitmapData(savanovic.width, savanovic.height);
qualityBitmapData.drawWithQuality(savanovic, null, null, null, null, false, StageQuality.BEST);
savaBitmap.bitmapData = qualityBitmapData;

We can substitute any stage quality we wish in place of that last argument. Here is a quick list of those which are available through flash.display.StageQuality:

  • StageQuality.BEST
  • StageQuality.HIGH
  • StageQuality.HIGH_16X16
  • StageQuality.HIGH_16X16_LINEAR
  • StageQuality.HIGH_8X8
  • StageQuality.HIGH_8X8_LINEAR
  • StageQuality.MEDIUM
  • StageQuality.LOW

The full code is below. Note that the FLA contains upon the Stage a MovieClip with the instance ID of savanovic from which we are gathering the visual data:

package  {
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.StageQuality;
	import flash.events.MouseEvent;
	import flash.events.Event;
	import flash.geom.Matrix;

	public class Main extends Sprite {
		public var savanovic:MovieClip;

		private var savaBitmap:Bitmap;
		private var normalBitmapData:BitmapData;
		private var yuckyBitmapData:BitmapData;
		private var qualityBitmapData:BitmapData;

		public function Main() {
			this.addEventListener(Event.ADDED_TO_STAGE, init);
		}

		protected function init(e:Event):void {
			savaBitmap = new Bitmap();
			savaBitmap.x = 45;
			savaBitmap.y = 10;
			addChild(savaBitmap);

			drawData();
		}

		protected function drawData():void {
			var scaleFactor:Number = 1.7;
			var savaMatrix:Matrix = new Matrix();
			savaMatrix.scale(scaleFactor, scaleFactor);

			normalBitmapData = new BitmapData(savanovic.width*scaleFactor, savanovic.height*scaleFactor);
			normalBitmapData.draw(savanovic,savaMatrix);
			yuckyBitmapData = new BitmapData(savanovic.width*scaleFactor, savanovic.height*scaleFactor);
			yuckyBitmapData.drawWithQuality(savanovic, savaMatrix, null, null, null, false, StageQuality.LOW);
			qualityBitmapData = new BitmapData(savanovic.width*scaleFactor, savanovic.height*scaleFactor);
			qualityBitmapData.drawWithQuality(savanovic, savaMatrix, null, null, null, false, StageQuality.BEST);

			renderButtons();
		}

		protected function renderButtons():void {
			var b1:Sprite = new Sprite();
			b1.graphics.beginFill(0x000000, 0.7);
			b1.graphics.drawRect(0,0,25,25);
			b1.graphics.endFill();
			b1.addEventListener(MouseEvent.CLICK, drawNormalPixels);
			b1.x = 10;
			b1.y = 10;
			addChild(b1);

			var b2:Sprite = new Sprite();
			b2.graphics.beginFill(0x000000, 0.7);
			b2.graphics.drawRect(0,0,25,25);
			b2.graphics.endFill();
			b2.addEventListener(MouseEvent.CLICK, drawYuckyPixels);
			b2.x = 10;
			b2.y = b1.y + b1.height + 10;
			addChild(b2);

			var b3:Sprite = new Sprite();
			b3.graphics.beginFill(0x000000, 0.7);
			b3.graphics.drawRect(0,0,25,25);
			b3.graphics.endFill();
			b3.addEventListener(MouseEvent.CLICK, drawQualityPixels);
			b3.x = 10;
			b3.y = b2.y + b2.height + 10;
			addChild(b3);
		}

		protected function drawNormalPixels(e:MouseEvent):void {
			savaBitmap.bitmapData = normalBitmapData;
		}

		protected function drawYuckyPixels(e:MouseEvent):void {
			savaBitmap.bitmapData = yuckyBitmapData;
		}

		protected function drawQualityPixels(e:MouseEvent):void {
			savaBitmap.bitmapData = qualityBitmapData;
		}

	}

}

Want the full package? Download below:
Packaged code and Flash Professional CS6 project



This Quickie has been brought to you by the rampaging Serbian vampire Sava Savanovic. Let’s hope they fix his watermill soon so that he can get some rest!

Flash Builder 4.7 and Adobe Scout 1.0 Released

Two new major Flash Platform releases have occurred: a new version of the Flash Builder development tool and a brand new product: Adobe Scout! This is part of the larger Adobe Game Developer Tools initiative – part of the Creative Cloud.

Flash Builder 4.7
After two beta releases on Adobe Labs, Flash Builder 4.7 has now been released!
This release introduces new development features and enhancements to Flash Builder and provides support for the new Apache Flex SDK and the new ActionScript Compiler 2.0.

So what are the new features in this version of Flash Builder?

  • Flash Builder is now a 64-bit application
  • Support for the Apache Flex 4.8 SDK
  • Support for the Adobe Scout 1.0 product
  • New ASC 2.0 compiler support for ActionScript Projects
  • Support for creating and managing ActionScript Workers
  • Configuring multiple build targets for multi-screen projects
  • Support for customizing ADT and ADL parameters
  • Support for creating ActionScript Library Projects
  • Real time error highlighting using the ASC 2.0 compiler
  • Enhanced Developer Productivity Features including new Quick Assists
  • Advanced support for iOS deploy/test/debug/sim

Note that Design View, as previously indicated, no longer exists in Flash Builder 4.7. If Design View is desired, you will probably want to use Flash Builder 4.6. Note that both versions can be installed in parallel!

Adobe Scout 1.0
Some of you may be confused about the name… “what is Scout?”… well, Scout is the 1.0 product name of the former Project ‘Monocle’. It’s out now, as part of the Creative Cloud Game Developer Tools. Not only that, but much like Edge Animate – the full version of Scout is FREE for a limited time. After Scout transitions to a paid offering, it will still be available for free with advanced telemetry features disabled.

Adobe Scout is the next-­generation profiler for Flash content running on both the desktop and on mobile devices. It gives you insight into the behavior of your Flash content that simply wasn’t possible in the past.

Scout relies on the new telemetry feature, which was introduced in Flash Player 11.4 and AIR 3.4. This feature gathers detailed information about the internals of the Flash runtime, as well as the ActionScript that it executes, and sends it all to Scout. Scout presents this data clearly, concisely, and graphically, so that you can quickly diagnose performance problems with your content.

To get detailed telemetry data to appear, a project must be compiled in such a way to enable this feature. This can be easily done with Flash Builder 4.7, or through other, less-supported means. Even without advanced telemetry enabled, there is still a good deal of information which can be gathered through the use of Scout.

AS3 Quickie – Event.VIDEO_FRAME and Camera.drawToBitmapData()

In the past, if we wanted to copy some data from a camera object to a bitmap, we would need to draw the data from the display object using flash.display.BitmapData.draw() and then manipulate it in some way. This is problematic at times… if there is no way of knowing whether we have valid bitmap data to draw from!

Using Flash Player 11.4, we have a number of alternatives to this workflow which allow us to both listen for an event to fire once a frame is available to have its data harvested, and a number of methods from retrieving the frame data from the camera object in order to manipulate it.

The example below demonstrates this through the capture of bitmap data through a Camera object. This data is then sent directly to a Bitmap object on the display list, enabling a sort of picture-in-picture view of the camera feed.

If you have a camera attached to whatever you are using to read this, try out the demo below. You will need to be sure an allow camera and microphone access for Flash Player.


Requires Flash Player 11.4 or above!

To get this working only takes a few steps in your ActionScript code. The main classes we need to work with are flash.events.Event to listen for a video frame event, and flash.media.Camera to grab a local camera object to attach the event listener to. For any of this to work, you must target Flash Player 11.4 or above.

Now… this tutorial SORT OF builds upon the Quickie which demos how to attach a Camera to StageVideo – so we won’t be covering those bits, exactly… have a look at that demo if you need a refresher for this area.

In ActionScript, we must perform the necessary imports:

import flash.events.Event;
import flash.media.Camera;
import flash.display.Bitmap;
import flash.display.BitmapData;

The first thing we do is to assign an event listener to our Camera instance. To do this, it is safest to wait until our object has been added to the stage, and then add an event listener to check for flash.events.Event.VIDEO_FRAME.

camera = Camera.getCamera();
camera.setMode(512, 384, 24);
camera.addEventListener(Event.VIDEO_FRAME, onVideoFrame);

The second bit is to listen for a flash.events.Event.VIDEO_FRAME to fire; at which time we can feel safe that the camera bitmap data is available for us to use in some way. In this example, we are invoking Camera.drawToBitmapData() to render the video frame to a bitmap object on the stage. We could alternatively use Camera.copyToByteArray() or Camera.copyToVector() as well!

protected function onVideoFrame(e:Event):void {
	camera.drawToBitmapData(bmd);
}

The full code is below. Note that the FLA contains upon the Stage a TextField with the instance ID of statusField:

package  {
	import flash.display.MovieClip;
	import flash.text.TextField;
	import flash.media.StageVideo;
	import flash.media.StageVideoAvailability;
	import flash.media.Camera;
	import flash.events.Event;
	import flash.events.StageVideoAvailabilityEvent;
	import flash.events.StageVideoEvent;
	import flash.geom.Rectangle;
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	
	public class Main extends MovieClip {
		
		public var statusField:TextField;
		
		private var camera:Camera;
		private var stageVideo:StageVideo;
		private var bmd:BitmapData;
		private var bm:Bitmap;
		
		public function Main() {
			this.addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		protected function init(e:Event):void {
			stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, availabilityChanged);
		}
		
		protected function availabilityChanged(e:StageVideoAvailabilityEvent):void {
			statusField.appendText("StageVideo => " + e.availability + "\n");
			if(e.availability == StageVideoAvailability.AVAILABLE){
				stageVideo = stage.stageVideos[0];
				bmd = new BitmapData(512, 384);
				bm = new Bitmap(bmd);
				bm.scaleX = 0.25;
				bm.scaleY = 0.25;
				bm.x = stage.stageWidth-bm.width;
				bm.y = stage.stageHeight-bm.height;
				addChild(bm);
				attachCamera();
			}
		}
		
		protected function attachCamera():void {
			statusField.appendText("Camera.isSupported => " + Camera.isSupported + "\n");
			if(Camera.isSupported){
				camera = Camera.getCamera();
				camera.setMode(512, 384, 24);
				camera.addEventListener(Event.VIDEO_FRAME, onVideoFrame);
				stageVideo.addEventListener(StageVideoEvent.RENDER_STATE, onRenderState);
				stageVideo.attachCamera(camera);
			}
		}
		
		protected function onRenderState(e:StageVideoEvent):void {
			stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
		}
		
		protected function onVideoFrame(e:Event):void {
			camera.drawToBitmapData(bmd);
		}
		
	}
	
}

Want the full package? Download below:
Packaged code and Flash Professional CS6 project

AS3 Quickie – Attach a Camera to StageVideo

In the past, we’ve been able to attach a local camera to Video display objects within Flash Player with relative ease. The flash.media.Video object, of course, is part of the traditional display list and is not accelerated by the system GPU whatsoever. Using the newer flash.media.StageVideo object, we can implement a GPU-accelerated video display beneath the Flash display list… but previous to Flash Player 11.4 we haven’t been able to attach a camera to StageVideo as we could with Video. Thankfully, now we can!

If you have a camera attached to whatever you are using to read this, try out the demo below. You will need to be sure an allow camera and microphone access for Flash Player. Trust me – I’m not capturing any data through this :)


Requires Flash Player 11.4 or above!

To get this working only takes a few steps in your ActionScript code. The main classes we need to work with are flash.media.StageVideo for the GPU-accelerated video, and flash.media.Camera to grab a local camera object to feed an image to our video. For any of this to work, you must target Flash Player 11.4 or above.

In ActionScript, we must perform the necessary imports (and there are a few!):

import flash.media.StageVideo;
import flash.media.StageVideoAvailability;
import flash.media.Camera;
import flash.events.StageVideoAvailabilityEvent;
import flash.events.StageVideoEvent;
import flash.geom.Rectangle;

The first thing we do is to set up our StageVideo instance. To do this, it is safest to wait until our class has been added to the Stage, and then add an event listener to check for flash.events.StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY.

stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, availabilityChanged);

Once the StageVideo availability changes, we can then set out StageVideo instance to stage.stageVideos[0], assigning a reference to a variable in ActionScript.

stageVideo = stage.stageVideos[0];

Now we need to grab a flash.media.Camera object and attach that Camera to our StageVideo.

camera = Camera.getCamera();
stageVideo.attachCamera(camera);

The final task is to listen for a flash.events.StageVideoEvent.RENDER_STATE to fire, at which time we can properly size our StageVideo. We absolutely need to do this since StageVideo will initially have a width and height of 0.

stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);

The full code is below. Note that the FLA contains upon the Stage a TextField with the instance ID of statusField:

package  {
	import flash.display.MovieClip;
	import flash.text.TextField;
	import flash.media.StageVideo;
	import flash.media.StageVideoAvailability;
	import flash.media.Camera;
	import flash.events.Event;
	import flash.events.StageVideoAvailabilityEvent;
	import flash.events.StageVideoEvent;
	import flash.geom.Rectangle;
	
	public class Main extends MovieClip {
		
		public var statusField:TextField;
		
		private var camera:Camera;
		private var stageVideo:StageVideo;
		
		public function Main() {
			this.addEventListener(Event.ADDED_TO_STAGE, init);
		}
		
		
		protected function init(e:Event):void {
			stage.addEventListener(StageVideoAvailabilityEvent.STAGE_VIDEO_AVAILABILITY, availabilityChanged);
		}
		
		protected function availabilityChanged(e:StageVideoAvailabilityEvent):void {
			statusField.appendText("StageVideo => " + e.availability + "\n");
			if(e.availability == StageVideoAvailability.AVAILABLE){
				stageVideo = stage.stageVideos[0];
				attachCamera();
			}
		}
		
		protected function attachCamera():void {
			statusField.appendText("Camera.isSupported => " + Camera.isSupported + "\n");
			if(Camera.isSupported){
				camera = Camera.getCamera();
				stageVideo.addEventListener(StageVideoEvent.RENDER_STATE, onRenderState);
				stageVideo.attachCamera(camera);
			}
		}
		
		protected function onRenderState(e:StageVideoEvent):void {
			stageVideo.viewPort = new Rectangle(0, 0, stage.stageWidth, stage.stageHeight);
		}
		
	}
	
}

Want the full package? Download below:
Packaged code and Flash Professional CS6 project