ActionScript 3, Flash Player, Flash Player 10

BitmapData and Stage size limit

Short tips post 🙂

BitmapData size limit:
Up to flash 9 the size limit for a BitmapData object was 4096×4096 pixels.
With flash player 10 that limit was removed, but what does this exactly mean? May we be able to create 4097×4097 sized bitmapdata instances? the answer is NO, we can’t.
I just found an official explanation of that here http://kb2.adobe.com/cps/496/cpsid_49662.html

Just in case you don’t have the timewill to read it:
– we still have a limit!
– the limit is set to the maximum amount of pixels (16,777,215 (the decimal equivalent of 0xFFFFFF))
– the maximum valid size of the bounding rectangle side is 8191

Stage size limit:
It actually depends on the stage quality.
With a high quality set the bound limit is 4050×4050, if your content exceeds it gets cropped.
With a low or medium quality the bound limit increases, there’s no official documentation about that (or at least I didn’t find it).
Note that Adobe’s saying that “graphic artifacts” could be displayed when our stage “approaches” 3840 pixels range.

have fun 🙂

Advertisements
3D, ActionScript 3, Community, Flash Player 10, Speaking, Tutorials

Flash Graphics Unleashed at WebTech

As my previous post was announcing, cancelling and reannouncing, I’m speaking at WebTech Conference here in Milan.
Here’s my presentation slides

And here’s the examples zip: www.flashfuck.it/webtech/examples.zip
If you need something to be more clear or some further explanation please feel free to comment this post 🙂

ActionScript 3, Flash, Flash Player 10

BitmapData manipulation benchmark

Today I encountered this blog post from Zevan (which blog is REALLY a good daily reading I suggest everyone to take) about bitmapData merging and I started tweaking some code doing some benchmarks to find out which way is the most performing. Here are my tests:

First strike (Zevan’s one): copyPixels

[SWF(width=650, height=650)]
var loader:Loader = new Loader();
loader.load(new URLRequest("http://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
var w:Number;
var h:Number;
var rows:Number = 20;
var cols:Number = 20;
var tiles:Vector. = new Vector.();
var locX:Vector. = new Vector.();
var locY:Vector. = new Vector.();
var rX:Vector. = new Vector.();
var rY:Vector. = new Vector.();
var sX:Vector. = new Vector.();
var sY:Vector. = new Vector.();
function onLoaded(evt:Event):void{
	w = evt.target.width;
	h = evt.target.height;
	var image:BitmapData = Bitmap(evt.target.content).bitmapData;
	var tileWidth:Number = w / cols;
	var tileHeight:Number = h / rows;
	var inc:int = 0;
	var pnt:Point = new Point();
	var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
	var startTime:Number = getTimer();
	for (var i:int = 0; i<rows; i++){
		for (var j:int = 0; j<cols; j ++){
			 var currTile:BitmapData= new BitmapData(tileWidth, tileHeight, true, 0x00000000);
			 rect.x = j * tileWidth;
			 rect.y = i * tileHeight;
			 currTile.copyPixels(image, rect, pnt, null, null, true);
			 tiles[inc] = currTile;
			 rect.x += 25;
			 rect.y += 25;
			 sX[inc] = rect.x;
			 sY[inc] = rect.y;
			 locX[inc] = rX[inc] = -rect.width * 2
			 locY[inc] = rY[inc] =  Math.random() * stage.stageHeight;
			 setTimeout(startAnimation, inc *4 + 100, inc, rect.x, rect.y);
			 inc++;
		}
	}
	trace("copyPixels",getTimer()-startTime,"ms");
	addEventListener(Event.ENTER_FRAME, onLoop);
}
function startAnimation(index:int, dx:Number, dy:Number):void{
	var interval:Number;
	var animate:Function = function(index:int):void{
		locX[index] += (dx - locX[index]) / 4;
		locY[index] += (dy - locY[index]) / 4;
		if (Math.abs(locX[index] - dx) <1 && Math.abs(locY[index] - dy)<1){
			locX[index] = dx;
			locY[index] = dy;
			clearInterval(interval);
		}
	}
   interval = setInterval(animate, 32, index);
}
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));
var loc:Point = new Point();

