I've spent all night trying to figure out why I can't draw a series of bitmaps completely adjacent to each other (with no gaps in between), using Android.
For context, I am building a spectrogram application which displays a vertical bitmap for each 'window' of audio data that comes in, providing the user with a heatmap of frequencies. At the moment I'm using pre-recorded audio so I can perform all my calculations before I have to display anything - I have an ArrayList of integer arrays, each of which represents one window's bitmap, which is drawn to a canvas using a timer thread.
I am aware that the approach below will ultimately break when the application tries to draw past the dimensions of the screen, but I am not worrying about that for now. The problem I would like to solve is that the below code results in a one-pixel (ish) gap between the drawn bitmaps, when I would actually like them to be absolutely adjacent.
This is the run() method for my timer thread:
public void run() {
Canvas c = null;
try {
c = sh.lockCanvas(null);
synchronized(sh) {
doDraw(c);
}
} finally {
if (c!=null) {
sh.unlockCanvasAndPost(c);
}
}
}
This is the doDraw() method which draws the bitmaps, and then skips along to the end of that drawn bitmap in order to draw the next one. It simply does so by incrementing the 'windowsDrawn' field:
private void doDraw(Canvas canvas) {
canvas.drawBitmap(spec.getBitmapWindow(windowsDrawn), 0, 1, windowsDrawn, 0, 1, h, false, null);
System.out.println("Windows drawn: "+windowsDrawn);
windowsDrawn++;
}
spec.getBitmapWindow(windowsDrawn) simply returns an integer array of pixel values for the vertical window to be drawn.
Here's a screenshot to show what I'm talking about.The image looks as if it is behind tiny prison bars and I would like to get rid of these.
Thanks!
I found out what I was doing wrong. The 'prison bars' effect was actually a side-effect of me writing incremental updates to the back-buffer, which was presumably being flipped every so often (hence losing some of my updates to the other buffer, giving the black vertical lines). The problem was solved by ensuring that I was instead writing to a buffer bitmap and then redrawing the entire frame each time. Incremental updates to the screen (like I was trying to do) are not allowed in Android.
Related
Is LibGDX SpriteBatch draw() smart enough not to redraw 100% exactly same sprite at each render() call?
public void render(float delta) {
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
if (shallFadeOut) { // becomes true when sound message finishes
doFadingOut();
showNextScreen();
dispose();
} else {
batch.begin();
// introSprite is a static image - it never changes a pixel (splash screen)
// I need to paint it once and then just wait until sound message finishes
// DON'T NEED TO REDRAW IT EVERY render() cycle 60 times/sec (~60 fpm)
introSprite.draw(batch);
batch.end();
}
}
In OpenGL, you typically redraw the whole screen on every frame. It doesn’t make sense to wonder if the batch is smart enough to avoid redrawing the same thing, because it has to redraw it to prevent it from disappearing.
If you want to avoid redrawing anything on the screen for a while to save battery on the device, you can use GDX.graphics.setContinuousRendering(false) but that means your render() method will stop getting called so you must set it back to true using a timer or input callback.
You could alternatively use a Boolean to decide whether to clear the screen with glClear and draw stuff, but under the hood, LibGDX will still be requesting OpenGL to copy the screen buffer data between the back buffer and the screen buffer.
Not that drawing a single sprite is super trivial and probably not worth a second of thought about optimizing it.
I am afraid that I do not think any accounting is done by the Batch between batching render steps to see if a texture was already loaded and is ready to be rendered, but the texture itself might.
The batching is intended for cases where you are drawing duplicates of the same thing in the same render step so that you are not unnecessarily context switching between sprites and having to reload data to your graphics card.
The batch is as I am sure you know defined by the begin and end calls you are using, and it depends on you actually rendering all of the sprites of a particular type one after the other without jumping to render something else. This can be seen int he batch draw code here, but for readability the relevant lines are below:
Texture texture = region.texture;
if (texture != lastTexture) {
switchTexture(texture);
} else if (idx == vertices.length) {
flush();
}
To take advantage of the batch - you should be grouping the rendering of all your sprite types, and between steps, the batch does not offer any efficiencies by design as far as I can tell.
But, between batching steps or game loops, when a texture is bound, it uses a fixed target and handle as can be seen here. This means that if all you are drawing is a single sprite for several render loops, libgdx should be using your memory and graphics card as efficiently as can be expected.
I'm trying to write a program that is under 140 characters(Twitter Char Limit). The program displays the final output I want but I don't understand why it's not showing the animation as it creates it. I thought it was because I don't have a draw function but I don't see why that matters if the drawing is all done within a For loop, anyways the draw function didn't help. I've tried bringing the framerate way down but yet for some reason it's only giving me a static final output. Any help is much appreciated.
int j=600, i=j/3;
size(j, j);
smooth();
translate(j/2, j/2);
for (i=1; i<12500; ) {
fill(i%j, i%j);
rotate(j%i*5);
line(i++%j, i++%j, i+++j, int(i/99)%99);
}
Processing uses double buffering, which means that when you draw "to the screen", you're actually drawing to an off-screen buffer. Since your code is not in the draw() function, this happens before the frame is even visible. Then when the frame becomes visible, it takes the entire off-screen buffer, and draws the whole thing to the screen.
That's why you only see the end result of the drawing. If you want to display an animation, you'll have to use the draw() function and timing of some kind (the millis() method, for example).
I'm drawing on a SurfaceView using a thread that has the following calssic run() method:
#Override
public void run() {
while (mRun) {
Canvas canvas = null;
synchronized (mSurfaceHolder) {
try {
canvas = mSurfaceHolder.lockCanvas();
update();
doDraw(canvas);
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
I wanted to save some work if the next frames that are about to be drawn from that moment on, are exactly the same as the previous one, so I skipped the onDraw, but than I got flickering issues due to the double buffering mechanism.
Then I went on and did something like this:
#Override
public void run() {
while (mRun) {
Canvas canvas = null;
synchronized (mSurfaceHolder) {
if (mIsAnimating) { // <---- Look ma! A flag!!!
try {
canvas = mSurfaceHolder.lockCanvas();
update();
doDraw(canvas);
} finally {
if (canvas != null) {
mSurfaceHolder.unlockCanvasAndPost(canvas);
}
}
}
}
}
}
If I turn off the mIsAnimating flag, thus stopping my run loop from locking the canvas and drawing on it, everything worked like a charm - I did some animation and after it was finished, I could freeze the drawing cycle and keep the animation's last frame on screen.
But then I had to go and see the following from Android docs:
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... how come my code works?
Your question is a good one. The SurfaceView is a strange animal.
First, Views, in general, don't use cached drawing by default (as far as I know, that is still true), and SurfaceView doesn't do anything to enable it. So I'm reasonably sure there's no caching going on.
What the SurfaceView is doing is cutting a hole in the view hierarchy's Window so that you can see another Window or something drawn directly on the frame buffer behind it. Android has added a non-existent Porter-Duff mode into their PorterDuff.Mode enum to accomplish this weird behavior. Because of this, the normal drawing rules don't apply to SurfaceView. Invalidating or requesting a layout simply cuts the hole again. The view hierarchy doesn't know or care anything about what's actually drawn in that area of the screen, so unless you specifically draw on it some other way (e.g., using the SurfaceHolder to procure a Canvas for that same area on a separate Window), there's a reasonable chance it just sits there untouched.
So what I really think is happening is that Android simply isn't redrawing that portion of the frame buffer. What was drawn there from the Surface stays there while the rest of the view hierarchy is drawn around it. The resources of the Surface very well could be deallocated and you would never know.
I'm curious what happens if you switch to a different Activity or the home screen and come back? My guess is that your last frame will no longer be there. If it is, then SurfaceView is even stranger than I am aware...
Edit: Just a brief note... a Window is basically a Surface with a little extra functionality. My habit of using the term Window interchangeably with Surface is because a Surface is basically ANativeWindow on the native side of things.
I think that one of two things are happening, first (very unlikely) you might be accidentally drawing the last frame of the animation repeatedly, IF NOT, then it means that your SurfaceView is not being invalidated by any change on the view hence keeping cached the last buffer of pixels showed, in order to test it, set a button on the screen and when the SurfaceView gets "frozen" tap on button and make the button itself gone, so you will force a redraw over all the elements on the screen, if the animation dissapears mean that it was actually being cached...
Regards!
You redraw only when your application surface changes. That works well. But - and that's what the Android document is refering to - any other App, Service, Toast or whatever can overwrite parts of your surface for a while. Take a Toast from a Service as example. Your App and your SurfaceView must update that part of the screen when the Toast disappears. But in your code you do not update as long as your flag is not set.
I have searched for quite a few hours and have not been able to find a concise definite andswer to my question. I have an application where I need to draw a sports field (including all pitch lines) to the screen. So far, I have extended the SurfaceView and pretty much copied the rest of the LunarLander demo as well.
All the data the application requires to draw the pitch to the correct dimensions is being received from a socket which works fine too. However, at the minute in the onDraw() function, I am drawing all lines each frame which is causing a fairly slow framerate in the emulator (e.g. ~10fps). Here is my onDraw() function:
#Override
public void onDraw(Canvas canvas) {
canvas.drawARGB(255,0,144,0);
canvas.drawLine(canvas, getFirstLine(), mPaint);
canvas.drawRect(canvas, getFirstRect(), mPaint);
canvas.drawRect(canvas, getSecondRect(), mPaint);
...
canvas.drawRect(canvas, getSecondRect(), mPaint);
drawAnimatedObjects();
}
I then draw circles and different positions over this background. My question is how do I make this more efficient? Is there a way that I can draw the lines at the application initialisation and not have to redraw them every frame?
Thanks for any help.
You should definitely be caching any canvas drawing which will not change to a bitmap at initialization time and then draw that bitmap in onDraw(). That will help render times a lot. Something like:
Bitmap mField = null;
void init()
{
mField = new Bitmap(...dimensions...);
Canvas c = new Canvas(mField);
c.drawRect(...);
...
}
void onDraw(Canvas c)
{
c.drawBitmap(mField);
}
Is your sports field static or does is scroll over the screen?
If it is static, you should consider to build it once and save it as an image which than will be redrawn each time.
Another thing: The emulator is very slow in comparison with modern devices. You should check your performance at least on a G1 or later to verify the real performance.
I'm writing an android application that draws directly to the canvas on the onDraw event of a View.
I'm drawing something that involves drawing each pixel individually, for this I use something like:
for (int x = 0; x < xMax; x++) {
for (int y = 0; y < yMax; y++){
MyColour = CalculateMyPoint(x, y);
canvas.drawPoint(x, y, MyColour);
}
}
The problem here is that this takes a long time to paint as the CalculateMyPoint routine is quite an expensive method.
Is there a more efficient way of painting to the canvas, for example should I draw to a bitmap and then paint the whole bitmap to the canvas on the onDraw event? Or maybe evaluate my colours and fill in an array that the onDraw method can use to paint the canvas?
Users of my application will be able to change parameters that affect the drawing on the canvas. This is incredibly slow at the moment.
As people have pointed out, if CalculateMyPixel() is expensive, having that called 150,000 (HVGA) or 384,00 times (WVGA) is just going to kill you.
On top of that, trying to draw your UI as individual pixels via canvas.drawPoint() each time there is an update is just about the least efficient way to do this.
If you are drawing individual pixels, you almost certainly want to have some kind of off-screen bitmap containing the pixels, which you draw with a simple Canvas.drawBitmap().
Then you can decide the best way to manage that bitmap. This simplest is to just make a Bitmap object of the desired size and use the APIs there to fill it in.
Alternatively, there is a version of drawBitmap() that takes a raw integer array, so you can fill that in directly with whatever values you want, avoiding a method call for each pixel.
Now you can move your pixel calculation out of the onDraw() method, which needs to be fast to have a responsive UI, and fill your pixels in somewhere else. Maybe you compute them once at initialization time. Maybe you compute them, and do selective updates of only the parts that have changed before calling invalidate() (for example the pixels from (0,0)-(10,l0) have changed so take the current bitmap and just modify that area). If computing pixels is really just intrinsically slow, you will probably want to create a separate thread to do that work in (updating the bitmap with the new pixels, then postInvalidate() on the UI to get them drawn).
Also now that you have your pixels in a bitmap, you can do tricks like make the size of the bitmap smaller and scale it when drawing to the screen, allowing you to take much less time updating those pixels while still filling the entire UI (albeit at a lower resolution).