AS3 - Scale BitmapData - actionscript-3

I'd like to scale a BitmapData to different sizes such as 200, 400, 600 and 800.
What is a good way to do that?

You can't directly scale a BitmapData but you can make a scaled clone of it.
Here is a quick example for scaling a BitmapData :
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Sprite;
import flash.geom.Matrix;
import mx.core.BitmapAsset;
public class Test extends Sprite {
[Embed(source="test.jpg")]
private var Image:Class;
public function Test() {
var originalBitmapData:BitmapData = BitmapAsset(new Image()).bitmapData;
function scaleBitmapData(bitmapData:BitmapData, scale:Number):BitmapData {
scale = Math.abs(scale);
var width:int = (bitmapData.width * scale) || 1;
var height:int = (bitmapData.height * scale) || 1;
var transparent:Boolean = bitmapData.transparent;
var result:BitmapData = new BitmapData(width, height, transparent);
var matrix:Matrix = new Matrix();
matrix.scale(scale, scale);
result.draw(bitmapData, matrix);
return result;
}
var bitmapA:Bitmap = new Bitmap(originalBitmapData);
addChild(bitmapA);
var bitmapB:Bitmap = new Bitmap(scaleBitmapData(originalBitmapData, 0.5));
addChild(bitmapB);
}
}
}

Setting the width and height of a bitmap object scales that image, so create a bitmap with your bitmapData then scale it using width/height, or use scaleX/Y if you want to use scaling values.
var bmp:Bitmap = new Bitmap(bmpData);
bmp.width = 400; // or 600,800 etc.
bmp.height = 400;
If you don't want to use a bitmap object and directy scale the BitmapData see this
What is the best way to resize a BitmapData object?

Related

Get a ByteArray from BitmapData in AS3

I would like to get a ByteArray from a BitmapData in AS3. I tried the following:
var ba:ByteArray = myBitmapData.getPixels(0,0);
It didn't work and I got this error ArgumentError: Error #1063: Argument count mismatch on flash.display::BitmapData/getPixels(). Expected 1, got 2.:
As Adobe said about BitmapData.getPixels : "Generates a byte array from a rectangular region of pixel data...", so your parameter should be a Rectangle object.
Take this example from Adobe.com :
import flash.display.BitmapData;
import flash.geom.Rectangle;
import flash.utils.ByteArray;
var bmd:BitmapData = new BitmapData(80, 40, true);
var seed:int = int(Math.random() * int.MAX_VALUE);
bmd.noise(seed);
var bounds:Rectangle = new Rectangle(0, 0, bmd.width, bmd.height);
var pixels:ByteArray = bmd.getPixels(bounds);
As was mentioned in the other answer, getpixels expects a .rect meaning rectangular area of some displayObject (like Sprite, MovieClip, Shape or other already exisiting BitmapData.
getPixels(). Expected 1, got 2. is becuase it should have been:
var ba:ByteArray = myBitmapData.getPixels( myBitmapData.rect);
Study this code and see if it helps you :
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.events.Event;
import flash.net.URLRequest;
import flash.display.BitmapData;
var ba:ByteArray = new ByteArray;
var picBMP:Bitmap; var picBMD:BitmapData;
var pic_canvas:Sprite = new Sprite(); //container for image
var loader:Loader = new Loader(); //image loader
loader.load(new URLRequest("pic1.jpg")); //load some JPG/PNG/GIF/SWF
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, img_load_Complete);
function img_load_Complete(evt:Event):void
{
picBMP = loader.content as Bitmap;
pic_canvas.addChild(picBMP);
stage.addChild(pic_canvas); //container now added to stage (screen)
BMP_toBytes(); //do function to convert to bytes
}
function BMP_toBytes():void
{
var PC = pic_canvas; //shortcut reference to pic_canvas (less typing)
picBMD = new BitmapData(PC.width, PC.height, true, 0xFFFFFF);
picBMD.draw(PC); //make bitmapdata snapshot of container
ba = picBMD.getPixels(picBMD.rect);
trace("BA length : " + ba.length); //check how many bytes in there
ba.position = 0; //reset position to avoid "End of File error"
bytes_toPixels(); //do function for bytes as pixels to some new container
}
function bytes_toPixels():void
{
var new_BMP:Bitmap = new Bitmap; var new_BMD:BitmapData;
var new_canvas:Sprite = new Sprite(); //another new container for pixels
ba.position = 0; //reset position to avoid "End of File error"
//we can reuse picBMD W/H sizes since we have copy of same pixels
new_BMD = new BitmapData(picBMD.width, picBMD.height, true, 0xFFFFFFFF);
new_BMD.setPixels(new_BMD.rect, ba); //paste from BA bytes
new_BMP.bitmapData = new_BMD; //update Bitmap to hold new data
new_canvas.x = 150; new_canvas.y = 0; new_canvas.addChild(new_BMP);
stage.addChild(new_canvas); //add to screen
}
Hope it helps..

As3 Auto Fitting the loaded content to stage

I am working on a Pure ActionScript 3.0 project in which the user will be allowed to upload there own swfs into the application.
My objective is to load the swf and fit exactly on the stage. The problem is, when the user uploads the masked swf it is not exactly fitting to the stage when other swfs are working fine.
Consider my stage resolution is 800x600, when the user uploads the masked swf with stage width and height is 600x550 but the content width and height is 900 x 800.
How to fit this kind of swf into the stage?
Per determining the bounds of a masked SWF, use BitmapData.
For example, I've created a 400x400 swf that's masked to 200x200 starting at 100x100 (download)
Loading this SWF would report 400x400 as width / height.
To obtain the visible bounds of the unmasked content, you could implement:
package
{
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.net.URLRequest;
[SWF(width = 400, height = 400, backgroundColor = 0xefefef, frameRate = 60)]
public class X extends Sprite
{
private var loader:Loader;
public function X()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
loader = new Loader();
var url:URLRequest = new URLRequest("http://jasonsturges.com/labs/stack-overflow/examples/swf-mask/masked.swf");
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.load(url);
}
protected function completeHandler(event:Event):void
{
var bitmapData:BitmapData = new BitmapData(loader.width, loader.height, true, 0);
bitmapData.draw(loader);
var bounds:Rectangle = bitmapData.getColorBoundsRect(0xff000000, 0xff000000, true);
trace(bounds);
}
}
}
Which reports:
(x=100, y=100, w=200, h=200)
So, the visible content is 200x200 starting at 100x100.
Per sizing to the stage, you need to determine what your constraint is to avoid blank / empty regions on the stage.
This can be implemented using a ratio variable:
var ratio:Number = 1.0;
Proportional scaling (by width):
var ratio:Number = stage.stageWidth / bounds.width;
loader.scaleX = ratio;
loader.scaleY = ratio;
loader.x = -(bounds.x * ratio);
loader.y = -(bounds.y * ratio);
Stretch display object to fit stage:
ader.scaleX = stage.stageWidth / bounds.width;
loader.scaleY = stage.stageHeight / bounds.height;
loader.x = -(bounds.x * (stage.stageWidth / bounds.width));
loader.y = -(bounds.y * (stage.stageHeight / bounds.height));
So, all together, to fit the loaded content stretched to fill the entire stage accounting for mask offset:
package
{
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.geom.Rectangle;
import flash.net.URLRequest;
[SWF(percentWidth = 100, percentHeight = 100, backgroundColor = 0xefefef, frameRate = 60)]
public class X extends Sprite
{
private var loader:Loader;
public function X()
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
loader = new Loader();
var url:URLRequest = new URLRequest("http://jasonsturges.com/labs/stack-overflow/examples/swf-mask/masked.swf");
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
loader.load(url);
}
protected function completeHandler(event:Event):void
{
var bitmapData:BitmapData = new BitmapData(loader.width, loader.height, true, 0);
bitmapData.draw(loader);
var bounds:Rectangle = bitmapData.getColorBoundsRect(0xff000000, 0xff000000, true);
trace(bounds);
addChild(loader);
loader.scaleX = stage.stageWidth / bounds.width;
loader.scaleY = stage.stageHeight / bounds.height;
loader.x = -(bounds.x * (stage.stageWidth / bounds.width));
loader.y = -(bounds.y * (stage.stageHeight / bounds.height));
}
}
}
You should compare both content and holder ratio to get an accurate fit. If you are loading external swf, your width/height information should come from the loaderInfo metadata of SWF not size of the loaded displayObject content. Here is a link to wonderfl to see it working: http://wonderfl.net/c/eRYv
And here the simple function to compare ratios:
function makeItFit(cont:Sprite, hold:Sprite):void{
var holderRatio:Number = hold.width/hold.height;
var contentRatio:Number = cont.width/cont.height;
//compare both ratios to get the biggest side
if(holderRatio < contentRatio){
cont.width = hold.width; //width bigger, lets make it fit
cont.scaleY = cont.scaleX; //adjust scale to avoid distortion
}else{
cont.height = hold.height;
cont.scaleX = cont.scaleY;
}
//now the size is ok, lets center the thing
cont.x = (hold.width - cont.width)/2;
cont.y = (hold.height - cont.height)/2;
}
Remember, use loaderInfo width/height data if loading external swf. This is just an example.