function onLoop(evt:Event):void {
	  canvas.fillRect(canvas.rect, 0xFFFFFF);
	  var startTime:Number = getTimer();
	  for (var i:int = 0; i<tiles.length; i++){
			var tile:BitmapData= tiles[i];
			loc.x = locX[i];
			loc.y = locY[i];
			canvas.copyPixels(tile, tile.rect, loc, null, null, true);
	  }
	  trace("copyPixels",getTimer()-startTime,"ms");
}

in my machine (mbp, osx) it takes ~27 ms to extract data and ~2-3ms each iteration for setting data on the canvas bitmapData

Second strike: getVectorsetVector

[SWF(width=650, height=650)]
var loader:Loader = new Loader();
loader.load(new URLRequest("http://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
var w:Number;
var h:Number;
var rows:Number = 20;
var cols:Number = 20;
var tiles:Vector.<vector.> = new Vector.<vector.>();
var tileRect:Rectangle;
var locX:Vector. = new Vector.();
var locY:Vector. = new Vector.();
var rX:Vector. = new Vector.();
var rY:Vector. = new Vector.();
var sX:Vector. = new Vector.();
var sY:Vector. = new Vector.();
function onLoaded(evt:Event):void{
	w = evt.target.width;
	h = evt.target.height;
	var image:BitmapData = Bitmap(evt.target.content).bitmapData;
	var tileWidth:Number = w / cols;
	var tileHeight:Number = h / rows;
	tileRect = new Rectangle(0,0,tileWidth,tileHeight);
	var inc:int = 0;
	var pnt:Point = new Point();
	var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
	var startTime:Number = getTimer();
	for (var i:int = 0; i<rows; i++){
		for (var j:int = 0; j<cols; j ++){
			 rect.x = j * tileWidth;
			 rect.y = i * tileHeight;
			 tiles[tiles.length] = image.getVector(rect);
			 rect.x += 25;
			 rect.y += 25;
			 sX[inc] = rect.x;
			 sY[inc] = rect.y;
			 locX[inc] = rX[inc] = -rect.width * 2
			 locY[inc] = rY[inc] =  Math.random() * stage.stageHeight;
			 setTimeout(startAnimation, inc *4 + 100, inc, rect.x, rect.y);
			 inc++;
		}
	}
	trace("vector push:",getTimer()-startTime,"ms");
	addEventListener(Event.ENTER_FRAME, onLoop);
}
function startAnimation(index:int, dx:Number, dy:Number):void{
	var interval:Number;
	var animate:Function = function(index:int):void{
		locX[index] += (dx - locX[index]) / 4;
		locY[index] += (dy - locY[index]) / 4;
		if (Math.abs(locX[index] - dx) <1 && Math.abs(locY[index] - dy)<1){
			locX[index] = dx;
			locY[index] = dy;
			clearInterval(interval);
		}
	}
   interval = setInterval(animate, 32, index);
}
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));
var loc:Point = new Point();

function onLoop(evt:Event):void {
	  canvas.fillRect(canvas.rect, 0xFFFFFF);
	  var tmpVec:Vector.;
	  var startTime:Number = getTimer();
	  for (var i:int = 0; i<tiles.length; i++){
		  	tmpVec = tiles[i];
			tileRect.x = locX[i];
			tileRect.y = locY[i];
			canvas.setVector(tileRect,tmpVec);
	  }
	  trace("vector push:",getTimer()-startTime,"ms");
}

on my machine it takes only ~9 ms to extract tiles slices (yes, 3 times faster!!!) and ~1ms for pushing them all in the canvas for each loop iteration

Third strike: getPixelssetPixels

