Comparing bitmap is not working - actionscript-3

I am trying to compare the bitmap 1 to bitmap 2 and if they equal to 0 I want them to do something. so for sake of seeing what it's returning I added a trace. but it seems like it's tracing out something totally different from what I expected. This is the code:
var scratch_c : Scratch_card = new Scratch_card ();
var backgr: Background = new Background ();
var mouseclick:Number;
var masking:Sprite = new Sprite()
var bmd1:BitmapData = new BitmapData(742, 165);
var bm:Bitmap = new Bitmap(bmd1);
addChild (bm);
bm.x=20;
bm.y=40;
bmd1.draw(scratch_c);
var bmd2:BitmapData = new BitmapData(742, 165);
var bm2:Bitmap = new Bitmap(bmd2);
addChild (bm2);
bm2.x=20;
bm2.y=40;
bmd2.draw(backgr);
bm2.mask = masking;
addChild(masking);
stage.addEventListener(MouseEvent.MOUSE_DOWN, Pressing);
stage.addEventListener(MouseEvent.MOUSE_MOVE, Moving);
stage.addEventListener(MouseEvent.MOUSE_UP, Lifting);
function Pressing(event:MouseEvent):void {
mouseclick = 1;
}
function Moving(event:MouseEvent):void {
if (mouseclick == 1) {
masking.graphics.beginFill(0x000000);
masking.graphics.drawEllipse(mouseX, mouseY, 70, 60);
masking.graphics.endFill();
}
}
function Lifting(event:MouseEvent):void {
trace(bmd1.compare(bmd2));
mouseclick = 0;
}
This is what it is tracing out
How can I make it compare the pixels of bmd1 and bmd2?
Why is it not returning a number?

Not a percentage but it should return a new BitMapData object that you could assign as an image.
BitMapData.compare( )
Returns Object — If the two BitmapData objects have the same dimensions (width and height), the method returns a new BitmapData object that has the difference between the two objects (see the main discussion). If the BitmapData objects are equivalent, the method returns the number 0. If the widths of the BitmapData objects are not equal, the method returns the number -3. If the heights of the BitmapData objects are not equal, the method returns the number -4.
Taken from here
Result:
Your bitmaps are not equivalent, thats why you get an object made of 2 different bitmaps.

Have you tried
if(bmd1.compare(bmd2) == bmd2){
// complete
}
However after looking at your code a little more. i think you need to test on the masking not bmd1
So something like this.
var mymask:BitmapData = new BitmapData(742, 165, true,0x000000000);
mymask.draw(masking);
if(mymask.compare(bmd2) == bmd2){
// complete
}
untested code but should put you on the right path

Related

Can I draw the same DisplayObject multiple times?

I have a loop that runs and adds a TextField from an array to a Sprite, and draws that Sprite to a BitmapData object. When I reference a previously drawn TextField, nothing is drawn to the BitmapData.
public class Pocket extends Sprite
{
public var inventory:Array = [textField1, textField2, textField3]; //array of TextFields, populated by another class
public var order:Array = [0,1,2,1]; //array of ints indicating the order of which items in "inventory" should be displayed (the error lies when an element is repeated - in this case, 1
public function getItem(index:int):Array
{
var bitmaps:Array = new Array();
for(var i:int = 0; i < order.length; i++)
{
var bitmapData:BitmapData = new BitmapData(32, 32);
var background:Sprite = new Sprite();
background.graphics.beginFill();
background.graphics.drawRect(0,0,32,32);
background.graphics.endFill();
bitmapData.draw(background); //this always executes as expected
var sprite:Sprite = new Sprite();
sprite.addChild(inventory[order[i]]);
bitmapData.draw(sprite);
var bitmap:Bitmap = new Bitmap(bitmapData);
bitmaps.push(bitmap);
sprite.removeChild(inventory[order[i]]);
}
return bitmaps;
}
}
When the last element in order is called (1), the resulting Bitmap does not contain a textfield.
When the loop runs the first time, adding the TextField inventory[0] to a Sprite, the Sprite is drawn as expected. The second box is drawn, using inventory[1]. However, when I run the same code trying to draw inventory[1] a second time, nothing happens. The same would happen if I try to draw inventory[0], but if I use a TextField that has yet to be added to a Sprite (ex. inventory[2], should one exist), it draws as expected.
Any ideas?
Thanks in advance.

