Why doesn't bitmap data from TextureView.getBitmap update in an ImageView? - android

I have a piece of code that displays camera preview in a TextureView. Once in a while (let's say the user presses a button) I obtain a bitmap from the TextureView and set it for display in an ImageView, like this:
private void freezeFrame() {
textureView.getBitmap(freezeBitmap);
imageView.setImageBitmap(freezeBitmap);
}
The freezeBitmap is pre-allocated and reused on multiple calls to the above method.
The problem is that the ImageView keeps showing the state of the bitmap from the first call to freezeFrame(). Calling imageView.invalidate() doesn't help.
The problem disappears when I do one of the following:
set imageView to use a software layer (this leads me to believe the bitmap is correctly obtained from the textureView but not uploaded to the GPU in hardware mode),
create a new bitmap on each call to getBitmap(),
modify the bitmap, e.g. with freezeBitmap.setPixel(0, 0, 0).
So I wonder: why doesn't ImageView "notice" that the bitmap has been updated by TextureView.getBitmap yet it does notice the change performed by setPixel()?
Is there a good way to force the update?

Related

How to rotate a Bitmap image received from the camera intent

I want to rotate a Bitmap image received from the camera intent, using matrix.setRotate() approach. However this method seems to have been removed from the Matrix class. Is there any other methods of being able to rotate a BitMap image?
Update: the setRotate() has not been removed I was looking the wrong class android.opengl.Matrix instead of android.graphics.Matrix.
Since setRotate() has not been removed, use setRotate().

BitmapFun sample has a caching issue

I am using the Android BitmapFun sample code to manage bitmaps in my application. I have been experiencing garbled or duplicated images in a ViewPager. I have tracked this down to the following code in ImageCache.java:
/**
* Notify the removed entry that is no longer being cached
*/
#Override
protected void entryRemoved(boolean evicted, String key,
BitmapDrawable oldValue, BitmapDrawable newValue) {
if (RecyclingBitmapDrawable.class.isInstance(oldValue)) {
// The removed entry is a recycling drawable, so notify it
// that it has been removed from the memory cache
((RecyclingBitmapDrawable) oldValue).setIsCached(false);
} else {
// The removed entry is a standard BitmapDrawable
if (Utils.hasHoneycomb()) {
// We're running on Honeycomb or later, so add the bitmap
// to a SoftRefrence set for possible use with inBitmap later
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
}
}
}
The bitmap is added to the reusable bitmap list when it is removed from the cache. In this case the bitmap is still in use by a ViewPager view. When a later view is created the bitmap (still in use) is reused and the bitmap appears in two positions in the ViewPager.
A bitmap that is removed from the LruCache isn't necessarily available for reuse. I have disabled the reuse of bitmaps in this code and am no longer having an issue. This problem doesn't occur with lower resolution images because the bitmaps aren't removed from the cache while in the range of the ViewPager's offscreen limit. I don't have an issue with 60 DPI images but see this issue frequently at 160 DPI. I think this would show up in the original BitmapFun sample with higher resolution images.
Anyone else experienced this problem or I am not understanding the issue properly?
Kevin
What I think the problem with the code is in the line
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue.getBitmap()));
That line adds a bitmap that was removed from LRU cache to a reusable bitmap set to be used for inBitmap re-use. It doesn't check whether it is still being used by an ImageView or not. If you try to re-use a bitmap that is still being used by an ImageView, the underlying bitmap will be replaced with another bitmap making it not valid anymore. My suggestion is to track whether a bitmap is still being used by an ImageView before adding it to the reusable bitmap set. I've created a sample github project for this issue. Tell me what you think with my solution.

Is it safe to call native function that locks pixels of Bitmap from non UI thread?

I'm implementing list that contains preview of different image filters (grayscale, sepia, etc.)
I want to move image processing out of UI thread, but I'm not sure is it safe. For example, when I'm calling AndroidBitmap_lockPixels for Bitmap of ImageView what will happen if UI thread will try to redraw ImageVIew?
EXAMPLE
public void someMethod(){
ImageView mImageView = /*initialization*/
final Bitmap bmp = ((BitmapDrawable) mImageView.getDrawable()).getBitmap();
new Thread(new Runnable(){
#Override
public void run(){
applyFilter(bmp);
}
}).start();
}
public native void applyFilter(Bitmap bmp);
I haven't tried this case yet; however, you're trying to modify the current being-used Bitmap on ImageView. I think when AndroidBitmap_lockPixels is called, some abnormal will happen, like crash on ImageView while trying to invalidate or something similar to Access Violation.
You can make a copy of the image, process it then apply the new Bitmap to ImageView; it's totally safe.
In that case, there's a chance that the Bitmap is directly referencing something in that ImageView. Otherwise, the Bitmap becomes just another set of 1s and 0s. You might be able to make a copy of the Bitmap and update the ImageView with the new image once you are done. Be careful about images on devices before Honeycomb (3.0) because they handle bitmaps weird, and you have to manage them almost like you would in a C++ program.
It shouldn't, but there's sometimes hidden things in Android, so watch out.
Update: there might be a way to modify the image without copying, but I'm basing this more off general graphics and not Android-specific

