Optimizing canvas drawing on Android - java

I'm not sure why my code is (slightly) laggy, and doesn't run reliably at the same speed- I think I must be doing something wrong, as many other games are completely smooth.
The pertinent code in my Renderer class is here:
public void drawPicString(Location location, String string,
RenderParams params) {
//canvas.save();
float rawX = location.getX();
float rawY = location.getY();
Bitmap rawbit = myImageCache.get(string);
//Bitmap rawbit = provider.getBitmap(string);
float thisWidth = rawbit.getWidth();
float thisHeight = rawbit.getHeight();
if(params.shouldResize){
thisWidth = params.resizex;
thisHeight = params.resizey;
}
float leftx = rawX-(thisWidth/2);
float topy = rawY-(thisHeight/2);
Matrix matrix = new Matrix();
matrix.setTranslate(leftx, topy);
if(params.shouldResize){
float toScaleY = ((float)thisHeight)/rawbit.getHeight();
float toScaleX = ((float)thisWidth)/rawbit.getWidth();
matrix.preScale(toScaleX, toScaleY);
}
if(params.shouldRotate){
matrix.preRotate((float)Math.toDegrees(params.myangle),(float)(thisWidth/2.0),(float)(thisHeight/2.0));
}
canvas.drawBitmap(rawbit, matrix, paint);
}
The only other information I can think of that's pertinent is that myImageCache is a cache of bitmaps so that it doesn't have to fetch them from resources every time they're drawn. "canvas" is a canvas drawing on a surfaceview that's passed to it each frame before the drawing begins.
Thanks in advance for any ideas!

Related

Why on earth am I getting unfilled space between the edge of my shape and the fill of the same shape?

