efficiently rotate bitmap android canvas - android

I am working on a live wallpaper with several bitmaps. The bitmaps are to rotate continuously. This is how I am rotating one bitmap:
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
planet_1 = BitmapFactory.decodeResource(getResources(),
R.drawable.planet_1);
}
void draw(){
Matrix m_planet1 = new Matrix();
m_planet1.setRotate(r_planet1++,
planet_1.getWidth() / 2,
planet_1.getHeight() / 2);
m_planet1.postTranslate(
c.getWidth() / 2 - planet_1.getWidth() / 2,
c.getHeight() - planet_1.getHeight());
c.drawBitmap(planet_1, m_planet1, p1);
r_planet1 = r_planet1++;}
This works fine for one bitmap but I want to have more than 10 bitmaps rotating at different speeds. When I do this for the 10 bitmaps, it starts to lag. Is there any way to fix this?

You can use a hardware accelerated SurfaceView like this:
In the manifest, add this to your application section:
android:hardwareAccelerated="true"
In onCreate, do this:
setWillNotDraw(false); // Turns on drawing for SurfaceView, disabled by default
Then, in onDraw:
protected void onDraw(Canvas c) {
...do you drawing...
invalidate(); // Triggers redraw
}
Also, instead of rotating each planet, try moving the canvas around instead, that is usually simpler and probably quicker as well.
c.save();
c.translate(centerOfPlanetX, centerOfPlanetY);
c.rotate(numDegrees);
c.drawBitmap(.....);
c.restore();

Related

Surface view and scaling

Before all, I apologies for my English.
I'm a newbie in programming of Android App and I recently discovered the Canvas and Bitmap, so I decided to make a "Game" that can only use landscape. I discovered too the SurfaceView and how implement a background on my SurfaceView, but the first of all is that my background is an image 496x288.
I was searching in internet to learn how I can scaled well to fill all the screen of my phone with the background, and another screens. So I find this code and try to use in my app.
#SuppressLint("WrongCall")
#Override
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
Bitmap background = BitmapFactory.decodeResource(getResources(), R.drawable.fondo1escalado);
float scale = (float) background.getHeight() / (float) getHeight();
int newWidth = Math.round((background.getWidth() / scale));
int newHeight = Math.round(background.getHeight() / scale);
scaled = Bitmap.createScaledBitmap(background, newWidth, newHeight, true);
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
#SuppressLint("WrongCall")
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLUE);
canvas.drawBitmap(scaled, 0,0, null);
}
It works, but it doesn't work well. I got this:
(Don't worry about the character)
It scales well but don't fit all the screen (See the blue background).
So, like a good programmer I try to fix this using a GUI that I have designed wich dimensions are 296x912, I used the same code, and it scalates well in the Height but not in the Width for all the screens even a tablet.
This is the code of the GUI
float scalaInterfazAltura = (float) interfaz.getHeight() / (float) getHeight();
int newHeightInterfaz = Math.round(interfaz.getHeight() / scalaInterfazAltura);
int newWidthInterfaz = Math.round(interfaz.getWidth() / scalaInterfazAltura);
scaledinterfaz = Bitmap.createScaledBitmap(interfaz, newWidthInterfaz, newHeightInterfaz, true);
canvas.drawBitmap(scaledinterfaz, getWidth() - interfaz.getWidth()/2, 0, null);
So, my question is: There is a thing to scale well in Surfaceview? I'm doing something wrong?
Thank you.
It happens cause Your display height more than View height.
Try to use surfaceView.getHeight() instead of interfaz.getHeight().
Also You can use
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
in Your Activity before setContentView() to make Your game fullscreen.

Animate bitmaps inside canvas

I'm doing a Live Wallpaper. I have a Runnable (which is updated every 5 seconds) where I call a draw() method which draws something on a Canvas. It calls a method in another class which is supposed to draw a series of bitmaps (with a delay, so it's animated). How would I change the code below, so that the next bitmap would be drawn with a delay?
int imageToDraw = 10;
while(imageToDraw>=0)
{
Bitmap b = BitmapFactory.decodeResource(mContext.getResources(), mContext.getResources().getIdentifier("image_"+imageToDraw, "raw", mContext.getPackageName()));
float centerX = (width - b.getWidth())/2;
float centerY = (height - b.getHeight())/2;
Paint p = new Paint();
p.setColor(Color.BLACK);
mCanvas.drawRect(0f, 0f, (float)width, (float)height, p); //draws a rectangle to clear the screen
mCanvas.drawBitmap(b, centerX, centerY, new Paint());
--imageToDraw;
b.recycle();
}
From Android's API Guide on Canvas and Drawables:
Draw your graphics directly to a Canvas. This way, you personally call
the appropriate class's onDraw() method (passing it your Canvas), or
one of the Canvas draw...() methods (like drawPicture()). In doing so,
you are also in control of any animation.
This means you have to perform the animation yourself, frame by frame. If you don't actually need to draw complex graphics, consider switching back to standard views so you can use help class like AnimationDrawable. Check here for an example of how to do your own animation in Canvas.

Rotate 'something that can be drawn on Canvas'

Hi have a problem regarding rotation. I'm currently drawing a few Objects on a Canvas. Currently this 'Objects' are just Bitmaps. After computing the rotation and passing it inside a Matrix, I'm forced to create a new Bitmap to get it rotated. A small snippet will clearify this:
public void onDraw(Canvas canvas){
mMatrix = new Matrix();
mMatrix.postRotate(getRotation());
Bitmap rotateBmp = Bitmap.createBitmap(mBitmap, 0, 0, mBitmap.getWidth(), mBitmap.getHeight(), mMatrix, true);
canvas.drawBitmap(rotateBmp, mCoords.x -(mBitmap.getWidth() / 2), mCoords.y - ( mBitmap.getHeight() / 2), null);
rotateBmp.recycle();
}
All I can do to save memory is calling recycle on the rotateBmp, and it will run most of the time correctly. Lets assume I have 10 Bitmap 'Objects' I want to rotate. That means I have to keep ten Bitmaps as 'sample' and create ten new Bitmaps in every draw-cycle plus an additional new Matrix (didn't find a way to 'reset' it). This sounds very weird to me. Is there another Way, to create 'something that can be drawn on a Canvas' on the fly (no XML), while keeping control of their rotation. Any Idea is very welcome. If its a View, a Drawable or another CustomClass doesn't matter. Maybe it is important that getRotation will be all the same for every "Something" that should be drawn. What is the best practice to do that?
You can rotate the Canvas using canvas.rotate(float degrees)
public void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mCoords.x -(mBitmap.getWidth() / 2), mCoords.y - ( mBitmap.getHeight() / 2), null);
canvas.rotate(getRotation());
}
With the great help of #Jason Robinson and how To Rotate Text in Canvas I figured out a Way, of how I can do it. Here is a short snippet, that will work:
Bitmap mBitmap = getResources()...
int xPositionOfBitmap
int yPositionOfBitmap
public void onDraw(Canvas canvas) {
int bitmapCenterX = xPositionOfBitmap + (mBitmap.getWidth() / 2)
int bitmapCenterY = yPositionOfBitmap + (mBitmap.getHeight() / 2)
canvas.save()
canvas.rotate(getRotation(),bitmapCenterX, bitmapCenterY)
canvas.drawBitmap(mBitmap, xPositionOfBitmap, yPositionOfBitmap)
canvas.restore()
}

Live Wallpaper gradient banding: is it possible to use ARGB_8888 or dithering to fix?

I am creating a Live Wallpaper and I'm painting to the canvas on every
Runnable.run() call with a changing colour and i'm hoping to put a
gradient over the top but the gradient i'm creating is banding
horribly. After Googling around for a few days I came up with 2 solutions:
set the dither to true
set the canvas bitmap to ARGB_8888
i've tried doing the first one (set dither to true) on the
getWallpaper() accessor and the Paint object but it's not helped (I can't see
any dithering at all) so I've tried changing the canvas bitmap but i'm
not sure how to actually display it
// _canvasBmp = Bitmap.createBitmap(metrics.widthPixels, metrics.heightPixels, Bitmap.Config.ARGB_8888);
_shadowPaint.setStyle(Paint.Style.FILL);
_shadowPaint.setShader(new RadialGradient(metrics.widthPixels / 2,
metrics.heightPixels / 2, metrics.heightPixels / 2, 0x00000000,0x33000000, Shader.TileMode.CLAMP));
_shadowPaint.setDither(true); // this hasn't seemed to have done anything to fix the banding
// my main rendering method is this (based on the Google live wallpaper example)
void drawFrame()
{
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try
{
c = holder.lockCanvas();
// c.setBitmap(_canvasBmp);// this was my attempt to update the bitmap to one that was ARGB_8888 but it didn't render at all
if (c != null)
{
// draw something
drawBackground(c);
drawTouchPoint(c);
drawShading(c);
drawBorder(c);
getWallpaper().setDither(true); // yet another attempt to get some kind of dithering going to no avail
}
}
finally
{
if (c != null)
holder.unlockCanvasAndPost(c);
}
_handler.removeCallbacks(_drawClock); // _drawClock is the Runnable object
if (_isVisible)
{
_handler.postDelayed(_drawClock, 1000 / 25);
}
}
private void drawShading(Canvas c)
{
c.drawRect(_screenBounds, _shadowPaint); // _screenBounds is a Rect set to the _metrics width and height
}
Thanks in advance for your time
In your PinupEngine class...
#Override
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
surfaceHolder.setFormat(android.graphics.PixelFormat.RGBA_8888);
}
I have found that the LiveWallpaper drawing is slower with the larger bitmap pixel type. I expect it will also use a lot more memory (More than Double). If you can it might pay to try and limit the use of RGBA_8888 if possible. Default is RGB_565 I think, not sure about that.

