I am a bit new to using blitting for graphics. But I have worked up a few demos myself, and I have been reading a lot of information on the methods used.
One common theme I have been seeing though is that all of them brute force rendering; drawing the farthest back object first and stepping through all other objects. even drawing objects that are going to be completely overlapped.
The reason all of them say this is that any kind of testing to see what should be drawn actually takes more time than just drawing everything with no checks.
Is there any kind of way to detect what should be drawn, that will run faster than just drawing everything?
It is hard to actually tell whether it is faster to check for what should be drawn or drawing stuff. You could maybe use both like If there is more than 5 images, use draw check, if not, draw them all. ...
So - the draw them all method is very obvious, and about the draw check it is like:
// drawCheck
var w:int = 300;
var h:int = 200;
var result:Bitmap = new Bitmap(w, h);
for (var x:int = 0; x < w; x++){
for (var y:int = 0; y < h; y++){
result.bitmapData.setPixel32(x, y, 0x00FFFFFF);
for (var iid:int = 0; iid < images.length; iid++){
var resC:uint = result.bitmapData.getPixel32(x, y);
var resA:uint = resC >>> 24;
var resR:uint = resC >>> 16 & 0xFF;
var resG:uint = resC >>> 8 & 0xFF;
var resB:uint = resC & 0xFF;
if (resA == 0xFF){
break;
}
var oriC:uint = images[iid].bitmapData.getPixel32(x, y);
var oriA:uint = oriC >>> 24 &;
var oriR:uint = oriC >>> 16 & 0xFF;
var oriG:uint = oriC >>> 8 & 0xFF;
var oriB:uint = oriC & 0xFF;
var newA:uint = resA + oriA;
var newR:uint = (256 / resA) * resR + (256 / oriA) * oriR;
var newG:uint = (256 / resA) * resR + (256 / oriA) * oriG;
var newB:uint = (256 / resA) * resR + (256 / oriA) * oriB;
var newC:uint = newA << 24 | newR << 16 | newG << 8 | newB;
result.bitmapData.setPixel32(x, y, newC);
}
}
}
Basically, MAYBE the drawing could be faster, but I am not sure - bitwise operations... Anyways - you should get the idea - this loops through X and Y coordinates and finally through the images.
Note: The images are stored like: 0 = front, images.length - 1 = back
It checks (HERE) if the resulting bitmap is already fully drawn by checking the alpha (if it equals 0xFF, there is no use of drawing), and if it is not, it merges the colours and adds the alpha.
You should do some performance tests so we know what is faster and when...
Related
I have an Avatar class that extends the Sprite class. I also have a Room class that contains a Bitmap of "walkable" areas and non-walkable areas, as well as the room's artwork itself.
I need to make sure that the user can only walk on the white/transparent parts of the room "mask," without actually showing the black and white mask. What functions can I use to ensure that the user doesn't go into the black parts of the mask, quickly?
You just need to have this 'walkable' bitmap in memory
private const ALLOWANCE:Number = .1;
private function isTurnAllowed(maskBMD:BitmapData, position:Point):Boolean
{
//0xAARRGGBB
var color32:uint = maskBMD.getPixel32(int(position.x), int(position.y));
var alpha:uint = color32 >> 24 & 0xff;
var red:uint = color32 >> 16 & 0xff;
var green:uint = color32 >> 8 & 0xff;
var blue:uint = color32 & 0xff;
var color24:uint =color32 >> 8 & 0xffffff;
/*
if (alpha == 0 || color24 == 0xffffff) return true
strictly speaking this string is enough but in real your bitmap mask after resampling can have some interpolation artifacts so you need some allowance to pass not strictly white.
*/
var absoluteLightness:Number = red + green + blue + (0xff - alpha);//transparent is 0x00
var maximalLight:Number = 0xff * 4;
var lightness:Number = absoluteLightness / maximalLight;
if (lightness > 1 - ALLOWANCE)
return true
else
return false
}
Lets say your Avatar:Sprite is properly aligned around its own (0,0) point. Then you create a Walkable:Sprite with a shape of walkable areas. It must be attached to the display list, but not necessarily visible, you can set Walkable.visible = false.
function moveBy(dx:Number, dy:Number):void
{
var aPoint:Point = new Point();
aPoint.x = Avatar.x + dx;
aPoint.y = Avatar.y + dy;
// hitTestPoint works with Stage coordinates.
aPoint = Avatar.parent.localToGlobal(aPoint);
if (Walkable.hitTestPoint(aPoint.x, aPoint.y, true))
{
Avatar.x += dx;
Avatar.y += dy;
}
}
This code is very simple, it just disallows to move your Avatar out of Walkable map.
I'm new to flash and can't find the bug in my code. I want a space ship to be able to accelerate in a vector, to not be able to accelerate over a max velocity, to keep that velocity vector when it stops accelerating but then suffer from friction (space dust ,) ). (It happens in a 2d stage.)
I think my math is correct, but I get a bug with velVector sometimes returns NaN - here's my code:
var friction:Number = .96;
var force:Number = .1;
var maxVel:Number = 3;
var accVector:Object = new Object();
var velVector:Object = new Object();
var velocity:Number;
var acceleration:Number;
If the ship points in the right direction it executes function 'accelerate' and if not it executes function 'drift'. It always executes 'moveship'
function accelerate():void {
curRotation.vx = Math.cos(rotation/180*Math.PI);
curRotation.vy = Math.sin(rotation/180*Math.PI);
var angle:Number = Math.atan2(curRotation.vy, curRotation.vx);
velocity = Math.sqrt(velVector.vx^2 + velVector.vy^2); //get velocity in both directions
acceleration = (maxVel - velocity)/maxVel*force; //this is to make it harder to accelerate when it approaches maxvelocity
accVector.vx = Math.cos(angle) * acceleration; //accelerate in each dimension
accVector.vy = Math.sin(angle) * acceleration;
velVector.vx += accVector.vx; //add acceleration to velocity
velVector.vy += accVector.vy;
}
function drift():void {
velVector.vx *= friction; //decrease velocity when not accelerating
velVector.vy *= friction;
}
function moveShip():void {
trace("velocity", velocity)
x += velVector.vx; //move
y += velVector.vy;
}
Many thanks!
I guess, your problem is in using of unsafe math operations without values validation. So, your velVector depends on accVector which depend on acceleration. You have a division operation counting "acceleration" val. That's it:
acceleration = (maxVel - velocity)/maxVel*force;
In AS3 you CAN divide by zero, and you'll get Infinity in such situation. But, there is also a chance to get NaN in case you are dividing zero by zero:
var acceleration:Number = 0 / 0;
trace(acceleration); //NaN
If you try doing something with NaN writing the result in Number or untyped variable (not int!), you'll always get NaN:
var a:Number = 5 * NaN;
trace(a); //NaN
So, I here is an example, which gives NaN:
acceleration = 0 / 0; //NaN
accVector.vx = Math.cos(angle) * acceleration; //NaN regardless on "angel" value
velVector.vx += accVector.vx; //NaN regardless on "velVector" value.
I hope, this will help.
Say I have the following setup in my AS code:
var color:String = "#0000FF"; //blue
var alpha:Number = 42; //42% or 42/100
How would I combine those into a #RRGGBBAA hex color? I've been Googling around and trying to figure out hexadecimal conversion and notation without luck.
There are two ways you could do this.
One is a bit hacky using int's toString() method and passing 16 as the radix/base:
var rgb:int = (int)("#0000FF".replace("#","0x"));//convert the string to a int (note you can type hex ints starting with 0x (e.g. 0x0000FF)
var a:int = 42;
var rgba:int = int("0x"+rgb.toString(16) + a.toString(16));
or the less hacky and probably faster computationally method using bitwise operators:
var rgb:uint = (uint)("#0000FF".replace("#","0x"));
//extract components using bit shifting (>>) and masking (0xFF)
var r:uint = rgb >> 16 & 0xFF;
var g:uint = rgb >> 8 & 0xFF;
var b:uint = rgb >> 0 & 0xFF;//same as rgb >> 0xFF, just added >> 0 to make the shift obvious
var a:uint = 42;
var rgba:uint = r << 24 | g << 16 | b << 8 | a;
var argb:uint = a << 24 | r << 16 | g << 8 | b;
//test
trace(rgba.toString(16));
trace(argb.toString(16));
Note that using toString(16) in the trace above is to make it useful to us humans,
you'd use the actual uint value when working with hex color values.
Also note that you might want to use ARGB in as3 sometimes, for example when working with BitmapData:
addChild(new BitmapData(100,100,true,0x2a0000ff));//add a 42% transparent blue box (100x100 px)
UPDATE
The above bitshift code snippet actually explains rgb extraction in detail which helps understand things better, but you already have rgb, so it's a matter of adding the alpha component. Also you mentioned 42% which is not the same as on a 0 to 255 scale. Therefore bellow lies your answer:
var rgb:uint = (uint)("#0000FF".replace("#","0x"));
var a:uint = (uint)((42 * .01) * 255);//map 42 from 0<>100 to 0<>255 ( *.01 is the same as / 100 but faster
var rgba:uint = rgb << 8 | a;
var argb:uint = a << 24 | rgb;
Regarding speed, if I run the two different conversion methods a million times here are execution times:
using strings (var rgba:int = int("0x"+rgb.toString(16) + a.toString(16));) takes 851 ms
using bitwise ops (var rgba:uint = rgb << 8| a;) takes 3 ms
As you can the bitwise version is much faster and for your case even less verbose than the string version. Also, now that you understand bitwise operators probably it's easier to read/understand.
In conclusion:
var color:String = "#0000FF"; //blue
var alpha:Number = 42; //42% or 42/100
var rgb:uint = (uint)(color.replace("#","0x"));
var a:uint = (uint)((alpha * .01) * 255);
var rgba:uint = rgb << 8 | a;
trace("hex: #",rgba.toString(16),"test",0x0000ff6b.toString(16));
Also, it's funny you mentioned Google, because you can use the search to convert to hex.
Update:
There seems to be a bit of confusion so I've split the 3 steps into functions:
converting a hex string to an int
converting a alpha percentage (0-100) to a 0-255 int
concatenating the above
Which would be:
function getHex(hexStr:String):uint{
return (uint)(hexStr.replace("#","0x"));
}
function getHexAlpha(alpha:uint):uint{
return (uint)((alpha * .01) * 255);
}
function rgbaConcat(rgb:uint,a:uint):uint{
return rgb << 8 | a;
}
trace("test",rgbaConcat(getHex("#FF9900"),getHexAlpha(50)).toString(16));
or all in one go:
function rgbaConcat(hexStr:String,alpha:uint):uint{
var rgb:uint = (uint)(hexStr.replace("#","0x"));
var a:uint = (uint)((alpha * .01) * 255);
return (rgb << 8 | a);
}
trace("test",rgbaConcat("#123456",100).toString(16));
I'm not sure if sprintf or something similar is available in action script, but you would use something like:
var alpha_2:int = Math.round(255*alpha/100);
var rgba:String = sprintf("%s%2x", color, alpha_2);
By the way, be sure to check whether it is supposed to be #RRGGBBAA or #AARRGGBB
So apparently sprintf is not available, you can use some substitute as mentioned in Is there something like printf in Action Script 3?
If you do not like to use a printf like function you can use:
function hex_char(value:int) {
if (value < 0)
return "X";
if (value < 10)
return String.fromCharCode("0".charCodeAt(0)+value);
if (value < 16)
return String.fromCharCode("A".charCodeAt(0)+value-10);
return "X";
}
var alpha_2:int = Math.round(255*alpha/100);
var rgba:String = color + hex_char(alpha_2/16) + hex_char(alpha_2%16);
alternatively you coulde use the following definition for hex_char which (I assume) will give you an exception/error for any value under 0 or over 15 instead of "X"
function hex_char(value:int) {
return "0123456789ABCDEF".charAt(value);
}
I know it was asked a thousand times before, but I still can't find a solution.
Searching SO, I indeed found the algorithm for it, but lacking the mathematical knowledge required to truly understand it, I am helplessly lost!
To start with the beginning, my goal is to compute an entire spectrogram and save it to an image in order to use it for a visualizer.
I tried using Sound.computeSpectrum, but this requires to play the sound and wait for it to end, I want to compute the spectrogram in a way shorter time than that will require to listen all the song. And I have 2 hours long mp3s.
What I am doing now is to read the bytes from a Sound object, the separate into two Vectors(.); Then using a timer, at each 100 ms I call a function (step1) where I have the implementation of the algorithm, as follows:
for each vector (each for a channel) I apply the hann function to the elements;
for each vector I nullify the imaginary part (I have a secondary vector for that)
for each vector I apply FFT
for each vector I find the magnitude for the first N / 2 elements
for each vector I convert squared magnitude to dB scale
end.
But I get only negative values, and only 30 percent of the results might be useful (in the way that the rest are identical)
I will post the code for only one channel to get rid off the "for each vector" part.
private var N:Number = 512;
private function step1() : void
{
var xReLeft:Vector.<Number> = new Vector.<Number>(N);
var xImLeft:Vector.<Number> = new Vector.<Number>(N);
var leftA:Vector.<Number> = new Vector.<Number>(N);
// getting sample range
leftA = this.channels.left.slice(step * N, step * (N) + (N));
if (leftA.length < N)
{
stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms);
return;
}
else if (leftA.length == 0)
{
stepper.removeEventListener(TimerEvent.TIMER, getFreq100ms);
return;
}
var i:int;
// hann window function init
m_win = new Vector.<Number>(N);
for ( var i:int = 0; i < N; i++ )
m_win[i] = (4.0 / N) * 0.5 * (1 - Math.cos(2 * Math.PI * i / N));
// applying hann window function
for ( i = 0; i < N; i++ )
{
xReLeft[i] = m_win[i]*leftA[i];
//xReRight[i] = m_win[i]*rightA[i];
}
// nullify the imaginary part
for ( i = 0; i < N; i++ )
{
xImLeft[i] = 0.0;
//xImRight[i] = 0.0;
}
var magnitutel:Vector.<Number> = new Vector.<Number>(N);
fftl.run( xReLeft, xImLeft );
current = xReLeft;
currf = xImLeft;
for ( i = 0; i < N / 2; i++ )
{
var re:Number = xReLeft[i];
var im:Number = xImLeft[i];
magnitutel[i] = Math.sqrt(re * re + im * im);
}
const SCALE:Number = 20 / Math.LN10;
var l:uint = this.total.length;
for ( i = 0; i < N / 2; i++ )
{
magnitutel[i] = SCALE * Math.log( magnitutel[i] + Number.MIN_VALUE );
}
var bufferl:Vector.<Number> = new Vector.<Number>();
for (i = 0; i < N / 2 ; i++)
{
bufferl[i] = magnitutel[i];
}
var complete:Vector.<Vector.<Number>> = new Vector.<Vector.<Number>>();
complete[0] = bufferl;
this.total[step] = complete;
this.step++;
}
This function is executed in the event dispatched by the timer (stepper).
Obviously I do something wrong, as I said I have only negative values and further more values range between 1 and 7000 (at least).
I want to thank you in advance for any help.
With respect,
Paul
Negative dB values are OK. Just add a constant (representing your volume control) until the number of points you want to color become positive. The remaining values that stay negative are usually just displayed or colored as black in a spectrogram. No matter how negative (as they might just be the FFT's numerical noise, which can be a huge negative dB number or even NaN or -Inf for log(0)).
I have several markers on my map and want to center dynamily each time I click on a selected point which show a bunch of markers group.
Does anyone know how to do that in As3?
You could try to use the a formula to get the centroid of the polygon drawn by your markers, assuming it's a polygon. If not, and they're a bunch of scattered points, you need to get the ones on that form the outer bounding segments first.Also, the code assumes the polygon is closed(loops), so the last point is your first point again.
function centreOfMass(polyPoints:Array):Point{
var cx:Number = 0;
var cy:Number = 0;
var area:Number = area(polyPoints);
var result:Point = new Point();
var i:Number,j:Number,n:Number = polyPoints.length;
var factor:Number = 0;
for(i = 0; i < n ; i++){
j = (i+1) % n;
factor = polyPoints[i].x * polyPoints[j].y - polyPoints[j].x * polyPoints[i].y;
cx += polyPoints[i].x + polyPoints[j].x * factor;
cy += polyPoints[i].y + polyPoints[j].y * factor;
}
area *= 6.0;
factor = 1 / area;
cx *= factor;
cy *= factor;
result.offset(cx,cy);//sets x and y to cx and cy
return result;
}
function area(polyPoints:Array):Number{
var i:int,j:int,n:int = polyPoints.length;
var area:Number = 0;
for(i = 0; i < n; i++){
j = (i+1) % n;
area += polyPoints[i].x * polyPoints[j].y;
area -= polyPoints[j].x * polyPoints[i].y;
}
area *= 0.5;
return area;
}
You create an array of points and you use the lat/lon coords as x,y coords. If you're using flash player 10, feel free to change the array into a Vector. and don't forget to do the import.flash.geom.Point.
I didn't come up with the code, I just ported what was on the amazing Paul Bourke website. Tons of handy stuff there.