AS3 - make a new bitmapData with non integar width and height - actionscript-3

Hey guys hoping someone can help here. I am cropping a bitmap image into tiles using the following function:
function crop( _x:Number, _y:Number, _width:Number, _height:Number, callingScope:MovieClip, displayObject:DisplayObject = null, pixelSnapping:Boolean = false):Bitmap
{
var cropArea:Rectangle = new Rectangle( 0, 0, _width, _height );
var croppedBitmap:Bitmap;
if(pixelSnapping == true)
{
croppedBitmap = new Bitmap( new BitmapData( _width, _height ), PixelSnapping.ALWAYS, true );
}
else
{
croppedBitmap = new Bitmap( new BitmapData( _width, _height ), PixelSnapping.NEVER, true );
}
croppedBitmap.bitmapData.draw( (displayObject!=null) ? displayObject : callingScope.stage, new Matrix(1, 0, 0, 1, -_x, -_y) , null, null, cropArea, true );
return croppedBitmap;
}
The width and height being passed in is a non integer value, for instance 18.75. When the new BitmapData is created it always rounds down the value to an integer, since the arguments for BitmapData are typed as such. In the case of my needs here, the width and height will not likely ever be integers. Is there a way to create these bitmap pieces of another image at the exact width and height I need or can a new bitmapData only be created with integer values for height and width?
Thanks for any insight.
EDIT: I realize you can't have a fraction of a pixel, but... What I am trying to achieve is dividing an image into tiles. I want the amount of tiles to be variable, say 4 rows by 4 columns, or 6 rows by 8 columns. the division of an image into X number of parts results in widths and heights in most cases to be non integar values like 18.75 for example. The goal is to divide an image up into tiles, and have that image appear, assembled seamlessly, above the source image, where I would then manipulate the individual tiles for various purposes (puzzle game, tiled animation to new scene, etc). I need the image, when assembled from all the tile pieces, to be an exact copy of the original image, with no lines between tiles, or white edges anywhere, and I need this to happen with non integer widths and heights for the bitmapData pieces that comprise the tiles. Does that makes sense? O_o

A BitmapData can only be created with integer values for the dimensions.
And to be honest, I'm trying to think what a BitmapData object with floating number values for dimensions would be like, but my brain starts to scream in pain: DOES NOT MAKE SENSE!
My brain's a bit melodramatic sometimes.
-- EDIT --
Answer to your edited question:
2 options:
1/ Just copy the original full bitmap data as many times as you have tiles and then use masks on bitmap objects to show the appropriate parts of the tiles
2/make sure no fractional widths or heights are generated by the slicing. For instance if you got an image of width 213 you want to split in 2 rows and 2 columns, then set the width of the left tiles to 106 and the width of the right tiles to 107
Maybe there are other options, but those two seem to me to be the easiest with most chance of success.

If you need to create tiles with preferred size you can use something like that:
private function CreateTilesBySize( bigImage:Bitmap, preferredTileWidth:int, preferredTileHeight:int ):Tiles
{
var result:Tiles = new Tiles();
var y:int = 0;
while ( y < bigImage.height ) {
result.NewRow();
const tileHeight:int = Math.min( preferredTileHeight, bigImage.height - y );
var x:int = 0;
while ( x < bigImage.width ) {
const tileWidth:int = Math.min( preferredTileWidth, bigImage.width - x );
const tile:Bitmap = Crop( x, y, tileWidth, tileHeight, bigImage );
result.AddTile( tile );
x += tileWidth;
}
y += tileHeight;
}
return result;
}
If you need to create exact amount of rows and calls. Then you can use:
private function CreateTilesByNum( bigImage:Bitmap, cols:int, rows:int ):Tiles
{
const preferredTileWidth:int = Math.ceil( bigImage.width / cols );
const preferredTileHeight:int = Math.ceil( bigImage.height / rows );
return CreateTilesBySize( bigImage, preferredTileWidth, preferredTileHeight );
}
But remember that tiles will have diferent sizes (last tiles in a row and last tiles in a column)

Related