Sprite Rotation in Android using Canvas.DrawBitmap. I am close, what am I doing wrong?

I have this sprite rotating algorithm (its poorly named and just used for testing). It is so close, sprites drawn with it do rotate. Everyframe I can add +5 degrees to it and see my nice little sprite rotate around. The problem is, the other stuff drawn to the canvas now flickers. If I don't do the rotation the regular drawn sprites work great. I think I am close but I just don't know what piece I am missing. Below is my two "Draw_Sprite" methods, one just draws the previously resource loaded bitmap to the canvas passed in. The other one, does some rotation the best I know how to rotate the sprite by so x many degrees..and then draw it. If I have a nice game loop that draws several objects, one type is the rotated kind. Then the non-rotated sprites flicker and yet the rotated sprite never does. Though if I draw the non-rotated sprites first, all is well, but then the Z-Ordering could be messed up (sprites on top of UI elements etc)... The method definitions:
/*************************************************
* rotated sprite, ignore the whatever, its for ease of use and testing to have this argument list
* #param c canvas to draw on.
* #param whatever ignore
* #param rot degrees to rotate
* #return
*/
public int Draw_Sprite(Canvas c, int whatever, int rot) {
//rotating sprite
Rect src = new Rect(0, 0, width, height);
Rect dst = new Rect(x, y, x + width, y + height);
Matrix orig = c.getMatrix();
mMatrix = orig;
orig.setTranslate(0, 0);
orig.postRotate(rot, x+width/2, y+height/2);
c.setMatrix(orig);
c.drawBitmap(images[curr_frame], src, dst, null);
c.setMatrix(mMatrix); //set it back so all things afterwards are displayed correctly.
isScaled=false;
return 1;
}
/********************************************************
* draw a regular sprite to canvas c
* #param c
* #return
*/
public int Draw_Sprite(Canvas c) {
Rect src = new Rect(0, 0, width, height);
Rect dst = new Rect(x, y, x + width, y + height);
c.drawBitmap(images[curr_frame], src, dst, null);
isScaled=false;
return 1;
}
And now the usage:
void onDraw(Canvas c)
{
canvas.drawRect( bgRect, bgPaint); //draw the background
//draw all game objects
// draw the normal items
for (GameEntity graphic : _graphics) {
graphic.toScreenCoords((int)player_x, (int)player_y);
if(graphic.getType().equals("planet")) //draw planets
graphic.Draw_Sprite(canvas); //before the rotation call draws fine
else
{
//rotate all space ships every frame so i see them spinning
//test rotation
mRot +=5;
if(mRot>=360)
mRot=0;
graphic.Draw_Sprite(canvas, 0, mRot); //yes function name will be better in future. this rotates spins draws fine
}
}
thePlayer.Draw_Sprite(canvas); //FLICKERS
drawUI(canvas);//all things here flickr
}
So it does do it, things after a call to a rotational draw are drawn correctly. But the problem is it flickrs. Now One could say I should just do all my non rotational stuff and save that last, but the zordering would be off.... suggestions as to how to tackle this issue of zordering or the flickering?
Just for the next guy who may read this. You can do this with only a few lines of code:
canvas.save();
canvas.rotate(rotation_angle, x + (widthofimage / 2), y + (heightofimage / 2));
canvas.drawBitmap(bitmap, x, y, null);
canvas.restore();
Try using canvas.save() before the rotation and canvas.restore() after manipulation is complete.
When performing manipulations on the canvas in order to change the way an object is drawn you have to remember the manipulations set how the canvas handles origins etc... So if you translate or rotate the canvas, that will be set for the lifetime of that canvas. In order to avoid this you first call save, which saves a snapshot of the canvas matrix before you manipulate it, then you run all your changes, then call restore which will restore the canvas back to the last saved point. Otherwise all your changes build up and you get unintended results.

Categories

Resources