Android - Update Bitmap from timer thread - android

I got an Android project composed by a single Layout with an ImageView.
public class MainActivity extends AppCompatActivity {
/* original and stretched sized bitmaps */
private Bitmap bitmapOriginal;
private Bitmap bitmapStretched;
/* the only view */
private ImageView iv;
....
}
This ImageView is updated by this runnable function
runnable = new Runnable() {
#Override
public void run() {
iv.setImageBitmap(bitmapStretched);
}
};
and the runnable is ran by a temporized JNI function, running on a background thread, that call it 60 times per second.
public void jniTemporizedCallback(int buf[]) {
/* set data to original sized bitmap */
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
/* calculate the stretched one */
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);
/* tell the main thread to update the image view */
runOnUiThread(runnable);
}
After some frame is drawn, the app crashes with the following message.
A/OpenGLRenderer: Task is already in the queue!
I guess this is because the renderer didn't finish to fully render the previous frame of the ImageView and gets angry.
If i remove runOnUiThread(runnable); the problem disappear (obviously)
How can avoid this? How can i syncronize my application with the openGL renderer?
I also tried to extend ImageView and draw the bitmap on canvas into the onDraw function but i got the same result

I guess you're trying create bitmapOriginal ouside the thread. Therefore, when compiler is trying to call again after 60 seconds, it's getting same objects and couldn't identify the task. I would suggest better as below.
public void jniTemporizedCallback(int buf[]) {
// Initialize
bitmapOriginal = Bitmap.createBitmap(///)
/* set data to original sized bitmap */
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
/* calculate the stretched one */
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);
/* tell the main thread to update the image view */
runOnUiThread(runnable);
}

The proper way to synchronize your drawing logic with the device's frame rate is to use a SurfaceView instead of an ImageView. Instead of pushing frames to the View with your own timer, you should create a rendering Thread that tries to render frames as fast as possible. When you call surfaceHolder.lockCanvas(), the Android system will automatically block until it is time to render the frame. When you unlock the canvas using unlockCanvasAndPost(), the system will draw the buffer to the screen.
See https://developer.android.com/guide/topics/graphics/2d-graphics.html#on-surfaceview for more info. Hope this helps!

Problem was totally unrelated to the Bitmap itself....
It was the real time clock signal that messed with Android RenderThread.
Further explanation here:
Android and JNI real time clock

Provide here purposes of use such method for rendering? What you want to do?, there are great animation functionality in android engine, may be this task can be done with this animation.
One more if you will use codes like yours battery of phone will run to zero very fast coz this will load cpu/gpu to max.
in anyway - try to place blocks from running task, set bool taskRun = true on start and check if (!taskRun){ taskRun = true; //here start your task..} and on ui thread after updating ui you can switch to taskRun = false; Using this you can skip some frames, but should not crash.

The problem is that the Handler of the main thread is keeping a reference to your Runnable. When you want to run your Runnable for the second time, the old Runnable is already in the Message Queue, hence Task is already in the queue message. If you create a Runnable every time u want to execute the Runnable like in the code below, I think the problem will be solved.
public void jniTemporizedCallback(int buf[]) {
/* set data to original sized bitmap */
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
/* calculate the stretched one */
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height, false);
/* tell the main thread to update the image view */
runOnUiThread(new Runnable() {
#Override
public void run() {
iv.setImageBitmap(bitmapStretched);
}
});
}

