About generating sound waveform in an ActionScript 3 Bitmap - actionscript-3

I am generating Bitmap object to show the sound waveform of a loaded sound. The bitmap is 1024x120 and after it has been generated I shrink its size to 655x120. My problem is the player that loads the bitmap in task manager becomes 260MB heavy.
I am also adding some gradients and I also cache it as bitmap, but if I remove this properties I dont get any big difference in size.
I also can try to small its size but still I think the bitmap will be big.
Any idea how to compress or whatever solution to solve this problem?
thanks
Here is some code
These are the sound and Bitmap settigns of the function that generate the bitmap.
public function generateBitmap(snd:Sound):void
{
samples = new ByteArray();
buffer = new BitmapData(1024, 120, false, backColor);
screen = new Bitmap(buffer);
rect = new Rectangle(0, 0, 1, 0);
var left:Number;
var right:Number;
screen.x = 0;
screen.y = 0;
screen.width = 655;
screen.height = 120;
buffer.fillRect( buffer.rect, backColor );
Now I am doing some samples extraction
var extract:Number = Math.floor ((snd.length / 1000) * 44100);
playingTime = snd.length;
ratio = playingTime / buffer.width;
var lng:Number = snd.extract(samples, extract);
samples.position = 0;
step = samples.length / 4096;
do
{
step-- ;
}
while ( step % 4 );
Follows the drawing method of the bitmap.
for (var c:int = 0; c < 4096; c++)
{
rect.x = c / 4;
left = samples.readFloat() * 25;
right = samples.readFloat() * 25;
samples.position = c * step;
// left channel
if (left > 0)
{
rect.y = 30 - left;
rect.height = left;
}
else
{
rect.y = 30;
rect.height = -left;
}
buffer.fillRect( rect, leftChColor );
// right channel
if (right > 0)
{
rect.y = 80 - right;
rect.height = right;
}
else
{
rect.y = 80;
rect.height = -right;
}
buffer.fillRect( rect, rightChColor );
}
screen.width = screenWidth;
screen.height = screenHeight;
addChild( screen );
}
Here is a reference of the code I have used. Tried it without my stuff in the player and get 193MB of RAM just for the flash player. So the code needs I guess the code needs refinement. Any idea or othere method to do the same stuff without eating so much RAM?

Before this line:
buffer = new BitmapData(1024, 120, false, backColor);
Add:
if ( buffer ) { buffer.dispose(); buffer = null; }
buffer = new BitmapData(1024, 120, false, backColor);

Related

Detect Colour Using Action Script 3

So I have made a game in scratch which uses the following code block:
I am trying to remake this game in Adobe Animate using Action Script 3 (for a class project), is there a similar way to do this in animate?
It is possible. The trick to do it is to create a tiny-teeny BitmapData object and to draw a small portion of stage under the mouse pointer into that object so that you can obtain the pixel color value.
// BitmapData object and some service objects.
var BD:BitmapData = new BitmapData(3, 3, true);
var R:Rectangle = new Rectangle(0, 0, 3, 3);
var M:Matrix = new Matrix;
// Let's create a TextField so we can output the pixel color value.
var T:TextField = new TextField;
var TF:TextFormat = new TextFormat("_typewriter", 12, 0x000000, true, false, false, null, null, TextFormatAlign.CENTER);
T.x = 10;
T.y = 10;
T.width = 100;
T.height = 18;
T.border = true;
T.background = true;
T.selectable = false;
T.mouseEnabled = false;
T.defaultTextFormat = TF;
addChild(T);
// Lets add some semi-transparent color circles
// so we have colored things to point the mouse at.
for (var i:int = 0; i < 10; i++)
{
var aColor:uint = 0;
aColor |= int(128 + 128 * Math.random()) << 16; // RED
aColor |= int(128 + 128 * Math.random()) << 8; // GREEN
aColor |= int(128 + 128 * Math.random()); // BLUE
var anX:int = stage.stageWidth / 8 + Math.random() * stage.stageWidth * 3 / 4;
var anY:int = stage.stageHeight / 8 + Math.random() * stage.stageHeight * 3 / 4;
var aRadius:int = 50 + 100 * Math.random();
var anAlpha:Number = 0.5 + 0.5 * Math.random();
graphics.beginFill(aColor, anAlpha);
graphics.drawCircle(anX, anY, aRadius);
graphics.endFill();
}
// Now let's watch the mouse every frame.
addEventListener(Event.ENTER_FRAME, onFrame);
function onFrame(e:Event):void
{
// Get pixel color as an RRGGBB String and print it.
T.text = "#" + addZeroes(getColorUnderMouse());
}
function getColorUnderMouse():uint
{
// Adjust Matrix so that we draw the correct piece of screen.
M.tx = -root.mouseX + 1;
M.ty = -root.mouseY + 1;
// Clear the BitmapData and capture the 3x3 piece under the mouse pointer.
BD.fillRect(R, 0xFFFFFFFF);
BD.draw(root, M, null, null, R);
// Read the pixel color value at the center of 3x3 and return it.
return BD.getPixel(1, 1);
}
// This function fixes the hexabinary value with leading
// zeroes if the color value is too small (like 0 = black).
function addZeroes(value:uint, count:uint = 6):String
{
var result:String = value.toString(16).toUpperCase();
while (result.length < count)
{
result = "0" + result;
}
return result;
}

How to place multiple bitmaps in a scrollable rectangle? AS3

This code builds a palette of tiles for use in a map maker program. It takes in an array set by its parent and uses the bitmaps(from the objects) in that array to display a grid of tiles. Right now it only does a 5x5 grid, but what if there are more than 25 tiles in my tileSet? I want to display only the 5x5 tile grid, but be able to scroll through the images. I imagine that I need to make another rectangle to use as its mask and use a ScrollBar to make it scrollRect, but I can't get this working. Please Help.
public function Palette(X:uint, Y:uint, tileSet:Array)
{
addChild(handleGraphics);
var palette:Rectangle = new Rectangle(X, Y, 5*32, tileSet.length*32); //Default size is 5x5 tiles.
handleGraphics.DrawGrid(32,palette.x,palette.y,5,5);
var counter:int = 0;
for(var i:int = 0; i < 5; i++)
{
paletteArray[i] = [];
for(var u:int = 0; u < 5; u++)
{
if(counter >= tileSet.length)
{
counter = 0; //Which frame to show?
}
var b:Bitmap = new Bitmap(tileSet[counter].Graphic);
b.x = (palette.x) + 32 * u; //Align with palette Rectangle.
b.y = (palette.y) + 32 * i; ///////////////////////////////
addChild(b);
var tileObj:Object = new Object();
tileObj.Name = tileSet[counter].Name;
tileObj.Frame = tileSet[counter].Frame;
tileObj.Graphic = tileSet[counter].Graphic;
paletteArray[i].push(tileObj);
setChildIndex(b, 0); //Under grid.
counter++;
}
}
ActivatePaletteListeners();
}
This code works great for a tileSet array that has less than 25 objects. It loops and shows them continuously until it hits 25. I could do without this I guess, but it is a neat affect.
In another class (HandleTiles) I cycle through my tileSet MovieClip and use each frame to create a new object for each tile.
public function GetPaletteTiles(MC:MovieClip)
{
if (tileArray != null)
{
tileArray.length = 0;
}
for(var i:int = 1; i <= MC.totalFrames; i++)
{
MC.gotoAndStop(i); //Change frame for new info.
var tileObj:Object = new Object(); //The object to push to an array of tiles.
var graphicData:BitmapData = new BitmapData(32,32);
graphicData.draw(MC); //Graphic data from sampleTS.
tileObj.Name = MC.currentFrameLabel;
tileObj.Frame = MC.currentFrame;
tileObj.Graphic = graphicData;
tileArray.push(tileObj);
}
BuildIndexArray(15, 20); //Default size 15 x 20.
}
And here I set the tileSet to use
private function ChangeActiveTileset(Mc:MovieClip)
{
activeTileset = Mc;
GetPaletteTiles(activeTileset);
UpdatePalette();
}
I can change the tileSet with a comboBox. That's why I tear down the tileArray every time I call GetPaletteTiles(). Each tileSet is a different MovieClip, like Buildings, Samples, InTheCity, etc.
Sorry I didn't have time to get this code together earlier. Here's tiling code pieces. Because you're using rectangle and you have to stay under max dimensions you have to move the source mc. I think you already know everything else in there.
// set the bmp dimensions to device screensize to prevent exceeding device's max bmp dimensions
if (bStagePortrait) {
iTileWidth = Capabilities.screenResolutionX;
iTileHeight = Capabilities.screenResolutionY;
} else {
iTileWidth = Capabilities.screenResolutionY;
iTileHeight = Capabilities.screenResolutionX;
}
// mcList.mcListVector is the source mc - a regular mc containing mcs, jpgs, dynamic text, vector shapes, etc.
// mcList.mcListBmp is an empty mc
aListTiles = new Array();
iNumberOfTiles = Math.ceil(mcList.height / iTileHeight);
for (i = 0; i < iNumberOfTiles; i++) {
var bmpTile: Bitmap;
// move the source mc
mcList.mcListVector.y = -(i * iTileHeight);
bmpTile = fDrawTile(mcList, 0, 0, iTileWidth, iTileHeight);
mcList.mcListBmp.addChild(bmpTile);
bmpTile.x = 0;
bmpTile.y = (i * iTileHeight);
aListTiles.push(bmpTile);
}
// remove the regular mc
mcList.mcListVector.removeChild(mcList.mcListVector.mcPic);
mcList.mcListVector.mcPic = null;
mcList.removeChild(mcList.mcListVector);
mcList.mcListVector = null;
}
function fDrawTile(pClip: MovieClip, pX: int, pY: int, pWidth: int, pHeight: int): Bitmap {
trace("fDrawTile: " + pX + "," + pY + " " + pWidth + "," + pHeight);
var rectTemp: Rectangle = new Rectangle(pX, pY, pWidth, pHeight);
var bdClip: BitmapData = new BitmapData(pWidth, pHeight, true, 0x00000000);
var bdTemp: BitmapData = new BitmapData(pWidth, pHeight, true, 0x00000000);
bdClip.draw(pClip, null, null, null, rectTemp, true);
bdTemp.copyPixels(bdClip, rectTemp, new Point(0, 0));
var bmpReturn: Bitmap = new Bitmap(bdTemp, "auto", true);
return bmpReturn;
}

Getting pixel data on setInterval with canvas

I want to build an animated alphabet, made up of particles. Basically, the particles transform from one letter shape to another.
My idea is to fill the letters as text on canvas real quickly (like for a frame), get the pixel data and put the particles to the correct location on setInterval. I have this code for scanning the screen right now:
var ctx = canvas.getContext('2d'),
width = ctx.canvas.width,
height = ctx.canvas.height,
particles = [],
gridX = 8,
gridY = 8;
function Particle(x, y) {
this.x = x;
this.y = y;
}
// fill some text
ctx.font = 'bold 80px sans-serif';
ctx.fillStyle = '#ff0';
ctx.fillText("STACKOVERFLOW", 5, 120);
// now parse bitmap based on grid
var idata = ctx.getImageData(0, 0, width, height);
// use a 32-bit buffer as we are only checking if a pixel is set or not
var buffer32 = new Uint32Array(idata.data.buffer);
// using two loops here, single loop with index-to-x/y is also an option
for(var y = 0; y < height; y += gridY) {
for(var x = 0; x < width; x += gridX) {
//buffer32[] will have a value > 0 (true) if set, if not 0=false
if (buffer32[y * width + x]) {
particles.push(new Particle(x, y));
}
}
}
// render particles
ctx.clearRect(0, 0, width, height);
particles.forEach(function(p) {
ctx.fillRect(p.x - 2, p.y - 2, 4, 4); // just squares here
})
But this way I am only showing one word, without any changes throughout the time. Also, I want to set up initially like 200 particles and reorganise them based on the pixel data, not create them on each scan.. How would you rewrite the code, so on every 1500ms I can pass a different letter and render it with particles?
Hopefully the different parts of this code should be clear enough : There are particles, that can draw and update, fillParticle will spawn particles out of a text string, and spawnChars will get a new part of the text rendered on a regular basis.
It is working quite well, play with the parameters if you wish, they are all at the start of the fiddle.
You might want to make this code cleaner, by avoiding globals and creating classes.
http://jsbin.com/jecarupiri/1/edit?js,output
// --------------------
// parameters
var text = 'STACKOVERFLOW';
var fontHeight = 80;
var gridX = 4,
gridY = 4;
var partSize = 2;
var charDelay = 400; // time between two chars, in ms
var spead = 80; // max distance from start point to final point
var partSpeed = 0.012;
// --------------------
var canvas = document.getElementById('cv'),
ctx = canvas.getContext('2d'),
width = ctx.canvas.width,
height = ctx.canvas.height,
particles = [];
ctx.translate(0.5,0.5);
// --------------------
// Particle class
function Particle(startX, startY, finalX, finalY) {
this.speed = partSpeed*(1+Math.random()*0.5);
this.x = startX;
this.y = startY;
this.startX = startX;
this.startY = startY;
this.finalX =finalX;
this.finalY =finalY;
this.parameter = 0;
this.draw = function() {
ctx.fillRect(this.x - partSize*0.5, this.y - partSize*0.5, partSize, partSize);
};
this.update = function(p) {
if (this.parameter>=1) return;
this.parameter += partSpeed;
if (this.parameter>=1) this.parameter=1;
var par = this.parameter;
this.x = par*this.finalX + (1-par)*this.startX;
this.y = par*this.finalY + (1-par)*this.startY;
};
}
// --------------------
// Text spawner
function fillParticle(text, offx, offy, spread) {
// fill some text
tmpCtx.clearRect(0,0,tmpCtx.canvas.width, tmpCtx.canvas.height);
tmpCtx.font = 'bold ' + fontHeight +'px sans-serif';
tmpCtx.fillStyle = '#A40';
tmpCtx.textBaseline ='top';
tmpCtx.textAlign='left';
tmpCtx.fillText(text, 0, 0);
//
var txtWidth = Math.floor(tmpCtx.measureText(text).width);
// now parse bitmap based on grid
var idata = tmpCtx.getImageData(0, 0, txtWidth, fontHeight);
// use a 32-bit buffer as we are only checking if a pixel is set or not
var buffer32 = new Uint32Array(idata.data.buffer);
// using two loops here, single loop with index-to-x/y is also an option
for(var y = 0; y < fontHeight; y += gridY) {
for(var x = 0; x < txtWidth; x += gridX) {
//buffer32[] will have a value > 0 (true) if set, if not 0=false
if (buffer32[y * txtWidth + x]) {
particles.push(new Particle(offx + x+Math.random()*spread - 0.5*spread,
offy + y+Math.random()*spread - 0.5*spread, offx+x, offy+y));
}
}
}
return txtWidth;
}
var tmpCv = document.createElement('canvas');
// uncomment for debug
//document.body.appendChild(tmpCv);
var tmpCtx = tmpCv.getContext('2d');
// --------------------------------
// spawn the chars of the text one by one
var charIndex = 0;
var lastSpawnDate = -1;
var offX = 30;
var offY = 30;
function spawnChars() {
if (charIndex>= text.length) return;
if (Date.now()-lastSpawnDate < charDelay) return;
offX += fillParticle(text[charIndex], offX, offY, spead);
lastSpawnDate = Date.now();
charIndex++;
}
// --------------------------------
function render() {
// render particles
particles.forEach(function(p) { p.draw();
});
}
function update() {
particles.forEach(function(p) { p.update(); } );
}
// --------------------------------
// animation
function animate(){
requestAnimationFrame(animate);
ctx.clearRect(0, 0, width, height);
render();
update();
//
spawnChars();
}
// launch :
animate();

