So… remember back in September of 2011 when Microsoft declared that Flash Player and other “plugins” would be barred from Internet Explorer in the Modern UI version of the browser?
Remember in May of 2012 when Microsoft relented and in place of an all-out ban, devised a troublesome whitelist which developers had to submit requests to Microsoft to be included upon? What a mess, right?
Well, starting tomorrow – the whitelist becomes a blacklist – essentially freeing up almost all Flash Player content on the web when viewed in Internet Explorer 10 on all platforms which support IE10.
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:
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;
}
}
}
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!
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.
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:
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);
}
}
}
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!):
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.
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);
}
}
}