I am experimenting with SurfaceView. My requirement is to simply render a node (simple drawable) first. Then, render more nodes at a later point in time.
The snippets of my thread's run method & my doDraw method are below. I am just trying to render 2 different drawables in subsequent passes while retaining both. The problem is it wipes away whatever gets written in 1st pass (see comment in code). How to retain the previously drawn object?
public void run() {
Canvas canvas;
while (_running) {
canvas = null;
try {
canvas = _surfaceHolder.lockCanvas(null);
synchronized (_surfaceHolder) {
doDraw(canvas, isUpdate);
}
} finally {
if (canvas != null) {
_surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
public void doDraw(Canvas canvas, boolean update){
if(update){
//goes here 2nd pass & wipes away things done in 1st pass
//I want to retain whatever was drawn in 1st pass
Bitmap thumb = BitmapFactory.decodeResource(getResources(),R.drawable.icon);
//canvas.drawColor(Color.RED);
canvas.drawBitmap(thumb, 0, 0, null);
} else{
//goes here 1st pass
Bitmap thumb = BitmapFactory.decodeResource(getResources(), R.drawable.thumb);
//canvas.drawColor(Color.BLACK);
canvas.drawBitmap(thumb, 300, 300, null);
this.isUpdate = true;
}
}
UPDATE 1:
Still does not seem to work. I changed the run code to this passing a non:
public void run() {
Canvas canvas;
while (_running) {
canvas = null;
try {
Rect dty = null;
if(isUpdate == true){
//--> In 2nd pass, I was hoping that only these co-ordinates will be updated
dty = new Rect(0,0,100,100);
canvas = _surfaceHolder.lockCanvas(dty);
}else{
canvas = _surfaceHolder.lockCanvas(null);
}
synchronized (_surfaceHolder) {
doDraw(canvas, isUpdate);
}
} finally {
if (canvas != null) {
_surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
Later I tried passing 0,0,1,1 to dirty rectangle. Could not get it to work yet...
The SurfaceView is double- or triple-buffered. The previous contents are "preserved" in the sense that the system doesn't go out of its way to clear older buffers, but you can't rely on that behavior.
If you specify a dirty rect, the framework will render whatever you ask, then copy the non-dirty region from the previous buffer on top of the new buffer.
The system is allowed to expand the dirty rectangle -- the Rect you pass to lockCanvas() may be updated. You're required to redraw every pixel inside it.
For a (somewhat eye-searing) example of this in action, see "Simple Canvas in TextureView" in Grafika.
For more details on how the system works, see this article.
I found this interesting note in the Android documentation:
The content of the Surface is never preserved between unlockCanvas()
and lockCanvas(), for this reason, every pixel within the Surface area
must be written. The only exception to this rule is when a dirty
rectangle is specified, in which case, non-dirty pixels will be
preserved.
So to do what you are trying to do, it looks like you need to provide a non-null dirty rectangle in your lockCanvas call. Also, this will only work as long as none of your node pixels intersect.
Related
I'm working on a game that at some point needs to run for x steps to update certain objects on the screen. As of now, all that's seen by the player is step 0, and step x. How do i show each step in-between for a set time?
I've tried adding a check to make sure onDraw() is called in-between, as well as sleeps, making sure to call PostInvalidate after each step is done in hopes of forcing onDraw() to be called, but haven't had any luck.
(ex. In step function, have a bool "nextStep" that is set to false at the end of the function, onDraw() sets this to true at the end. If it's false, step function will wait to update the 2d array of objects that gets drawn to the screen until "ondraw" sets the bool to true.)
This works
for (int i = 0; i < steps; i++) {
// postDelayed fixes this because it tells the GPU to draw on next cycle otherwise, it has already passed the draw by now.
postDelayed(new Runnable() {
#Override
public void run() {
Canvas c = null;
step(CellType.PLAYER);
invalidate();
try {
c = getHolder().lockCanvas();
synchronized (getHolder()) {
draw(c);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (c != null) {
getHolder().unlockCanvasAndPost(c);
}
}
}
}, stepDelay*i);
}
I am working on a live wallpaper with a scrolling background. I have two bitmap objects which I alternate between in order to keep the previously drawn pixels for the next frame. I draw a new line at the top of the canvas, then call drawBitmap to copy the rest of the pixels onto the canvas.
I am using a Runnable object to do the heavy lifting. It does all copying and calculations required and then locks the canvas, enters a synchronous block on the holder, and makes a single call to Canvas.drawBitmap(bitmap,rect,rect,paint). Occasionally there will be a white flash on the screen, which seems to correlate with high CPU activity. In using traceview, I found that the drawBitmap operation, specifically Canvas.native_drawBitmap(), is taking much longer than normal. Typically it completes in 2-4msec, but when I see a white flash, it can take anywhere from 10 to 100 msec.
private void draw() {
SurfaceHolder holder = getSurfaceHolder();
Canvas canvas = null;
prepareFrame();
try {
canvas = holder.lockCanvas();
synchronized (holder) {
if (canvas != null) {
drawFrame(canvas);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
afterDrawFrame();
handler.removeCallbacks(drawRunner);
if (visible) {
handler.post(drawRunner);
}
}
The draw() function is called in the run() of the Runnable.
private void prepareFrame() {
num++;
if (num%2 == 0) {
mainBmp = mainBmp1;
mainCan.setBitmap(mainBmp1);
mainCan.drawBitmap(mainBmp2, source, destination, null);
} else {
mainBmp = mainBmp2;
mainCan.setBitmap(mainBmp2);
mainCan.drawBitmap(mainBmp1, source, destination, null);
}
}
The prepareFrame() function is how I keep hold of the previous pixels I've drawn. The Rect called source is one row short of full screen sized at the bottom, where as destination is one row short at the top. The drawBitmap() calls in prepareFrame() are never longer than 2-4msec.
private void drawFrame(Canvas can) {
can.drawBitmap(mainBmp, source, destination,null);
}
This single operation is done on the canvas while holding the lock.
private void afterDrawFrame() {
ca.calcNextRow();
mainBmp.setPixels(ca.getRow(), 0, canWidth, 0, 0, canWidth, 1);
}
Then the next new row of pixels is drawn onto one of my bitmaps in memory.
I have tried using the various signatures of drawBitmap() but only found them slower on average and still resulting in the anomalous white flashes.
My overall speed is great. Without the intermittent flashes, it works really well. Does anyone have suggestions on how to eliminate the flashes?
It's kind of hard to know exactly what's going on here because you're not including the definition or use of some central variables like "mainCan" or "ca". A more complete source reference would be great.
But...
What's probably happening is that since drawFrame(canvas) is synchronized on holder, but
handler.post(drawRunner);
is not, there will be occurences where you are trying to draw mainBmp to the system canvas at the same time as you are writing to it in prepareFrame().
The best solution to this problem would probably be some kind of double buffering, where you do something like
1) Write to a temporary bitmap
2) Change the ref of that bitmap to the double buffer i.e. mainBmp = tempBitmap;
The main objective is to never do long writes to the variables you are using for system canvas rendering, just change the object reference.
Hope this helps.
I have a live wallpaper which I created using the android canvas. Upon testing, I felt it necessary to harness the power of OpenGL, and so am experimenting with AndEngine. I am wondering how I can achieve the following.
I have a background image that fills the whole screen, with many smaller bitmaps floating over the top (not animated movements)
So far I have this for the background image:
#Override
public void onLoadResources()
{
mtexture = new Texture(1024, 1024, TextureOptions.BILINEAR);
TextureRegionFactory.setAssetBasePath("gfx/");
mtextureRegion = TextureRegionFactory.createFromResource(mtexture , this, R.drawable.background1, 0, 0);
this.mEngine.getTextureManager().loadTexture(this.mtexture );
}
#Override
public Scene onLoadScene(){
final Scene scene = new Scene(1);
Sprite background = new Sprite(0, 0, CAMERA_WIDTH*2, CAMERA_HEIGHT, mtextureRegion )
SpriteBackground sb = new SpriteBackground(background);
scene.setBackground(sb);
scene.setBackgroundEnabled(true);
return scene;
}
This works fine for the background, but I require moving sprites.
In my canvas code, I do the following to update the position & physics of the moving objects and draw the canvas every few ms
private final Runnable drawScreen = new Runnable() {
public void run() {
drawFrame();
}};
-
void drawFrame() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
//draw
}
} finally {
if (c != null) holder.unlockCanvasAndPost(c);
}
mHandler.removeCallbacks(drawScreen);
mHandler.postDelayed(drawScreen, 10);
}
What is the appropriate way to do this on AndEngine? do I use the same code and substitute openGL calls?
I had a look at GLEngine, am I supposed to send Runnables to the GlThread queue?
EDIT - I think I found the answer...an UpdateHandler. But how can I inform the handler of an update (i.e. to call the onUpdate method). If I make a timed Handler, what happens if I call too often, does a queue of requests build up?
First of all, don't use the constructor Scene(int), it's deprecated. Use Scene() instead.
Correct, you should use an update handler.
You can create an UpdateHandler, and then register it to your scene:
scene.registerUpdateHandler(mUpdateHandler);
This way, the code in mUpdateHandler.onUpdate method is executed each time the scene updates (Each frame.). You don't call it manually. If you want to stop it, call:
scene.unregisterUpdateHandler(mUpdateHandler);
So, the onUpdate method is always executed in the UpdateThread, so you can be sure you can do any change to entities you want there. So you can move around and sprite you want, etc...
By the way, why is the background's width CAMERA_WIDTH*2? It means that only the left half of your sprite is shown. If you don't plan moving the camera, then the right half won't ever show.
I'm writing a little game as part of a programming assignment. I've got my sprites/images moving the way I want to, except they're very jerky. Here's how I'm loading up the image:
//missile and paint are private members of the animation thread
missile = Bitmap.createBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.missile));
paint = new Paint();
paint.setAntiAlias(true);
startTime = new Date().getTime();
The run method of my thread is like this:
#Override
public void run() {
while(running) {
Canvas canvas = null;
try {
canvas = surfaceHolder.lockCanvas(null);
synchronized (surfaceHolder) {
updatePhysics();
doDraw(canvas);
}
} finally {
if(canvas != null) {
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
updatePhysics simply performs calculations and stores the value inside a private member of the thread called projectilePosition:
function updatePhysics() {
projectilePosition = interceptionCalculationService.positionAtTime((startTime - new Date().getTime()) / 1000);
}
Then, in my doDraw method I do:
private void doDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
...
//I update the position of the missile
canvas.drawBitmap(missile, (int) projectilePosition.getDistance() * 2, (int) (getHeight() - (95 + projectilePosition.getHeight())), paint);
canvas.restore();
}
The problem is that the animation is extremely jerky. The missile follows the correct path, but it's not smooth at all. I assume that's because I don't really control the interval when the thread's run method is called. How can I make my animation more smooth? I was looking at TranslateAnimation, but I can't figure out how to use it.
This is my first time writing a game and doing graphics/animation, so I'm not well aware of the best practices or even the tools. I got this far by looking up numerous tutorials.
Yes the doDraw method is called as fast as the processor can handle and operating system allows, which means it is affected by other processes and also means it is using a ton of processor time. As a fairly simple but not recommended solution add Thread.Sleep(50) in your while loop. Look up how to use timers for animation.
My solution was to stop relying on the system time and to use a variable (that is a private member of the thread) called currentTime that gets incremented in either doDraw or updatePhysics. So updatePhysics becomes:
function updatePhysics() {
projectilePosition = interceptionCalculationService.positionAtTime(currentTime);
currentTime++;
}
currentTime is initialized to 0 in the constructor of the thread. This made my animation smooth.
I don't know how this is possible, but a canvas from Activity A is appearing on top of my canvas in activity B. Both activities are always alive (they are in an activity group). How is it even possible that content from a canvas on one activity could be showing on top of my other activity?
I call this when i'm done with either activity A or B, but it obviously isn't working:
void clearPlayerCanvas()
{
runOnUiThread(new Runnable(){
public void run()
{
Canvas canvas = null;
try
{
canvas = holder.lockCanvas();
if (canvas == null)
{
System.out.println("Cannot lock canvas, skipping MJpeg frame");
return;
}
canvas.drawColor(Color.BLACK);
}
finally
{
if (canvas != null)
holder.unlockCanvasAndPost(canvas);
}
}
});
}
This code simply overwrites (its supposed to) the current canvas with black. In any case, i shouldn't even be seeing this black canvas in activity b, but I am. I am also using SurfaceHolder.
You shouldn't have two Activities running at the same time (in fact, I don't think you can).
You didn't stop the thread drawing to your SurfaceView in ActivityA. Threads continue to run even when an Activity pauses, so I assume that was it.