Applying blur filter to freeform areas

I have to build a flash application for simple wrinkle retouching. The user should be able to upload a portrait photo, select the wrinkled areas and then a blur filter will be applied to the selected parts. My question is - is it somehow possible to apply the filter to a freeform area? I know it would be easy to make the selection rectangular, but that would not be very useful to really mark the correct areas. Ideally the user should get some kind of round brush to use for marking the areas, press "OK" and then the filter will be applied.
Is there any way to do this? And do you maybe have some further recommendations on how to approach this task? I have very little experience with manipulating bitmap data with ActionScript.
Any help is appreciated! Thanks very much in advance :-)
The algorithm is as follows:
create a BitmapData of selection shape using BitmapData.draw() (let it be A)
cut a rectangular piece of original image that is covered by selection area, add some margins for blurred area using Bitmapdata.copyPixels() (let it be B).
remove all pixels from B, that are not covered by shape A with BitmapData.threshold() using A as source bitmap.
blur the resulting image
copy blurred pixels back to target image using Bitmapdata.copyPixels()
Here's a complete and working example.
I've written the code anyway while figuring out the solution.
Hope you find it usefull.
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Loader;
import flash.display.Shape;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.filters.BlurFilter;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.system.LoaderContext;
[SWF(width="800", height="600",backgroundColor="#FFFFFF")]
public class TestBlur extends Sprite
{
private const loader:Loader = new Loader();
public function TestBlur()
{
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadComplete);
loader.load(new URLRequest("http://i.stack.imgur.com/u3iEv.png"), new LoaderContext(true));
}
protected function onLoadComplete(event:Event):void
{
trace(event);
var bmp:Bitmap = loader.content as Bitmap;
addChild(bmp);
// create some test selection area
var selection:Shape = new Shape();
selection.graphics.lineStyle(30, 0xFF0000, .5);
selection.graphics.curveTo(75, -50, 200, 10);
selection.x = 40;
selection.y = 60;
addChild(selection);
// create a duplicate of the original image
var target:BitmapData = bmp.bitmapData.clone();
var targetBmp:Bitmap = new Bitmap(target);
targetBmp.x = bmp.x + bmp.width;
addChild(targetBmp);
//
// *** main work starts here ***
// by now we have selection shape and a bitmap to blur
const destPoint:Point = new Point();
const drawMatrix:Matrix = new Matrix();
const blurMargin:uint = 10;
const blur:BlurFilter = new BlurFilter(2, 2, 3);
var rect:Rectangle;
// 0: prepare an image of selection area
// we'll need it at step 3
rect = selection.getBounds(selection);
rect.x -= blurMargin;
rect.y -= blurMargin;
rect.width += blurMargin*2;
rect.height += blurMargin*2;
var selectionImage:BitmapData = new BitmapData(rect.width, rect.height, true, 0);
drawMatrix.identity();
drawMatrix.translate(-rect.x, -rect.y);
selectionImage.draw(selection, drawMatrix);
// just some testing
var test0:Bitmap = new Bitmap(selectionImage.clone());
test0.y = bmp.y + bmp.height;
addChild(test0);
// 1: cut a rectangular piece of original image that is covered by selection area
rect = selection.getBounds(selection.parent);
rect.x -= blurMargin;
rect.y -= blurMargin;
rect.width += blurMargin*2;
rect.height += blurMargin*2;
var area:BitmapData = new BitmapData(rect.width, rect.height, true, 0);
area.copyPixels(bmp.bitmapData, rect, destPoint);
// just some testing
var test1:Bitmap = new Bitmap(area.clone());
test1.y = bmp.y + bmp.height;
test1.x = test0.x + test0.width;
addChild(test1);
// 2: remove all pixels that are not covered by selection
area.threshold(selectionImage, area.rect, destPoint, "==", 0, 0, 0xFF000000);
// just some testing
var test2:Bitmap = new Bitmap(area.clone());
test2.y = test0.y + test0.height;
test2.x = test0.x;
addChild(test2);
// 3: blur copied area
area.applyFilter(area, area.rect, destPoint, blur);
// just some testing
var test3:Bitmap = new Bitmap(area.clone());
test3.y = test0.y + test0.height;
test3.x = test2.x + test2.width;
addChild(test3);
// 4: copy blurred pixels back to target image
destPoint.x = rect.x;
destPoint.y = rect.y;
target.copyPixels(area, area.rect, destPoint);
}
}
}