Loading images does not load them into memory

I am loading a batch of 150 HD images into my app - it is basically a 3D view of an object. Once I load the image files using Loader instances I store the loaders' first child's bitmapdata in a Vector. When all of the loaded, I want to begin to "rotate" the object = meaning I am simply swapping the images. I take the Vector where I have the bitmapdatas and draw them onto a canvas bitmapdata one after the other. No science there, it all works as intended.
The problem is that once all the images are loaded and stored in a vector and BEFORE they are drawn to the canvas, they are not in the memory. That means that the first rotation of my 3D object (-> all 150 images drawn) is really slow. After the first rotation there is no problem and all is fluid. My question is: is there a way to force the images to get loaded into the memory without drawing them onto the stage? I expected that they would simply get loaded to memory once they are loaded to the app (Wrong!).
I tried to use addChild() instead of drawing them to a canvas bitmap, same result. I don't think the code is necessary but just in case:
private var _loaders:Vector.<Loader>;
private static const NAME:String = "img_00";
private static const MIN:uint = 0;
private static const MAX:uint = 150;
private var _loaded:uint = 0;
private var _currentFrameIndex:uint = 0;
private var _canvas:Bitmap;
private var _bitmaps:Vector.<BitmapData>;
private var _destPoint:Point;
public function loadImages():void {
var s:String;
for(var i:int=MIN; i<=MAX; i++) {
if(i < 10) s = "00" + i;
else if(i < 100) s = "0" + i;
else s = i.toString();
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadHandler);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, loadHandler);
loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, loadHandler);
loader.load(new URLRequest("images/JPEG/"+ NAME + s + ".jpg"));
_loaders.push(loader);
}
}
private function loadHandler(e:Event):void {
_loaded++;
if(_loaded > (MAX - MIN)) {
_bitmaps = new Vector.<BitmapData>(_loaders.length);
for(var i:int=0; i<_loaders.length; i++) {
var loader:Loader = _loaders[i];
_bitmaps[i] = Bitmap(loader.getChildAt(0)).bitmapData;
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, loadHandler);
loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, loadHandler);
loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, loadHandler);
}
setFrame(0);
dispatchEvent(new Event(LOAD_COMPLETE));
}
}
public function setFrame(frame:uint):void {
if(frame >= 0 && frame < _bitmaps.length) {
_currentFrameIndex = frame;
var bmpData:BitmapData = _bitmaps[_currentFrameIndex];
_canvas.bitmapData.copyPixels(bmpData, bmpData.rect, _destPoint);
}
}
"Not in the memory" means that the images are loaded, but not yet decoded, and this decode is done on the fly, and this takes the time you observe as slowness. You can attempt to "virtually" rotate the image by having a bitmap that's not yet added to stage to be the reference to each of the bitmapDatas of your vector. Make a progress bar that shows how much of the vector has already been decoded, and once this happens, display the bitmap and give the user smooth rotation.
addEventListener(Event.ENTER_FRAME,prerender);
var b:Bitmap=new Bitmap();
/* optional
b.x=stage.stageWidth;
b.y=stage.stageHeight;
addChild(b);
*/
var vi:int=0;
var sh:Shape=new Shape();
sh.graphics.lineStyle(4,0,1); // a simple progress bar
sh.graphics.moveTo(0,0);
sh.graphics.lineTo(100,0);
sh.scaleX=0;
sh.x=stage.stageWidth/2-50; // centered by X
sh.y=stage.stageHeight/2;
addChild(sh);
function prerender(e:Event):void {
if (vi==_bitmaps.length) {
// finished prerender
removeEventListener(Event.ENTER_FRAME, prerender);
removeChild(sh);
// removeChild(b); if optional enabled
setFrame(0);
return;
}
b.bitmapData=_bitmaps[vi];
vi++;
}
Also, it's always better to assign the bitmapData property to a Bitmap object if you don't plan to have that bitmapdata changed. So, instead of your _canvas.bitmapData.copyPixels(bmpData, bmpData.rect, _destPoint); you just do _canvas.bitmapData = bmpData; and it'll work.
UPDATE: Your issue might as well nail to the last point, that is assigning instead of copying. If your destPoint is something else than (0,0), you just make another Bitmap object on top of your _canvas with desired offset, and assign bitmapdatas in there. I have remembered that when I first made multiple animated objects based on a single Vector.<BitmapData> like yours, and tried doing copyPixels(), my animations were jittering and not displaying proper frames, but once I did _bitmap.bitmapData=_bitmaps[currentFrame] everything went as smooth as it should be.

