Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Bitmap Object as a draw buffer

Setup: I have implemented a native (read JNI) mechanism to copy pixels from a Bitmap object, to native memory. This is done by malloc() uint23_t array in native memory and later using memcpy() to copy pixels to/from Bitmap's native pointer. This works well and have been tested. Pixels are successfully saved in native memory from a Bitmap object, and copied back to a Bitmap object, and visible on screen. Its pretty fast in copying, up to order of several milliseconds for fairly large bitmaps. But extremely slow in rendering it.

Intention: The above was done to break free of heap limit on default android Bitmaps (refer to https://stackoverflow.com/a/1949205/1531054). There would be only 1 Java Bitmap object acting as buffer between native memory and target canvas.

Save a shape:

  1. clear Buffer Bitmap.
  2. Draw shape on Bitmap.
  3. Copy pixels to native memory, and save the memory pointer.
  4. Clear Buffer Bitmap.

So, any number of shapes can be saved to native memory, without running into heap size limits. This works.

Later when need to draw a shape (say in onDraw()):

  1. clear Buffer Bitmap.
  2. Copy pixels from native memory, to Buffer Bitmap, using the saved memory pointer.
  3. Draw Buffer Bitmap on canvas.
  4. Clear Buffer Bitmap.
  5. Repeat again for next shape.

Problem When quickly drawing many shapes from memory, The Buffer Bitmap sorts of lags. Basically we're doing

clear bitmap -> load pixels from memory onto it -> draw it on view canvas

in Quick succession inside onDraw(), only the latest shape's pixels are drawn onto canvas. It appears as if:

  1. The internal canvas.drawBitmap() is asynchronous and copies pixels off the bitmap later sometimes.
  2. Android's Bitmaps have some hidden caching mechanism.

Has anyone run into such trouble before ? Or has some insight regarding this ?

I know one can get native skia lib's canvas instance in JNI and draw on it, but this is a non standard way.

like image 681
S.D. Avatar asked Sep 12 '26 19:09

S.D.


2 Answers

In recent Android versions (3.0 and on, which is the majority of devices), pixels use regular Java memory heap. With the introduction of hardware acceleration, bitmaps are drawn asynchronously, and there is a caching system that manages bitmaps loaded as textures to the GPU. Therefore the hack you are trying to do will probably degrade performance on new devices. If you need more memory, try to use largeHeap="true" in your manifest.

like image 81
yoah Avatar answered Sep 14 '26 09:09

yoah


On relatively new Androids (from 3.0 if I recall correctly) with hardware acceleration canvas.drawBitmap method does not actually draws anything (as well as dispatchDraw, draw, and onDraw). Instead, it creates record in display list which:

  1. Might be cached for an indefinite amount of time.
  2. Might (and will be) drawn in the future, not right away. It is not exactly asynchronous as for now, it just executed later in the same thread.

Those two points, I think are the answer to your question.

Alternatively, you can disable hardware acceleration for your view/window and see if your approach is working.

For further reading:

http://android-developers.blogspot.de/2011/03/android-30-hardware-acceleration.html

http://developer.android.com/guide/topics/graphics/hardware-accel.html#model

like image 38
Dmitry Zaytsev Avatar answered Sep 14 '26 07:09

Dmitry Zaytsev