positioning bitmapdata

I have the following code:
public function Application()
{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
var urlRequest:URLRequest = new URLRequest("image/1.jpg");
loader.load(urlRequest);
addChild(loader);
}
private function completeHandler(e:Event):void{
loader.content.width = 800;
loader.content.scaleY = loader.content.scaleX;
piece = Math.round(loader.height/10);
drawBitmaps();
}
private function drawBitmaps():void{
var bmdata:BitmapData = new BitmapData(loader.width, piece, true, 0x000000);
bmdata.draw(loader);
var bitmap:Bitmap = new Bitmap(bmdata);
addChild(bitmap);
loader.visible = false;
}
the result is a bitmap wich contains a pice of the image. The height is 80. and it starts at the top of the image. But how can i tell the bitmapdata to start drawing the image from lets say 80pixels? so it draws a middle piece of the image? Because atm it allways draws from the top of the image.
You should use BitmapData::draw clipRect parameter.
Here is an example:
package {
import flash.geom.Rectangle;
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Sprite;
public class BitmapDataTest extends Sprite {
public function BitmapDataTest() {
var c:Sprite = new Sprite();
var g:Graphics;
g = c.graphics;
g.beginFill(0xFF0000);
g.drawCircle(30,30,30);
g.endFill();
addChild(c);
c.x = 10;
c.y = 10;
var bmdata:BitmapData = new BitmapData(60, 60, true, 0x000000);
bmdata.draw(c,null,null,null, new Rectangle(0,30,30,30));
var bitmap:Bitmap = new Bitmap(bmdata);
addChild(bitmap);
bitmap.x = 80;
bitmap.y = 10;
}
}
}
Please notice that the target bitmap data should have the same dimensions as source or this won't work. If you really have to cut it down you should use BitmapData::copyPixels method.
The easiest way would be to apply a mask, dynamically. Here is a good example: Actionscript 3 and dynamic masks
You can create a rectangle, the height you're looking for and position it appropriately.
Hope this helps.

Papervision3D; rotate child objects within camera view

It's my first time with Papervision3D and I have created a slide show of images that is skewed on the y-axis. Smallest photos on the left, and they increase in size going to the right. So they zoom from left to right, smallest to biggest.
I have a tooltip that pops up when you hovers over the photo, but the tooltip also gets skewed proportionate to the camera view (slanted). I want the tooltip's angle to be independent of the entire camera view.
Any idea how to rotate objects independent of the parent's camera angle?
Thanks!
my_obj = new DisplayObject3D();
my_plane = my_obj.addChild(new Plane(bla bla));
my_obj.lookAt(camera);
The 'lookAt' bit is what you need.
Why not draw the tooltips in 2d? You can get the on-screen position of the images and then just draw a regular Sprite like so:
package
{
import flash.display.Sprite;
import flash.text.TextField;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.typography.Text3D;
import org.papervision3d.view.BasicView;
public class PVTest extends Sprite
{
private var world:BasicView;
private var text:Text3D;
private var text2d:TextField;
public function PVTest()
{
world = new BasicView(stage.width, stage.height, true, true);
var colorMat:ColorMaterial = new ColorMaterial();
colorMat.interactive = true;
var planeContainer:DisplayObject3D = new DisplayObject3D();
var plane:Plane;
for(var i:int = 0; i < 11; i++)
{
plane= new Plane(
colorMat,
100, 100,
10);
plane.x = (i * 105) - 500;
plane.addEventListener(InteractiveScene3DEvent.OBJECT_OVER, handleMouseOver);
plane.addEventListener(InteractiveScene3DEvent.OBJECT_OUT, handleMouseOut);
planeContainer.addChild(plane);
}
planeContainer.rotationY = 10;
world.scene.addChild(planeContainer);
world.camera.z = -500;
addChild(world);
world.startRendering();
}
private function handleMouseOver(event:InteractiveScene3DEvent):void
{
var plane:Plane = Plane(event.displayObject3D);
plane.calculateScreenCoords(world.camera);
const OFFSET_X:int = -20;
const OFFSET_Y:int = 30;
text2d = new TextField();
text2d.text = "toolTip";
text2d.x = plane.screen.x + (stage.width/2) + OFFSET_X;
text2d.y = plane.screen.y + (stage.height/2) + OFFSET_Y;
addChild(text2d);
}
private function handleMouseOut(event:InteractiveScene3DEvent):void
{
removeChild(text2d);
}
}
}
Even for this example you'd have to offset the y position of the tooltip based on the objects scale but it may be easier than working out the rotations and is the best way to get a consistent looking result.