Drawing a bitmap based on a transformed movieclip

I'm drawing bitmaps of movieclips which I then feed into my hittest function to test for collisions. However, I'm not quite sure how i would add to the code below to take into account and draw bitmaps for movieclips which have been scaled and/or rotated. The below code obviously only works for non-transformed movieclips. I've included in comments code which i've already tried but to no success.
When adding the drawn bitmap to the stage, no matter whether the movieclip in question is transformed or not, the drawn bitmap is "cut off" and incorrectly drawn - it appears to only draw a section of it. However, this does not particularly affect the collision testing for the non-transformed movieclips, but has an adverse effect on transformed movieclips.
All of the movieclips I want to be drawn have been created through the graphics property.
//for example:
var mymc:MovieClip = new MovieClip();
var g:Graphics = mymc.graphics;
g.moveTo(0,0);
g.lineTo(17.5,0);
g.lineTo(8.75,17.5);
g.lineTo(-8.75,17.5);
g.lineTo(0,0);
main code:
for each(var mc:MovieClip in impassable) {
//var bMatrix:Matrix = new Matrix();
//bMatrix.scale(mc.scaleX, mc.scaleY);
//bMatrix.rotate(mc.rotation * (Math.PI/180));
var bData:BitmapData = new BitmapData(mc.width, mc.height, true, 0);
//bData.draw(mc, bMatrix);
bData.draw(mc);
var bitmap:Bitmap = new Bitmap(bData);
bitmap.x = mc.x;
bitmap.y = mc.y;
var HitTest:Number = newCollision(bitmap, centerX, centerY, 13.7);
Any thoughts? thanks
This function will create a BitmapData clone of a DisplayObject, taking into account its transform matrix, though it doesn't take into account bitmap filters. (Based on this answer.)
function createBitmapClone(target:DisplayObject):BitmapData {
var targetTransform:Matrix = target.transform.concatenatedMatrix;
var targetGlobalBounds:Rectangle = target.getBounds(target.stage);
var targetGlobalPos:Point = target.localToGlobal(new Point());
// Calculate difference between target origin and top left.
var targetOriginOffset:Point = new Point(targetGlobalPos.x - targetGlobalBounds.left, targetGlobalPos.y - targetGlobalBounds.top);
// Move transform matrix so that top left of target will be at (0, 0).
targetTransform.tx = targetOriginOffset.x;
targetTransform.ty = targetOriginOffset.y;
var cloneData:BitmapData = new BitmapData(targetGlobalBounds.width, targetGlobalBounds.height, true, 0x00000000);
cloneData.draw(target, targetTransform);
return cloneData;
}
When you call successive transforms on a Matrix, the ordering is very important and can really mess things up.
Luckily there is a helper method that allows you to specify translation, rotation and scaling in one go and avoid those issues - createBox
For your case, something like this:
var matrix:Matrix = new Matrix();
matrix.createBox(mc.scaleX, mc.scaleY, mc.rotation*Math.PI/180, 0, 0);
(the two zeros are for x and y translation)

Duplicated a swf loaded with LoaderMax

i'm trying to duplicate a swf loaded using greensocks LoaderMax but i don't seem to be able to
i'm using the following code:
private var _assetCache : Dictionary;
public function getAssetById(assetId:String):DisplayObject
{
var asset:DisplayObject;
if (!_assetCache[assetId])
{
_assetCache[assetId] = LoaderMax.getContent(assetId).rawContent;
}
asset = _assetCache[assetId];
// duplicate bitmap
if (asset is Bitmap)
{
var bmd:BitmapData = new BitmapData(asset.width, asset.height, true, 0);
return new Bitmap(bmd, "auto", true);
}
// otherwise return
return SpriteUtils.duplicateDisplayObject(asset);
//return asset; // if previous line is commented out, this works well but there will be only a single copy of the loaded swf, kinda negating the use of a cache
}
and this is SpriteUtils.duplicateDisplayObject(asset) (taken from this
static public function duplicateDisplayObject(target:DisplayObject, autoAdd:Boolean = false):DisplayObject
{
// create duplicate
var targetClass:Class = Object(target).constructor;
var duplicate:DisplayObject = new targetClass();
trace(duplicate, duplicate.height); // traces [MovieClip, 0]
// duplicate properties
duplicate.transform = target.transform;
duplicate.filters = target.filters;
duplicate.cacheAsBitmap = target.cacheAsBitmap;
duplicate.opaqueBackground = target.opaqueBackground;
if (target.scale9Grid)
{
var rect:Rectangle = target.scale9Grid;
// WAS Flash 9 bug where returned scale9Grid is 20x larger than assigned
// rect.x /= 20, rect.y /= 20, rect.width /= 20, rect.height /= 20;
duplicate.scale9Grid = rect;
}
// add to target parent's display list
// if autoAdd was provided as true
if (autoAdd && target.parent)
{
target.parent.addChild(duplicate);
}
return duplicate;
}
if i simply return the asset from _assetCache (which is a dictionary) without duplicating it, it works and traces as a MovieClip but when i try to duplicate it, even though the traces tell me that the duplicate is a movieclip. Note the clip being loaded is a simple vector graphic on the stage of the root of the timeline
thanks in advance
obie
I don't think simply calling the constructor will work.
Try doing this (I've assumed certain previous knowledge since you're able to get rawContent above):
1) Load the data in using DataLoader in binary mode.... in the context of LoaderMax it would be like: swfLoader = new LoaderMax({name: "swfLoader", onProgress:swfProgressHandler, onChildComplete: swfLoaded, auditSize:false, maxConnections: 2}); swfLoader.append(new DataLoader(url, {format: 'binary'})); (the main point is to use DataLoader with format='binary')
2) Upon complete, store the ByteArray which this returns into your dictionary. The first part of your above code will basically be unchanged, though the bytearray might be in content rather than rawContent
3) Whenever you need a duplicate copy, use Loader.loadBytes() on the ByteArray. i.e. var ldr:Loader = new Loader(); ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, swfReallyLoaded); ldr.loadBytes(_assetCache[assetId]);
As with all loading, you might need to set a LoaderContext, and if in AIR- allowLoadBytesCodeExecution = true; allowCodeImport = true;

