Reusing a bitmap

ukimiku

Active Member
Licensed User
Longtime User
I draw, through canvas c, into a (mutable) bitmap b. It gets displayed in image view i.

Now what I am trying to achieve is to resize bitmap b to get smaller or larger (user choice).

Any combination of commands I have tried has just resulted in another bitmap being created and drawn on top of the existing one. I can't have that as there may be many bitmaps created, which would use up too much device memory.

I have the feeling that the solution may have something to do with "recycling" bitmap b, but the post(s) I read on that topic have been eluding me. Do I have to remove the image view i from the activity? Do I have to set the reference to c, b, or i to "null"?

Thank you.

Regards,
 

Informatix

Expert
Licensed User
Longtime User
I draw, through canvas c, into a (mutable) bitmap b. It gets displayed in image view i.

Now what I am trying to achieve is to resize bitmap b to get smaller or larger (user choice).

Any combination of commands I have tried has just resulted in another bitmap being created and drawn on top of the existing one. I can't have that as there may be many bitmaps created, which would use up too much device memory.

I have the feeling that the solution may have something to do with "recycling" bitmap b, but the post(s) I read on that topic have been eluding me. Do I have to remove the image view i from the activity? Do I have to set the reference to c, b, or i to "null"?

If you want to change the size of your bitmap, you have to create another bitmap. Once you set it as the bitmap of your ImageView, the previous bitmap loses its reference and will be collected by the garbage collector. "Recycle" is a command to speed up the recycling by the GC. Nothing more.

What Google says about "recycle":
This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Thank you. I think I understand now.

I find it a bit odd that the memory management of B4A can dynamically allocate and deallocate memory for data structures, re-dim variables, and so forth, but when it comes to bitmaps, things get (unnecessarily) complicated.

Now I guess I will have to use two bitmap references, the one in use (b) and another one created as soon as the need for resizing arises (b2). But a few seconds later, the user may again choose to resize, and I need to either create a third unique reference to a (new) bitmap, b3, or try to reuse the reference to the old bitmap (b) that had been unhinged from the image view before.

1) (How) can I make sure that I can safely reuse b again?

2) Would it be feasible to store the references to the bitmaps in an array, say bitm() and in case of resizing simply choose the next free index?

Again, thank you.

Regards,
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
I find it a bit odd that the memory management of B4A can dynamically allocate and deallocate memory for data structures, re-dim variables, and so forth, but when it comes to bitmaps, things get (unnecessarily) complicated.
That has nothing to do with B4A. In Java, it's exactly the same problem (and not only under Android OS).

Now I guess I will have to use two bitmap references, the one in use (b) and another one created as soon as the need for resizing arises (b2).

The solution I use:
You declare a bitmap in Globals. This is the source bitmap. The one you fill with LoadBitmap.
Then you create a second bitmap locally. It is the destination bitmap, with the new size. The one you assign to your ImageView.
This way, each ImageView has its own bitmap. But only one (and always the same) bitmap has been used to load the pixel data.
If you assign another bitmap to your ImageView, the previous one is collectible by the GC (it lost its reference).
If you remove the ImageView from your activity, the bitmap is not collectible yet because the ImageView is not destroyed. The IV is still in memory (you can readd it to the Activity) and the bitmap reference still exists. You have to do ImageView.Bitmap = Null to unbind the bitmap.
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Informatix,

thank you for explaining. I will do it the way you explained. One thing is unclear to me, though. You wrote:
But only one (and always the same) bitmap has been used to load the pixel data.
Did you mean to say "But only one (...) image view has been used to load the pixel data"? As I understand your explanation, I use two bitmaps, but only one image view, the one to which the bitmaps are being assigned alternatingly.
Is my understanding correct?

Thank you. You have been most helpful.

Regards,


P.S.
That has nothing to do with B4A. In Java, it's exactly the same problem (and not only under Android OS).
That I find strange, as bitmaps are just another kind of data structure - and B4A can "re-dim" other kinds of data structures. What is it that specifically sets apart bitmaps?
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
Did you mean to say "But only one (...) image view has been used to load the pixel data"? As I understand your explanation, I use two bitmaps, but only one image view, the one to which the bitmaps are being assigned alternatingly.
Is my understanding correct?