Merge two images and retain its transparency

It was a while since I programmed AS3. Now I have a problem where I need to merge the two images where the upper image is a png that must retain its transparency. The upper image is an area that must pass through the lower image. A bit like a masked layer.
The result of this merge should result in a a display object. This object will later be sent to a method with the following signature:
public function addImage (
display_object:DisplayObject,
x:Number = 0,
y:Number = 0,
width:Number = 0,
height:Number = 0,
image_format:String = "PNG",
quality:Number = 100,
alpha:Number = 1,
resizeMode:String = "None",
blendMode:String = "Normal",
keep_transformation:Boolean = true,
link:String = ''
):void
Any advice is of the utmost interest. Thanks!
UPDATE;
After some struggling I've come up with this:
var bitmapDataBuffer:BitmapData = new BitmapData ( front.loader.width, front.loader.height, true );
bitmapDataBuffer.draw ( front.loader );
var bitmapOverlay:BitmapData = new BitmapData ( front.loader.width, front.loader.height, true );
bitmapOverlay.draw ( frontBanner.loader );
var rect:Rectangle = new Rectangle(0, 0, front.loader.width, front.loader.height);
var pt:Point = new Point(0, 0);
var mult:uint = 0x00;
bitmapOverlay.merge(bitmapDataBuffer, rect, pt, mult, mult, mult, mult);
var bmp:Bitmap = new Bitmap(bitmapOverlay);
pdf.addImage(bmp,0,0,0,0,ImageFormat.PNG,100,1,ResizeMode.FIT_TO_PAGE);
The problem is that my background image (represented by bitmapDataBuffer) will be totally overwritten by my upper image (the one I call overlay).
The overlay image is a png image. This image has a part of it that is transparent. Through this transparency I want to see my background image.
Any more suggestions?
You should be more specific about what kind of merge you want. You have a few options:
BitmapData.copyPixels - Provides a fast routine to perform pixel manipulation between images with no stretching, rotation, or color effects. This method copies a rectangular area of a source image to a rectangular area of the same size at the destination point of the destination BitmapData object.
BitmapData.merge - Performs per-channel blending from a source image to a destination image. For each channel and each pixel, a new value is computed based on the channel values of the source and destination pixels.
BitmapData.draw - Draws the source display object onto the bitmap image, using the Flash runtime vector renderer. You can specify matrix, colorTransform, blendMode, and a destination clipRect parameter to control how the rendering performs.
Each will work out for a different thing - the first will just copy some image over another (can keep/merge alphas). The second will merge channels data and modify them. The third one is the easiest and can draw one bitmap over another, as well as use blend modes.
Just chose one! :)
In order to make overlay image over the buffer image in your case, you are to use copyPixels() with mergeAlpha set to true.
bitmapDataBuffer.copyPixels(bitmapOverlay, rect, new Point(), null, null, true);
This will place data from bitmapOverlay to those parts of bitmapDataBuffer where overlay's alpha is above 0, blending semitransparent regions with the background.

Wierd behaviour with cc.PhysicsSprite in cocos2dx and chipmunk