I think you are right with reason, because you cannot be sure, that Android render images in 60 FPS. And yeah, I think you need just synchronize Bitmap Native Callback with Android Render. So, lets start.
I prefer using Lock from concurrency stack Java. Because you see, when you lock object, and when you unlock. In case of using volatile (for example, sure there also reference restrictions) on Bitmap object, you need to check locking this object in very places, where you using Bitmap.
Also I think you should use Lock from THIS EXAMPLE (to unlock Lock object from any other Thread). So, here is example. Example below will work properly. Just don't forget about Context deleting and stopping task:
public class MainActivity extends AppCompatActivity {
/* Initialize lock (avoid lazy init, with your methods) */
private ReentrantLock lock = new ReentrantLock();
............
private runnableDrawImage = new Runnable() {
#Override
public void run() {
iv.setImageBitmap(bitmapStretched);
lock.unlock();
}
};
..........
public void jniTemporizedCallback(int buf[]) {
/* synchronize by locking state*/
lock.lock();
bitmapOriginal = Bitmap.createBitmap(///)
bitmapOriginal.setPixels(buf, 0, origWidth, 0, 0, origWidth, origHeight);
bitmapStretched = Bitmap.createScaledBitmap(bitmapOriginal, width, height,false);
MainActivity.this.runOnUiThread(runnableDrawImage);
}
}

Related

Flickering while using surface view to draw frame by frame bitmap

I am using surface view to render frames after extracted from video, the problem is that there is a flickering effect while rendering, the origin video was smooth, I play the frames in ImageViewer and hold on press NEXT key to switch next and next it was smooth too, only flicker after render them in SurfaceView.
the problem is I have a period between frames drawing, because I want to control the playing frame rate, make it slower or faster via user's choice, once I give up the delay drawing the problem gone, but that's no my intention, I need to make them delay.
I understand that this is due to double/triple buffering problem, even though I went through many posts, including turn to use GLSurfaceView to render, also drawBitmap twice intent to keep front-buffer and back-buffer align, it doesn't help to fix this problem.
I found this Flickering while using surface view post, and try all the solution-like mention inside, but it's not work, the accepted answer mention about dirty rect, remind me to update every pixels if call lockCanvas() without rect specified, but I think I already draw the whole bitmap in the next, imply I updated every pixels, so I get not idea of this.
below are the code and the problem's gif, please take a look at my code and help me get this fixed.
class CustomView(
context: Context, attrs: AttributeSet?
) : SurfaceView(context, attrs), Runnable {
private var animationThread: Thread? = null
#Volatile private var running = false
private var frameList: List<BitmapFrame1>? = null
private var index = 0
fun start(frameList: List<BitmapFrame1>) {
if (running) return
running = true
index = 0
this.frameList = frameList
animationThread = Thread(this).apply {
start()
}
}
override fun run() {
val surHolder = holder
var nextFrameTimeMs = 0L
while (running) {
if (!surHolder.surface.isValid) continue
if (SystemClock.uptimeMillis() >= nextFrameTimeMs) {
val currentFrame = frameList!!.getOrNull(++index)
if (currentFrame == null) {
running = false
} else {
val canvas = surHolder.lockCanvas()
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, Paint())
val drawTimestamp = SystemClock.uptimeMillis()
surHolder.unlockCanvasAndPost(canvas)
nextFrameTimeMs = drawTimestamp + currentFrame.delayMs
}
} else {
// have tried to draw the current frame again before delay time's up,
// but not effect
val currentFrame = frameList!![index]
val canvas = surHolder.lockCanvas()
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, Paint())
surHolder.unlockCanvasAndPost(canvas)
}
}
}
}
You have to use Choreographer and not lock Canvas in "random" moment (this wrong moment could occurs when Surface is swapping from one buffer to another and the Bitmap/Texture is uploaded on wrong buffer) but only after a Choreographer callback event.
When this event occurs it's guaranteed that the next frame is uploaded on the right buffer and rendered well in next Drawing call.
Choreographer.getInstance().postFrameCallback(new Callback(){
#Override
public doFrame(final long frameTimeNanos) {
surHolder.lockCanvas();
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, paint);
surHolder.unlockCanvasAndPost(canvas);
}
});
So if you want to slow down rendering you need to "queue" requests using an Handler and send a delayed Message to trigger the drawing procedure (below it's pseudo code):
private Handler mHandler;
private final Choreographer.Callback mCallback = new Callback() {
#Override
public doFrame(final long frameTimeNanos) {
surHolder.lockCanvas();
canvas.drawBitmap(currentFrame.bitmap, 0f, 0f, paint);
surHolder.unlockCanvasAndPost(canvas);
}
}
public void prepareHandler() {
mHandler = new Handler(Looper.getMainLooper(), new method() {
#Override
private void handleMessage(#NonNull final Message message) {
switch (message.what) {
case 1234: {
Choreographer.getInstance().postFrameCallback(mCallback);
break;
}
}
}
});
}
private void postponeDraw(final long millis) {
mHandler.sendEmptyMessageDelayed(1234, 500/*milliseconds*/);
}
You may need to implement Choreographer.FrameCallback to synchronize the device's frame rate apart from playing frame rate.
Some good samples are Google's Grafika. Here is a comment from RecordFBOActivity.java:
We use Choreographer so our animation matches vsync, and a separate
rendering thread to keep the heavy lifting off of the UI thread.
Ideally we'd let the render thread receive the Choreographer events
directly, but that appears to be creating a permanent JNI global
reference to the render thread object, preventing it from being
garbage collected (which, in turn, causes the Activity to be
retained). So instead we receive the vsync on the UI thread and
forward it.
They made the RenderThread as a Looper thread. Also prepared a Handler for the thread. Implementing Choreographer.FrameCallback to the SurfaceView, they forward doFrame callback to RenderThread by posting a Handler message.

Draw on a canvas with delay - "make onDraw() slow down"

I use functions for canvas like drawCircle and drawPoint in android.
This works fine.
But the problem now is to draw these different items with a delay, so it looks like an animation.
What kind of mechanism should I use? Have tried with async but I dont like that way of doing it.
Should I use some kind of timer that just draw with an interval or is there other clever ways to do this?
I use this strategy, first I declare a Handler and a Runnable that way:
private final Observable mObservable = new Observable();
private final static int TIME_STEP_MS = 5;
private final Handler mHandler = new Handler();
private final Runnable mTimeManager = new Runnable()
{
public void run()
{
mObservable.notifyObservers(TIME_STEP_MS);
mHandler.postDelayed(mTimeManager, TIME_STEP_MS);
}
};
Then when I want to start my time manager I just call the mTimeManager.run() and it will start to notify my Observer s (previously added) periodically.
If you need for some reason stop the timer or something you just do that:
mHandler.removeCallbacks(mTimeManager);
[ EDIT - More complete code ]
Ok than let's make it clearer, first I made a custom Observable object like that [that's optional]:
private final Observable mObservable = new Observable()
{
public void notifyObservers()
{
setChanged();
super.notifyObservers();
};
#Override
public void notifyObservers(Object data)
{
setChanged();
super.notifyObservers(data);
};
};
the reason for that is just because I can't call setChanged() outside Observable class - it's protected, if it's not changed it doesn't notify any observer.
The other declarations keep the same as shown before, now I need to start this TimeManager somewhere, my app is a LiveWallpaper and I make all rendering stuff into a class that extends a Thread but you don't need that necessarily, I made a method called resumeDrawing(), this one is called right after super.start(); at my #Override of public synchronized void start() from Thread class, the method looks like that:
public void resumeDrawing()
{
if (!mTimeManagerRunning) // just a boolean field in my class
{
System.err.println("Resuming renderer."); // just for debug
mTimeManager.run();
mTimeManagerRunning = true;
}
else
{
System.err.println("Renderer already running."); // just for debug
}
}
and it's dual:
public void pauseDrawing()
{
if (mTimeManagerRunning)
{
System.err.println("Pausing renderer.");
mHandler.removeCallbacks(mTimeManager);
mTimeManagerRunning = false;
}
else
{
System.err.println("Renderer already paused.");
}
}
Ok, now we can start and stop the time manager, but who's listening? Nobody! so let's add'em: On the constructor of my Renderer I add some Observer s to my mObservable object, one of those is the Renderer itself, so my renderer extends Thread and implements Observer:
#Override // from Observer interface
public void update(Observable arg0, Object arg1)
{
mElapsedMsRedraw += (Integer) arg1;
if (mElapsedMsRedraw >= mDrawingMsPerFrame)
{
mElapsedMsRedraw = 0;
drawEm(); // refresh the canvas and stuff
}
}
to add observers you simply do mObservable.addObserver(THE_OBJECT - Implements Observer)
you can see that I don't re-render my stuff each time I'm notified, that's because I use this TimeManager for other thinks than just refresh the Canvas like updating the position of the objects I want to draw just internally.
So, what you need to slow down the drawing is to change the way your objects change internally while the time passes, I mean your circles and points etc, or you can chance your time step, I recommend the first one.
Was it clearer? I hope it helps.
I would use a timer, or create Animations. You can create Animations that will do all sorts of things including changing transparency over time.
Here's the Android Documentation for Animation Resources
I believe there may be sophisticated ways of doing this, but for my needs I used a simple method that has a lot of advantages:
I first create records of coordinates (and any other data needed) for every point of the drawing -- instead of drawing the points on the spot -- and then reproduce them using a timer (Android handler, preferably). This also offers a lot of possibilities while actual drawing: pause, go faster/slower, go backwards, ...
I don't know if this method can be used for complicated drawings, but it is fine for drawing shapes, curves, surfaces, etc.

