My ByteArray isn't retaining filters - actionscript-3

I have an RTMP stream that I need to take a screenshot of. I got a security error using bitmapData.draw() since it was coming from AWS S3 so I found a workaround that allows me to take a screenshot of the video:
var bitmap = new Bitmap();
var graphicsData : Vector.<IGraphicsData>;
graphicsData = container.graphics.readGraphicsData(); //-- container is a sprite that holds my video element
bitmap.bitmapData = GraphicsBitmapFill(graphicsData[0]).bitmapData;
var image:ByteArray = new JPGEncoder(85).encode(bitmap.bitmapData);
At this point, I send the ByteArray to PHP, create a JPG out of the ByteArray and save it to the server. That all works great.
The issue is I apply filters realtime to the container sprite which alters the look of the video like brightness, contrast, saturation etc, but when saving to the server, the saved image doesn't contain the filters I had applied.
I tried reapplying the filters to bitmap, graphicsData and bitmap.bitmapData, but nothing worked.
How can I either retain the filters applied or reapply the filters to the above code?
EDIT
Here is how I initially apply the filters:
private function applyEffects(effects:Array):void
{
currentEffects = effects;
var props:Object = {};
for (var i:String in effects)
{
props[effects[i].effect] = effects[i].amount;
}
TweenLite.to(container,0,{colorMatrixFilter:props});
}
props object could look something like: {contrast:0.5,saturation:1}

Ok, I have solved this on my own with a little suggestion Jack Doyle (GSAP Author) gave me. I have described what I did differently in the comments of my code:
var graphicsData:Vector.<IGraphicsData>;
graphicsData = container.graphics.readGraphicsData();
var origBitmap:Bitmap = new Bitmap();
origBitmap.bitmapData = GraphicsBitmapFill(graphicsData[0]).bitmapData;
//-- at this point I have a screenshot of the video
var props:Object = {};
for (var i:String in currentEffects)
{
props[currentEffects[i].effect] = currentEffects[i].amount;
}
TweenLite.to(origBitmap,0,{colorMatrixFilter:props});
//-- at this point I've reapplied the effects.
//-- originally, sending bitmap.bitmapData to the encoder didn't retain the filters so I went 1 step further
//-- create a new bitmapData and draw the reapplied filter'd bitmap
var filteredBitmapData:BitmapData = new BitmapData(640,360);
var filteredBitmap:Bitmap = new Bitmap(filteredBitmapData);
filteredBitmapData.draw(origBitmap);
//-- encode the new bitmap data
var image:ByteArray = new JPGEncoder(85).encode(filteredBitmapData); //-- filteredBitmapData is now filtered and I can send this to the server

Related

Can anyone help me change this code from AS2 to AS3

This will be a simple clock. I've been using AS2 since I learned Flash in early 2000s. It's time to move on.
for (a=1; a<60; a++) {
duplicateMovieClip("dot0", "dot"+a, 10+a);
_root["dot"+a]._rotation = a*6;
_root["dot"+a].gotoAndStop(1);
}
Adobe published ActionScript 2.0 Migration that helped me tremendously back in the day.
Some specifics related to your code:
Properties aren't prefixed with underscores, ex _rotation is now rotation, and _root is now root.
root is not global, it is a property of display objects, and it is null if the display object is not in the display list.
duplicateMovieClip does not exist anymore. You should export your symbol to a class and use new operator and addChild() to create an instance and add it to the display, ex:
var dot:Dot = new Dot();
addChild(dot);
Display objects created in code are not automatically given a name and assigned to a property on its parent when added to the display. You can set the name and use getChildByName on its parent. Example:
var dot:Dot = new Dot();
dot.name = "dot" + i;
addChild(dot);
var n:int = 10;
var dot10:Dot = getChildByName("dot" + n) as Dot;
But this is a bit cumbersome, so in most cases it makes more sense to just store your display objects in your own array and reference them by index:
var dots:Array = [];
var dot:Dot = new Dot();
addChild(dot);
dots.push(dot);
var firstDot:Dot = dots[0];
That should get you started.
Thanks to help here and elsewhere, this is what works for me:
var i:Number = 1;
var dots:Array = [];
for (i=0; i<60; i++) {
var dot:Dot = new Dot;
addChild(dot);
dots.push(dot);
dot.x=683;
dot.y=436;
dot.rotation = i*6;
dot.gotoAndStop(1);
}
The clock I'm making don't have hands but the dots change color for the hours minutes and seconds. Thanks everybody who helped

Flex Mobile AS3, PersistenceManager storing/receiving BitmapData

I'm using the PersistenceManager to store data from my App. If I store BitmapData, it will stored correctly, but after a restart the BitmapData is now an Object. If I cast the Object to BitmapData it doesn't work.
pm.setProperty("sign", signArea.getBitmapData());
And this is the way I try to load it.
pm.getProperty("sign") as BitmapData;
If I doesn't stop the app, it would loaded correctly, but after a restart the "sign" is not BitmapData anymore. It's now an Object.
I don't think you can safely store an instance of BitmapData in a shared object (internally used in the PersistanceManager). It is not explicitly mentioned in the docs: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/SharedObject.html
You can however save the data of the BitmapData as a ByteArray and convert is back when retrieving.
// write
var byteArray:ByteArray = bitmap.bitmapData.getPixels(bitmap.bitmapData.rect);
so.data.byteArray = byteArray;
so.data.width = bitmap.bitmapData.rect.width;
so.data.height = bitmap.bitmapData.rect.height;
so.flush();
// read
var byteArray:ByteArray = so.data.byteArray;
var bitmapData:BitmapData = new BitmapData(so.data.width, so.data.height);
bitmapData.setPixels(bitmapData.rect, byteArray);
Notice that you'll also need to store the width and the height of the image. You could wrap this in an object so that you have 1 entry in the persistance manager instead of 3. This might be more convenient if you want to store multiple bitmaps.
// write
var bd:BitmapData = signArea.getBitmapData();
var r:Rectangle = bd.rect;
pm.setProperty("sign", {bytes:bd.getPixels(r), width:r.width, height:r.height});
// read
var signData:Object = pm.getProperty("sign");
var bitmapData:BitmapData = new BitmapData(signData.width, signData.height);
bitmapData.setPixels(bitmapData.rect, signData.bytes);