I have a game where balls bounce around the screen - simple. I have walls that prevent balls from escaping the screen space. I use the following code to add balls to the screen, which happens with some delay between each addition:
var ball = {};
ball.markedForDelete = false;
ball.dir = spawnPoint.dir;
var ballMass = 1;
var ballBody = new cp.Body( ballMass, cp.momentForCircle( ballMass, 0, this._ballRadius, cp.v(0,0) ) );
ballBody.setPos( spawnPoint.position );
var magnitude = 200;
var ang = spawnPoint.dir * Math.PI / 180;
var vel = cp.v( magnitude * Math.cos(ang), magnitude * Math.sin(ang) );
var shape = new cp.CircleShape( ballBody, this._ballRadius, cp.v( 0,0 ) );
shape.ballShapeId = this.model.getNextBallShapeId ();
shape.setElasticity( 1 );
shape.setFriction( 0 );
shape.setCollisionType( collision_type );
this._space.addShape( shape );
ball.shape = shape;
shape.ball = ball;
var sprite = cc.PhysicsSprite.create( file );
sprite.nodeName = "Ball:" + shape.ballShapeId;
sprite.setColor( cc.c3b( 255, 255, 255 ) );
sprite.setScale ( 0.5 * this.ballScale );
sprite.setBody( ballBody );
this._gameStage.addChild( sprite, 5 );
ball.sprite = sprite;
sprite.ball = ball;
this._space.addBody( ballBody );
ballBody.setVel( vel ) ;
return ball;
This code works as expected when running on a device (iOS or Android), but when I use exact same code on the web things get weird. First, it spawns about 4 balls from random spawn points without a hitch, but all following balls get spawned on top of an existing ball and seems like the two (or three or four depending on how long you play) share physics. If I enable debug physics node:
cc.PhysicsDebugNode.create( this._space );
I can see that these balls are right over each other and they stack velocities. What is going on? Is there a limit on how many I can have? Any pointers? I am so lost and confused.
Thanks!
So, i figured it out. I created an array of 4 spawn points (notice it matches how many balls i was seeing). Each spawn point was something like this spawnPoint: { position: cc.p ( x, y ) }. So, when I assign position for the body using ballBody.setPos( spawnPoint.position ) it was not creating a new cc.p object to store position, but actually using the one i provided. So, every ball i created shared one of these 4 positions. On mobile devices this works properly and cc.p is actually created, instead of just pointing to the one I provided. Hope it helps someone.

libGDX texture a 2d terrain

I work at a truck game with libgdx and box2d.
In my game 1 meter = 100 pixels.
My 2d terrain is generated by me, and is made by points.
What I did, is made a polygonregion for the whole polygon and used texturewrap.repeat.
The problem is that, my game size is scaled down by 100 times, to fit the box2d units.
So my camera width is 800 / 100 and height 480 / 100. (8x4.8 pixels)
How I created my polygon region
box = new Texture(Gdx.files.internal("box.png"));
box.setFilter(TextureFilter.Linear, TextureFilter.Linear);
box.setWrap(TextureWrap.Repeat, TextureWrap.Repeat);
TextureRegion region = new TextureRegion(box);
psb = new PolygonSpriteBatch();
float[] vertices = new float[paul.size];
for (int i = 0; i < paul.size; i++) {
vertices[i] = paul.get(i);
if (i % 2 == 1)
vertices[i] += 1f;
}
EarClippingTriangulator a = new EarClippingTriangulator();
ShortArray sar = a.computeTriangles(vertices);
short[] shortarray = new short[sar.size];
for (int i = 0; i < sar.size; i++)
shortarray[i] = sar.get(i);
PolygonRegion pr = new PolygonRegion(region, vertices, shortarray);
System.out.println(vertices.length + " " + shortarray.length);
ps = new PolygonSprite(pr);
Now i'll just draw the polygonsprite to my polygonsprite batch.
This will render the texture on the polygon repeatedly, but the picture will be 100 times bigger and is very streched.
The left example is the one that i want to make, and the right one is the way that my game looks..
This PR was merged which looks like it does what you want:
https://github.com/libgdx/libgdx/pull/3799
See RepeatablePolygonSprite.
I am not completely sure if this will solve your problem (can't test it right now), but you need to set the texture coordinates of your TextureRegion to a higher value, probably your factor of 100.
So you could try to use region.setU2(100) and region.setV2(100). Since Texture Coordinates usually go from [0,1], the values higher than that will be outside. And because you set the TextureWrap to repeat, this will then repeat your texture over and over.
This way, the TextureRegion will alredy show your one texture repeated 100 times in x and y direction. If you then tell the PolygonSprite to use that region, it should show it as in the image you posted.
Hope that helps... :)
You could create a new texture by code. Take your level size and fill it with your texture then delete to background the top side. Look at pixelmap. Maybe this will help you.
Edit:
TextureRegion doesn't repeat to fit the size you even use texture, or you use TiledDrawable.

AS3 drawing lines, making shorter after that