Add multiple movieclips, not replacing the old ones

So, in short, my problem is this. I am using a variable which is a movieclip loaded from an external swf. I want to "spawn" multiple instances of the movieclip that all react to the same code, so for example if I say var1.x = 100, they all are at 100x. But my problem is when I run addChild(var1) multiple times(I'm not actually typing in addChild(var1) over and over, I just have it set to add them at random times), the new child just replaces the old one, instead of making multiple movieclips. Should I do something like
var var1:MovieClip
var var2:MovieClip = new var1 ?(which doesnt work for me btw, gives me errors)
Oh, heres the code, and also, I am pretty new to as3 fyi, still don't even know how arrays work, which was my second guess to the problem.
var zombieExt:MovieClip;
var ldr2:Loader = new Loader();
ldr2.contentLoaderInfo.addEventListener(Event.COMPLETE, swfLoaded2);
ldr2.load(new URLRequest("ZombieSource.swf"));
function swfLoaded2(event:Event):void
{
zombieExt = MovieClip(ldr2.contentLoaderInfo.content);
ldr2.contentLoaderInfo.removeEventListener(Event.COMPLETE, swfLoaded2);
//zombieExt.addEventListener(Event.ENTER_FRAME, moveZombie)
zombieExt.addEventListener(Event.ENTER_FRAME,rotate2);
function rotate2 (event:Event)
{
var the2X:int = playerExt.x - zombieExt.x;
var the2Y:int = (playerExt.y - zombieExt.y) * 1;
var angle = Math.atan(the2Y/the2X)/(Math.PI/180);
if (the2X<0) {
angle += 180;
}
if (the2X>=0 && the2Y<0) {
angle += 360;
}
//angletext.text = angle;
zombieExt.rotation = (angle*1) + 90;
}
playerExt.addEventListener(Event.ENTER_FRAME,spawn1);
function spawn1 (event:Event)
{
if(playerExt.y < 417)
{
var someNum:Number = Math.round(Math.random()*20);
if(someNum == 20)
{
addChild(zombieExt)
zombieExt.x = Math.round(Math.random()*100)
zombieExt.y = Math.round(Math.random()*100)
}
}
}
}
addChild() does not create new instances. It is used to add an already created instance to the display list. If you call addChild() multiple times on the same instance then you are just readding itself.
Also each instance is unique, you can not globally change the x position of an instance by changing another one of them. What you would do is as Henry suggests and add each new instance of a MovieClip into an array, then whenever you change something you can loop through the array and apply the changes to each instance.
You can not go var2:MovieClip = new var1 either since var1 is an instance and not a class.
Here's a different method of receiving loaded MovieClips, which i use when i need many copies of the item.
in the swf you are loading, give the target movieclip a linkage name in the library, for this example i will use "foo"
private var loadedSwfClass:Class
private var newZombie:MovieClip;
private var zombieArray:Array = new Array();
function swfLoaded2(event:Event):void
{
loadedSwfClass = event.target.applicationDomain.getDefinition("foo");
for(var n:int = 0; n<100; n++){
newZombie = new loadedSwfClass()
zombieArray.push(newZombie);
addChild(newZombie);
}
}
as per this tutorial
http://darylteo.com/blog/2007/11/16/abstracting-assets-from-actionscript-in-as30-asset-libraries/
although the comments say that
var dClip:MovieClip = this;
var new_mc = new dClip.constructor();
this.addChild(new_mc);
will also work.
It sounds like you might be accessing the same instance some how in your code. It would be helpful to see your code to figure this one out.
If I wanted to load in one swf files and add a MovieClip multiple times I would place it in the library of that SWF file. And then instantiate it and store it into an object pool or a hash or some list.
// after the library were finished loading
var list:Array = [];
for(var i:int=0; i<10; i++) {
var myCreation:MySpecialThing = new MySpecialThing();
addChild(myCreation);
list.push(myCreation);
}
where my library would contain a linkage to the class MySpecialThing.
Calling addChild(var1) multiple times on the same parent doesn't have any effect (unless you have added another child to the same parent in between, in which case it will change the child index and bring var1 to the top). If you call it on different parents, it will just change the parent of var1, doesn't duplicate. Call addChild(new MovieClassName()) at random times instead to add new copies of it. Use an array as suggested here to access them later.
Wow, thanks there henry, just using an array did exactly what I needed, and made things alot simpler.
when you load in using a loader you only get 1 instance, however you can do some funky reflection to determine what class type the given loader.content is, and then instantiate them using that. For Example:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loader_completeHandler);
loader.load(new URLRequest("ZombieSource.swf"));
var classType:Class;
function loader_completeHandler(evt:Event):void
{
var loadInfo:LoaderInfo = (evt.target as LoaderInfo);
var loadedInstance:DisplayObject = loadInfo.content;
// getQualifiedClassName() is a top-level function, like trace()
var nameStr:String = getQualifiedClassName(loadedInstance);
if( loadInfo.applicationDomain.hasDefinition(nameStr) )
{
classType = loadInfo.applicationDomain.getDefinition(nameStr) as Class;
init();
}
else
{
//could not extract the class
}
}
function init():void
{
// to make a new instance of the ZombieMovie object, you create it
// directly from the classType variable
var i:int = 0;
while(i < 10)
{
var newZombie:DisplayObject = new classType();
// your code here
newZombie.x = stage.stageWidth * Math.random();
newZombie.x = stage.stageHeight * Math.random();
i++;
}
}
Any problems let me know, hope this helps.