Android MultiThread game

I am working on a simple android game. I came to a point that I would like to optimize my engine with multithreading. I am working in OpenGL ES, Android 2.2
Now UpdateGame() and RenderScene() run in single thread and are executed in onDrawFrame(GL10 gl)
I have a RenderObject class that has Position, Rotation, Scale, and Color members. All RenderObjects are created when I start my game and are stored in RenderObject array.
In UpdateGame() function I go through RenderObjects in RenderObject array and update new Position, Rotation, Scale and Color.
In RenderScene() function I go through RenderObjects in RenderObject array and render them with new Position, Rotation, Scale and Color.
This works ok in single thread.
So then I tried create a thread for UpdateGame().
So my idea is to:
Update Thread:................Render Thread:
Update Frame 0
Update Frame 1................Render Frame 0....Update and Render work in parallel
Update Frame 2................Render Frame 1
Update Frame 3................Render Frame 2
But first I modified RenderObject class so that it has RenderPosition, RenderRotation, RenderScale and RenderColor members.
These members get copied just before rendering previous frame. So that parallel update thread can modify new Position, Rotation, Scale, Color.
Runnable pRunnable;
Thread pThread;
public int renderframe = 0;
piblic int updateframe = 0;
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
// I create a new thread in this function
pRunnable = new Runnable(){
public void run(){
while(true)
{
while( renderframe < updateframe )
{
// WAITING for render to finish so that data does not get corrupterd
}
//
// I update RenderObject Position, Rotation, Scale, Color members here
UpdateGame();
updateframe++;
}
}
};
pThread = new Thread(pRunnable);
pThread.start();
}
public void onDrawFrame(GL10 gl)
{
while( renderframe == updateframe )
{
// Wait for update to finish
}
for(int a=0;a<MAX_RENDER_OBJECTS;a++)
{
RenderObject ro = aRenderObjects[a];
// I do this in a function
ro.RenderPosition[0] = ro.Position[0];
ro.RenderPosition[1] = ro.Position[1];
ro.RenderPosition[2] = ro.Position[2];
ro.RenderPosition[3] = ro.Position[3];
// I do the same for Rotation, Scale, Color
}
renderframe++;
// When rendering scene I use RenderPosition, RenderRotation members when calling OpenGL API
RenderScene();
}
But when I run my game. The graphics are incorrect. It appears as if data is not in sync.
Any suggestion there on how to appropriately sync RenderObject data between Update and Render threads.
Thank you.
This style seems generally inefficient (spin-waiting loops that will block the two threads from working simultaneously) and dangerous (I don't know how Android implements this, but Java memory model does not guarantee that data from one thread is visible to another thread without going through a synchronized block).
My suggestion is this: the main thread is the rendering thread (GL contexts are thread-local, so you can't use it from another thread (though you can create another context on another thread to perform some data sharing)), and spawns the update thread. The main thread then goes on a loop, waiting on a mutex on every iteration. The update thread updates the game logic, and creates a new object representing everything that has to be drawn (e.g. a list of drawing commands). This object is then put on a queue and the main thread is woken (with notify()). The main thread then gets the drawing data and draws it.
You'll probably want to dismiss older sets of data in case the update loop is much faster than the render loop, but this general idea should get you started.

AndEngine updating screen

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.

Android : Displaying score as a string on canvas is creating a new string per draw command. How do I get around this?

I'm making a game that displays some numbers on a canvas (score, time, etc).
The way that I currently do this is with the drawtext command on a canvas
// score is some int
draw(Canvas c) {
c.drawText(score+"", x, y, paintSyle);
}
I hear that object creation and garbage collection are expensive operations, and I think this is creating a new string every time it is called.
Right now my game with all bitmap drawing and everything jumps around from 25 to 60 fps. I'd like it to stay closer to the higher number and I'm trying to find ways to speed it up.
Would it be faster/better to make(or find?) some mutable subclass of string and work around this problem? Is there another way to solve this issue? Or is this just how it is?
Introduce two new private member variables String renderedScoreString and int rederedScore and rewrite your draw()-method like that:
draw(Canvas c) {
if (this.score != this.renderedScore || this.renderedScoreString == null) {
this.renderedScore = this.score;
this.renderedScoreString = Integer.toString(this.renderedScore);
}
c.drawText(this.renderedScore, x, y, paintStyle);
}
that should save you a lot! of object creations. You could also hide the boilerplate code behind a getter method, e.g. String getScoreString() which does the same, so you don't have it in the draw()-method.
A friend of mine tipped me in on a solution to this problem. When you want to draw something over time, one of the best (and simplest) mechanisms to do so is to split up what you need to do into two completely separate processes.
ie. Only use the draw command exclusively for drawing stuff, keep logic/assignment in Draw() to an absolute minimum.
private final long TIMER_PERIOD = 500;
private String timeString;
private Runnable updateRunnable;
private Handler updateHandler = new Handler();
public void onCreate(Bundle savedInstanceState) {
updateRunnable = new Runnable() {
#Override
public void run() {
timeString = GetTimeString();
updateHandler.postDelayed(updateRunnable, TIMER_PERIOD);
}
}
}
Draw(Canvas c) {
c.drawText(timeString, x, y, paintStyle);
}
In this example the Draw command simply takes timeString in its current state and draws it to the screen. This is highly efficient use of the draw function as it does not require any object creation, and no logic is present that is not immediately required for any drawing to occur. . In the background a Runnable is executing the run() function every 500 miliseconds (approximately). Simply update the Run() function with whatever logic you need to calculate the time (example has a dummy function GetTimeString())
I hope this is helpful.
I know I'm resurrecting a dead thread, but there is one extra optimisation you can add to this which restricts String creation to a one-time thing and thus only triggers the GC once at the start and not during the game (which is quite important for an android game).
Somewhere during the start of your game (onCreate, onResume, as part of a singleton during application startup, etc) create a large String[] which can hold the maximum score (my game fills an array of 10000, so the max score would be 9999). Then loop over it with a for loop, filling each index with a String.valueOf(i).
for (int i = 0; i <scoreStrings.length; i++)
{
scoreStrings[i] = String.valueOf(i);
}
Now, when you need to draw the score, just use the int you use to store the score in as an index to that array, and "hey, presto!", you get the correct string for your score.
canvas.drawText(scoreStrings[score], x, y, paint);

Categories

Resources