6
\$\begingroup\$

I have a Surfaceview thread and an off canvas texture bitmap that is being generated (changed), first row (line), every frame and then copied one position (line) down on regular surfaceview bitmap to make a scrolling effect, and I then continue to draw other things on top of that. Well that is what I really want, however I can't get it to work even though I am creating a separate canvas for off screen bitmap. It is just not scrolling at all.

I other words I have a memory bitmap, same size as Surfaceview canvas, which I need to scroll (shift) down one line every frame, and then replace top line with new random texture, and then draw that on regular Surfaceview canvas.

Here is what I thought would work;

My surfaceChanged where I specify bitmap and canvasses and start thread:

@Override
    public void surfaceCreated(SurfaceHolder holder) {
    intSurfaceWidth = mSurfaceView.getWidth();
    intSurfaceHeight = mSurfaceView.getHeight();

    memBitmap = Bitmap.createBitmap(intSurfaceWidth, intSurfaceHeight,
            Bitmap.Config.ARGB_8888);
    memCanvas = new Canvas(memCanvas);

    myThread = new MyThread(holder, this);
    myThread.setRunning(true);
    blnPause = false;
    myThread.start();
}

My thread, only showing essential middle running part:

@Override
public void run() {
  while (running) {     
    c = null;
try {
    // Lock canvas for drawing
    c = myHolder.lockCanvas(null);                  

    synchronized (mSurfaceHolder) {
        // First draw off screen bitmap to off screen canvas one line down
        memCanvas.drawBitmap(memBitmap, 0, 1, null);

        // Create random one line(row) texture bitmap
        memTexture = Bitmap.createBitmap(imgTexture, 0, rnd.nextInt(intTextureImageHeight), intSurfaceWidth, 1);

        // Now add this texture bitmap to top of off screen canvas and hopefully bitmap
        memCanvas.drawBitmap(textureBitmap,
                    intSurfaceWidth, 0, null);

        // Draw above updated off screen bitmap to regular canvas, at least I thought it would update (save changes) shifting down and add the texture line to off screen bitmap the off screen canvas was pointing to.
        c.drawBitmap(memBitmap, 0, 0, null);

        // Other drawing to canvas comes here


    } finally {
        // do this in a finally so that if an exception is thrown
        // during the above, we don't leave the Surface in an
        // inconsistent state
        if (c != null) {
            myHolder.unlockCanvasAndPost(c);
        }
    }
}       
}

For my game Tunnel Run.
Right now I have a working solution where I instead have an array of bitmaps, size of surface height, that I populate with my random texture and then shift down in a loop for each frame. I get 50 frames per second, but I think I can do better by instead scrolling bitmap.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

At the moment, you are taking the super-fast fps but incredibly memory inneficient approach. I'm not expert with surfaceview / canvas but I'd guess you should be using this: public void drawBitmap (int[] colors, int offset, int stride, float x, float y, int width, int height, boolean hasAlpha, Paint paint) (link)

and setting x to be 0 and y to be the number of lines from the top the bitmap should be. Or maybe try using canvas.translate(0,number_of_lines); before each call of canvas.drawbitmap

UPDATE:

Again, use with caution, but this code will scroll the icon down the screen:

package com.example.SurfaceViewTest;

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Paint.Style;
import android.graphics.drawable.BitmapDrawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class SurfaceViewTest extends Activity implements SurfaceHolder.Callback {

SurfaceView mSurfaceView;
SurfaceHolder mSurfaceHolder;
int intSurfaceWidth, intSurfaceHeight;

static Paint paint = new Paint();

static final String TAG = null;

// bitmap to scroll
Bitmap myScrollingImg;

// y position of bitmap. 0 = top of screen
int y = 0;

// check that surface has not been destroyed
boolean surfaceExists = false;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.main);

    mSurfaceView = (SurfaceView) this.findViewById(R.id.Surface);
    mSurfaceHolder = mSurfaceView.getHolder();
    mSurfaceHolder.addCallback(this);

    paint.setColor(0xFFFFFFFF);
    paint.setStyle(Style.STROKE);

    // load the bitmap
    myScrollingImg = ((BitmapDrawable)getResources().getDrawable(R.drawable.icon)).getBitmap();

}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
        int height) {}

@Override
public void surfaceCreated(SurfaceHolder holder) {

    // start a new thread. According to android docs, canvas can only be
    // manipulated between surfaceCreated and surfaceDestroyed
    surfaceExists = true;
    MyThread thread = new MyThread();
    thread.execute(null);

}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
        surfaceExists = false;
}

class MyThread extends AsyncTask{

    @Override
    protected Object doInBackground(Object... params) {

        while(surfaceExists){
        Canvas rCanvas = mSurfaceHolder.lockCanvas();

        // reset the canvas to blank at the start
        rCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        // translate to the desired position
        rCanvas.translate(0,y);

        // draw the bitmap
        rCanvas.drawBitmap(myScrollingImg, 0, 0, paint);

        mSurfaceHolder.unlockCanvasAndPost(rCanvas);

        // increment y so that bitmap is 1 pixel further down next call     
        y++;        
        }
        return null;
    }

}

}
\$\endgroup\$
8
  • \$\begingroup\$ Thanks I appreciate your help, however there seems to be an issue with shifting (scrolling) off screen bitmap via off screen canvas in a SurfaceView. See my post here for test code proving my claim stackoverflow.com/questions/8821880/… \$\endgroup\$
    – DKDiveDude
    Jan 12, 2012 at 14:12
  • \$\begingroup\$ Yep don't work with; memCanvas.translate(0,1); memCanvas.drawBitmap(memBitmap, 0, 0, paint); Again this smears bitmap down on screen. Moving it up works (-1) works fine - weird! Again see here for exact sample code showing problem stackoverflow.com/questions/8821880/… \$\endgroup\$
    – DKDiveDude
    Jan 12, 2012 at 15:52
  • \$\begingroup\$ again, no guarantees this will work as I've not tried it, but it sounds like the screen is not getting cleared after each frame / call of onSurfaceChanged() (hence the 'smearing'). Try putting canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR) as the last line in onSurfaceChanged(). See stackoverflow.com/questions/5729377/… \$\endgroup\$ Jan 12, 2012 at 16:06
  • \$\begingroup\$ Where are you suggesting I add this, the code on this page or in the link I listed showing problem, and exactly where - After unlocking canvas. And what canvas to use; Off memory canvas or on screen canvas? \$\endgroup\$
    – DKDiveDude
    Jan 12, 2012 at 19:54
  • \$\begingroup\$ Ok realized canvas had to be on-screen canvas, and after unlcocking canvas, in which case it still smears. Tried with SDK 1.5 up to 2.1. Again remember scrolling up works fine, even without clearing canvas. No this is something else strange - A seemingly bug! \$\endgroup\$
    – DKDiveDude
    Jan 12, 2012 at 19:57
1
\$\begingroup\$

I had this problem and figured out why this happened. It's due to a bit of negligence in android's translate function. Basically it's because the function ONLY works by copying pixels left-to-right per row from top-to-bottom, without checking which direction you're translating to. So going left and down, it works because it is copying existing pixels to 'old' pixels, but the other way around, you're copying pixels to pixels you want to copy later on.

The trick to solving this is to use the faster translate function in the right/down translation, but use a slower, bitmap copying way for the other directions. Or maybe find some way to override the translate function with one which does it correctly, but I haven''t figured out how to do that yet.

I have a little writeup here: http://spiralcode.wordpress.com/2013/06/17/long-time-no-see-of-rivers-cartography-and-tiling/

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .