Canvas draw : not able to render animation with runtime generated Bitmap images - android

Need a little help here : I'm out of ideas now...
Here's what I need to do :
Render "base" image [ it is created from an ARGB.8888 byte array ]
user clicks 2 points on the screen; I need to perform the the pixel manipulation on a region of pixels around the path taken from one point to another... [ I need to calculate a squre block of pixel for each pixel on the whole path ].
display the modification of the image as the code progresses in animation form.
I am able to display the whole path; I am able to calculate & manipulate the pixels properly .. But what I am unable to do is show the animation as my code is progressing on the path... with the current implementation I am able to display the whole calculated path at the end ...
public void onDraw(Canvas canvas){
canvas.drawColor(Color.WHITE);
Paint p = new Paint();
canvas.drawBitmap(base,0,0,p);
traverse clickEvent1.x -> clickEvent2.x
traverse for clickEvent1.Y -> clickEvent2.Y
{
newBitMap = calculateNewBitMap(base)
// I nee to redraw Canvas with (newBitMap)
// canvas.drawBitMap(newBitMap);
//Doesn't work
//postInvalidate();
//invalidate()
//AnimationDrawable.addFrame(newBitMap)
// I am not calling start here
//but just wanted to let you know that I do call animation start to display the frames stored in it
// animation.start();
}
// obviously wouldn't work here As it is already out of the loop
//invalidate();
}
Please NOTE :
The newBitMap image is generated at runtime, it would not be available to me beforehand...
I tried invalidate() in the loop as well ; but it would only draw the cumulative result after the whole loop has traversed and not the intermediate states of the newBitMaps.
Performance is of critical importance + I am dealing with HUGE image sizes .. so please keep that in mind as well ... if I create multiple bitmaps for temporary storing the JVM crashes due to "OutofMemory" ..
I tried storing the new Images in "AnimationDrawable" form as well; but tht's not solving the problem as well....
AnimationDrawable animDrawable = new AnimationDrawable();
Drawable frame1 = new BitmapDrawable(newCaclBitMap);
animDrawable.addFrame(frame1, 250);
Thanks for any pointers / suggestions ..

It seems like what you are doing is running all of your drawing logic in a single call to onDraw() when I think what you want is to have onDraw() called once for each frame of your animation.
So, instead of something like this in your trace:
onDraw()
drawFrame()
drawFrame()
drawFrame()
...
You would have this:
onDraw()
drawFrame()
onDraw()
drawFrame()
onDraw()
drawFrame()
...
The CubeLiveWallpaper example has an example of this type of thing.

Animation objects are used to animate View objects. What you want to do is animate a canvas. This is more complicated but potentially more powerful. Essentially you need to derive an equation that will govern the movement of you drawing as a function of time. When the animation starts you get the current time stamp and then in you onDraw method you draw what the canvas should look like at that point in time. Basically you have to draw every step.

Related

Android canvas multiple draws causes flickering

the keywords about this topic:
CustomSurfaceView: three custom surfaceview for three different levels.
Canvas: lock/unlockAndPost method (i'm not using custom bitmap )
Multi thread ( each surface is a separate thread )
Shapes ( shapes on canvas )
Client/Server ( architecture )
Flickering ( IS WHY I'M HERE )
We are developing a client/server application and I'm working on the client side. I'm receiving messages from the server containing general data (coordinates, color, width [...] ) about paths like, circle, rectangle, line and other shapes. The web application allows the user to send these data drawing on HTML5 Canvas, to an android device that receiving this messages and parsing it, will be able to redraw all the shapes. From my own experience on this subjects, I learned that the best way to keep in control all the things you draw on the canvas, is saving everything into a buffer, array, list or something like that, then reuse it when you want (for example, you can use the older path for show, hide, move or simply change something on the canvas). In my opinion, the android application follows the best practice of android development and OOP paradigm so I'm not assuming errors related to the bad architecture. In this case, I'm saving the messages on web client side. When the user draws on HTML5 Canvas, the messages which contain shape info are perfectly reported to the android canvas, but the problem appears when:
[example]
Consider you draw 10 objects (10 messages) and you want delete only one object on web app canvas, so the only way is clearing all the canvas, and redraw all the previous shapes without the deleted shape (so resend to the client 9 messages by loop the messages buffer ). This method works perfectly for the web app but cause flickering problem on android client. So after too many experiments I found a workaround, using a Thread.sleep(100)(Whooo! 100ms is too much), in order to parse slowly the messages and let the surfaceview thread to read correctly the data (data access through singleton pattern) and write on the double-buffer of the canvas.Well, it's slow and ugly but it works ! Actually I don't like this “horrible” workaround so please help me to see an exit strategy.
This is a piece of code where the canvas get data from shapes containers and draw if data are present. The data of each containers came from server messages.
#Override
public void run() {
Canvas canvas = null;
while (running) {
//this is the surface's canvas
try {
canvas = shapesSurfaceHolder.lockCanvas();
synchronized (shapesSurfaceHolder) {
if (shapesSurfaceHolder.getSurface().isValid()) {
if(!Parser.cmdClear){
//draw all the data present
canvas.drawPath(PencilData.getInstance().getPencilPath(),
PencilData.getInstance().getPaint());
canvas.drawPath(RectData.getInstance().getRectPath(),
RectData.getInstance().getPaint());
canvas.drawPath(CircleData.getInstance().getCirclePath(),
CircleData.getInstance().getPaint());
canvas.drawPath(LineData.getInstance().getLinePath(),
LineData.getInstance().getPaint());
canvas.drawText(TextData.getInstance().getText(),
TextData.getInstance().getX(),
TextData.getInstance().getY(),
TextData.getInstance().getPaint());
} else {
//remove all canvas content and clear data.
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (int i = 0; i < AbstractFactory.SHAPE_NUM; i++) {
abstracFactory.getShape(i).clearData();
}
}
}
}
} finally {
if (canvas != null) {
shapesSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}//end_run()
I can summarize that, apparently, my problem is to draw too quickly
Note:
Similar concept: Android thread controlling multiple texture views causes strange flickering
Hardware acceleration is enabled.
minSdkVersion 17
Tested on
Tablet Samsung SM-T113
Google Nexus 5
The TextureView issue was due to a bug specific to TextureView. You're using SurfaceView, so that does not apply here.
When drawing on a SurfaceView's Surface, you must update every pixel inside the dirty rect (i.e. the optional arg passed to lockCanvas()) every time. If you don't provide a dirty rect, that means the entire screen must be updated. This is because the Surface is double- or triple-buffered, and swapped when you call unlockCanvasAndPost(). If you lock / clear / unlock, then the next time you lock / draw / unlock, you will not be drawing into the buffer you previously cleared.
If you want to do incremental rendering, you should point your Canvas at an off-screen Bitmap and do all your rendering there. Then just blit the entire bitmap between lock and unlock. The alternative is to store up the drawing commands, starting with the initial clear, and play them all back between lock/unlock.
The phrase "three custom surfaceview" is somewhat concerning if they're all on screen at once. If you have them all at different Z depths (default, media overlay, top) then they will behave correctly, but the system is generally more efficient if you can put everything on one.

Modify bitmap after being drawn but before onDraw() finishes

I'm creating a home made game engine for Android and I'm having some problems with the reuse of a Bitmap canvas:
Visible game objects receive the main Canvas in each frame so they can draw themselves with a static bitmap or with a sprite. So far everything works fine.
The sprite consists in two bitmaps. First contains the complete sequence of drawings, and the second is dynamically drawn through a Canvas(bitmap). The result bitmap and its canvas are created in the sprite constructor to avoid reallocations in the draw loop.
I reuse this sprites for each object that needs the same sequence of drawings. This sprites also have a function that allows to obtain a bitmap containing a different position of the drawings sequence. So, if the object wants the sprite in another position calls its function to get the Bitmap and draws to the main canvas.
So the problem is that all objects using the same sprite end up drawing the same image.
In the visible game object:
if(spriteDelay > 0) img = sprite.getDelayedBitmap(spriteDelay);
else img = sprite.getCurrentBitmap();
canvas.drawBitmap(img, null, focusLocation, null);
In the sprite:
currentBmpCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
currentBmpCanvas.drawBitmap(scaledImage, delayedIni, fin, null);
return currentBmp;
If I create a new result bitmap and a new canvas in the returning function all works fine:
currentBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
currentBmpCanvas = new Canvas(currentBmp);
I assume that all bitmaps are not drawn until onDraw() is finished, so is there some way to solve this without reallocating the bitmap in each loop?
Thanks!
After some hours thinking, I opted to implement a simple solution that didn't cross my mind before.
Like the object game receives the main canvas, why not do the same with the sprite?
So basicly this is the code for the game object:
sprite.smashDelayedCanvas(canvas, focusLocation);
And for the sprite:
canvas.drawBitmap(scaledImage, delayedIni, dest, null);

Speed up bitmap animation in custom View

In my custom view i have 1 animation that i need to run at demand (on tile click). Currently i am using this method:
public boolean onTouchEvent(MotionEvent event) {
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
//check what tile for clicked
getHandler().removeCallbacks(explosionThread);
getHandler().post(explosionThread);
}
}
break;
}
return super.onTouchEvent(event);
}
So i am calling (or sending to view thread, to be specific) a Runnable that calls it self until it comes to an end of an image...
private Runnable explosionThread=new Runnable(){
#Override
public void run() {
invalidate();
if(expCount<15){
getHandler().postDelayed(this, 10);
}
}
};
In my onDraw() method i implemented logic to go threw bitmap and draw it on screen ( using cnavas.drawBitmap(bitmap,srcRect,destRect,paint)....
Now, I want to avoid using SurfaceView (i have only 1 animation and View uses less resources).
I think the animation is slow because onDraw needs to draw whole screen each time invalidate() is called witch can be slow ( drawing 64 tiles with png images). I know that there is an overload of invalidate method, invalidate(Rect dirty) but i don't really know how to use it. If u think that that is the answer please write how to avoid drawing whole onDraw method ( or what method can I overwrite that is used by invalidate(Rect) method, if there is any).
If you have any other better way to speed up animation post it plz.
Thanks in advance....
That's right. One of the way to speed up rendering through canvas is to use invalidate(Rect). Rect passed to invalidate method defines area which will be redrawn. Your onDraw will be called after invalidate with clipping region being set up on canvas. So all your "drawBitmap" will be clipped by the rect.
for running the animation are using a .gif file or you are using a sequence of images run on a thread to show as an animation ?

using canvas.drawBitmap() with canvas.translate()

My code here receives a value for bmpX and bmpY and uses them to draw a bitmap at that location on the screen. This works exactly as I want it to, but I would rather use the canvas.translate() function to handle the movement of the image, rather than just canvas.drawBitmap(), because I will be applying other draw elements to the canvas and I want them all to move the same amount and direction.
My question is: where can I move the canvas.drawBitmap() in the code so that it just draws onto the canvas at the start, but it is moved any time after that using canvas.translate() instead? Anywhere I place the code still freezes the image at that point where I draw it, regardless of how much I change the position of canvas.translate(). I'm imaging its possible to "stick" the image to the canvas, so to speak, and then move the canvas, which also moves the image with it.
public void run() {
// TODO Auto-generated method stub
while(isRunning) {
if(!surfaceHolder.getSurface().isValid()) {
continue;
}
canvas = surfaceHolder.lockCanvas();
canvas.drawRGB(255, 255, 255);
canvas.drawBitmap(bmp, bmpX-(bmp.getWidth()/2),
bmpY-(bmp.getHeight()/2), null);
//canvas.translate(bmpX, bmpY);
surfaceHolder.unlockCanvasAndPost(canvas);
}
}
I hope I explained my problem clearly enough. Thanks!
Answering my own question here for clarity, afetr info I got offline. UI cannot be done on a thread like this, I needed to be using an android UI thread instead. runOnUIUpdatethread and async tasks

Scrolling a Canvas smoothly in Android

I'm new to Android.
I am drawing bitmaps, lines and shapes onto a Canvas inside the OnDraw(Canvas canvas) method of my view. I am looking for help on how to implement smooth scrolling in response to a drag by the user. I have searched but not found any tutorials to help me with this.
The reference for Canvas seems to say that if a Canvas is constructed from a Bitmap (called bmpBuffer, say) then anything drawn on the Canvas is also drawn on bmpBuffer. Would it be possible to use bmpBuffer to implement a scroll ... perhaps copy it back to the Canvas shifted by a few pixels at a time? But if I use Canvas.drawBitmap to draw bmpBuffer back to Canvas shifted by a few pixels, won't bmpBuffer be corrupted? Perhaps, therefore, I should copy bmpBuffer to bmpBuffer2 then draw bmpBuffer2 back to the Canvas.
A more straightforward approach would be to draw the lines, shapes, etc. straight into a buffer Bitmap then draw that buffer (with a shift) onto the Canvas but so far as I can see the various methods: drawLine(), drawShape() and so on are not available for drawing to a Bitmap ... only to a Canvas.
Could I have 2 Canvases? One of which would be constructed from the buffer bitmap and used simply for plotting the lines, shapes, etc. and then the buffer bitmap would be drawn onto the other Canvas for display in the View?
I should welcome any advice!
Answers to similar questions here (and on other websites) refer to "blitting". I understand the concept but can't find anything about "blit" or "bitblt" in the Android documentation. Are Canvas.drawBitmap and Bitmap.Copy Android's equivalents?
I seem to have found an answer. I have put the bulk of the drawing code (which was previously in onDraw()) in a new doDrawing() method. This method starts by creating a new bitmap larger than the screen (large enough to hold the complete drawing). It then creates a second Canvas on which to do the detailed drawing:
BufferBitmap = Bitmap.createBitmap(1000, 1000, Bitmap.Config.ARGB_8888);
Canvas BufferCanvas = new Canvas(BufferBitmap);
The rest of the doDrawing() method is taken up with detailed drawing to BufferCanvas.
The entire onDraw() method now reads as follows:
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(BufferBitmap, (float) -posX, (float) -posY, null);
}
The position variables, posX and posY, are initialised at 0 in the application's onCreate()method. The application implements OnGestureListener and uses the distanceX and distanceY arguments returned in the OnScroll notification to increment posX and posY.
That seems to be about all that's needed to implement smooth scrolling. Or am I over-looking something!?
I had this problem too,
I did the drawing like this:
Canvas BigCanvas = new Canvas();
Bitmap BigBitmap = new Bitmap(width,height);
int ScrollPosX , ScrollPosY // (calculate these with the onScrollEvent handler)
void onCreate()
{
BigCanvas.SetBitmap(BigBitmap);
}
onDraw(Canvas TargetCanvas)
{
// do drawing stuff
// ie. BigCanvas.Draw.... line/bitmap/anything
//draw to the screen with the scrolloffset
//drawBitmap (Bitmap bitmap, Rect src, Rect dst, Paint paint)
TargetCanvas.DrawBitmap(BigBitmap(new Rect(ScrollPosX,ScrollPosY,ScrollPosX + BigBitmap.getWidth(),ScrollPosY + BigBitmap.getHeight(),new Rect(0,0,ScreenWidth,ScreenHeight),null);
}
for smooth scrolling you'd need to make some sort of method that takes a few points after scrolling (i.e the first scroll point and the 10th) , subtract those and scroll by that number in a for each loop that makes it gradually slower ( ScrollAmount - turns - Friction ).
I Hope this gives some more insight.
Continuation of reply to Viktor ...
In fact, the situation is more complicated. Because the doDrawing process is quite slow (taking 2-3 seconds on my slow old HTC Hero phone) I found it desirable to pop up a Toast message to advise the user that it was happening and to indicate the reason. The obvious way to do this was to create a new method containing just 2 lines:
public void redrawBuffer(String strReason) {
Toaster.Toast(strReason, "Short");`
doDrawing();
}
and to call this method from other places in my program instead of doDrawing().
However, I found that the Toaster either never appeared or flashed up so briefly that it could not be read. My workaround has been to use a time check Handler to force the program to sleep for 200 milliseconds between displaying the Toast and calling doDrawing(). Although this slightly delays the start of a redraw I feel this is a price worth paying in terms of the program's usability because the user knows what is going on.
reDrawBuffer() now reads:
public void redrawBuffer(String strReason) {
Toaster.Toast(strReason, "Short");
mTimeCheckHandler.sleep(200);
}`
and the Handler code (which is nested within my View class) is:
private timeCheckHandler mTimeCheckHandler = new timeCheckHandler();
class timeCheckHandler extends Handler {
#Override
public void handleMessage(Message msg) {
doDrawing();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
}`
No need for the activity to be restarted! (Per prepbgg's Jan 27 10 reply to his Jan 17 10 'answer') Rather than recycling the bitmap and incurring the overhead of having the activity reloaded, you can avoid having the application loaded by putting the 'android:configChanges' attribute shown below, in the 'activity' element of the AndroidManifest.xml file for the app. This tells the system the the app will handle orientation changes and that it doesn't need to restart the app.
<activity android:name=".ANote"
android:label="#string/app_name"
android:configChanges="orientation|screenLayout">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
This method can be used to get a notification when the orienation is changed:
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
prt("onConfigurationChanged: "+newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
prt(" PORTRAIT");
} else {
prt(" LANDSCAPE");
}
} // end of onConfigurationChanged
prepbgg: I don't think the code will work because canvas.drawBitmap does not draw into the bitmap but draws the bitmap on-to the canvas.
Correct me if I am wrong!

Categories

Resources