i have a little math/coding problem witch i don`t have any idea how could i do it work in a simple way, so the problem is is need to make a line shorter, with 15
in my program i have :
http://gyazo.com/aff5ff61fb9ad3ecedde3118d9c0895e
the line takes the center coordinates of both circles and draws from one to another, but i need it to be from the circumference of the circles, so it wont get inside
the code im using is :
var line:Shape = new Shape();
line.graphics.lineStyle(3,0xFF0000,2);
line.graphics.moveTo(sx,sy);
line.graphics.lineTo(fx,fy);
this.addChild(line);
arrow2(sx,sy,fx,fy);
var rline:Shape = new Shape();
rline.graphics.lineStyle(3,0xFF0000,2);
rline.graphics.moveTo(fx,fy);
rline.graphics.lineTo(xa,ya);
this.addChild(rline);
var rline2:Shape = new Shape();
rline2.graphics.lineStyle(3,0xFF0000,2);
rline2.graphics.moveTo(fx,fy);
rline2.graphics.lineTo(xb,yb);
this.addChild(rline2);
the rline and rline2 function is for the arrow lines, now my question is how do i make it shorter not depending on it direction so it will not overlap the circle
You can use vectors to solve your problem; they're pretty easy to get the hang of, and pretty much indispensable for things like game dev or what you're trying to do. You can get an overview here: http://www.mathsisfun.com/algebra/vectors.html or by searching "vector math" in google
So first step is to get a vector from one circle to another (pretty much what you've done):
var vector:Point = new Point( circle2.x - circle1.x, circle2.y - circle1.y );
var length:Number = vector.length; // store the length of the vector for later
This is the equivalent of saying "if you start at circle1 and move along vector, you'll arrive at circle2"
Next thing we're going to do is normalise it; all this does is set the length to 1 - the direction is unchanged - this makes it easier to work with for what you're looking to do. A vector with length 1.0 is called a unit vector:
vector.normalize( 1.0 ); // you can pass any length you like, but for this example, we'll stick with 1.0
Now, to draw a line from one circle to another, but starting from the outside, we simply find the start and the end points. The starting point is simple the position of circle1 plus vector (normalised to unit length) multiplied by the radius of circle1:
var sx:Number = circle1.x + vector.x * circle1.radius; // or circle1.width * 0.5 if you don't store the radius
var sy:Number = circle1.y + vector.y * circle1.radius;
The ending point can be found by starting at our start point, and continuing along our vector for a distance equal to the distance between the two circles (minus their radii). The length value that we created earlier is the distance between your two circles, from one center point to another, so we can use that to get the distance minus the radii:
var dist:Number = length - ( circle1.radius + circle2.radius ); // or circle1.width * 0.5 etc
And so the end point:
var ex:Number = sx + vector.x * dist;
var ey:Number = sy + vector.y * dist;
And to draw the line between them:
var line:Shape = new Shape;
line.graphics.lineStyle( 1.0, 0x000000 );
line.graphics.moveTo( sx, sy );
line.graphics.lineTo( ex, ey );
this.addChild( line )

Scaling and cropping an image with fixed dimensions

I want to display an image, and it should be transformed like this:
If for example, my original image is 200x300 pixels in size, I want it to have a width of 150, and then scale the height accordingly. Then I want to crop the image, so that the result has a dimension of 150x150 pixels.
I've tried several ways, but haven't figured out how to do it.
You can calculate the scale factor by dividing new width by old width:
var scale : Number = 150 / myImage.width;
myImage.scaleX = myImage.scaleY = scale;
To crop, either use a mask on the scaled clip, or draw to a new Bitmap:
var myBitmapData : BitmapData = new BitmapData ( 150, 150 );
// use concatenated matrix of the image to scale the new bitmap data
var matrix : Matrix = myImage.transform.concatenatedMatrix;
myBitmapData.draw ( myImage, matrix );
var myBitmap : Bitmap = new Bitmap ( myBitmapData );
addChild ( myBitmap );
Depending on how many images there are, and what you are going to do with them later, you should always test both possibilities for performance.