Android (Camera) - How to synchronize stopPreview() with onPictureTaken()?

I have an app in which the client uses the camera to take a picture. The preview of the image is being shown in the tablet using a SurfaceView, before the person hits my "click" button. When the person hits the click button, the method onPictureTaken is called, and, in that method, I save the image and also call the camera.stopPreview() method (so the user can see the picture that was taken).
There is an issue, however... If the user is moving around the tablet at the moment that the picture is taken, the still picture actually shown after the stopPreview method is called DOES NOT correspond to the one that I get in the byte array of the onPictureTaken method. There is a delay of some miliseconds there in which make that difference to stand out when the user is moving around the tablet just before the picture is taken (I know that 99% of the people will not move the tablet around while taken the picture, but my client actually noticed this issue and want it fixed...). I have tried to move the save operation to a separete thread, as shown below, so the onPictureTaken method can execute as fast as possible. Still, it had no effect at all...
private PictureCallback pictureCallback = new PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
camera.stopPreview();
reference = data;
new PictureCallbackHeavy().execute();
}
};
I have also trield to call camera.stopPreview() just BEFORE I call the takePicture method (and not inside the onPictureTaken() method). But the result is the same.
What can I do to sync the stopPreview method so I can show EXACTLY the image that was taken and that is in the byte array of the onPictureTaken() callback?
Thank you in advance!! =)
Unfortunately you can't acquire a reasonable good preview image just by calling stopPreview() because between the moment the picture is taken and the moment onPictureTaken() is called there can pass quite some time because it works like this:
The camera actually takes the picture (that's what you want to preview)
onShutter() is called
onPictureTaken() for the raw image data is called (on some devices)
onPictureTaken() for a scaled preview image is called (on some devices)
onPictureTaken() for the final compressed image data is called (the one we are talking about here)
So you have to convert the byte[] data in your onPictureTaken() callback into a Bitmap and map that Bitmap onto an ImageView that you should position above your SurfaceView to show the still preview image.
The code will probably look something like this:
public void onPictureTaken(byte[] data, Camera camera) {
camera.stopPreview();
final Bitmap image = BitmapFactory.decodeByteArray(data, 0, data.length);
surfaceView.setVisibility(SurfaceView.GONE);
imageView.setVisibility(ImageView.VISIBLE);
imageView.setImageBitmap(image);
reference = data;
new PictureCallbackHeavy().execute();
}

SurfaceView getting black when calling Camera.stopPreview()

I am doing some image processing of previews captured by the camera. It is cpu consuming task and I have to stop preview to make it faster. Before new frame is being processed I am calling Camera.stopPreview() and after Camera.startPreview().
However, I would like to have last captured frame displayed on SurfaceView after stopping the preview. It works 'out of the box' on 2.3 devices, however, SurfaceView gets black after calling Camera.stopPreview() on older versions of SDK. Does anyone know what has changed and what to do?
Yes, this was an improvement of the 2.3.
I had this problem in 2.2 as well, there was no way to work on a preview image despite the fact this was theoretically possible according to the API. To solve this I had to actually take a picture using Camera.takePicture(null, null, Camera.PictureCallback myCallback) (see info here) and then to implement a callback to handle the taken picture. The instance of the class that implements this callback is actually the parameter to pass to Camera.takePicture() and the callback method itself looks like this:
public void onPictureTaken(byte[] JPEGData, Camera camera) {
final Bitmap bitmap = createBitmapFromView(JPEGData);
// do something with the Bitmap
}
Doing that way prevents the picture to be saved on the external storage with the regular pictures taken with the camera application. Should you need to serialize the Bitmap you'll have to do it explicitely. But it doesn't prevent the camera's trigger sound from being emitted.
Camera.takePicture() has to be called wile the preview is running. stopPreview() can be called right after.
One thing to be careful with /!\:
Camera.takePicture() is not reentrant (at all). The callback must have returned before any subsequent call of Camera.takePicture(). This was freezing my phone, I had to shutdown and restart it before it to be usable again. As the action was triggered by a button, on my side, I had to shield it with a boolean:
if (!mPictureTaken) {
mPictureTaken = true; // absolutely NOT reentrant. Any double click sticks the phone otherwise.
mCameraView.takePicture(callback);
}

Categories

Resources