[SWF(width=650, height=650)]
var loader:Loader = new Loader();
loader.load(new URLRequest("http://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
var w:Number;
var h:Number;
var rows:Number = 20;
var cols:Number = 20;
var tiles:Vector. = new Vector.();
var tileRect:Rectangle;
var locX:Vector. = new Vector.();
var locY:Vector. = new Vector.();
var rX:Vector. = new Vector.();
var rY:Vector. = new Vector.();
var sX:Vector. = new Vector.();
var sY:Vector. = new Vector.();
function onLoaded(evt:Event):void{
	w = evt.target.width;
	h = evt.target.height;
	var image:BitmapData = Bitmap(evt.target.content).bitmapData;
	var tileWidth:Number = w / cols;
	var tileHeight:Number = h / rows;
	tileRect = new Rectangle(0,0,tileWidth,tileHeight);
	var inc:int = 0;
	var pnt:Point = new Point();
	var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
	var startTime:Number = getTimer();
	for (var i:int = 0; i<rows; i++){
		for (var j:int = 0; j<cols; j ++){
			 rect.x = j * tileWidth;
			 rect.y = i * tileHeight;
			 tiles[tiles.length] = image.getPixels(rect);
			 rect.x += 25;
			 rect.y += 25;
			 sX[inc] = rect.x;
			 sY[inc] = rect.y;
			 locX[inc] = rX[inc] = -rect.width * 2
			 locY[inc] = rY[inc] =  Math.random() * stage.stageHeight;
			 setTimeout(startAnimation, inc *4 + 100, inc, rect.x, rect.y);
			 inc++;
		}
	}
	trace("getPixels",getTimer()-startTime,"ms");
	addEventListener(Event.ENTER_FRAME, onLoop);
}
function startAnimation(index:int, dx:Number, dy:Number):void{
	var interval:Number;
	var animate:Function = function(index:int):void{
		locX[index] += (dx - locX[index]) / 4;
		locY[index] += (dy - locY[index]) / 4;
		if (Math.abs(locX[index] - dx) <1 && Math.abs(locY[index] - dy)<1){
			locX[index] = dx;
			locY[index] = dy;
			clearInterval(interval);
		}
	}
   interval = setInterval(animate, 32, index);
}
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));
var loc:Point = new Point();

function onLoop(evt:Event):void {
	  canvas.fillRect(canvas.rect, 0xFFFFFF);
	  var startTime:Number = getTimer();
	  for (var i:int = 0; i<tiles.length; i++){
			tileRect.x = locX[i];
			tileRect.y = locY[i];
			canvas.setPixels(tileRect,tiles[i]);
			tiles[i].position = 0;
	  }
	  trace("setPixels",getTimer()-startTime,"ms");
}

a nice one, ~ 17 ms to extract and ~2ms to loop on my mac, faster than copyPixels but vectors are still leading…

Fourth strike: merge