Three.js image disappearing after a few calls to requestAnimationFrame()

I am trying to get some basic movement/refreshing working in Three.js. I've cut the problem back to the following code.
A sphere displays fine first render, and twice (dictate by Nb), but the image vanishes for 3 renders that are called via requestAnimationFrame(simulate) (for 4 it displays then disappears); Am I missing something in how repeated rendering should happen ?
var sphere, WIDTH, HEIGHT, VIEW_ANGLE, ASPECT, NEAR, FAR, renderer, camera, scene, sphereMaterial, radius, sphere, pointLight, container;
function init() {
WIDTH = 400;
HEIGHT = 300;
VIEW_ANGLE = 45;
ASPECT = WIDTH / HEIGHT;
NEAR = 0.1;
FAR = 10000;
container = $('#container');
renderer = new THREE.WebGLRenderer();
camera = new THREE.PerspectiveCamera( VIEW_ANGLE,
ASPECT,
NEAR,
FAR );
scene = new THREE.Scene();
camera.position.z = 200;
renderer.setSize( WIDTH, HEIGHT );
container.append(renderer.domElement);
sphereMaterial = new THREE.MeshLambertMaterial(
{
color: 0xCC0000
});
radius = 50; segments = 16; rings = 16;
sphere = new THREE.Mesh(
new THREE.SphereGeometry(radius, segments, rings),
sphereMaterial);
//sphere.position.z -= 100;
scene.add(sphere);
scene.add(camera);
pointLight = new THREE.PointLight( 0xFFFFFF );
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
};
var Nb = 3;
var j = 0;
function simulate() {
console.log("simulate " + sphere.position.z);
if (j == Nb) { return; }
j++;
//sphere.position.z -= 1;
render();
requestAnimationFrame(simulate);
};
function render() {
console.log("rendering" + sphere.position.z);
renderer.render(scene,camera);
};
init();
simulate();`
This is solved after an update of Chromium browser in this case (and possible related libs/drivers) to version 25.0.1346.160. Isolated by using jsfiddle as shown above by Tomalak.

AIR - Add Images From Drag&Drop To Stage

I'm building a Tile Viewer in Adobe AIR and so far I have implemented the Drag and Drop function that allows the user to load PNG and JPG files.
The function returns a ':File' format, but how can i gram the ':Bitmap' data and place the image in an existing movieclip while looping and make that one tile image repeat in x and y so a preview of a tilemap will shown ?
my thought was to simply add instances of that one file, but i dont know the exact code for that.
( create a Loader, pass the File format to that, and get the BitmapData and place it in place by defining position x and y and also defining width and height. also i want to make instances, to the code doesnt load the image 10 x 10 times )
i would be glad for all helpfull answers.
Edit:
example theory:
( without correct syntax )
image = new ImageFromDropBox(); // bitmap or whatever
for( x = 0; x < 10; x++) {
for( y = 0; y < 10; y++) {
image.x = tilesize * x;
image.y = tileSize * y;
image.width = tileSize;
image.height = tileSize;
stage.addChild( image ); // so each of the 100 places images should be just instances of the ONE LOADED IMAGE
}
}
When working with the File class, you can use the fileInstance.url parameter to load said file in a loader:
var loader:Loader = new Loader();
var urlReq:URLRequest = new URLRequest(file.url);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loaded);
loader.load(urlReq);
function loaded(e:Event):void {
var bmp:Bitmap = loader.content as Bitmap
//scale it down and create a scaled down bitmap data for more efficient memory usage
bmp.width = tileSize;
bmp.height = tileSize;
var bData:BitmapData = new BitmapData(tileSize,tileSize); //your new bitmap data
bData.draw(bmp); //capture the scaled down version of the bitmap
//if you don't need the original anymore, dispose of it
bmp.bitmapData.dispose();
var image:Bitmap;
for( var x:int = 0; x < 10; x++) {
for( var y:int = 0; y < 10; y++) {
image = new Bitmap(bmp.bData);
image.x = tilesize * x;
image.y = tileSize * y;
image.width = tileSize;
image.height = tileSize;
stage.addChild( image );
}
}
}