As part of an Android Wear watch face I started putting together, I have a class which implements rendering for one hand of a watch. It looks like this (after I edited out the bits which aren't relevant):
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
class Hand {
private final float centerX;
private final float centerY;
private final Path untransformedPath;
private final Path path = new Path();
private final Paint fillPaint;
private final Matrix matrix = new Matrix();
Hand2(Context context,
int widthId, int fillColorId, int strokeWidthId,
float centerX, float centerY, float handLength) {
this.centerX = centerX;
this.centerY = centerY;
float halfHandWidth = context.getResources().getDimension(widthId) / 2;
int fillColor = Workarounds.getColor(context, fillColorId);
fillPaint = new Paint();
fillPaint.setColor(fillColor);
fillPaint.setStrokeWidth(context.getResources().getDimension(strokeWidthId));
fillPaint.setStrokeCap(Paint.Cap.BUTT);
fillPaint.setStyle(Paint.Style.FILL_AND_STROKE);
fillPaint.setAntiAlias(true);
untransformedPath = new Path();
float centreRadius = context.getResources().getDimension(R.dimen.analog_centre_hand_start_radius);
float centreDegrees = (float) Math.toDegrees(Math.asin(halfHandWidth / centreRadius));
float startOffset = (float) Math.sqrt(centreRadius * centreRadius - halfHandWidth * halfHandWidth);
float centreStartDegrees = 90.0f - centreDegrees;
float centreSweepDegrees = 2.0f * centreDegrees;
untransformedPath.moveTo(-halfHandWidth, startOffset);
untransformedPath.lineTo(-halfHandWidth, handLength - halfHandWidth);
untransformedPath.lineTo(0, handLength);
untransformedPath.lineTo(halfHandWidth, handLength - halfHandWidth);
untransformedPath.lineTo(halfHandWidth, startOffset);
untransformedPath.arcTo(
-centreRadius, -centreRadius, centreRadius, centreRadius,
centreStartDegrees, centreSweepDegrees, false);
untransformedPath.close();
}
void updateAngle(float angleDegrees) {
matrix.reset();
matrix.setRotate(angleDegrees, 0, 0);
matrix.postTranslate(centerX, centerY);
untransformedPath.transform(matrix, path);
}
Path getPath() {
return path;
}
void draw(Canvas canvas) {
canvas.drawPath(path, fillPaint);
}
void updateHighQuality(boolean highQuality) {
fillPaint.setAntiAlias(highQuality);
}
}
I had noticed the rendering appeared to flicker for some reason and no amount of double buffering appeared to solve it. So I did a crazy thing and hit screenshot many times in a row and eventually caught a screenshot of what's going on:
You can see something like a black line going around one of the hands. If you look carefully, it isn't actually a black line, but rather, it's as if there is a gap in what got filled which is right next to the border of the shape.
What exactly is going on here? I'm rendering a single shape which is FILL_AND_STROKE, so it seems kind of crazy that it would somehow be leaving any of that shape unpainted. And yet, that is exactly what it appears to be doing.
If you're wondering about the glow painted below the hands, that is a separate thing rendered on the background by another class before the hands are drawn, which is why you don't see me doing that here. (The getPath() method is the way which that other class grabs the current path so that it knows where to put the glow.)
Further investigation:
Reversing the order of the vertices in the path has no effect.
Removing the curve in the path does appear to stop whatever is happening, interestingly.
Filling and stroking separately using exactly the same settings also appears to stop it happening.

Rotate an imageView at an angle using Seekbar

I simply want to rotate an image from min to max. But for that i have used multiple images to show Progress. Can some one suggest me a way to use a single image. which can rotate at an angle from min to max.
I know there are two possible ways to achieve it.
Using Animation Classes
Custom View
I simply want to rotate this image using SeekBar in any number of steps.
How can i achieve this?
To roate an Image
private void rotate(float degree) {
final RotateAnimation rotateAnim = new RotateAnimation(0.0f, degree,
RotateAnimation.RELATIVE_TO_SELF, 0.5f,
RotateAnimation.RELATIVE_TO_SELF, 0.5f);
rotateAnim.setDuration(0);
rotateAnim.setFillAfter(true);
imgview.startAnimation(rotateAnim);
}
Second Approach
imageView.setRotation(angle); // but requires API >= 11
I can use Matrix
Matrix matrix = new Matrix();
imageView.setScaleType(ScaleType.MATRIX); //required
matrix.postRotate((float) angle, pivX, pivY);
imageView.setImageMatrix(matrix);
How can i set Start and end angle to SeekBar min and max respectively. Which approach is better and Whether i must put it in FrameLayout to let it rotate freely.
You can do this way :
// load the origial BitMap (500 x 500 px)
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),
R.drawable.android);
int width = bitmapOrg.width();
int height = bitmapOrg.height();
// createa matrix for the manipulation
Matrix matrix = new Matrix();
// rotate the Bitmap
matrix.postRotate(45);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bitmapOrg, 0, 0, width, height, matrix, true);
// make a Drawable from Bitmap to allow to set the BitMap
// to the ImageView, ImageButton or what ever
BitmapDrawable bmd = new BitmapDrawable(resizedBitmap);
// set the Drawable on the ImageView
imageView.setImageDrawable(bmd);
For details check this out.
http://www.anddev.org/resize_and_rotate_image_-_example-t621.html
matrix.postRotate(45); //here 45 is the degree of angle to rotate
You can use a Matrix as suggested here
Matrix matrix = new Matrix();
imageView.setScaleType(ScaleType.MATRIX); //required
matrix.postRotate((float) angle, pivX, pivY);
imageView.setImageMatrix(matrix);
This would be an infinite rotation but you could spin it however you want.
This view just spins 360 degrees infinitely.
final RotateAnimation R = new RotateAnimation(0, 360, view.getWidth() / 2.0f, view.getHeight() / 2.0f);
R.setRepeatCount(Animation.INFINITE);
R.setDuration(800);
view.startAnimation(R);

setAngularVelocity rotates really slowly