I said "bitmap", not "ImageView". If your application has only one ImageView, you have two bitmaps. If your application has three ImageViews, you have four bitmaps (the bitmap used to load data and three bitmaps associated to your ImageViews). An ImageView is a place to display your bitmap. It is not a bitmap itself.
You assign to the ImageView only the final bitmap, the one with the new size.

That I find strange, as bitmaps are just another kind of data structure - and B4A can "re-dim" other kinds of data structures. What is it that specifically sets apart bitmaps?

If the language (Java here) doesn't allow to do it, you can't do it. And that would not reduce the memory usage. I don't know how it's handled by B4A, but most of the time a "Redim" consists in creating another array, bigger, and copying each value from one to the other.
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Thank you for clarifying.

Your ideas got me thinking and finally to solve my problem. I now only need one bitmap (the one I declare globally) which I then have "recycled" (by a forced garbage collection) and subsequently re-used. I'd rather not have two big bitmaps in memory at the same time (source and destination), since the app is supposed use memory very conservatively.

If the language (Java here) doesn't allow to do it, you can't do it. And that would not reduce the memory usage. I don't know how it's handled by B4A, but most of the time a "Redim" consists in creating another array, bigger, and copying each value from one to the other.

Yes, I figured as much. But then there is no reason to exclude bitmaps from this kind of treatment, is there? Why should I as a B4A programmer worry about these things when the programming language could do that better (faster and with more insight)?

Regards,
 
Last edited:
Upvote 0

Informatix

Expert
Licensed User
Longtime User
Thank you for clarifying.

Your ideas got me thinking and finally to solve my problem. I now only need one bitmap (the one I declare globally) which I then have "recycled" (by a forced garbage collection) and subsequently re-used.

Finally, you don't resize anything ? Because I can't see how you can use only one bitmap to do the thing.
And if you recycle a bitmap referenced by an ImageView, you're going to face problems very quickly.

Why should I as a B4A programmer worry about these things when the programming language could do that better (faster and with more insight)?

Why do you think there's a faster or better solution?
 
Last edited:
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Of course, before I can recycle the bitmap in question, I have to "unhinge" (deference) it from all data structures that hold references to it. In my case, these are the image view displaying the bitmap and the canvas used to draw into the bitmap. The image view is then removed from the activity, the canvas set to "null", and then, I think, I can safely recycle the bitmap, as there are no more instances of anything holding references to the bitmap. Then I initialize the bitmap again as a mutable one, with a new size. I imagine that if I didn't reset the references to it, I would indeed run into serious problems quite quickly. So far (after very limited testing, as the solution is only two hours old), everything has been working as expected.

I think that a B4A solution might be better since the compiler has more comprehensive access to JAVA that I don't have (not to mention my lack of the necessary JAVA knowledge). Such a solution could be coded more directly (without the translation layer that transforms B4A code into JAVA code) and thus more efficiently, I reckon. Wouldn't it be quite handy if you had a command like "b.Resize(new_width, new_height)" where you didn't have to worry about all these details? Isn't that what makes B4A stand out as a programming language, its ease of use?

Regards,
 
Last edited:
Upvote 0

thedesolatesoul

Expert
Licensed User
Longtime User
Yes, I figured as much. But then there is no reason to exclude bitmaps from this kind of treatment, is there? Why should I as a B4A programmer worry about these things when the programming language could do that better (faster and with more insight)?
Regards,
Bitmaps are trickier to handle. See here: Displaying Bitmaps Efficiently | Android Developers
It is actually not stored in the DVM heap, but the native OS heap.


Its true that you declare just one Bitmap object and re-use it. Make sure you remove all references from the old bitmap. Whether a forced GC will remove your bitmap immediately or not depends a lot on the OS behaviour, there is no guarantee. You best hope is that the OS removes the bitmap when it falls low on memory.
This thread has a lot of information: http://www.b4x.com/forum/basic4andr...aps-causing-out-memory-error-2.html#post98734
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Hi thedesolatesoul,