[SWF(width=650, height=650)]
var loader:Loader = new Loader();
loader.load(new URLRequest("http://actionsnippet.com/wp-content/chair.jpg"));
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaded);
var w:Number;
var h:Number;
var rows:Number = 20;
var cols:Number = 20;
var tiles:Vector. = new Vector.();
var locX:Vector. = new Vector.();
var locY:Vector. = new Vector.();
var rX:Vector. = new Vector.();
var rY:Vector. = new Vector.();
var sX:Vector. = new Vector.();
var sY:Vector. = new Vector.();
function onLoaded(evt:Event):void{
	w = evt.target.width;
	h = evt.target.height;
	var image:BitmapData = Bitmap(evt.target.content).bitmapData;
	var tileWidth:Number = w / cols;
	var tileHeight:Number = h / rows;
	var inc:int = 0;
	var pnt:Point = new Point();
	var rect:Rectangle = new Rectangle(0,0,tileWidth,tileHeight);
	var startTime:Number = getTimer();
	for (var i:int = 0; i<rows; i++){
		for (var j:int = 0; j<cols; j ++){
			 var currTile:BitmapData= new BitmapData(tileWidth, tileHeight, true, 0x00000000);
			 rect.x = j * tileWidth;
			 rect.y = i * tileHeight;
			 currTile.merge(image,rect,pnt,0xFF,0xFF,0xFF,0xFF);
			 tiles[inc] = currTile;
			 rect.x += 25;
			 rect.y += 25;
			 sX[inc] = rect.x;
			 sY[inc] = rect.y;
			 locX[inc] = rX[inc] = -rect.width * 2
			 locY[inc] = rY[inc] =  Math.random() * stage.stageHeight;
			 setTimeout(startAnimation, inc *4 + 100, inc, rect.x, rect.y);
			 inc++;
		}
	}
	trace("merge",getTimer()-startTime,"ms");
	addEventListener(Event.ENTER_FRAME, onLoop);
}
function startAnimation(index:int, dx:Number, dy:Number):void{
	var interval:Number;
	var animate:Function = function(index:int):void{
		locX[index] += (dx - locX[index]) / 4;
		locY[index] += (dy - locY[index]) / 4;
		if (Math.abs(locX[index] - dx) <1 && Math.abs(locY[index] - dy)<1){
			locX[index] = dx;
			locY[index] = dy;
			clearInterval(interval);
		}
	}
   interval = setInterval(animate, 32, index);
}
var canvas:BitmapData = new BitmapData(stage.stageWidth,stage.stageHeight,false, 0xFFFFFF);
addChild(new Bitmap(canvas));
var loc:Point = new Point();

function onLoop(evt:Event):void {
	  canvas.fillRect(canvas.rect, 0xFFFFFF);
	  var startTime:Number = getTimer();
	  for (var i:int = 0; i<tiles.length; i++){
			var tile:BitmapData= tiles[i];
			loc.x = locX[i];
			loc.y = locY[i];
			canvas.merge(tile, tile.rect, loc, 0xFF,0xFF,0xFF,0xFF);
	  }
	  trace("merge",getTimer()-startTime,"ms");
}

~36 ms to extract and ~12ms to merge tiles on the canvas!!!…too slow all the way…:

There are still some methods left such as getPixelsetPixel, getPixel32setPixel32, and copyChannel but they’re a too much restrictive choice because they’re handling one pixel, or channel at time therefore a further loop would be required to get them doing this task.

Summary:
getVectorsetVector : ~9ms~1ms
getPixelssetPixels: ~17ms~2ms
copyPixels: ~27ms~2-3ms
merge: ~36ms~12ms

make your choice 😉

NOTE: these benchmarks are valid from flash player 10 because we (both me and Zevan) used the Vector native type to store lists of typed data. To make them valid for previous version of the player make sure to replace vectors with arrays and check other types are already supported by the target player.

stay tuned 😉

3D, ActionScript 3, Community, Flash, Flash Player 10

My entry to Bit-101s 25 Lines competition

Yep, i made my submission to 25lines contest just few days ago (right in time 🙂 ), so (as Sakri did some days before me) I’m publishing my code. It’s an easy terrain generator…

Actually, I think it can be somehow improved both in lines of code and actual performances, so feel free to edit or tell me “you’d better to do that this other way…” 🙂

What’s going on is:

  • generate a shape filled with a gradient to create a reference color for differents “height”
  • generate a perlinNoise everyframe for dataprovider use
  • detect each perlinNoise pixel depth according with its main channel value (blue in this case..)
  • generating a vector of Bitmaps to be employed in the view