I am using the basic libgdx box2d to manage physics operations of a game. Everything is working properly, except the rotations: even when I set
anyobject.body.setAngularVelocity(someLargeConstant);
the object rotates really slowly(and almost at the same speed) no matter what the 'someLargeConstant' is. Except when I use small numbers for parameter, it can rotate slower. Thus I think I somehow have a maximum angular velocity constant inside my world object, which should be set to some small value.
(I also had a similar issue with linear velocity before and I solved it by adjusting the pixels/meter scale. So its unlikely that the problem is a scaling issue.)
How can I enable the objects to rotate faster?
Here is the code I use:
private static World world = new World(new Vector2(0, 0), true); //Create a world with no gravity
to create an object I call another class
public Object(World world, short category, short mask, float x, float y, float radius, Sprite image,
float maxSpeed, float frictionStrength, float linearDamping, float angularDamping, boolean movable,
float elasticity, float mass){
this.world = world;
this.category = category;
this.mask = mask;
// We set our body type
this.bodyDef = new BodyDef();
if(movable==true){bodyDef.type = BodyType.DynamicBody;}else{bodyDef.type = BodyType.StaticBody;}
// Set body's starting position in the world
bodyDef.position.set(x, y);
bodyDef.linearDamping = linearDamping;
bodyDef.angularDamping = angularDamping;
// Create our body in the world using our body definition
this.body = world.createBody(bodyDef);
// Create a circle shape and set its radius
CircleShape circle = new CircleShape();
circle.setRadius(radius);
// Create a fixture definition to apply our shape to
fixtureDef = new FixtureDef();
fixtureDef.shape = circle;
fixtureDef.density = (float) (mass/(Math.PI*radius*radius));
fixtureDef.friction = frictionStrength;
fixtureDef.restitution = elasticity;
fixtureDef.filter.categoryBits = category;
fixtureDef.filter.maskBits = mask;
// Create our fixture and attach it to the body
this.fixture = body.createFixture(fixtureDef);
// BodyDef and FixtureDef don't need disposing, but shapes do.
circle.dispose();
... unrelated functions after that
}
and here I just try to make it rotate fast:
tempBall.body.setAngularVelocity(20000);
angularvilocity is used to set the direction of the rotation when it comes to use it with an actionListener as like key or mouse lister , here is an example of use :
case KeyEvent.VK_RIGHT:
ball.setAngularVelocity(-20); // Directly set the angular velocity
case KeyEvent.VK_LEFT:
ball.setAngularVelocity(20); // Directly set the angular velocity
like you can see here the code make the ball body rotate to the right in Key_Right pressed and to the left in Key_Left pressed , and i can play aroud with it's argument to increase or lower the rotation speed and it works pretty well for me , here is my body definition try to apply the same values and it must work with no problem :
private Body createObject(Shape shape, BodyType type, Vec2 position, float orientation, Sprite sprite) throws InvalidSpriteNameException {
for(Sprite s:spriteList) {
if(s.getName().equals(sprite.getName())) {
throw new InvalidSpriteNameException(sprite.getName()+" already used.");
}
}
Body body = null;
FixtureDef fixDef = new FixtureDef();
fixDef.shape = shape;
fixDef.density = 0.1f;
fixDef.isSensor = false;
fixDef.restitution = 0.1f;
BodyDef bodyDef = new BodyDef();
bodyDef.type = type;
bodyDef.angularDamping = 0.1f;
bodyDef.linearDamping = 0.1f;
bodyDef.fixedRotation = false;
bodyDef.gravityScale = 1f;
bodyDef.linearVelocity = new Vec2(0,0);
bodyDef.angularVelocity = 0;
bodyDef.position = new Vec2(position);
bodyDef.angle = orientation;
bodyDef.allowSleep = true;
spriteList.add(sprite); // Save the sprite to the list (sprites must be serialiazed in the PhysicalWorld)
bodyDef.userData = sprite; // Link the body and the sprite
do {
body = jBox2DWorld.createBody(bodyDef);
} while(body== null); // Wait until the object is really created
sprite.linkToBody(body); // Link the body to the sprite (this link is not serialiazed)
body.createFixture(fixDef);
return body;
}
I just found the problem, and it was pretty simple. Im just going to post this here for future googlers:
Object was actually rotating properly, the problem was in my drawing method, I didn't use conversion between radians to degrees in my batch.draw, and it interpreted everything in radians. I know, such an amateur mistake! Thanks a lot for your time.

Java Rotation Matrices & OBB