Save JPG from CDROM to PC

I am using following function to Save the output from SWF and send it to a PHP page so that it creates JPG, my problem if there is not INTERNET connection and the SWF is in a CDROM can the Save function be used in FLASH so that it outputs JPG and save it on a computer.
In short can we Save movieclip output as an JPG
/**
Screenshot and jpg output
**/
import flash.display.BitmapData;
import flash.geom.Matrix;
//Buttons handlers. Should add an extra function because delegate doesn't allow to pass parameters
shaF.onPress = mx.utils.Delegate.create(this,makeShadow);
//Helper functions to pass parameters
function makeShadow() { capture(0) }
/*
create a function that takes a snapshot of the Video object whenever it is called
and shows in different clips
*/
function capture(nr){
this["snapshot"+nr] = new BitmapData(abc._width,abc._height);
//the bitmap object with no transformations applied
this["snapshot"+nr].draw(abc,new Matrix());
var t:MovieClip = createEmptyMovieClip("bitmap_mc"+nr,nr);
//positions clip in correct place
//t._x = 350; t._y = 10+(nr*130); t._xscale = t._yscale = 50
//display the specified bitmap object inside the movie clip
t.attachBitmap(this["snapshot"+nr],1);
output(nr);
//attachMovie("print_but", "bot"+nr, 100+nr, {_x:t._x+t._width+50, _y:t._y+t._height/2})
}
//Create a new bitmapdata, resize it 50 %, pass image data to a server script
// using a LoadVars object (large packet)
function output(nr){
//Here we will copy pixels data
var pixels:Array = new Array()
//Create a new BitmapData
var snap = new BitmapData(this["snapshot"+nr].width, this["snapshot"+nr].height);
//Matrix to scale the new image
myMatrix = new Matrix();
myMatrix.scale(1, 1)
//Copy image
snap.draw(this["snapshot"+nr], myMatrix);
var w:Number = snap.width, tmp
var h:Number = snap.height
//Build pixels array
for(var a=0; a<=w; a++){
for(var b=0; b<=h; b++){
tmp = snap.getPixel32(a, b).toString(16)
//if(tmp == "-fcffff")
//{
//tmp="-ff0000";
//}
pixels.push(tmp.substr(1))
}
}
//Create the LoadVars object and pass data to PHP script
var output:LoadVars = new LoadVars()
output.img = pixels.toString()
output.height = h
output.width = w
//The page (and this movie itself) should be in a server to work
output.send("show.php", "output", "POST")
}
stop()
Since you already have the BitmapData, this is fairly easy. Modifying your output function:
//Copy image
snap.draw(this["snapshot"+nr], myMatrix);
//Now check if we want to save as JPG instead of sending data to the server
if (runningFromCdrom) {
var encoder:JPEGEncoder = new JPEGEncoder();
var bytes:ByteArray = encoder.encode(snap);
var file:FileReference = new FileReference();
file.save(bytes);
} else {
// ...the rest of your output method...
}
How to determine the runningFromCdrom value is up to you. If the SWF is being run inside an HTML document, the best way to tell if the program is being run from a CD-ROM would be to specify it in the FlashVars.
Yes, you can save JPEG's directly from Flash. There is no need for the intermediate step to PHP.
You can use one of these JPEG encoders:
https://github.com/mikechambers/as3corelib
http://www.switchonthecode.com/tutorials/flex-tutorial-an-asynchronous-jpeg-encoder
I would recommend the second as it can work asynchronously (kind of) which means your UI shouldn't lock up while encoding the JPEG.

AS3 : Setting the instance name of a loaded swf

I am running a for loop that loads swfs onto the stage. _componentData is an XMLList.
private function loadDevices():void
{
for each (var d:XML in _componentData.device)
{
var iname:String = d. # iname;
var mLoader:Loader = new Loader();
var mRequest:URLRequest = new URLRequest(d. # path);
mLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onDeviceLoadComplete);
mLoader.load(mRequest);
}
}
Inside onDeviceLoadComplete i want to be able to set the instance name of the loaded swf. Can i send extra parameters to the event handler function? Or is there a better approach?
Pretty sure you can't change an instance name on a dynamically-generated object. In any case, it would probably be easier to push them into an array for reference. You could use associative keys in the array and reference them as such:
var myArray:Object = new Object();
myArray.apple = "red";
for (var item in myArray) {
trace(item); // apple
trace(myArray[item]); // red
}
I found a nice link that pointed me in the right direction since i cannot set the instance name.
What i am doing is setting the name prop of the loader to iname and then using e.target.loader.name instead of the instance name. From there i am able to move forward in my development. Thanks!

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.