/**
 * 25-Line ActionScript Contest Entry
 *
 * Project: Random Terrain 3D Generator
 * Author:  Piergiorgio Niero (aka pigiuz) piergiorgio.niero[at]gmail.com
 * Date:    11/24/08
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

// 3 free lines! Alter the parameters of the following lines or remove them.
// Do not substitute other code for the three lines in this section
[SWF(width=800, height=800, backgroundColor=0xffffff, frameRate=24)]
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
// 25 lines begins here!
var _bd:BitmapData = new BitmapData(50,50,false,0x0000FF);
var _points:Array = new Array(new Point());
var _vxCont:Sprite = Sprite(addChild(new Sprite));
_vxCont.x = _vxCont.y = 400;
var _vexels:Vector. = new Vector.((2500),true);
var _hMap:BitmapData = new BitmapData(255,1,false);
var _gradient:Shape = new Shape();
_gradient.graphics.beginGradientFill( GradientType.LINEAR,new Array( 0x4267F9, 0xF9EAB0, 0x9EF07D, 0x8DF273, 0x9D5E1E, 0xFFFFFF ),new Array( 1, 1, 1, 1, 1, 1 ),new Array( 90, 105, 110, 120, 145, 185 ),new Matrix(0.2456396484375,0,0,0.0006103524625,127.5,.5));
_gradient.graphics.drawRect(0,0,255,1);
_hMap.draw(_gradient);
addEventListener(Event.ENTER_FRAME,generatePerlinNoise);
function generatePerlinNoise(e:Event=null):void{
	_bd.perlinNoise(25,25,1,0,false,true,4,false,_points);
	Point(_points[0]).y+=1;
	for(var v:uint=0;v&lt;(2500);v++){
		_vexels[v] = (_vexels[v]==null)?generateVoxel(v):_vexels[v];
		_vexels[v].y = Math.pow((_bd.getPixel(v%50,Math.floor((v/50))) & 0xFF)/255*6,3)*24*.24;
		_vxCont.rotationX = mouseY*.1;
		_vxCont.rotationY = (_vxCont.rotationY-(90/stage.stageWidth*(mouseX-stage.stageWidth)+45))*.5;
		_vexels[v].bitmapData.floodFill(0,0,_hMap.getPixel(255-(_bd.getPixel(v%50,Math.floor((v/50))) & 0xFF),0));}}
function generateVoxel(v:uint):Bitmap{
	var b:Bitmap = new Bitmap(new BitmapData(24*.5,24*.5,false,0x000000),"auto",false);
	b.x = v%50*24-(_bd.width*24*.5);
	b.z = Math.floor((v/50))*24-(_bd.height*24*.5);
	return Bitmap(_vxCont.addChild(b));}
// 25 lines ends here!

enjoy ;)
ActionScript 3, AIR, Flash Player 10, News

Adobe Stratus on Adobe Labs

Did you remember “Pacifica” project? Now it’ [UPDATE: Adobe Stratus is a rendezvous service for RTMFP, a new protocol built in to Flash Player 10 and AIR 1.5. Neither RTMFP nor Stratus are related to the project codenamed Pacifica.] Something new is on adobe labs and its name is “Stratus“..WONDERFUL! 😀

Let’s explain what i’m talking about:

Stratus is “hosted rendezvous service that aids establishing communications between Flash Player endpoints”.
This technology enables clients’ flash player (10 +, or AIR 1.5) to connect directly each other to share runtime informations…actually PEER TO PEER!! (underline this: Stratus is a service by Adobe, not a technology to run on own servers).

Stratus does support only “end to end” p2p, multicast or swarming are not supported. This means we’re not enabled to create a new air-mule service over stratus, but we can build our p2p video chat, p2p real time games, etc…

Stratus introduces a new data transfer protocol: RTMFP, which uses UDP instead of clean RTMP which uses TCP. (note, RTMFP is not RTMP*, which is the encrypted protocol for FMS).

Stratus is now beta, and you can test a sample application hosted on the labs Stratus page

Stratus is going to be released next year (hopefully “early”) …it seems we’re going to have real time “anything” in few months 😀

This could be a new red pill for our webapps,
Stay tuned 😉

3D, Flash Player 10

MMO Test with Papervision3D + Flash Player 10

Here is my very first test on MMOs with Papervision3D on Flash Player 10 (needed to watch properly).

pv3d_isometric_01

here’s the link http://www.flashfuck.it/test/pv3d_isometry_01/

this is just the beginning…it has to be tuned and refined but, yes, it can be done 😉

PS: I stolen the model somewhere on the web…please if it is yours don’t offend yourself, i stole it because it’s good 😉 (anyway let me know so i can put your name somewhere 🙂 )