Hi I am attempting to build OBB's into my 3d java game using lwjgl. Currently I am just attempting to rotate the OBB around using matrix4f's and testing it by rendering the points. So, when I render it, with its xyx=0,0,0 and its angle on the x axis =1 it will rotate fine. But when i move the y axis up say y=5 the rotation will no longer go around the center.
I tried fixing this with translation but It doesnt work. Im also wondering if there is a way to access opengl's push/pop and rotate methods to get those variables for my points because opengl rotate does it perfectly.
This is my OBB class:
public OBB(float x, float y, float z, float angleX, float angleY, float angleZ, float sizeX, float sizeY, float sizeZ){
this.x=x;
this.y=y;
this.z=z;
this.angleX=angleX;
this.angleY=angleY;
this.angleZ=angleZ;
this.sizeX=sizeX;
this.sizeY=sizeY;
this.sizeZ=sizeZ;
posUBR = new Vector3f(x-sizeX,y+sizeY,z+sizeZ);//UpperBackRight
posUBL = new Vector3f(x-sizeX,y+sizeY,z-sizeZ);//UpperBackLeft
posUFL = new Vector3f(x+sizeX,y+sizeY,z-sizeZ);//UpperForLeft
posUFR = new Vector3f(x+sizeX,y+sizeY,z+sizeZ);//UpperForRight
posLBR = new Vector3f(x-sizeX,y-sizeY,z+sizeZ);//LowerBackRight
posLBL = new Vector3f(x-sizeX,y-sizeY,z-sizeZ);//LowerBackLeft
posLFL = new Vector3f(x+sizeX,y-sizeY,z-sizeZ);//LowerForLeft
posLFR = new Vector3f(x+sizeX,y-sizeY,z+sizeZ);//LowerForRight
posUBR=rotMat(posUBR);
posUBL=rotMat(posUBL);
posUFL=rotMat(posUFL);
posUFR=rotMat(posUFR);
posLBR=rotMat(posLBR);
posLBL=rotMat(posLBL);
posLFL=rotMat(posLFL);
posLFR=rotMat(posLFR);
}
This is my rotation method:
public Vector3f rotMatrix(Vector3f point) {
Matrix4f rotationMatrix = new Matrix4f();
rotationMatrix.m00 = point.x;
rotationMatrix.m10 = point.y;
rotationMatrix.m20 = point.z;
rotationMatrix.translate(new Vector3f(-x,-y,-z));
rotationMatrix.rotate(angleX,new Vector3f(1,0,0));
rotationMatrix.rotate(angleY,new Vector3f(0,1,0));
rotationMatrix.rotate(angleZ,new Vector3f(0,0,1));
rotationMatrix.translate(new Vector3f(x,y,-z));
return new Vector3f(rotationMatrix.m00, rotationMatrix.m10, rotationMatrix.m20);
}
public void rotate(){
posUBR=rotMatrix(posUBR);
posUBL=rotMatrix(posUBL);
posUFL=rotMatrix(posUFL);
posUFR=rotMatrix(posUFR);
posLBR=rotMatrix(posLBR);
posLBL=rotMatrix(posLBL);
posLFL=rotMatrix(posLFL);
posLFR=rotMatrix(posLFR);
}
My render function is a bit long to put in here but it basically renders a cube.
Sorry all I needed to do was this set to origin function:
public void setToOrigin(){
posUBR = new Vector3f(0-sizeX,0+sizeY,0+sizeZ);
posUBL = new Vector3f(0-sizeX,0+sizeY,0-sizeZ);
posUFL = new Vector3f(0+sizeX,0+sizeY,0-sizeZ);
posUFR = new Vector3f(0+sizeX,0+sizeY,0+sizeZ);
posLBR = new Vector3f(0-sizeX,0-sizeY,0+sizeZ);
posLBL = new Vector3f(0-sizeX,0-sizeY,0-sizeZ);
posLFL = new Vector3f(0+sizeX,0-sizeY,0-sizeZ);
posLFR = new Vector3f(0+sizeX,0-sizeY,0+sizeZ);
}

LWJGL: gluLookAt that returns Matrix4f in opengl 3+?