thank you for expanding on this. The information you provided was in fact new to me, as it hadn't come up in my forum searches. On my device, the bitmap seems to be collected instantly, at least there are no more "overlays" of bitmaps that I had observed in my previous attempts. Overall, this all seems like a very unsatisfactory situation for programmers who have to resize pictures frequently. I'd rather not "hope" for the gc to do its work, but be able to rely on a system service to do its work when it is being invoked.

Thanks for pointing to the "trickier" aspects of bitmap handling. I will learn from that, too.

Regards,
 
Upvote 0

thedesolatesoul

Expert
Licensed User
Longtime User
On my device, the bitmap seems to be collected instantly, at least there are no more "overlays" of bitmaps that I had observed in my previous attempts. Overall, this all seems like a very unsatisfactory situation for programmers who have to resize pictures frequently. I'd rather not "hope" for the gc to do its work, but be able to rely on a system service to do its work when it is being invoked.
Maybe I was being too pessimistic when i said 'hope'. As long as you give GC the chance it will do the job depending on how frequently you need to resize.
You can use ddms to see how the memory usage changes in real-time.
Using DDMS | Android Developers
Android Developers Blog: Memory Analysis for Android Applications
Using DDMS to Profile Android Apps - android10
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Thank you, also for the link to the monitoring tool.

Do you know how I can determine whether or not there is sufficient free memory available to my app to create a mutable bitmap of size (width, height)? I would rather not have my app crash on picture dimensions that overtax the capabilities of the device.

Can I change the color space of the mutable bitmap to something that uses only 256 colors? Or 65536 colors?

Regards,
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
Can I change the color space of the mutable bitmap to something that uses only 256 colors? Or 65536 colors?
In my Bitmap+ library, you'll find a function named ReduceColor. It removes the alpha channel used for transparency and converts the colors to the RGB565 format (5 bits for red, 6 bits for blue, 5 bits for green, hence the name). Visually, it's difficult to see a difference, but in memory, no problem to see a difference. The size can be halved. But beware: that should be called as the last transformation of the bitmap.
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
Thank you for the tip with bitmap+ library. Is it freely available here somewhere? A search for "bitmap+ library" and for "bitmap+" did not show your library. Is it the same as "BitmapExtended"? Could you please provide a link? Thanks.

So I think that in order to use your lib, I will have to create a normal bitmap (which will take up the normal size) and after that, maybe before saving the bitmap to a file, reduce the colors?

Regards,
 
Last edited:
Upvote 0

thedesolatesoul

Expert
Licensed User
Longtime User
Do you know how I can determine whether or not there is sufficient free memory available to my app to create a mutable bitmap of size (width, height)? I would rather not have my app crash on picture dimensions that overtax the capabilities of the device.
Maybe this library: http://www.b4x.com/forum/additional...tended-safer-loadbitmapsample.html#post109992
However, I prefer to know in advance rather than do this at runtime, because if you are running out of memory what do you do? Probably better to design it so you dont go over (not always possible though).
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
Thank you for the tip with bitmap+ library. Is it freely available here somewhere? A search for "bitmap+ library" and for "bitmap+" did not show your library. Is it the same as "BitmapExtended"? Could you please provide a link? Thanks.

So I think that in order to use your lib, I will have to create a normal bitmap (which will take up the normal size) and after that, maybe before saving the bitmap to a file, reduce the colors?

Regards,

Sorry, here it is. It's a part of the CustomGallery class.
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
I got the bitmap+ library now, thanks.
Regards,
 
Upvote 0

Informatix

Expert
Licensed User
Longtime User
So I think that in order to use your lib, I will have to create a normal bitmap (which will take up the normal size) and after that, maybe before saving the bitmap to a file, reduce the colors?

No, you can also use the ABExtDrawing library to load the bitmap right into the RGB565 format. But most of the transformations you're going to apply on it will convert it to ARGB_8888 again (there's probably a way to avoid this but I don't know it).
 
Upvote 0

ukimiku

Active Member
Licensed User
Longtime User
@thedesolatesoul: I need to determine the memory at runtime, as I have no way of knowing how large the user wants the picture to be. If s/he's got a powerful device, a couple of thousands pixels in each dimension would be quite feasible.

Thank you for the link. I will explore.

Regards,
 
Upvote 0
Top