I'm trying to write a little game with third-person-camera and I'm just wondering about gluLookAt function. It works with Opengl 1.1, but I'm using 3.2 one so I need something that can return Matrix4f to me, but I didn't find anything on the Internet except some code in C++ and I found it extremely hard to translate it to the LWJGL (their API's are not the same, no sir). For example, I tried to remake this code (This link) :
// ----------------------------------------------------
// View Matrix
//
// note: it assumes the camera is not tilted,
// i.e. a vertical up vector (remmeber gluLookAt?)
//
void setCamera(float posX, float posY, float posZ,
float lookAtX, float lookAtY, float lookAtZ) {
float dir[3], right[3], up[3];
up[0] = 0.0f; up[1] = 1.0f; up[2] = 0.0f;
dir[0] = (lookAtX - posX);
dir[1] = (lookAtY - posY);
dir[2] = (lookAtZ - posZ);
normalize(dir);
crossProduct(dir,up,right);
normalize(right);
crossProduct(right,dir,up);
normalize(up);
float aux[16];
viewMatrix[0] = right[0];
viewMatrix[4] = right[1];
viewMatrix[8] = right[2];
viewMatrix[12] = 0.0f;
viewMatrix[1] = up[0];
viewMatrix[5] = up[1];
viewMatrix[9] = up[2];
viewMatrix[13] = 0.0f;
viewMatrix[2] = -dir[0];
viewMatrix[6] = -dir[1];
viewMatrix[10] = -dir[2];
viewMatrix[14] = 0.0f;
viewMatrix[3] = 0.0f;
viewMatrix[7] = 0.0f;
viewMatrix[11] = 0.0f;
viewMatrix[15] = 1.0f;
setTranslationMatrix(aux, -posX, -posY, -posZ);
multMatrix(viewMatrix, aux);
}
I can understand everything until "float aux[16]", then it just gets messy in my mind, especially in the end.
Can someone make it clear for me? Maybe someone already made "gluLookAt-clone" or something?
EDIT:
Thank you, Brett, now I must understand how to express that in code). You say that "aux" is a matrix, but we give it only 3 floats, so its better be a vector, BUT if it is a vector, so how do I multiply it with 4x4 ViewMatrix? And I cant find a way to just fill Matrix4f with numbers, there is no methods in lib to do that (most likely that because I'm noob and I can't find it, but hey, I really can't)
FINAL EDIT:
Finally I got it to work. I just wasn't understanding full matrix stuff that was required. Here is final working code, if someone interested(I guess not but whatever)). To deal with it, don't forget to setup projection matrix in the begining.
void setCamera(float posX, float posY, float posZ,
float lookAtX, float lookAtY, float lookAtZ) {
Vector3f dir = new Vector3f(lookAtX - posX, lookAtY - posY, lookAtZ - posZ);
Vector3f up = new Vector3f(0, 1f, 0);
Vector3f right = new Vector3f();
dir.normalise();
Vector3f.cross(dir,up,right);
right.normalise();
Vector3f.cross(right,dir,up);
up.normalise();
Matrix4f aux = new Matrix4f();
viewMatrix = new Matrix4f();
viewMatrix.m00 = right.getX();
viewMatrix.m01 = right.getY();
viewMatrix.m02 = right.getZ();
viewMatrix.m03 = 0.0f;
viewMatrix.m10 = up.getX();
viewMatrix.m11 = up.getY();
viewMatrix.m12 = up.getZ();
viewMatrix.m13 = 0.0f;
viewMatrix.m20 = -dir.getX();
viewMatrix.m21 = -dir.getY();
viewMatrix.m22 = -dir.getZ();
viewMatrix.m23 = 0.0f;
viewMatrix.m30 = 0.0f;
viewMatrix.m31 = 0.0f;
viewMatrix.m32 = 0.0f;
viewMatrix.m33 = 1.0f;
//setup aux as a translation matrix by placing positions in the last column
aux.m30 = -posX;
aux.m31 = -posY;
aux.m32 = -posZ;
//multiplication(in fact translation) viewMatrix with aux
Matrix4f.mul(viewMatrix, aux, viewMatrix);
}
I have a first-person camera class that I use (on OpenGL 3.2) which handles movement: x, y and z positions as well as pitch, yaw and roll. The way that I do this is update positions each cycle and a part of rendering I apply these updates from the camera by creating a new view matrix and sending it as a uniform to my vertex shader.
Here is the method that accomplishes this:
#Override
public void applyTranslations(int uniformLocation) {
viewMatrix = new Matrix4f();
Matrix4f.rotate(MatrixUtils.degreesToRadians(pitch), new Vector3f(1, 0, 0), viewMatrix, viewMatrix);
Matrix4f.rotate(MatrixUtils.degreesToRadians(yaw), new Vector3f(0, 1, 0), viewMatrix, viewMatrix);
Matrix4f.rotate(MatrixUtils.degreesToRadians(roll), new Vector3f(0, 0, 1), viewMatrix, viewMatrix);
Matrix4f.translate(new Vector3f(-x, -y, -z), viewMatrix, viewMatrix);
viewMatrix.store(matrix44Buffer); matrix44Buffer.flip();
glUniformMatrix4(uniformLocation, false, matrix44Buffer);
}
Where uniformLocation is the location of my viewMatrix uniform within my shaders.
Steps are to:
Create a new 4x4 matrix
Apply rotations for x, y and z axes
Apply translation for the x, y and z axes
Send matrix to shaders
The code is just populating the matrix as described in the gluLookAt() documentation.
Up until float aux[16], the code is creating an orthonormal basis (3 mutually perpendicular vectors as 'axes'). The properties of orthogonal rotation matrices allow viewMatrix elements to be set directly. aux is then (presumably) populated as a translation matrix, and the transforms are then concatenated with multMatrix.

Categories

Resources